mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-01-01 18:23:32 +00:00
Core - Reworked brush/effect property storage
Profile editor - Added brush selection Profile editor - Added playback controls
This commit is contained in:
parent
022beb6a48
commit
1832a25426
@ -94,7 +94,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the general properties of the layer
|
/// Gets the general properties of the layer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyGroupDescription(Name = "General", Description = "A collection of general properties")]
|
[PropertyGroupDescription(Identifier = "General", Name = "General", Description = "A collection of general properties")]
|
||||||
public LayerGeneralProperties General
|
public LayerGeneralProperties General
|
||||||
{
|
{
|
||||||
get => _general;
|
get => _general;
|
||||||
@ -104,7 +104,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the transform properties of the layer
|
/// Gets the transform properties of the layer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyGroupDescription(Name = "Transform", Description = "A collection of transformation properties")]
|
[PropertyGroupDescription(Identifier = "Transform", Name = "Transform", Description = "A collection of transformation properties")]
|
||||||
public LayerTransformProperties Transform
|
public LayerTransformProperties Transform
|
||||||
{
|
{
|
||||||
get => _transform;
|
get => _transform;
|
||||||
@ -208,19 +208,20 @@ namespace Artemis.Core
|
|||||||
LayerBrushStore.LayerBrushRemoved += LayerBrushStoreOnLayerBrushRemoved;
|
LayerBrushStore.LayerBrushRemoved += LayerBrushStoreOnLayerBrushRemoved;
|
||||||
|
|
||||||
// Layers have two hardcoded property groups, instantiate them
|
// Layers have two hardcoded property groups, instantiate them
|
||||||
Attribute generalAttribute = Attribute.GetCustomAttribute(
|
PropertyGroupDescriptionAttribute generalAttribute = (PropertyGroupDescriptionAttribute) Attribute.GetCustomAttribute(
|
||||||
GetType().GetProperty(nameof(General))!,
|
GetType().GetProperty(nameof(General))!,
|
||||||
typeof(PropertyGroupDescriptionAttribute)
|
typeof(PropertyGroupDescriptionAttribute)
|
||||||
)!;
|
)!;
|
||||||
Attribute transformAttribute = Attribute.GetCustomAttribute(
|
PropertyGroupDescriptionAttribute transformAttribute = (PropertyGroupDescriptionAttribute) Attribute.GetCustomAttribute(
|
||||||
GetType().GetProperty(nameof(Transform))!,
|
GetType().GetProperty(nameof(Transform))!,
|
||||||
typeof(PropertyGroupDescriptionAttribute)
|
typeof(PropertyGroupDescriptionAttribute)
|
||||||
)!;
|
)!;
|
||||||
|
|
||||||
General.GroupDescription = (PropertyGroupDescriptionAttribute) generalAttribute;
|
LayerEntity.GeneralPropertyGroup ??= new PropertyGroupEntity {Identifier = generalAttribute.Identifier};
|
||||||
General.Initialize(this, "General.", Constants.CorePluginFeature);
|
LayerEntity.TransformPropertyGroup ??= new PropertyGroupEntity {Identifier = transformAttribute.Identifier};
|
||||||
Transform.GroupDescription = (PropertyGroupDescriptionAttribute) transformAttribute;
|
|
||||||
Transform.Initialize(this, "Transform.", Constants.CorePluginFeature);
|
General.Initialize(this, null, generalAttribute, LayerEntity.GeneralPropertyGroup);
|
||||||
|
Transform.Initialize(this, null, transformAttribute, LayerEntity.TransformPropertyGroup);
|
||||||
|
|
||||||
General.ShapeType.CurrentValueSet += ShapeTypeOnCurrentValueSet;
|
General.ShapeType.CurrentValueSet += ShapeTypeOnCurrentValueSet;
|
||||||
ApplyShapeType();
|
ApplyShapeType();
|
||||||
@ -241,8 +242,7 @@ namespace Artemis.Core
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
LayerBrushReference? current = General.BrushReference.CurrentValue;
|
LayerBrushReference? current = General.BrushReference.CurrentValue;
|
||||||
if (e.Registration.PluginFeature.Id == current?.LayerBrushProviderId &&
|
if (e.Registration.PluginFeature.Id == current?.LayerBrushProviderId && e.Registration.LayerBrushDescriptor.LayerBrushType.Name == current.BrushType)
|
||||||
e.Registration.LayerBrushDescriptor.LayerBrushType.Name == current.BrushType)
|
|
||||||
ActivateLayerBrush();
|
ActivateLayerBrush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +282,13 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
General.ApplyToEntity();
|
General.ApplyToEntity();
|
||||||
Transform.ApplyToEntity();
|
Transform.ApplyToEntity();
|
||||||
LayerBrush?.BaseProperties?.ApplyToEntity();
|
|
||||||
|
// Don't override the old value of LayerBrush if the current value is null, this avoid losing settings of an unavailable brush
|
||||||
|
if (LayerBrush != null)
|
||||||
|
{
|
||||||
|
LayerBrush.Save();
|
||||||
|
LayerEntity.LayerBrush = LayerBrush.LayerBrushEntity;
|
||||||
|
}
|
||||||
|
|
||||||
// LEDs
|
// LEDs
|
||||||
LayerEntity.Leds.Clear();
|
LayerEntity.Leds.Clear();
|
||||||
@ -712,53 +718,32 @@ namespace Artemis.Core
|
|||||||
#region Brush management
|
#region Brush management
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the current layer brush to the brush described in the provided <paramref name="descriptor" />
|
/// Changes the current layer brush to the provided layer brush and activates it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ChangeLayerBrush(LayerBrushDescriptor descriptor)
|
public void ChangeLayerBrush(BaseLayerBrush? layerBrush)
|
||||||
{
|
{
|
||||||
if (descriptor == null)
|
General.BrushReference.SetCurrentValue(layerBrush != null ? new LayerBrushReference(layerBrush.Descriptor) : null, null);
|
||||||
throw new ArgumentNullException(nameof(descriptor));
|
LayerBrush = layerBrush;
|
||||||
|
|
||||||
if (LayerBrush != null)
|
if (LayerBrush != null)
|
||||||
{
|
ActivateLayerBrush();
|
||||||
BaseLayerBrush brush = LayerBrush;
|
else
|
||||||
LayerBrush = null;
|
OnLayerBrushUpdated();
|
||||||
brush.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the brush reference matches the brush
|
|
||||||
LayerBrushReference? current = General.BrushReference.BaseValue;
|
|
||||||
if (!descriptor.MatchesLayerBrushReference(current))
|
|
||||||
General.BrushReference.SetCurrentValue(new LayerBrushReference(descriptor), null);
|
|
||||||
|
|
||||||
ActivateLayerBrush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the current layer brush from the layer
|
|
||||||
/// </summary>
|
|
||||||
public void RemoveLayerBrush()
|
|
||||||
{
|
|
||||||
if (LayerBrush == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
BaseLayerBrush brush = LayerBrush;
|
|
||||||
DeactivateLayerBrush();
|
|
||||||
LayerEntity.PropertyEntities.RemoveAll(p => p.FeatureId == brush.ProviderId && p.Path.StartsWith("LayerBrush."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ActivateLayerBrush()
|
internal void ActivateLayerBrush()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LayerBrushReference? current = General.BrushReference.CurrentValue;
|
if (LayerBrush == null)
|
||||||
if (current == null)
|
{
|
||||||
|
// If the brush is null, try to instantiate it
|
||||||
|
LayerBrushReference? brushReference = General.BrushReference.CurrentValue;
|
||||||
|
if (brushReference?.LayerBrushProviderId != null && brushReference.BrushType != null)
|
||||||
|
ChangeLayerBrush(LayerBrushStore.Get(brushReference.LayerBrushProviderId, brushReference.BrushType)?.LayerBrushDescriptor.CreateInstance(this, LayerEntity.LayerBrush));
|
||||||
|
// If that's not possible there's nothing to do
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
LayerBrushDescriptor? descriptor = current.LayerBrushProviderId != null && current.BrushType != null
|
|
||||||
? LayerBrushStore.Get(current.LayerBrushProviderId, current.BrushType)?.LayerBrushDescriptor
|
|
||||||
: null;
|
|
||||||
descriptor?.CreateInstance(this);
|
|
||||||
|
|
||||||
General.ShapeType.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
General.ShapeType.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
||||||
General.BlendMode.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
General.BlendMode.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
|
||||||
@ -778,9 +763,9 @@ namespace Artemis.Core
|
|||||||
if (LayerBrush == null)
|
if (LayerBrush == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BaseLayerBrush brush = LayerBrush;
|
BaseLayerBrush? brush = LayerBrush;
|
||||||
LayerBrush = null;
|
LayerBrush = null;
|
||||||
brush.Dispose();
|
brush?.Dispose();
|
||||||
|
|
||||||
OnLayerBrushUpdated();
|
OnLayerBrushUpdated();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,11 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class PropertyDescriptionAttribute : Attribute
|
public class PropertyDescriptionAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The identifier of this property used for storage, if not set one will be generated property name in code
|
||||||
|
/// </summary>
|
||||||
|
public string? Identifier { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user-friendly name for this property, shown in the UI
|
/// The user-friendly name for this property, shown in the UI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -8,12 +8,17 @@ namespace Artemis.Core
|
|||||||
public class PropertyGroupDescriptionAttribute : Attribute
|
public class PropertyGroupDescriptionAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user-friendly name for this property, shown in the UI.
|
/// The identifier of this property group used for storage, if not set one will be generated based on the group name in code
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Name { get; set; }
|
public string? Identifier { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user-friendly description for this property, shown in the UI.
|
/// The user-friendly name for this property group, shown in the UI.
|
||||||
|
/// </summary>
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user-friendly description for this property group, shown in the UI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,11 @@ namespace Artemis.Core
|
|||||||
/// Gets the description attribute applied to this property
|
/// Gets the description attribute applied to this property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PropertyDescriptionAttribute PropertyDescription { get; }
|
PropertyDescriptionAttribute PropertyDescription { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the profile element (such as layer or folder) this property is applied to
|
||||||
|
/// </summary>
|
||||||
|
RenderProfileElement ProfileElement { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The parent group of this layer property, set after construction
|
/// The parent group of this layer property, set after construction
|
||||||
@ -43,7 +48,7 @@ namespace Artemis.Core
|
|||||||
public bool DataBindingsSupported { get; }
|
public bool DataBindingsSupported { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the unique path of the property on the layer
|
/// Gets the unique path of the property on the render element
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Path { get; }
|
string Path { get; }
|
||||||
|
|
||||||
@ -56,7 +61,7 @@ namespace Artemis.Core
|
|||||||
/// Indicates whether the BaseValue was loaded from storage, useful to check whether a default value must be applied
|
/// Indicates whether the BaseValue was loaded from storage, useful to check whether a default value must be applied
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsLoadedFromStorage { get; }
|
bool IsLoadedFromStorage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the layer property
|
/// Initializes the layer property
|
||||||
/// <para>
|
/// <para>
|
||||||
@ -64,7 +69,7 @@ namespace Artemis.Core
|
|||||||
/// <see cref="LayerProperty{T}" />
|
/// <see cref="LayerProperty{T}" />
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description, string path);
|
void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to load and add the provided keyframe entity to the layer property
|
/// Attempts to load and add the provided keyframe entity to the layer property
|
||||||
|
|||||||
@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
@ -27,10 +28,10 @@ namespace Artemis.Core
|
|||||||
// These are set right after construction to keep the constructor (and inherited constructs) clean
|
// These are set right after construction to keep the constructor (and inherited constructs) clean
|
||||||
ProfileElement = null!;
|
ProfileElement = null!;
|
||||||
LayerPropertyGroup = null!;
|
LayerPropertyGroup = null!;
|
||||||
Path = null!;
|
|
||||||
Entity = null!;
|
Entity = null!;
|
||||||
PropertyDescription = null!;
|
PropertyDescription = null!;
|
||||||
DataBinding = null!;
|
DataBinding = null!;
|
||||||
|
Path = "";
|
||||||
|
|
||||||
CurrentValue = default!;
|
CurrentValue = default!;
|
||||||
DefaultValue = default!;
|
DefaultValue = default!;
|
||||||
@ -117,13 +118,11 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Gets the profile element (such as layer or folder) this property is applied to
|
public RenderProfileElement ProfileElement { get; private set; }
|
||||||
/// </summary>
|
|
||||||
public RenderProfileElement ProfileElement { get; internal set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public LayerPropertyGroup LayerPropertyGroup { get; internal set; }
|
public LayerPropertyGroup LayerPropertyGroup { get; private set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -457,16 +456,18 @@ namespace Artemis.Core
|
|||||||
internal PropertyEntity Entity { get; set; }
|
internal PropertyEntity Entity { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description, string path)
|
public void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("LayerProperty");
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
|
|
||||||
|
if (description.Identifier == null)
|
||||||
|
throw new ArtemisCoreException("Can't initialize a property group without an identifier");
|
||||||
|
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
|
|
||||||
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
|
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
|
||||||
LayerPropertyGroup = group ?? throw new ArgumentNullException(nameof(group));
|
LayerPropertyGroup = group ?? throw new ArgumentNullException(nameof(group));
|
||||||
Path = path;
|
|
||||||
Entity = entity ?? throw new ArgumentNullException(nameof(entity));
|
Entity = entity ?? throw new ArgumentNullException(nameof(entity));
|
||||||
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
|
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
|
||||||
IsLoadedFromStorage = fromStorage;
|
IsLoadedFromStorage = fromStorage;
|
||||||
@ -475,6 +476,9 @@ namespace Artemis.Core
|
|||||||
if (PropertyDescription.DisableKeyframes)
|
if (PropertyDescription.DisableKeyframes)
|
||||||
KeyframesSupported = false;
|
KeyframesSupported = false;
|
||||||
|
|
||||||
|
// Create the path to this property by walking up the tree
|
||||||
|
Path = LayerPropertyGroup.Path + "." + description.Identifier;
|
||||||
|
|
||||||
OnInitialize();
|
OnInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,9 +31,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
// These are set right after construction to keep the constructor (and inherited constructs) clean
|
// These are set right after construction to keep the constructor (and inherited constructs) clean
|
||||||
GroupDescription = null!;
|
GroupDescription = null!;
|
||||||
Feature = null!;
|
Path = "";
|
||||||
ProfileElement = null!;
|
|
||||||
Path = null!;
|
|
||||||
|
|
||||||
_layerProperties = new List<ILayerProperty>();
|
_layerProperties = new List<ILayerProperty>();
|
||||||
_layerPropertyGroups = new List<LayerPropertyGroup>();
|
_layerPropertyGroups = new List<LayerPropertyGroup>();
|
||||||
@ -42,47 +40,32 @@ namespace Artemis.Core
|
|||||||
LayerPropertyGroups = new ReadOnlyCollection<LayerPropertyGroup>(_layerPropertyGroups);
|
LayerPropertyGroups = new ReadOnlyCollection<LayerPropertyGroup>(_layerPropertyGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the description of this group
|
|
||||||
/// </summary>
|
|
||||||
public PropertyGroupDescriptionAttribute GroupDescription { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the plugin feature this group is associated with
|
|
||||||
/// </summary>
|
|
||||||
public PluginFeature Feature { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the profile element (such as layer or folder) this group is associated with
|
/// Gets the profile element (such as layer or folder) this group is associated with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RenderProfileElement ProfileElement { get; internal set; }
|
public RenderProfileElement ProfileElement { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the description of this group
|
||||||
|
/// </summary>
|
||||||
|
public PropertyGroupDescriptionAttribute GroupDescription { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The parent group of this group
|
/// The parent group of this group
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[LayerPropertyIgnore]
|
[LayerPropertyIgnore] // Ignore the parent when selecting child groups
|
||||||
public LayerPropertyGroup? Parent { get; internal set; }
|
public LayerPropertyGroup? Parent { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The path of this property group
|
/// Gets the unique path of the property on the render element
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Path { get; internal set; }
|
public string Path { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether this property groups properties are all initialized
|
/// Gets whether this property groups properties are all initialized
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool PropertiesInitialized { get; private set; }
|
public bool PropertiesInitialized { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The layer brush this property group belongs to
|
|
||||||
/// </summary>
|
|
||||||
public BaseLayerBrush? LayerBrush { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The layer effect this property group belongs to
|
|
||||||
/// </summary>
|
|
||||||
public BaseLayerEffect? LayerEffect { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether the property is hidden in the UI
|
/// Gets or sets whether the property is hidden in the UI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -96,6 +79,11 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the entity this property group uses for persistent storage
|
||||||
|
/// </summary>
|
||||||
|
public PropertyGroupEntity? PropertyGroupEntity { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of all layer properties in this group
|
/// A list of all layer properties in this group
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -194,17 +182,20 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Initialize(RenderProfileElement profileElement, string path, PluginFeature feature)
|
internal void Initialize(RenderProfileElement profileElement, LayerPropertyGroup? parent, PropertyGroupDescriptionAttribute groupDescription, PropertyGroupEntity? propertyGroupEntity)
|
||||||
{
|
{
|
||||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
if (groupDescription.Identifier == null)
|
||||||
|
throw new ArtemisCoreException("Can't initialize a property group without an identifier");
|
||||||
|
|
||||||
// Doubt this will happen but let's make sure
|
// Doubt this will happen but let's make sure
|
||||||
if (PropertiesInitialized)
|
if (PropertiesInitialized)
|
||||||
throw new ArtemisCoreException("Layer property group already initialized, wut");
|
throw new ArtemisCoreException("Layer property group already initialized, wut");
|
||||||
|
|
||||||
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
|
ProfileElement = profileElement;
|
||||||
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
|
Parent = parent;
|
||||||
Path = path.TrimEnd('.');
|
GroupDescription = groupDescription;
|
||||||
|
PropertyGroupEntity = propertyGroupEntity ?? new PropertyGroupEntity {Identifier = groupDescription.Identifier};
|
||||||
|
Path = parent != null ? parent.Path + "." + groupDescription.Identifier : groupDescription.Identifier;
|
||||||
|
|
||||||
// Get all properties implementing ILayerProperty or LayerPropertyGroup
|
// Get all properties implementing ILayerProperty or LayerPropertyGroup
|
||||||
foreach (PropertyInfo propertyInfo in GetType().GetProperties())
|
foreach (PropertyInfo propertyInfo in GetType().GetProperties())
|
||||||
@ -271,61 +262,68 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription)
|
private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription)
|
||||||
{
|
{
|
||||||
string path = $"{Path}.{propertyInfo.Name}";
|
// Ensure the description has an identifier and name, if not this is a good point to set it based on the property info
|
||||||
|
if (string.IsNullOrWhiteSpace(propertyDescription.Identifier))
|
||||||
if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
|
propertyDescription.Identifier = propertyInfo.Name;
|
||||||
throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path}");
|
|
||||||
|
|
||||||
if (!(Activator.CreateInstance(propertyInfo.PropertyType, true) is ILayerProperty instance))
|
|
||||||
throw new ArtemisPluginException($"Failed to create instance of layer property at {path}");
|
|
||||||
|
|
||||||
// Ensure the description has a name, if not this is a good point to set it based on the property info
|
|
||||||
if (string.IsNullOrWhiteSpace(propertyDescription.Name))
|
if (string.IsNullOrWhiteSpace(propertyDescription.Name))
|
||||||
propertyDescription.Name = propertyInfo.Name.Humanize();
|
propertyDescription.Name = propertyInfo.Name.Humanize();
|
||||||
|
|
||||||
PropertyEntity entity = GetPropertyEntity(ProfileElement, path, out bool fromStorage);
|
if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
|
||||||
instance.Initialize(ProfileElement, this, entity, fromStorage, propertyDescription, path);
|
throw new ArtemisPluginException($"Property with PropertyDescription attribute must be of type ILayerProperty: {propertyDescription.Identifier}");
|
||||||
|
if (Activator.CreateInstance(propertyInfo.PropertyType, true) is not ILayerProperty instance)
|
||||||
|
throw new ArtemisPluginException($"Failed to create instance of layer property: {propertyDescription.Identifier}");
|
||||||
|
|
||||||
|
PropertyEntity entity = GetPropertyEntity(propertyDescription.Identifier, out bool fromStorage);
|
||||||
|
instance.Initialize(ProfileElement, this, entity, fromStorage, propertyDescription);
|
||||||
propertyInfo.SetValue(this, instance);
|
propertyInfo.SetValue(this, instance);
|
||||||
|
|
||||||
_layerProperties.Add(instance);
|
_layerProperties.Add(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeChildGroup(PropertyInfo propertyInfo, PropertyGroupDescriptionAttribute propertyGroupDescription)
|
private void InitializeChildGroup(PropertyInfo propertyInfo, PropertyGroupDescriptionAttribute propertyGroupDescription)
|
||||||
{
|
{
|
||||||
string path = Path + ".";
|
// Ensure the description has an identifier and name name, if not this is a good point to set it based on the property info
|
||||||
|
if (string.IsNullOrWhiteSpace(propertyGroupDescription.Identifier))
|
||||||
if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
|
propertyGroupDescription.Identifier = propertyInfo.Name;
|
||||||
throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup");
|
|
||||||
|
|
||||||
if (!(Activator.CreateInstance(propertyInfo.PropertyType) is LayerPropertyGroup instance))
|
|
||||||
throw new ArtemisPluginException($"Failed to create instance of layer property group at {path + propertyInfo.Name}");
|
|
||||||
|
|
||||||
// Ensure the description has a name, if not this is a good point to set it based on the property info
|
|
||||||
if (string.IsNullOrWhiteSpace(propertyGroupDescription.Name))
|
if (string.IsNullOrWhiteSpace(propertyGroupDescription.Name))
|
||||||
propertyGroupDescription.Name = propertyInfo.Name.Humanize();
|
propertyGroupDescription.Name = propertyInfo.Name.Humanize();
|
||||||
|
|
||||||
instance.Parent = this;
|
if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
|
||||||
instance.GroupDescription = propertyGroupDescription;
|
throw new ArtemisPluginException($"Property with PropertyGroupDescription attribute must be of type LayerPropertyGroup: {propertyGroupDescription.Identifier}");
|
||||||
instance.LayerBrush = LayerBrush;
|
if (!(Activator.CreateInstance(propertyInfo.PropertyType) is LayerPropertyGroup instance))
|
||||||
instance.LayerEffect = LayerEffect;
|
throw new ArtemisPluginException($"Failed to create instance of layer property group: {propertyGroupDescription.Identifier}");
|
||||||
instance.Initialize(ProfileElement, $"{path}{propertyInfo.Name}.", Feature);
|
|
||||||
|
PropertyGroupEntity entity = GetPropertyGroupEntity(propertyGroupDescription.Identifier);
|
||||||
|
instance.Initialize(ProfileElement, this, propertyGroupDescription, entity);
|
||||||
|
|
||||||
propertyInfo.SetValue(this, instance);
|
propertyInfo.SetValue(this, instance);
|
||||||
_layerPropertyGroups.Add(instance);
|
_layerPropertyGroups.Add(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PropertyEntity GetPropertyEntity(RenderProfileElement profileElement, string path, out bool fromStorage)
|
private PropertyEntity GetPropertyEntity(string identifier, out bool fromStorage)
|
||||||
{
|
{
|
||||||
PropertyEntity? entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.FeatureId == Feature.Id && p.Path == path);
|
if (PropertyGroupEntity == null)
|
||||||
|
throw new ArtemisCoreException($"Can't execute {nameof(GetPropertyEntity)} without {nameof(PropertyGroupEntity)} being setup");
|
||||||
|
|
||||||
|
PropertyEntity? entity = PropertyGroupEntity.Properties.FirstOrDefault(p => p.Identifier == identifier);
|
||||||
fromStorage = entity != null;
|
fromStorage = entity != null;
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
{
|
{
|
||||||
entity = new PropertyEntity {FeatureId = Feature.Id, Path = path};
|
entity = new PropertyEntity {Identifier = identifier};
|
||||||
profileElement.RenderElementEntity.PropertyEntities.Add(entity);
|
PropertyGroupEntity.Properties.Add(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PropertyGroupEntity GetPropertyGroupEntity(string identifier)
|
||||||
|
{
|
||||||
|
if (PropertyGroupEntity == null)
|
||||||
|
throw new ArtemisCoreException($"Can't execute {nameof(GetPropertyGroupEntity)} without {nameof(PropertyGroupEntity)} being setup");
|
||||||
|
|
||||||
|
return PropertyGroupEntity.PropertyGroups.FirstOrDefault(g => g.Identifier == identifier) ?? new PropertyGroupEntity() {Identifier = identifier};
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -97,20 +97,10 @@ namespace Artemis.Core
|
|||||||
internal void SaveRenderElement()
|
internal void SaveRenderElement()
|
||||||
{
|
{
|
||||||
RenderElementEntity.LayerEffects.Clear();
|
RenderElementEntity.LayerEffects.Clear();
|
||||||
foreach (BaseLayerEffect layerEffect in LayerEffects)
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||||
{
|
{
|
||||||
LayerEffectEntity layerEffectEntity = new()
|
baseLayerEffect.Save();
|
||||||
{
|
RenderElementEntity.LayerEffects.Add(baseLayerEffect.LayerEffectEntity);
|
||||||
Id = layerEffect.EntityId,
|
|
||||||
ProviderId = layerEffect.Descriptor?.PlaceholderFor ?? layerEffect.ProviderId,
|
|
||||||
EffectType = layerEffect.GetEffectTypeName(),
|
|
||||||
Name = layerEffect.Name,
|
|
||||||
Suspended = layerEffect.Suspended,
|
|
||||||
HasBeenRenamed = layerEffect.HasBeenRenamed,
|
|
||||||
Order = layerEffect.Order
|
|
||||||
};
|
|
||||||
RenderElementEntity.LayerEffects.Add(layerEffectEntity);
|
|
||||||
layerEffect.BaseProperties?.ApplyToEntity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Condition
|
// Condition
|
||||||
@ -310,7 +300,7 @@ namespace Artemis.Core
|
|||||||
foreach (LayerEffectEntity layerEffectEntity in RenderElementEntity.LayerEffects)
|
foreach (LayerEffectEntity layerEffectEntity in RenderElementEntity.LayerEffects)
|
||||||
{
|
{
|
||||||
// If there is a non-placeholder existing effect, skip this entity
|
// If there is a non-placeholder existing effect, skip this entity
|
||||||
BaseLayerEffect? existing = LayerEffectsList.FirstOrDefault(e => e.EntityId == layerEffectEntity.Id);
|
BaseLayerEffect? existing = LayerEffectsList.FirstOrDefault(e => e.LayerEffectEntity.Id == layerEffectEntity.Id);
|
||||||
if (existing != null && existing.Descriptor.PlaceholderFor == null)
|
if (existing != null && existing.Descriptor.PlaceholderFor == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -351,7 +341,7 @@ namespace Artemis.Core
|
|||||||
List<BaseLayerEffect> pluginEffects = LayerEffectsList.Where(ef => ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
|
List<BaseLayerEffect> pluginEffects = LayerEffectsList.Where(ef => ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
|
||||||
foreach (BaseLayerEffect pluginEffect in pluginEffects)
|
foreach (BaseLayerEffect pluginEffect in pluginEffects)
|
||||||
{
|
{
|
||||||
LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.EntityId);
|
LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.LayerEffectEntity.Id);
|
||||||
LayerEffectsList.Remove(pluginEffect);
|
LayerEffectsList.Remove(pluginEffect);
|
||||||
pluginEffect.Dispose();
|
pluginEffect.Dispose();
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core.LayerBrushes
|
namespace Artemis.Core.LayerBrushes
|
||||||
@ -24,6 +25,7 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
// Both are set right after construction to keep the constructor of inherited classes clean
|
// Both are set right after construction to keep the constructor of inherited classes clean
|
||||||
_layer = null!;
|
_layer = null!;
|
||||||
_descriptor = null!;
|
_descriptor = null!;
|
||||||
|
LayerBrushEntity = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -35,6 +37,11 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
internal set => SetAndNotify(ref _layer, value);
|
internal set => SetAndNotify(ref _layer, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the brush entity this brush uses for persistent storage
|
||||||
|
/// </summary>
|
||||||
|
public LayerBrushEntity LayerBrushEntity { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the descriptor of this brush
|
/// Gets the descriptor of this brush
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -185,6 +192,13 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
public override string BrokenDisplayName => Descriptor.DisplayName;
|
public override string BrokenDisplayName => Descriptor.DisplayName;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
internal void Save()
|
||||||
|
{
|
||||||
|
// No need to update the type or provider ID, they're set once by the LayerBrushDescriptors CreateInstance and can't change
|
||||||
|
BaseProperties?.ApplyToEntity();
|
||||||
|
LayerBrushEntity.PropertyGroup = BaseProperties?.PropertyGroupEntity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
|
||||||
namespace Artemis.Core.LayerBrushes
|
namespace Artemis.Core.LayerBrushes
|
||||||
{
|
{
|
||||||
@ -32,12 +33,11 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
internal set => _properties = value;
|
internal set => _properties = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InitializeProperties()
|
internal void InitializeProperties(PropertyGroupEntity? propertyGroupEntity)
|
||||||
{
|
{
|
||||||
Properties = Activator.CreateInstance<T>();
|
Properties = Activator.CreateInstance<T>();
|
||||||
Properties.GroupDescription = new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description};
|
PropertyGroupDescriptionAttribute groupDescription = new() {Identifier = "Brush", Name = Descriptor.DisplayName, Description = Descriptor.Description};
|
||||||
Properties.LayerBrush = this;
|
Properties.Initialize(Layer, null, groupDescription, propertyGroupEntity);
|
||||||
Properties.Initialize(Layer, "LayerBrush.", Descriptor.Provider);
|
|
||||||
PropertiesInitialized = true;
|
PropertiesInitialized = true;
|
||||||
|
|
||||||
EnableLayerBrush();
|
EnableLayerBrush();
|
||||||
|
|||||||
@ -33,7 +33,7 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
|
|
||||||
internal override void Initialize()
|
internal override void Initialize()
|
||||||
{
|
{
|
||||||
TryOrBreak(InitializeProperties, "Failed to initialize");
|
TryOrBreak(() => InitializeProperties(Layer.LayerEntity.LayerBrush?.PropertyGroup), "Failed to initialize");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
|
||||||
namespace Artemis.Core.LayerBrushes
|
namespace Artemis.Core.LayerBrushes
|
||||||
@ -57,23 +58,20 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an instance of the described brush and applies it to the layer
|
/// Creates an instance of the described brush and applies it to the layer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal void CreateInstance(Layer layer)
|
public BaseLayerBrush CreateInstance(Layer layer, LayerBrushEntity? entity)
|
||||||
{
|
{
|
||||||
if (layer == null) throw new ArgumentNullException(nameof(layer));
|
if (layer == null)
|
||||||
if (layer.LayerBrush != null)
|
throw new ArgumentNullException(nameof(layer));
|
||||||
throw new ArtemisCoreException("Layer already has an instantiated layer brush");
|
|
||||||
|
|
||||||
BaseLayerBrush brush = (BaseLayerBrush) Provider.Plugin.Kernel!.Get(LayerBrushType);
|
BaseLayerBrush brush = (BaseLayerBrush) Provider.Plugin.Kernel!.Get(LayerBrushType);
|
||||||
brush.Layer = layer;
|
brush.Layer = layer;
|
||||||
brush.Descriptor = this;
|
brush.Descriptor = this;
|
||||||
|
brush.LayerBrushEntity = entity ?? new LayerBrushEntity { ProviderId = Provider.Id, BrushType = LayerBrushType.FullName };
|
||||||
|
|
||||||
brush.Initialize();
|
brush.Initialize();
|
||||||
brush.Update(0);
|
brush.Update(0);
|
||||||
|
|
||||||
layer.LayerBrush = brush;
|
return brush;
|
||||||
layer.OnLayerBrushUpdated();
|
|
||||||
|
|
||||||
if (layer.ShouldBeEnabled)
|
|
||||||
brush.InternalEnable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
|
|
||||||
internal override void Initialize()
|
internal override void Initialize()
|
||||||
{
|
{
|
||||||
TryOrBreak(InitializeProperties, "Failed to initialize");
|
TryOrBreak(() => InitializeProperties(Layer.LayerEntity.LayerBrush?.PropertyGroup), "Failed to initialize");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core.LayerEffects
|
namespace Artemis.Core.LayerEffects
|
||||||
@ -24,16 +25,13 @@ namespace Artemis.Core.LayerEffects
|
|||||||
_profileElement = null!;
|
_profileElement = null!;
|
||||||
_descriptor = null!;
|
_descriptor = null!;
|
||||||
_name = null!;
|
_name = null!;
|
||||||
|
LayerEffectEntity = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the unique ID of this effect
|
/// Gets the
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid EntityId
|
public LayerEffectEntity LayerEffectEntity { get; internal set; }
|
||||||
{
|
|
||||||
get => _entityId;
|
|
||||||
internal set => SetAndNotify(ref _entityId, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the profile element (such as layer or folder) this effect is applied to
|
/// Gets the profile element (such as layer or folder) this effect is applied to
|
||||||
@ -109,8 +107,6 @@ namespace Artemis.Core.LayerEffects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual LayerPropertyGroup? BaseProperties => null;
|
public virtual LayerPropertyGroup? BaseProperties => null;
|
||||||
|
|
||||||
internal string PropertyRootPath => $"LayerEffect.{EntityId}.{GetType().Name}.";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether the layer effect is enabled or not
|
/// Gets a boolean indicating whether the layer effect is enabled or not
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -227,5 +223,17 @@ namespace Artemis.Core.LayerEffects
|
|||||||
public override string BrokenDisplayName => Name;
|
public override string BrokenDisplayName => Name;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
// No need to update the ID, type and provider ID. They're set once by the LayerBrushDescriptors CreateInstance and can't change
|
||||||
|
LayerEffectEntity.Name = Name;
|
||||||
|
LayerEffectEntity.Suspended = Suspended;
|
||||||
|
LayerEffectEntity.HasBeenRenamed = HasBeenRenamed;
|
||||||
|
LayerEffectEntity.Order = Order;
|
||||||
|
|
||||||
|
BaseProperties?.ApplyToEntity();
|
||||||
|
LayerEffectEntity.PropertyGroup = BaseProperties?.PropertyGroupEntity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,8 +36,7 @@ namespace Artemis.Core.LayerEffects
|
|||||||
internal void InitializeProperties()
|
internal void InitializeProperties()
|
||||||
{
|
{
|
||||||
Properties = Activator.CreateInstance<T>();
|
Properties = Activator.CreateInstance<T>();
|
||||||
Properties.LayerEffect = this;
|
Properties.Initialize(ProfileElement, null, new PropertyGroupDescriptionAttribute(){Identifier = "LayerEffect"}, LayerEffectEntity.PropertyGroup);
|
||||||
Properties.Initialize(ProfileElement, PropertyRootPath, Descriptor.Provider);
|
|
||||||
PropertiesInitialized = true;
|
PropertiesInitialized = true;
|
||||||
|
|
||||||
EnableLayerEffect();
|
EnableLayerEffect();
|
||||||
|
|||||||
@ -54,11 +54,28 @@ namespace Artemis.Core.LayerEffects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an instance of the described effect and applies it to the render element
|
/// Creates an instance of the described effect and applies it to the render element
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal void CreateInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
|
internal void CreateInstance(RenderProfileElement renderElement, LayerEffectEntity? entity)
|
||||||
{
|
{
|
||||||
// Skip effects already on the element
|
if (LayerEffectType == null)
|
||||||
if (renderElement.LayerEffects.Any(e => e.EntityId == entity.Id))
|
throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its LayerEffectType");
|
||||||
return;
|
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
entity = new LayerEffectEntity
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Suspended = false,
|
||||||
|
Order = renderElement.LayerEffects.Count + 1,
|
||||||
|
ProviderId = Provider.Id,
|
||||||
|
EffectType = LayerEffectType.FullName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Skip effects already on the element
|
||||||
|
if (renderElement.LayerEffects.Any(e => e.LayerEffectEntity.Id == entity.Id))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (PlaceholderFor != null)
|
if (PlaceholderFor != null)
|
||||||
{
|
{
|
||||||
@ -66,12 +83,9 @@ namespace Artemis.Core.LayerEffects
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LayerEffectType == null)
|
|
||||||
throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its LayerEffectType");
|
|
||||||
|
|
||||||
BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType);
|
BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType);
|
||||||
effect.ProfileElement = renderElement;
|
effect.ProfileElement = renderElement;
|
||||||
effect.EntityId = entity.Id;
|
effect.LayerEffectEntity = entity;
|
||||||
effect.Order = entity.Order;
|
effect.Order = entity.Order;
|
||||||
effect.Name = entity.Name;
|
effect.Name = entity.Name;
|
||||||
effect.Suspended = entity.Suspended;
|
effect.Suspended = entity.Suspended;
|
||||||
|
|||||||
@ -13,7 +13,7 @@ namespace Artemis.Core.LayerEffects.Placeholder
|
|||||||
OriginalEntity = originalEntity;
|
OriginalEntity = originalEntity;
|
||||||
PlaceholderFor = placeholderFor;
|
PlaceholderFor = placeholderFor;
|
||||||
|
|
||||||
EntityId = OriginalEntity.Id;
|
LayerEffectEntity = originalEntity;
|
||||||
Order = OriginalEntity.Order;
|
Order = OriginalEntity.Order;
|
||||||
Name = OriginalEntity.Name;
|
Name = OriginalEntity.Name;
|
||||||
Suspended = OriginalEntity.Suspended;
|
Suspended = OriginalEntity.Suspended;
|
||||||
|
|||||||
11
src/Artemis.Storage/Entities/Profile/LayerBrushEntity.cs
Normal file
11
src/Artemis.Storage/Entities/Profile/LayerBrushEntity.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Entities.Profile;
|
||||||
|
|
||||||
|
public class LayerBrushEntity
|
||||||
|
{
|
||||||
|
public string ProviderId { get; set; }
|
||||||
|
public string BrushType { get; set; }
|
||||||
|
|
||||||
|
public PropertyGroupEntity PropertyGroup { get; set; }
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile
|
namespace Artemis.Storage.Entities.Profile
|
||||||
{
|
{
|
||||||
@ -11,5 +12,7 @@ namespace Artemis.Storage.Entities.Profile
|
|||||||
public bool Suspended { get; set; }
|
public bool Suspended { get; set; }
|
||||||
public bool HasBeenRenamed { get; set; }
|
public bool HasBeenRenamed { get; set; }
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
public PropertyGroupEntity PropertyGroup { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,9 +24,12 @@ namespace Artemis.Storage.Entities.Profile
|
|||||||
public List<LedEntity> Leds { get; set; }
|
public List<LedEntity> Leds { get; set; }
|
||||||
public List<IAdaptionHintEntity> AdaptionHints { get; set; }
|
public List<IAdaptionHintEntity> AdaptionHints { get; set; }
|
||||||
|
|
||||||
|
public PropertyGroupEntity GeneralPropertyGroup { get; set; }
|
||||||
|
public PropertyGroupEntity TransformPropertyGroup { get; set; }
|
||||||
|
public LayerBrushEntity LayerBrush { get; set; }
|
||||||
|
|
||||||
[BsonRef("ProfileEntity")]
|
[BsonRef("ProfileEntity")]
|
||||||
public ProfileEntity Profile { get; set; }
|
public ProfileEntity Profile { get; set; }
|
||||||
|
|
||||||
public Guid ProfileId { get; set; }
|
public Guid ProfileId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,22 +1,14 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile
|
namespace Artemis.Storage.Entities.Profile;
|
||||||
|
|
||||||
|
public class PropertyEntity
|
||||||
{
|
{
|
||||||
public class PropertyEntity
|
public string Identifier { get; set; }
|
||||||
{
|
public string Value { get; set; }
|
||||||
public PropertyEntity()
|
public bool KeyframesEnabled { get; set; }
|
||||||
{
|
|
||||||
KeyframeEntities = new List<KeyframeEntity>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FeatureId { get; set; }
|
public DataBindingEntity DataBinding { get; set; }
|
||||||
public string Path { get; set; }
|
public List<KeyframeEntity> KeyframeEntities { get; set; } = new();
|
||||||
public DataBindingEntity DataBinding { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
public bool KeyframesEnabled { get; set; }
|
|
||||||
|
|
||||||
public List<KeyframeEntity> KeyframeEntities { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
10
src/Artemis.Storage/Entities/Profile/PropertyGroupEntity.cs
Normal file
10
src/Artemis.Storage/Entities/Profile/PropertyGroupEntity.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Entities.Profile;
|
||||||
|
|
||||||
|
public class PropertyGroupEntity
|
||||||
|
{
|
||||||
|
public string Identifier { get; set; }
|
||||||
|
public List<PropertyEntity> Properties { get; set; } = new();
|
||||||
|
public List<PropertyGroupEntity> PropertyGroups { get; set; } = new();
|
||||||
|
}
|
||||||
@ -49,13 +49,13 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
|||||||
{
|
{
|
||||||
if (LayerProperty.ProfileElement is Layer layer)
|
if (LayerProperty.ProfileElement is Layer layer)
|
||||||
{
|
{
|
||||||
layer.ChangeLayerBrush(SelectedDescriptor);
|
// layer.ChangeLayerBrush(SelectedDescriptor);
|
||||||
if (layer.LayerBrush?.Presets != null && layer.LayerBrush.Presets.Any())
|
// if (layer.LayerBrush?.Presets != null && layer.LayerBrush.Presets.Any())
|
||||||
Execute.PostToUIThread(async () =>
|
// Execute.PostToUIThread(async () =>
|
||||||
{
|
// {
|
||||||
await Task.Delay(400);
|
// await Task.Delay(400);
|
||||||
await _dialogService.ShowDialogAt<LayerBrushPresetViewModel>("LayerProperties", new Dictionary<string, object> {{"layerBrush", layer.LayerBrush}});
|
// await _dialogService.ShowDialogAt<LayerBrushPresetViewModel>("LayerProperties", new Dictionary<string, object> {{"layerBrush", layer.LayerBrush}});
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -332,19 +332,19 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
|||||||
if (SelectedProfileElement == null)
|
if (SelectedProfileElement == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove VMs of effects no longer applied on the layer
|
// // Remove VMs of effects no longer applied on the layer
|
||||||
List<LayerPropertyGroupViewModel> toRemove = Items
|
// List<LayerPropertyGroupViewModel> toRemove = Items
|
||||||
.Where(l => l.LayerPropertyGroup.LayerEffect != null && !SelectedProfileElement.LayerEffects.Contains(l.LayerPropertyGroup.LayerEffect))
|
// .Where(l => l.LayerPropertyGroup.LayerEffect != null && !SelectedProfileElement.LayerEffects.Contains(l.LayerPropertyGroup.LayerEffect))
|
||||||
.ToList();
|
// .ToList();
|
||||||
Items.RemoveRange(toRemove);
|
// Items.RemoveRange(toRemove);
|
||||||
|
//
|
||||||
foreach (BaseLayerEffect layerEffect in SelectedProfileElement.LayerEffects)
|
// foreach (BaseLayerEffect layerEffect in SelectedProfileElement.LayerEffects)
|
||||||
{
|
// {
|
||||||
if (Items.Any(l => l.LayerPropertyGroup.LayerEffect == layerEffect) || layerEffect.BaseProperties == null)
|
// if (Items.Any(l => l.LayerPropertyGroup.LayerEffect == layerEffect) || layerEffect.BaseProperties == null)
|
||||||
continue;
|
// continue;
|
||||||
|
//
|
||||||
Items.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerEffect.BaseProperties));
|
// Items.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerEffect.BaseProperties));
|
||||||
}
|
// }
|
||||||
|
|
||||||
SortProperties();
|
SortProperties();
|
||||||
}
|
}
|
||||||
@ -355,11 +355,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
|||||||
List<LayerPropertyGroupViewModel> nonEffectProperties = Items
|
List<LayerPropertyGroupViewModel> nonEffectProperties = Items
|
||||||
.Where(l => l.TreeGroupViewModel.GroupType != LayerEffectRoot)
|
.Where(l => l.TreeGroupViewModel.GroupType != LayerEffectRoot)
|
||||||
.ToList();
|
.ToList();
|
||||||
// Order the effects
|
// // Order the effects
|
||||||
List<LayerPropertyGroupViewModel> effectProperties = Items
|
// List<LayerPropertyGroupViewModel> effectProperties = Items
|
||||||
.Where(l => l.TreeGroupViewModel.GroupType == LayerEffectRoot && l.LayerPropertyGroup.LayerEffect != null)
|
// .Where(l => l.TreeGroupViewModel.GroupType == LayerEffectRoot && l.LayerPropertyGroup.LayerEffect != null)
|
||||||
.OrderBy(l => l.LayerPropertyGroup.LayerEffect.Order)
|
// .OrderBy(l => l.LayerPropertyGroup.LayerEffect.Order)
|
||||||
.ToList();
|
// .ToList();
|
||||||
|
|
||||||
// Put the non-effect properties in front
|
// Put the non-effect properties in front
|
||||||
for (int index = 0; index < nonEffectProperties.Count; index++)
|
for (int index = 0; index < nonEffectProperties.Count; index++)
|
||||||
@ -369,13 +369,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
|||||||
((BindableCollection<LayerPropertyGroupViewModel>) Items).Move(Items.IndexOf(layerPropertyGroupViewModel), index);
|
((BindableCollection<LayerPropertyGroupViewModel>) Items).Move(Items.IndexOf(layerPropertyGroupViewModel), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put the effect properties after, sorted by their order
|
// // Put the effect properties after, sorted by their order
|
||||||
for (int index = 0; index < effectProperties.Count; index++)
|
// for (int index = 0; index < effectProperties.Count; index++)
|
||||||
{
|
// {
|
||||||
LayerPropertyGroupViewModel layerPropertyGroupViewModel = effectProperties[index];
|
// LayerPropertyGroupViewModel layerPropertyGroupViewModel = effectProperties[index];
|
||||||
if (Items.IndexOf(layerPropertyGroupViewModel) != index + nonEffectProperties.Count)
|
// if (Items.IndexOf(layerPropertyGroupViewModel) != index + nonEffectProperties.Count)
|
||||||
((BindableCollection<LayerPropertyGroupViewModel>) Items).Move(Items.IndexOf(layerPropertyGroupViewModel), index + nonEffectProperties.Count);
|
// ((BindableCollection<LayerPropertyGroupViewModel>) Items).Move(Items.IndexOf(layerPropertyGroupViewModel), index + nonEffectProperties.Count);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ToggleEffectsViewModel()
|
public async void ToggleEffectsViewModel()
|
||||||
|
|||||||
@ -87,8 +87,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
|||||||
|
|
||||||
public void UpdateOrder(int order)
|
public void UpdateOrder(int order)
|
||||||
{
|
{
|
||||||
if (LayerPropertyGroup.LayerEffect != null)
|
// if (LayerPropertyGroup.LayerEffect != null)
|
||||||
LayerPropertyGroup.LayerEffect.Order = order;
|
// LayerPropertyGroup.LayerEffect.Order = order;
|
||||||
NotifyOfPropertyChange(nameof(IsExpanded));
|
NotifyOfPropertyChange(nameof(IsExpanded));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Models
|
|||||||
|
|
||||||
public KeyframeClipboardModel(ILayerPropertyKeyframe layerPropertyKeyframe)
|
public KeyframeClipboardModel(ILayerPropertyKeyframe layerPropertyKeyframe)
|
||||||
{
|
{
|
||||||
FeatureId = layerPropertyKeyframe.UntypedLayerProperty.LayerPropertyGroup.Feature.Id;
|
// FeatureId = layerPropertyKeyframe.UntypedLayerProperty.LayerPropertyGroup.Feature.Id;
|
||||||
Path = layerPropertyKeyframe.UntypedLayerProperty.Path;
|
Path = layerPropertyKeyframe.UntypedLayerProperty.Path;
|
||||||
KeyframeEntity = layerPropertyKeyframe.GetKeyframeEntity();
|
KeyframeEntity = layerPropertyKeyframe.GetKeyframeEntity();
|
||||||
}
|
}
|
||||||
@ -70,15 +70,15 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Models
|
|||||||
|
|
||||||
public ILayerPropertyKeyframe Paste(List<ILayerProperty> properties, TimeSpan offset)
|
public ILayerPropertyKeyframe Paste(List<ILayerProperty> properties, TimeSpan offset)
|
||||||
{
|
{
|
||||||
ILayerProperty property = properties.FirstOrDefault(p => p.LayerPropertyGroup.Feature.Id == FeatureId && p.Path == Path);
|
// ILayerProperty property = properties.FirstOrDefault(p => p.LayerPropertyGroup.Feature.Id == FeatureId && p.Path == Path);
|
||||||
if (property != null)
|
// if (property != null)
|
||||||
{
|
// {
|
||||||
KeyframeEntity.Position += offset;
|
// KeyframeEntity.Position += offset;
|
||||||
ILayerPropertyKeyframe keyframe = property.AddKeyframeEntity(KeyframeEntity);
|
// ILayerPropertyKeyframe keyframe = property.AddKeyframeEntity(KeyframeEntity);
|
||||||
KeyframeEntity.Position -= offset;
|
// KeyframeEntity.Position -= offset;
|
||||||
|
//
|
||||||
return keyframe;
|
// return keyframe;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,94 +56,94 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
|||||||
? LayerPropertyGroupViewModel.Items
|
? LayerPropertyGroupViewModel.Items
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
public void OpenBrushSettings()
|
// public void OpenBrushSettings()
|
||||||
{
|
// {
|
||||||
BaseLayerBrush layerBrush = LayerPropertyGroup.LayerBrush;
|
// BaseLayerBrush layerBrush = LayerPropertyGroup.LayerBrush;
|
||||||
LayerBrushConfigurationDialog configurationViewModel = (LayerBrushConfigurationDialog) layerBrush.ConfigurationDialog;
|
// LayerBrushConfigurationDialog configurationViewModel = (LayerBrushConfigurationDialog) layerBrush.ConfigurationDialog;
|
||||||
if (configurationViewModel == null)
|
// if (configurationViewModel == null)
|
||||||
return;
|
// return;
|
||||||
|
//
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// // Limit to one constructor, there's no need to have more and it complicates things anyway
|
||||||
|
// ConstructorInfo[] constructors = configurationViewModel.Type.GetConstructors();
|
||||||
|
// if (constructors.Length != 1)
|
||||||
|
// throw new ArtemisUIException("Brush configuration dialogs must have exactly one constructor");
|
||||||
|
//
|
||||||
|
// // Find the BaseLayerBrush parameter, it is required by the base constructor so its there for sure
|
||||||
|
// ParameterInfo brushParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerBrush).IsAssignableFrom(p.ParameterType));
|
||||||
|
// ConstructorArgument argument = new(brushParameter.Name, layerBrush);
|
||||||
|
// BrushConfigurationViewModel viewModel = (BrushConfigurationViewModel) layerBrush.Descriptor.Provider.Plugin.Kernel.Get(configurationViewModel.Type, argument);
|
||||||
|
//
|
||||||
|
// _layerBrushSettingsWindowVm = new LayerBrushSettingsWindowViewModel(viewModel, configurationViewModel);
|
||||||
|
// _windowManager.ShowDialog(_layerBrushSettingsWindowVm);
|
||||||
|
//
|
||||||
|
// // Save changes after the dialog closes
|
||||||
|
// _profileEditorService.SaveSelectedProfileConfiguration();
|
||||||
|
// }
|
||||||
|
// catch (Exception e)
|
||||||
|
// {
|
||||||
|
// _dialogService.ShowExceptionDialog("An exception occured while trying to show the brush's settings window", e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
try
|
// public void OpenEffectSettings()
|
||||||
{
|
// {
|
||||||
// Limit to one constructor, there's no need to have more and it complicates things anyway
|
// BaseLayerEffect layerEffect = LayerPropertyGroup.LayerEffect;
|
||||||
ConstructorInfo[] constructors = configurationViewModel.Type.GetConstructors();
|
// LayerEffectConfigurationDialog configurationViewModel = (LayerEffectConfigurationDialog) layerEffect.ConfigurationDialog;
|
||||||
if (constructors.Length != 1)
|
// if (configurationViewModel == null)
|
||||||
throw new ArtemisUIException("Brush configuration dialogs must have exactly one constructor");
|
// return;
|
||||||
|
//
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// // Limit to one constructor, there's no need to have more and it complicates things anyway
|
||||||
|
// ConstructorInfo[] constructors = configurationViewModel.Type.GetConstructors();
|
||||||
|
// if (constructors.Length != 1)
|
||||||
|
// throw new ArtemisUIException("Effect configuration dialogs must have exactly one constructor");
|
||||||
|
//
|
||||||
|
// ParameterInfo effectParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerEffect).IsAssignableFrom(p.ParameterType));
|
||||||
|
// ConstructorArgument argument = new(effectParameter.Name, layerEffect);
|
||||||
|
// EffectConfigurationViewModel viewModel = (EffectConfigurationViewModel) layerEffect.Descriptor.Provider.Plugin.Kernel.Get(configurationViewModel.Type, argument);
|
||||||
|
//
|
||||||
|
// _layerEffectSettingsWindowVm = new LayerEffectSettingsWindowViewModel(viewModel, configurationViewModel);
|
||||||
|
// _windowManager.ShowDialog(_layerEffectSettingsWindowVm);
|
||||||
|
//
|
||||||
|
// // Save changes after the dialog closes
|
||||||
|
// _profileEditorService.SaveSelectedProfileConfiguration();
|
||||||
|
// }
|
||||||
|
// catch (Exception e)
|
||||||
|
// {
|
||||||
|
// _dialogService.ShowExceptionDialog("An exception occured while trying to show the effect's settings window", e);
|
||||||
|
// throw;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// Find the BaseLayerBrush parameter, it is required by the base constructor so its there for sure
|
// public async void RenameEffect()
|
||||||
ParameterInfo brushParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerBrush).IsAssignableFrom(p.ParameterType));
|
// {
|
||||||
ConstructorArgument argument = new(brushParameter.Name, layerBrush);
|
// object result = await _dialogService.ShowDialogAt<RenameViewModel>(
|
||||||
BrushConfigurationViewModel viewModel = (BrushConfigurationViewModel) layerBrush.Descriptor.Provider.Plugin.Kernel.Get(configurationViewModel.Type, argument);
|
// "PropertyTreeDialogHost",
|
||||||
|
// new Dictionary<string, object>
|
||||||
_layerBrushSettingsWindowVm = new LayerBrushSettingsWindowViewModel(viewModel, configurationViewModel);
|
// {
|
||||||
_windowManager.ShowDialog(_layerBrushSettingsWindowVm);
|
// {"subject", "effect"},
|
||||||
|
// {"currentName", LayerPropertyGroup.LayerEffect.Name}
|
||||||
// Save changes after the dialog closes
|
// }
|
||||||
_profileEditorService.SaveSelectedProfileConfiguration();
|
// );
|
||||||
}
|
// if (result is string newName)
|
||||||
catch (Exception e)
|
// {
|
||||||
{
|
// LayerPropertyGroup.LayerEffect.Name = newName;
|
||||||
_dialogService.ShowExceptionDialog("An exception occured while trying to show the brush's settings window", e);
|
// LayerPropertyGroup.LayerEffect.HasBeenRenamed = true;
|
||||||
}
|
// _profileEditorService.SaveSelectedProfileConfiguration();
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
public void OpenEffectSettings()
|
//
|
||||||
{
|
// public void DeleteEffect()
|
||||||
BaseLayerEffect layerEffect = LayerPropertyGroup.LayerEffect;
|
// {
|
||||||
LayerEffectConfigurationDialog configurationViewModel = (LayerEffectConfigurationDialog) layerEffect.ConfigurationDialog;
|
// if (LayerPropertyGroup.LayerEffect == null)
|
||||||
if (configurationViewModel == null)
|
// return;
|
||||||
return;
|
//
|
||||||
|
// LayerPropertyGroup.ProfileElement.RemoveLayerEffect(LayerPropertyGroup.LayerEffect);
|
||||||
try
|
// _profileEditorService.SaveSelectedProfileConfiguration();
|
||||||
{
|
// }
|
||||||
// Limit to one constructor, there's no need to have more and it complicates things anyway
|
|
||||||
ConstructorInfo[] constructors = configurationViewModel.Type.GetConstructors();
|
|
||||||
if (constructors.Length != 1)
|
|
||||||
throw new ArtemisUIException("Effect configuration dialogs must have exactly one constructor");
|
|
||||||
|
|
||||||
ParameterInfo effectParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerEffect).IsAssignableFrom(p.ParameterType));
|
|
||||||
ConstructorArgument argument = new(effectParameter.Name, layerEffect);
|
|
||||||
EffectConfigurationViewModel viewModel = (EffectConfigurationViewModel) layerEffect.Descriptor.Provider.Plugin.Kernel.Get(configurationViewModel.Type, argument);
|
|
||||||
|
|
||||||
_layerEffectSettingsWindowVm = new LayerEffectSettingsWindowViewModel(viewModel, configurationViewModel);
|
|
||||||
_windowManager.ShowDialog(_layerEffectSettingsWindowVm);
|
|
||||||
|
|
||||||
// Save changes after the dialog closes
|
|
||||||
_profileEditorService.SaveSelectedProfileConfiguration();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_dialogService.ShowExceptionDialog("An exception occured while trying to show the effect's settings window", e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void RenameEffect()
|
|
||||||
{
|
|
||||||
object result = await _dialogService.ShowDialogAt<RenameViewModel>(
|
|
||||||
"PropertyTreeDialogHost",
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{"subject", "effect"},
|
|
||||||
{"currentName", LayerPropertyGroup.LayerEffect.Name}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (result is string newName)
|
|
||||||
{
|
|
||||||
LayerPropertyGroup.LayerEffect.Name = newName;
|
|
||||||
LayerPropertyGroup.LayerEffect.HasBeenRenamed = true;
|
|
||||||
_profileEditorService.SaveSelectedProfileConfiguration();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteEffect()
|
|
||||||
{
|
|
||||||
if (LayerPropertyGroup.LayerEffect == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
LayerPropertyGroup.ProfileElement.RemoveLayerEffect(LayerPropertyGroup.LayerEffect);
|
|
||||||
_profileEditorService.SaveSelectedProfileConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SuspendedToggled()
|
public void SuspendedToggled()
|
||||||
{
|
{
|
||||||
@ -175,10 +175,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
|||||||
GroupType = LayerPropertyGroupType.General;
|
GroupType = LayerPropertyGroupType.General;
|
||||||
else if (LayerPropertyGroup is LayerTransformProperties)
|
else if (LayerPropertyGroup is LayerTransformProperties)
|
||||||
GroupType = LayerPropertyGroupType.Transform;
|
GroupType = LayerPropertyGroupType.Transform;
|
||||||
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerBrush != null)
|
// else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerBrush != null)
|
||||||
GroupType = LayerPropertyGroupType.LayerBrushRoot;
|
// GroupType = LayerPropertyGroupType.LayerBrushRoot;
|
||||||
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerEffect != null)
|
// else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerEffect != null)
|
||||||
GroupType = LayerPropertyGroupType.LayerEffectRoot;
|
// GroupType = LayerPropertyGroupType.LayerEffectRoot;
|
||||||
else
|
else
|
||||||
GroupType = LayerPropertyGroupType.None;
|
GroupType = LayerPropertyGroupType.None;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -151,8 +151,8 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
|||||||
ProfileElement.AddChild(layer, 0);
|
ProfileElement.AddChild(layer, 0);
|
||||||
// Could be null if the default brush got disabled
|
// Could be null if the default brush got disabled
|
||||||
LayerBrushDescriptor brush = _layerBrushService.GetDefaultLayerBrush();
|
LayerBrushDescriptor brush = _layerBrushService.GetDefaultLayerBrush();
|
||||||
if (brush != null)
|
// if (brush != null)
|
||||||
layer.ChangeLayerBrush(brush);
|
// layer.ChangeLayerBrush(brush);
|
||||||
|
|
||||||
layer.AddLeds(_rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
layer.AddLeds(_rgbService.EnabledDevices.SelectMany(d => d.Leds));
|
||||||
_profileEditorService.SaveSelectedProfileConfiguration();
|
_profileEditorService.SaveSelectedProfileConfiguration();
|
||||||
|
|||||||
@ -93,8 +93,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
|
|||||||
folder.AddChild(newLayer);
|
folder.AddChild(newLayer);
|
||||||
|
|
||||||
LayerBrushDescriptor brush = _layerBrushService.GetDefaultLayerBrush();
|
LayerBrushDescriptor brush = _layerBrushService.GetDefaultLayerBrush();
|
||||||
if (brush != null)
|
// if (brush != null)
|
||||||
newLayer.ChangeLayerBrush(brush);
|
// newLayer.ChangeLayerBrush(brush);
|
||||||
|
|
||||||
newLayer.AddLeds(selectedLeds);
|
newLayer.AddLeds(selectedLeds);
|
||||||
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
|
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Artemis.Core;
|
using System;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
@ -6,11 +7,14 @@ namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a profile editor command that can be used to change the brush of a layer.
|
/// Represents a profile editor command that can be used to change the brush of a layer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChangeLayerBrush : IProfileEditorCommand
|
public class ChangeLayerBrush : IProfileEditorCommand, IDisposable
|
||||||
{
|
{
|
||||||
private readonly Layer _layer;
|
private readonly Layer _layer;
|
||||||
private readonly LayerBrushDescriptor _layerBrushDescriptor;
|
private readonly LayerBrushDescriptor _layerBrushDescriptor;
|
||||||
private readonly LayerBrushDescriptor? _previousDescriptor;
|
private readonly BaseLayerBrush? _previousBrush;
|
||||||
|
|
||||||
|
private BaseLayerBrush? _newBrush;
|
||||||
|
private bool _executed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="ChangeLayerBrush" /> class.
|
/// Creates a new instance of the <see cref="ChangeLayerBrush" /> class.
|
||||||
@ -19,7 +23,7 @@ public class ChangeLayerBrush : IProfileEditorCommand
|
|||||||
{
|
{
|
||||||
_layer = layer;
|
_layer = layer;
|
||||||
_layerBrushDescriptor = layerBrushDescriptor;
|
_layerBrushDescriptor = layerBrushDescriptor;
|
||||||
_previousDescriptor = layer.LayerBrush?.Descriptor;
|
_previousBrush = _layer.LayerBrush;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Implementation of IProfileEditorCommand
|
#region Implementation of IProfileEditorCommand
|
||||||
@ -30,14 +34,32 @@ public class ChangeLayerBrush : IProfileEditorCommand
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Execute()
|
public void Execute()
|
||||||
{
|
{
|
||||||
_layer.ChangeLayerBrush(_layerBrushDescriptor);
|
// Create the new brush
|
||||||
|
_newBrush ??= _layerBrushDescriptor.CreateInstance(_layer, null);
|
||||||
|
// Change the brush to the new brush
|
||||||
|
_layer.ChangeLayerBrush(_newBrush);
|
||||||
|
|
||||||
|
_executed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Undo()
|
public void Undo()
|
||||||
{
|
{
|
||||||
if (_previousDescriptor != null)
|
_layer.ChangeLayerBrush(_previousBrush);
|
||||||
_layer.ChangeLayerBrush(_previousDescriptor);
|
_executed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_executed)
|
||||||
|
_previousBrush?.Dispose();
|
||||||
|
else
|
||||||
|
_newBrush?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -3,22 +3,86 @@ using System.Threading.Tasks;
|
|||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Services.ProfileEditor
|
namespace Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access the the profile editor back-end logic.
|
||||||
|
/// </summary>
|
||||||
|
public interface IProfileEditorService : IArtemisSharedUIService
|
||||||
{
|
{
|
||||||
public interface IProfileEditorService : IArtemisSharedUIService
|
/// <summary>
|
||||||
{
|
/// Gets an observable of the currently selected profile configuration.
|
||||||
IObservable<ProfileConfiguration?> ProfileConfiguration { get; }
|
/// </summary>
|
||||||
IObservable<RenderProfileElement?> ProfileElement { get; }
|
IObservable<ProfileConfiguration?> ProfileConfiguration { get; }
|
||||||
IObservable<ProfileEditorHistory?> History { get; }
|
|
||||||
IObservable<TimeSpan> Time { get; }
|
|
||||||
IObservable<double> PixelsPerSecond { get; }
|
|
||||||
|
|
||||||
void ChangeCurrentProfileConfiguration(ProfileConfiguration? profileConfiguration);
|
/// <summary>
|
||||||
void ChangeCurrentProfileElement(RenderProfileElement? renderProfileElement);
|
/// Gets an observable of the currently selected profile element.
|
||||||
void ChangeTime(TimeSpan time);
|
/// </summary>
|
||||||
|
IObservable<RenderProfileElement?> ProfileElement { get; }
|
||||||
|
|
||||||
void ExecuteCommand(IProfileEditorCommand command);
|
/// <summary>
|
||||||
void SaveProfile();
|
/// Gets an observable of the current editor history.
|
||||||
Task SaveProfileAsync();
|
/// </summary>
|
||||||
}
|
IObservable<ProfileEditorHistory?> History { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an observable of the profile preview playback time.
|
||||||
|
/// </summary>
|
||||||
|
IObservable<TimeSpan> Time { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an observable of the profile preview playing state.
|
||||||
|
/// </summary>
|
||||||
|
IObservable<bool> Playing { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an observable of the zoom level.
|
||||||
|
/// </summary>
|
||||||
|
IObservable<double> PixelsPerSecond { get; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the selected profile by its <see cref="Core.ProfileConfiguration" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profileConfiguration">The profile configuration of the profile to select.</param>
|
||||||
|
void ChangeCurrentProfileConfiguration(ProfileConfiguration? profileConfiguration);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the selected profile element.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="renderProfileElement">The profile element to select.</param>
|
||||||
|
void ChangeCurrentProfileElement(RenderProfileElement? renderProfileElement);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the current profile preview playback time.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="time">The new time.</param>
|
||||||
|
void ChangeTime(TimeSpan time);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the provided command and adds it to the history.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command">The command to execute.</param>
|
||||||
|
void ExecuteCommand(IProfileEditorCommand command);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the current profile.
|
||||||
|
/// </summary>
|
||||||
|
void SaveProfile();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously saves the current profile.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task representing the save action.</returns>
|
||||||
|
Task SaveProfileAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resumes profile preview playback.
|
||||||
|
/// </summary>
|
||||||
|
void Play();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pauses profile preview playback.
|
||||||
|
/// </summary>
|
||||||
|
void Pause();
|
||||||
}
|
}
|
||||||
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Services.ProfileEditor;
|
namespace Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
|
||||||
@ -15,19 +16,27 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
private readonly Dictionary<ProfileConfiguration, ProfileEditorHistory> _profileEditorHistories = new();
|
private readonly Dictionary<ProfileConfiguration, ProfileEditorHistory> _profileEditorHistories = new();
|
||||||
private readonly BehaviorSubject<RenderProfileElement?> _profileElementSubject = new(null);
|
private readonly BehaviorSubject<RenderProfileElement?> _profileElementSubject = new(null);
|
||||||
private readonly BehaviorSubject<TimeSpan> _timeSubject = new(TimeSpan.Zero);
|
private readonly BehaviorSubject<TimeSpan> _timeSubject = new(TimeSpan.Zero);
|
||||||
|
private readonly BehaviorSubject<bool> _playingSubject = new(false);
|
||||||
|
private readonly BehaviorSubject<bool> _suspendedEditingSubject = new(false);
|
||||||
private readonly BehaviorSubject<double> _pixelsPerSecondSubject = new(300);
|
private readonly BehaviorSubject<double> _pixelsPerSecondSubject = new(300);
|
||||||
|
private readonly ILogger _logger;
|
||||||
private readonly IProfileService _profileService;
|
private readonly IProfileService _profileService;
|
||||||
|
private readonly IModuleService _moduleService;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
|
|
||||||
public ProfileEditorService(IProfileService profileService, IWindowService windowService)
|
public ProfileEditorService(ILogger logger, IProfileService profileService, IModuleService moduleService, IWindowService windowService)
|
||||||
{
|
{
|
||||||
|
_logger = logger;
|
||||||
_profileService = profileService;
|
_profileService = profileService;
|
||||||
|
_moduleService = moduleService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
|
|
||||||
ProfileConfiguration = _profileConfigurationSubject.AsObservable();
|
ProfileConfiguration = _profileConfigurationSubject.AsObservable();
|
||||||
ProfileElement = _profileElementSubject.AsObservable();
|
ProfileElement = _profileElementSubject.AsObservable();
|
||||||
History = Observable.Defer(() => Observable.Return(GetHistory(_profileConfigurationSubject.Value))).Concat(ProfileConfiguration.Select(GetHistory));
|
History = Observable.Defer(() => Observable.Return(GetHistory(_profileConfigurationSubject.Value))).Concat(ProfileConfiguration.Select(GetHistory));
|
||||||
Time = _timeSubject.AsObservable();
|
Time = _timeSubject.AsObservable();
|
||||||
|
Playing = _playingSubject.AsObservable();
|
||||||
|
SuspendedEditing = _suspendedEditingSubject.AsObservable();
|
||||||
PixelsPerSecond = _pixelsPerSecondSubject.AsObservable();
|
PixelsPerSecond = _pixelsPerSecondSubject.AsObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,10 +56,46 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
public IObservable<RenderProfileElement?> ProfileElement { get; }
|
public IObservable<RenderProfileElement?> ProfileElement { get; }
|
||||||
public IObservable<ProfileEditorHistory?> History { get; }
|
public IObservable<ProfileEditorHistory?> History { get; }
|
||||||
public IObservable<TimeSpan> Time { get; }
|
public IObservable<TimeSpan> Time { get; }
|
||||||
|
public IObservable<bool> Playing { get; }
|
||||||
|
public IObservable<bool> SuspendedEditing { get; }
|
||||||
public IObservable<double> PixelsPerSecond { get; }
|
public IObservable<double> PixelsPerSecond { get; }
|
||||||
|
|
||||||
public void ChangeCurrentProfileConfiguration(ProfileConfiguration? profileConfiguration)
|
public void ChangeCurrentProfileConfiguration(ProfileConfiguration? profileConfiguration)
|
||||||
{
|
{
|
||||||
|
if (ReferenceEquals(_profileConfigurationSubject.Value, profileConfiguration))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_logger.Verbose("ChangeCurrentProfileConfiguration {profile}", profileConfiguration);
|
||||||
|
|
||||||
|
// Stop playing and save the current profile
|
||||||
|
Pause();
|
||||||
|
if (_profileConfigurationSubject.Value?.Profile != null)
|
||||||
|
_profileConfigurationSubject.Value.Profile.LastSelectedProfileElement = _profileElementSubject.Value;
|
||||||
|
SaveProfile();
|
||||||
|
|
||||||
|
// No need to deactivate the profile, if needed it will be deactivated next update
|
||||||
|
if (_profileConfigurationSubject.Value != null)
|
||||||
|
_profileConfigurationSubject.Value.IsBeingEdited = false;
|
||||||
|
|
||||||
|
// Deselect whatever profile element was active
|
||||||
|
ChangeCurrentProfileElement(null);
|
||||||
|
|
||||||
|
// The new profile may need activation
|
||||||
|
if (profileConfiguration != null)
|
||||||
|
{
|
||||||
|
profileConfiguration.IsBeingEdited = true;
|
||||||
|
_moduleService.SetActivationOverride(profileConfiguration.Module);
|
||||||
|
_profileService.ActivateProfile(profileConfiguration);
|
||||||
|
_profileService.RenderForEditor = true;
|
||||||
|
|
||||||
|
if (profileConfiguration.Profile?.LastSelectedProfileElement is RenderProfileElement renderProfileElement)
|
||||||
|
ChangeCurrentProfileElement(renderProfileElement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_moduleService.SetActivationOverride(null);
|
||||||
|
_profileService.RenderForEditor = false;
|
||||||
|
}
|
||||||
_profileConfigurationSubject.OnNext(profileConfiguration);
|
_profileConfigurationSubject.OnNext(profileConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +106,7 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
|
|
||||||
public void ChangeTime(TimeSpan time)
|
public void ChangeTime(TimeSpan time)
|
||||||
{
|
{
|
||||||
|
Tick(time);
|
||||||
_timeSubject.OnNext(time);
|
_timeSubject.OnNext(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,4 +147,48 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
{
|
{
|
||||||
await Task.Run(SaveProfile);
|
await Task.Run(SaveProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Play()
|
||||||
|
{
|
||||||
|
if (!_playingSubject.Value)
|
||||||
|
_playingSubject.OnNext(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Pause()
|
||||||
|
{
|
||||||
|
if (_playingSubject.Value)
|
||||||
|
_playingSubject.OnNext(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Tick(TimeSpan time)
|
||||||
|
{
|
||||||
|
if (_profileConfigurationSubject.Value?.Profile == null || _suspendedEditingSubject.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TickProfileElement(_profileConfigurationSubject.Value.Profile.GetRootFolder(), time);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TickProfileElement(ProfileElement profileElement, TimeSpan time)
|
||||||
|
{
|
||||||
|
if (profileElement is not RenderProfileElement renderElement)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (renderElement.Suspended)
|
||||||
|
{
|
||||||
|
renderElement.Disable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renderElement.Enable();
|
||||||
|
renderElement.Timeline.Override(
|
||||||
|
time,
|
||||||
|
(renderElement != _profileElementSubject.Value || renderElement.Timeline.Length < time) && renderElement.Timeline.PlayMode == TimelinePlayMode.Repeat
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (ProfileElement child in renderElement.Children)
|
||||||
|
TickProfileElement(child, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -24,8 +24,8 @@
|
|||||||
</Styles.Resources>
|
</Styles.Resources>
|
||||||
<StyleInclude Source="/Styles/Border.axaml" />
|
<StyleInclude Source="/Styles/Border.axaml" />
|
||||||
<StyleInclude Source="/Styles/Button.axaml" />
|
<StyleInclude Source="/Styles/Button.axaml" />
|
||||||
<StyleInclude Source="/Styles/TextBlock.axaml" />
|
<StyleInclude Source="/Styles/Condensed.axaml" />
|
||||||
<StyleInclude Source="/Styles/TextBox.axaml" />
|
<StyleInclude Source="/Styles/TextBlock.axaml" />
|
||||||
<StyleInclude Source="/Styles/Sidebar.axaml" />
|
<StyleInclude Source="/Styles/Sidebar.axaml" />
|
||||||
<StyleInclude Source="/Styles/InfoBar.axaml" />
|
<StyleInclude Source="/Styles/InfoBar.axaml" />
|
||||||
<StyleInclude Source="/Styles/TreeView.axaml" />
|
<StyleInclude Source="/Styles/TreeView.axaml" />
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
<Styles xmlns="https://github.com/avaloniaui"
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:controls1="clr-namespace:Artemis.UI.Shared.Controls">
|
||||||
<Design.PreviewWith>
|
<Design.PreviewWith>
|
||||||
<Border Padding="50">
|
<Border Padding="50">
|
||||||
<StackPanel Spacing="5">
|
<StackPanel Spacing="5">
|
||||||
@ -24,6 +25,9 @@
|
|||||||
<ComboBoxItem>Bluheheheheh</ComboBoxItem>
|
<ComboBoxItem>Bluheheheheh</ComboBoxItem>
|
||||||
<ComboBoxItem>Bluhgfdgdsheheh</ComboBoxItem>
|
<ComboBoxItem>Bluhgfdgdsheheh</ComboBoxItem>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
|
||||||
|
<controls:ColorPickerButton Color="Firebrick"></controls:ColorPickerButton>
|
||||||
|
<controls:ColorPickerButton Color="Firebrick" Classes="condensed"></controls:ColorPickerButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Design.PreviewWith>
|
</Design.PreviewWith>
|
||||||
@ -31,19 +35,34 @@
|
|||||||
<!-- Add Styles Here -->
|
<!-- Add Styles Here -->
|
||||||
<Style Selector="TextBox.condensed">
|
<Style Selector="TextBox.condensed">
|
||||||
<Setter Property="Padding" Value="4 2" />
|
<Setter Property="Padding" Value="4 2" />
|
||||||
<Setter Property="FontSize" Value="14" />
|
<Setter Property="FontSize" Value="13" />
|
||||||
<Setter Property="MinHeight" Value="25" />
|
<Setter Property="MinHeight" Value="24" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="controls|NumberBox.condensed /template/ TextBox#InputBox">
|
<Style Selector="controls|NumberBox.condensed /template/ TextBox#InputBox">
|
||||||
<Setter Property="Padding" Value="4 2" />
|
<Setter Property="Padding" Value="4 2" />
|
||||||
<Setter Property="FontSize" Value="14" />
|
<Setter Property="FontSize" Value="13" />
|
||||||
<Setter Property="MinHeight" Value="25" />
|
<Setter Property="MinHeight" Value="24" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="ComboBox.condensed">
|
<Style Selector="ComboBox.condensed">
|
||||||
<Setter Property="Padding" Value="4 2" />
|
<Setter Property="Padding" Value="4 2" />
|
||||||
<Setter Property="FontSize" Value="14" />
|
<Setter Property="FontSize" Value="13" />
|
||||||
<Setter Property="Height" Value="25" />
|
<Setter Property="Height" Value="24" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="controls1|EnumComboBox.condensed > ComboBox">
|
||||||
|
<Setter Property="Padding" Value="4 2" />
|
||||||
|
<Setter Property="FontSize" Value="13" />
|
||||||
|
<Setter Property="Height" Value="24" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="controls|ColorPickerButton.condensed">
|
||||||
|
<Setter Property="RenderTransform">
|
||||||
|
<Setter.Value>
|
||||||
|
<ScaleTransform ScaleX="0.72" ScaleY="0.72"></ScaleTransform>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
</Styles>
|
</Styles>
|
||||||
@ -12,6 +12,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Artemis.UI.Avalonia.csproj.DotSettings" />
|
<None Remove="Artemis.UI.Avalonia.csproj.DotSettings" />
|
||||||
<None Remove="Artemis.UI.csproj.DotSettings" />
|
<None Remove="Artemis.UI.csproj.DotSettings" />
|
||||||
|
<None Remove="Assets\Images\Logo\application.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="0.10.11" />
|
<PackageReference Include="Avalonia" Version="0.10.11" />
|
||||||
|
|||||||
BIN
src/Avalonia/Artemis.UI/Assets/Images/Logo/application.ico
Normal file
BIN
src/Avalonia/Artemis.UI/Assets/Images/Logo/application.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using FluentAvalonia.UI.Media;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Converters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts <see cref="SKColor" /> into <see cref="Color2" />.
|
||||||
|
/// </summary>
|
||||||
|
public class SKColorToColor2Converter : IValueConverter
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is SKColor skColor)
|
||||||
|
return new Color2(skColor.Red, skColor.Green, skColor.Blue, skColor.Alpha);
|
||||||
|
return new Color2();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is Color2 color2)
|
||||||
|
return new SKColor(color2.R, color2.G, color2.B, color2.A);
|
||||||
|
return SKColor.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Converters;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// Converts <see cref="SKColor" />into <see cref="T:System.String" />.
|
||||||
|
/// </summary>
|
||||||
|
public class SKColorToStringConverter : IValueConverter
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return value?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value as string))
|
||||||
|
return SKColor.Empty;
|
||||||
|
|
||||||
|
return SKColor.TryParse((string) value!, out SKColor color) ? color : SKColor.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,8 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.DefaultTypes.PropertyInput.BoolPropertyInputView">
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.BoolPropertyInputView">
|
||||||
TODO
|
<controls:EnumComboBox Classes="condensed" Width="200" Value="{Binding SelectedBooleanOption}" VerticalAlignment="Center" />
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,13 +1,31 @@
|
|||||||
using Artemis.Core;
|
using System;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
using Artemis.UI.Shared.Services.PropertyInput;
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
namespace Artemis.UI.DefaultTypes.PropertyInput;
|
||||||
|
|
||||||
public class BoolPropertyInputViewModel : PropertyInputViewModel<bool>
|
public class BoolPropertyInputViewModel : PropertyInputViewModel<bool>
|
||||||
{
|
{
|
||||||
|
private BooleanOptions _selectedBooleanOption;
|
||||||
|
|
||||||
public BoolPropertyInputViewModel(LayerProperty<bool> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
public BoolPropertyInputViewModel(LayerProperty<bool> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
|
||||||
: base(layerProperty, profileEditorService, propertyInputService)
|
: base(layerProperty, profileEditorService, propertyInputService)
|
||||||
{
|
{
|
||||||
|
this.WhenAnyValue(vm => vm.InputValue).Subscribe(v => SelectedBooleanOption = v ? BooleanOptions.True : BooleanOptions.False);
|
||||||
|
this.WhenAnyValue(vm => vm.SelectedBooleanOption).Subscribe(v => InputValue = v == BooleanOptions.True);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BooleanOptions SelectedBooleanOption
|
||||||
|
{
|
||||||
|
get => _selectedBooleanOption;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _selectedBooleanOption, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BooleanOptions
|
||||||
|
{
|
||||||
|
True,
|
||||||
|
False
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
<ComboBox Classes="brush condensed"
|
<ComboBox Classes="brush condensed"
|
||||||
Width="200"
|
Width="200"
|
||||||
HorizontalAlignment="Left"
|
VerticalAlignment="Center"
|
||||||
Items="{Binding Descriptors}"
|
Items="{Binding Descriptors}"
|
||||||
SelectedItem="{Binding SelectedDescriptor}">
|
SelectedItem="{Binding SelectedDescriptor}">
|
||||||
<ComboBox.ItemTemplate>
|
<ComboBox.ItemTemplate>
|
||||||
|
|||||||
@ -66,6 +66,16 @@ public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushRefe
|
|||||||
Dispatcher.UIThread.InvokeAsync(() => _windowService.CreateContentDialog().WithViewModel(out LayerBrushPresetViewModel _, ("layerBrush", layer.LayerBrush)).ShowAsync());
|
Dispatcher.UIThread.InvokeAsync(() => _windowService.CreateContentDialog().WithViewModel(out LayerBrushPresetViewModel _, ("layerBrush", layer.LayerBrush)).ShowAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Overrides of PropertyInputViewModel<LayerBrushReference>
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnInputValueChanged()
|
||||||
|
{
|
||||||
|
this.RaisePropertyChanged(nameof(SelectedDescriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private void UpdateDescriptorsIfChanged(PluginFeatureEventArgs e)
|
private void UpdateDescriptorsIfChanged(PluginFeatureEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PluginFeature is not LayerBrushProvider)
|
if (e.PluginFeature is not LayerBrushProvider)
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.DefaultTypes.PropertyInput.EnumPropertyInputView">
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.EnumPropertyInputView">
|
||||||
TODO
|
<controls:EnumComboBox Classes="condensed" Width="200" Value="{Binding InputValue}" VerticalAlignment="Center" />
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -2,7 +2,31 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||||
|
xmlns:shared="clr-namespace:Artemis.UI.Shared"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.DefaultTypes.PropertyInput.SKColorPropertyInputView">
|
x:Class="Artemis.UI.DefaultTypes.PropertyInput.SKColorPropertyInputView">
|
||||||
TODO
|
<UserControl.Resources>
|
||||||
</UserControl>
|
<converters:SKColorToStringConverter x:Key="SKColorToStringConverter" />
|
||||||
|
<converters:SKColorToColor2Converter x:Key="SKColorToColor2Converter" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
<Grid Height="24">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBox Classes="condensed"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Text="{Binding InputValue, Converter={StaticResource SKColorToStringConverter}}"/>
|
||||||
|
<controls:ColorPickerButton Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Color="{Binding InputValue, Converter={StaticResource SKColorToColor2Converter}}"
|
||||||
|
Classes="condensed"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="-6 0 -8 0"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
@ -5,7 +5,7 @@
|
|||||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.MainWindow"
|
x:Class="Artemis.UI.MainWindow"
|
||||||
Icon="/Assets/Images/Logo/bow.ico"
|
Icon="/Assets/Images/Logo/application.ico"
|
||||||
Title="Artemis 2.0">
|
Title="Artemis 2.0">
|
||||||
<!-- Use a panel here so the main window can host ContentDialogs -->
|
<!-- Use a panel here so the main window can host ContentDialogs -->
|
||||||
<Panel>
|
<Panel>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.LayerBrushes;
|
||||||
|
using Artemis.Core.LayerEffects;
|
||||||
using Artemis.UI.Screens.Device;
|
using Artemis.UI.Screens.Device;
|
||||||
using Artemis.UI.Screens.Plugins;
|
using Artemis.UI.Screens.Plugins;
|
||||||
using Artemis.UI.Screens.ProfileEditor;
|
using Artemis.UI.Screens.ProfileEditor;
|
||||||
@ -67,6 +69,8 @@ namespace Artemis.UI.Ninject.Factories
|
|||||||
{
|
{
|
||||||
ProfileElementPropertyViewModel ProfileElementPropertyViewModel(ILayerProperty layerProperty);
|
ProfileElementPropertyViewModel ProfileElementPropertyViewModel(ILayerProperty layerProperty);
|
||||||
ProfileElementPropertyGroupViewModel ProfileElementPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup);
|
ProfileElementPropertyGroupViewModel ProfileElementPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup);
|
||||||
|
ProfileElementPropertyGroupViewModel ProfileElementPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush layerBrush);
|
||||||
|
ProfileElementPropertyGroupViewModel ProfileElementPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerEffect layerEffect);
|
||||||
|
|
||||||
TreeGroupViewModel TreeGroupViewModel(ProfileElementPropertyGroupViewModel profileElementPropertyGroupViewModel);
|
TreeGroupViewModel TreeGroupViewModel(ProfileElementPropertyGroupViewModel profileElementPropertyGroupViewModel);
|
||||||
// TimelineGroupViewModel TimelineGroupViewModel(ProfileElementPropertiesViewModel profileElementPropertiesViewModel);
|
// TimelineGroupViewModel TimelineGroupViewModel(ProfileElementPropertiesViewModel profileElementPropertiesViewModel);
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
xmlns:reactiveUi="http://reactiveui.net"
|
xmlns:reactiveUi="http://reactiveui.net"
|
||||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
||||||
x:Class="Artemis.UI.Screens.Debugger.DebugView"
|
x:Class="Artemis.UI.Screens.Debugger.DebugView"
|
||||||
Icon="/Assets/Images/Logo/bow.ico"
|
Icon="/Assets/Images/Logo/application.ico"
|
||||||
Title="Artemis | Debugger"
|
Title="Artemis | Debugger"
|
||||||
Width="1200"
|
Width="1200"
|
||||||
Height="800">
|
Height="800">
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
||||||
x:Class="Artemis.UI.Screens.Device.DevicePropertiesView"
|
x:Class="Artemis.UI.Screens.Device.DevicePropertiesView"
|
||||||
Icon="/Assets/Images/Logo/bow.ico"
|
Icon="/Assets/Images/Logo/application.ico"
|
||||||
Title="Artemis | Device Properties"
|
Title="Artemis | Device Properties"
|
||||||
Width="1250"
|
Width="1250"
|
||||||
Height="900">
|
Height="900">
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Plugins.PluginSettingsWindowView"
|
x:Class="Artemis.UI.Screens.Plugins.PluginSettingsWindowView"
|
||||||
Icon="/Assets/Images/Logo/bow.ico"
|
Icon="/Assets/Images/Logo/application.ico"
|
||||||
Title="{Binding DisplayName}"
|
Title="{Binding DisplayName}"
|
||||||
Width="800"
|
Width="800"
|
||||||
Height="800"
|
Height="800"
|
||||||
|
|||||||
@ -0,0 +1,63 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="48"
|
||||||
|
x:Class="Artemis.UI.Screens.ProfileEditor.Playback.PlaybackView">
|
||||||
|
<DockPanel Margin="8 0">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
|
<Button Classes="icon-button icon-button-large"
|
||||||
|
ToolTip.Tip="Play from start (Shift+Space)" Command="{Binding PlayFromStart}"
|
||||||
|
Focusable="False">
|
||||||
|
<avalonia:MaterialIcon Kind="StepForward" />
|
||||||
|
</Button>
|
||||||
|
<Button Classes="icon-button icon-button-large"
|
||||||
|
ToolTip.Tip="Toggle play/pause (Space)" Command="{Binding TogglePlay}" Focusable="False">
|
||||||
|
<StackPanel>
|
||||||
|
<avalonia:MaterialIcon Kind="Play" IsVisible="{Binding !Playing}" />
|
||||||
|
<avalonia:MaterialIcon Kind="Pause" IsVisible="{Binding Playing}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button Classes="icon-button icon-button-large" ToolTip.Tip="Go to start" Command="{Binding GoToStart}" Focusable="False">
|
||||||
|
<avalonia:MaterialIcon Kind="SkipBackward" />
|
||||||
|
</Button>
|
||||||
|
<Button Classes="icon-button icon-button-large" ToolTip.Tip="Go to end" Command="{Binding GoToEnd}" Focusable="False">
|
||||||
|
<avalonia:MaterialIcon Kind="SkipForward" />
|
||||||
|
</Button>
|
||||||
|
<Button Classes="icon-button icon-button-large" ToolTip.Tip="Previous frame" Command="{Binding GoToPreviousFrame}" Focusable="False">
|
||||||
|
<avalonia:MaterialIcon Kind="SkipPrevious" />
|
||||||
|
</Button>
|
||||||
|
<Button Classes="icon-button icon-button-large" ToolTip.Tip="Next frame" Command="{Binding GoToNextFrame}" Focusable="False">
|
||||||
|
<avalonia:MaterialIcon Kind="SkipNext" />
|
||||||
|
</Button>
|
||||||
|
<ToggleButton Classes="icon-button icon-button-large"
|
||||||
|
IsChecked="{Binding Repeating}"
|
||||||
|
Focusable="False"
|
||||||
|
Command="{Binding CycleRepeating}">
|
||||||
|
<ToolTip.Tip>
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel IsVisible="{Binding Repeating}">
|
||||||
|
<TextBlock Text="Repeat entire timeline"
|
||||||
|
IsVisible="{Binding RepeatTimeline}" />
|
||||||
|
<TextBlock Text="Repeat segment"
|
||||||
|
IsVisible="{Binding RepeatSegment}" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock IsVisible="{Binding !Repeating}">
|
||||||
|
Don't repeat the timeline
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock TextWrapping="Wrap">
|
||||||
|
This setting only applies to the editor and does not affect the repeat mode during normal profile playback
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</ToolTip.Tip>
|
||||||
|
<Grid>
|
||||||
|
<avalonia:MaterialIcon Kind="Repeat" IsVisible="{Binding RepeatTimeline}" />
|
||||||
|
<avalonia:MaterialIcon Kind="RepeatOne" IsVisible="{Binding RepeatSegment}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Classes="h4" Text="{Binding FormattedCurrentTime, FallbackValue=00.000}" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0"/>
|
||||||
|
</DockPanel>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.ProfileEditor.Playback
|
||||||
|
{
|
||||||
|
public partial class PlaybackView : ReactiveUserControl<PlaybackViewModel>
|
||||||
|
{
|
||||||
|
public PlaybackView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,193 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.ProfileEditor.Playback;
|
||||||
|
|
||||||
|
public class PlaybackViewModel : ActivatableViewModelBase
|
||||||
|
{
|
||||||
|
private readonly ICoreService _coreService;
|
||||||
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
|
private RenderProfileElement? _profileElement;
|
||||||
|
private ObservableAsPropertyHelper<TimeSpan>? _currentTime;
|
||||||
|
private ObservableAsPropertyHelper<string?>? _formattedCurrentTime;
|
||||||
|
private ObservableAsPropertyHelper<bool>? _playing;
|
||||||
|
private bool _repeating;
|
||||||
|
private bool _repeatTimeline;
|
||||||
|
private bool _repeatSegment;
|
||||||
|
|
||||||
|
public PlaybackViewModel(ICoreService coreService, IProfileEditorService profileEditorService, ISettingsService settingsService)
|
||||||
|
{
|
||||||
|
_coreService = coreService;
|
||||||
|
_profileEditorService = profileEditorService;
|
||||||
|
_settingsService = settingsService;
|
||||||
|
|
||||||
|
this.WhenActivated(d =>
|
||||||
|
{
|
||||||
|
_profileEditorService.ProfileElement.Subscribe(e => _profileElement = e).DisposeWith(d);
|
||||||
|
_currentTime = _profileEditorService.Time.ToProperty(this, vm => vm.CurrentTime).DisposeWith(d);
|
||||||
|
_formattedCurrentTime = _profileEditorService.Time.Select(t => $"{Math.Floor(t.TotalSeconds):00}.{t.Milliseconds:000}").ToProperty(this, vm => vm.FormattedCurrentTime).DisposeWith(d);
|
||||||
|
_playing = _profileEditorService.Playing.ToProperty(this, vm => vm.Playing).DisposeWith(d);
|
||||||
|
|
||||||
|
Observable.FromEventPattern<FrameRenderingEventArgs>(x => coreService.FrameRendering += x, x => coreService.FrameRendering -= x)
|
||||||
|
.Subscribe(e => CoreServiceOnFrameRendering(e.EventArgs))
|
||||||
|
.DisposeWith(d);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeSpan CurrentTime => _currentTime?.Value ?? TimeSpan.Zero;
|
||||||
|
public string? FormattedCurrentTime => _formattedCurrentTime?.Value;
|
||||||
|
public bool Playing => _playing?.Value ?? false;
|
||||||
|
|
||||||
|
public bool Repeating
|
||||||
|
{
|
||||||
|
get => _repeating;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _repeating, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RepeatTimeline
|
||||||
|
{
|
||||||
|
get => _repeatTimeline;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _repeatTimeline, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RepeatSegment
|
||||||
|
{
|
||||||
|
get => _repeatSegment;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _repeatSegment, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PlayFromStart()
|
||||||
|
{
|
||||||
|
GoToStart();
|
||||||
|
if (!Playing)
|
||||||
|
_profileEditorService.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TogglePlay()
|
||||||
|
{
|
||||||
|
if (!Playing)
|
||||||
|
_profileEditorService.Play();
|
||||||
|
else
|
||||||
|
_profileEditorService.Pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GoToStart()
|
||||||
|
{
|
||||||
|
_profileEditorService.ChangeTime(TimeSpan.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GoToEnd()
|
||||||
|
{
|
||||||
|
if (_profileElement == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_profileEditorService.ChangeTime(_profileElement.Timeline.EndSegmentEndPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GoToPreviousFrame()
|
||||||
|
{
|
||||||
|
if (_profileElement == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double frameTime = 1000.0 / _settingsService.GetSetting("Core.TargetFrameRate", 30).Value;
|
||||||
|
double newTime = Math.Max(0, Math.Round((CurrentTime.TotalMilliseconds - frameTime) / frameTime) * frameTime);
|
||||||
|
_profileEditorService.ChangeTime(TimeSpan.FromMilliseconds(newTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GoToNextFrame()
|
||||||
|
{
|
||||||
|
if (_profileElement == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double frameTime = 1000.0 / _settingsService.GetSetting("Core.TargetFrameRate", 30).Value;
|
||||||
|
double newTime = Math.Round((CurrentTime.TotalMilliseconds + frameTime) / frameTime) * frameTime;
|
||||||
|
newTime = Math.Min(newTime, _profileElement.Timeline.EndSegmentEndPosition.TotalMilliseconds);
|
||||||
|
_profileEditorService.ChangeTime(TimeSpan.FromMilliseconds(newTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CycleRepeating()
|
||||||
|
{
|
||||||
|
if (!Repeating)
|
||||||
|
{
|
||||||
|
RepeatTimeline = true;
|
||||||
|
RepeatSegment = false;
|
||||||
|
Repeating = true;
|
||||||
|
}
|
||||||
|
else if (RepeatTimeline)
|
||||||
|
{
|
||||||
|
RepeatTimeline = false;
|
||||||
|
RepeatSegment = true;
|
||||||
|
}
|
||||||
|
else if (RepeatSegment)
|
||||||
|
{
|
||||||
|
RepeatTimeline = true;
|
||||||
|
RepeatSegment = false;
|
||||||
|
Repeating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimeSpan GetCurrentSegmentStart()
|
||||||
|
{
|
||||||
|
if (_profileElement == null)
|
||||||
|
return TimeSpan.Zero;
|
||||||
|
|
||||||
|
if (CurrentTime < _profileElement.Timeline.StartSegmentEndPosition)
|
||||||
|
return TimeSpan.Zero;
|
||||||
|
if (CurrentTime < _profileElement.Timeline.MainSegmentEndPosition)
|
||||||
|
return _profileElement.Timeline.MainSegmentStartPosition;
|
||||||
|
if (CurrentTime < _profileElement.Timeline.EndSegmentEndPosition)
|
||||||
|
return _profileElement.Timeline.EndSegmentStartPosition;
|
||||||
|
|
||||||
|
return TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimeSpan GetCurrentSegmentEnd()
|
||||||
|
{
|
||||||
|
if (_profileElement == null)
|
||||||
|
return TimeSpan.Zero;
|
||||||
|
|
||||||
|
if (CurrentTime < _profileElement.Timeline.StartSegmentEndPosition)
|
||||||
|
return _profileElement.Timeline.StartSegmentEndPosition;
|
||||||
|
if (CurrentTime < _profileElement.Timeline.MainSegmentEndPosition)
|
||||||
|
return _profileElement.Timeline.MainSegmentEndPosition;
|
||||||
|
if (CurrentTime < _profileElement.Timeline.EndSegmentEndPosition)
|
||||||
|
return _profileElement.Timeline.EndSegmentEndPosition;
|
||||||
|
|
||||||
|
return TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CoreServiceOnFrameRendering(FrameRenderingEventArgs e)
|
||||||
|
{
|
||||||
|
if (!Playing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TimeSpan newTime = CurrentTime.Add(TimeSpan.FromSeconds(e.DeltaTime));
|
||||||
|
if (_profileElement != null)
|
||||||
|
{
|
||||||
|
if (Repeating && RepeatTimeline)
|
||||||
|
{
|
||||||
|
if (newTime > _profileElement.Timeline.Length)
|
||||||
|
newTime = TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
else if (Repeating && RepeatSegment)
|
||||||
|
{
|
||||||
|
if (newTime > GetCurrentSegmentEnd())
|
||||||
|
newTime = GetCurrentSegmentStart();
|
||||||
|
}
|
||||||
|
else if (newTime > _profileElement.Timeline.Length)
|
||||||
|
{
|
||||||
|
newTime = _profileElement.Timeline.Length;
|
||||||
|
_profileEditorService.Pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_profileEditorService.ChangeTime(newTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,18 +3,45 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileElementProperties"
|
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileElementProperties"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.ProfileElementPropertiesView">
|
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.ProfileElementPropertiesView">
|
||||||
<Grid ColumnDefinitions="*,Auto,*">
|
|
||||||
<ItemsControl Grid.Column="0" Items="{Binding PropertyGroupViewModels}">
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<TreeDataTemplate DataType="{x:Type local:ProfileElementPropertyGroupViewModel}" ItemsSource="{Binding Children}">
|
|
||||||
<ContentControl Content="{Binding TreeGroupViewModel}" />
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
|
|
||||||
<GridSplitter Grid.Column="1"></GridSplitter>
|
<Grid ColumnDefinitions="*,Auto,*" RowDefinitions="48,*">
|
||||||
</Grid>
|
<ContentControl Grid.Row="0" Content="{Binding PlaybackViewModel}"></ContentControl>
|
||||||
|
|
||||||
</UserControl>
|
<GridSplitter Grid.Row="0" Grid.Column="1" Cursor="SizeWestEast" Foreground="Transparent" Background="Transparent" />
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Name="TreeScrollViewer"
|
||||||
|
Offset="{Binding #TimelineScrollViewer.Offset, Mode=OneWay}"
|
||||||
|
Background="{DynamicResource CardStrokeColorDefaultSolidBrush}">
|
||||||
|
<ItemsControl Items="{Binding PropertyGroupViewModels}" Padding="0 0 8 0">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<TreeDataTemplate DataType="{x:Type local:ProfileElementPropertyGroupViewModel}" ItemsSource="{Binding Children}">
|
||||||
|
<ContentControl Content="{Binding TreeGroupViewModel}" />
|
||||||
|
</TreeDataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<GridSplitter Grid.Row="1" Grid.Column="1" Cursor="SizeWestEast" Foreground="Transparent" Background="{DynamicResource CardStrokeColorDefaultSolidBrush}" />
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="1"
|
||||||
|
Grid.Column="2"
|
||||||
|
Name="TimelineScrollViewer"
|
||||||
|
Offset="{Binding #TreeScrollViewer.Offset, Mode=OneWay}"
|
||||||
|
VerticalScrollBarVisibility="Hidden"
|
||||||
|
Background="{DynamicResource CardStrokeColorDefaultSolidBrush}">
|
||||||
|
<ItemsControl Items="{Binding PropertyGroupViewModels}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<TreeDataTemplate DataType="{x:Type local:ProfileElementPropertyGroupViewModel}" ItemsSource="{Binding Children}">
|
||||||
|
<ContentControl Content="{Binding TreeGroupViewModel}" />
|
||||||
|
</TreeDataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
@ -1,18 +1,17 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties
|
namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties;
|
||||||
{
|
|
||||||
public partial class ProfileElementPropertiesView : ReactiveUserControl<ProfileElementPropertiesViewModel>
|
|
||||||
{
|
|
||||||
public ProfileElementPropertiesView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeComponent()
|
public class ProfileElementPropertiesView : ReactiveUserControl<ProfileElementPropertiesViewModel>
|
||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
public ProfileElementPropertiesView()
|
||||||
}
|
{
|
||||||
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,9 +6,10 @@ using System.Reactive;
|
|||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.LayerBrushes;
|
||||||
using Artemis.Core.LayerEffects;
|
using Artemis.Core.LayerEffects;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree;
|
using Artemis.UI.Screens.ProfileEditor.Playback;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
@ -17,20 +18,18 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties;
|
|||||||
|
|
||||||
public class ProfileElementPropertiesViewModel : ActivatableViewModelBase
|
public class ProfileElementPropertiesViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
|
private readonly Dictionary<LayerPropertyGroup, ProfileElementPropertyGroupViewModel> _cachedViewModels;
|
||||||
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
|
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
|
||||||
private ProfileElementPropertyGroupViewModel? _brushPropertyGroup;
|
|
||||||
private ObservableAsPropertyHelper<RenderProfileElement?>? _profileElement;
|
private ObservableAsPropertyHelper<RenderProfileElement?>? _profileElement;
|
||||||
private readonly Dictionary<RenderProfileElement, List<ProfileElementPropertyGroupViewModel>> _profileElementGroups;
|
|
||||||
private ObservableCollection<ProfileElementPropertyGroupViewModel> _propertyGroupViewModels;
|
private ObservableCollection<ProfileElementPropertyGroupViewModel> _propertyGroupViewModels;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ProfileElementPropertiesViewModel(IProfileEditorService profileEditorService, ILayerPropertyVmFactory layerPropertyVmFactory)
|
public ProfileElementPropertiesViewModel(IProfileEditorService profileEditorService, ILayerPropertyVmFactory layerPropertyVmFactory, PlaybackViewModel playbackViewModel)
|
||||||
{
|
{
|
||||||
_profileEditorService = profileEditorService;
|
|
||||||
_layerPropertyVmFactory = layerPropertyVmFactory;
|
_layerPropertyVmFactory = layerPropertyVmFactory;
|
||||||
PropertyGroupViewModels = new ObservableCollection<ProfileElementPropertyGroupViewModel>();
|
_propertyGroupViewModels = new ObservableCollection<ProfileElementPropertyGroupViewModel>();
|
||||||
_profileElementGroups = new Dictionary<RenderProfileElement, List<ProfileElementPropertyGroupViewModel>>();
|
_cachedViewModels = new Dictionary<LayerPropertyGroup, ProfileElementPropertyGroupViewModel>();
|
||||||
|
PlaybackViewModel = playbackViewModel;
|
||||||
|
|
||||||
// Subscribe to events of the latest selected profile element - borrowed from https://stackoverflow.com/a/63950940
|
// Subscribe to events of the latest selected profile element - borrowed from https://stackoverflow.com/a/63950940
|
||||||
this.WhenAnyValue(vm => vm.ProfileElement)
|
this.WhenAnyValue(vm => vm.ProfileElement)
|
||||||
@ -47,10 +46,11 @@ public class ProfileElementPropertiesViewModel : ActivatableViewModelBase
|
|||||||
.Subscribe(_ => UpdateGroups());
|
.Subscribe(_ => UpdateGroups());
|
||||||
// React to service profile element changes as long as the VM is active
|
// React to service profile element changes as long as the VM is active
|
||||||
|
|
||||||
this.WhenActivated(d => _profileElement = _profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d));
|
this.WhenActivated(d => _profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d));
|
||||||
this.WhenAnyValue(vm => vm.ProfileElement).Subscribe(_ => UpdateGroups());
|
this.WhenAnyValue(vm => vm.ProfileElement).Subscribe(_ => UpdateGroups());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlaybackViewModel PlaybackViewModel { get; }
|
||||||
public RenderProfileElement? ProfileElement => _profileElement?.Value;
|
public RenderProfileElement? ProfileElement => _profileElement?.Value;
|
||||||
public Layer? Layer => _profileElement?.Value as Layer;
|
public Layer? Layer => _profileElement?.Value as Layer;
|
||||||
|
|
||||||
@ -68,57 +68,51 @@ public class ProfileElementPropertiesViewModel : ActivatableViewModelBase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_profileElementGroups.TryGetValue(ProfileElement, out List<ProfileElementPropertyGroupViewModel>? viewModels))
|
ObservableCollection<ProfileElementPropertyGroupViewModel> viewModels = new();
|
||||||
{
|
|
||||||
viewModels = new List<ProfileElementPropertyGroupViewModel>();
|
|
||||||
_profileElementGroups[ProfileElement] = viewModels;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<LayerPropertyGroup> groups = new();
|
|
||||||
|
|
||||||
if (Layer != null)
|
if (Layer != null)
|
||||||
{
|
{
|
||||||
// Add default layer groups
|
// Add base VMs
|
||||||
groups.Add(Layer.General);
|
viewModels.Add(GetOrCreateViewModel(Layer.General, null, null));
|
||||||
groups.Add(Layer.Transform);
|
viewModels.Add(GetOrCreateViewModel(Layer.Transform, null, null));
|
||||||
// Add brush group
|
|
||||||
|
// Add brush VM if the brush has properties
|
||||||
if (Layer.LayerBrush?.BaseProperties != null)
|
if (Layer.LayerBrush?.BaseProperties != null)
|
||||||
groups.Add(Layer.LayerBrush.BaseProperties);
|
viewModels.Add(GetOrCreateViewModel(Layer.LayerBrush.BaseProperties, Layer.LayerBrush, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add effect groups
|
// Add effect VMs
|
||||||
foreach (BaseLayerEffect layerEffect in ProfileElement.LayerEffects)
|
foreach (BaseLayerEffect layerEffect in ProfileElement.LayerEffects.OrderBy(e => e.Order))
|
||||||
{
|
|
||||||
if (layerEffect.BaseProperties != null)
|
if (layerEffect.BaseProperties != null)
|
||||||
groups.Add(layerEffect.BaseProperties);
|
viewModels.Add(GetOrCreateViewModel(layerEffect.BaseProperties, null, layerEffect));
|
||||||
}
|
|
||||||
|
|
||||||
// Remove redundant VMs
|
// Map the most recent collection of VMs to the current list of VMs, making as little changes to the collection as possible
|
||||||
viewModels.RemoveAll(vm => !groups.Contains(vm.LayerPropertyGroup));
|
for (int index = 0; index < viewModels.Count; index++)
|
||||||
|
|
||||||
// Create VMs for missing groups
|
|
||||||
foreach (LayerPropertyGroup group in groups)
|
|
||||||
{
|
{
|
||||||
if (viewModels.All(vm => vm.LayerPropertyGroup != group))
|
ProfileElementPropertyGroupViewModel profileElementPropertyGroupViewModel = viewModels[index];
|
||||||
viewModels.Add(_layerPropertyVmFactory.ProfileElementPropertyGroupViewModel(group));
|
if (index > PropertyGroupViewModels.Count - 1)
|
||||||
|
PropertyGroupViewModels.Add(profileElementPropertyGroupViewModel);
|
||||||
|
else if (!ReferenceEquals(PropertyGroupViewModels[index], profileElementPropertyGroupViewModel))
|
||||||
|
PropertyGroupViewModels[index] = profileElementPropertyGroupViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all non-effect properties
|
while (PropertyGroupViewModels.Count > viewModels.Count)
|
||||||
List<ProfileElementPropertyGroupViewModel> nonEffectProperties = viewModels
|
PropertyGroupViewModels.RemoveAt(PropertyGroupViewModels.Count - 1);
|
||||||
.Where(l => l.TreeGroupViewModel.GroupType != LayerPropertyGroupType.LayerEffectRoot)
|
}
|
||||||
.ToList();
|
|
||||||
// Order the effects
|
|
||||||
List<ProfileElementPropertyGroupViewModel> effectProperties = viewModels
|
|
||||||
.Where(l => l.TreeGroupViewModel.GroupType == LayerPropertyGroupType.LayerEffectRoot && l.LayerPropertyGroup.LayerEffect != null)
|
|
||||||
.OrderBy(l => l.LayerPropertyGroup.LayerEffect?.Order)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
ObservableCollection<ProfileElementPropertyGroupViewModel> propertyGroupViewModels = new();
|
private ProfileElementPropertyGroupViewModel GetOrCreateViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush? layerBrush, BaseLayerEffect? layerEffect)
|
||||||
foreach (ProfileElementPropertyGroupViewModel viewModel in nonEffectProperties)
|
{
|
||||||
propertyGroupViewModels.Add(viewModel);
|
if (_cachedViewModels.TryGetValue(layerPropertyGroup, out ProfileElementPropertyGroupViewModel? cachedVm))
|
||||||
foreach (ProfileElementPropertyGroupViewModel viewModel in effectProperties)
|
return cachedVm;
|
||||||
propertyGroupViewModels.Add(viewModel);
|
|
||||||
|
|
||||||
PropertyGroupViewModels = propertyGroupViewModels;
|
ProfileElementPropertyGroupViewModel createdVm;
|
||||||
|
if (layerBrush != null)
|
||||||
|
createdVm = _layerPropertyVmFactory.ProfileElementPropertyGroupViewModel(layerPropertyGroup, layerBrush);
|
||||||
|
else if (layerEffect != null)
|
||||||
|
createdVm = _layerPropertyVmFactory.ProfileElementPropertyGroupViewModel(layerPropertyGroup, layerEffect);
|
||||||
|
else
|
||||||
|
createdVm = _layerPropertyVmFactory.ProfileElementPropertyGroupViewModel(layerPropertyGroup);
|
||||||
|
|
||||||
|
_cachedViewModels[layerPropertyGroup] = createdVm;
|
||||||
|
return createdVm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,6 +3,8 @@ using System.Collections.ObjectModel;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.LayerBrushes;
|
||||||
|
using Artemis.Core.LayerEffects;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree;
|
using Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
@ -33,8 +35,23 @@ public class ProfileElementPropertyGroupViewModel : ViewModelBase
|
|||||||
PopulateChildren();
|
PopulateChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ProfileElementPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService, BaseLayerBrush layerBrush)
|
||||||
|
: this(layerPropertyGroup, layerPropertyVmFactory, propertyInputService)
|
||||||
|
{
|
||||||
|
LayerBrush = layerBrush;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileElementPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService, BaseLayerEffect layerEffect)
|
||||||
|
: this(layerPropertyGroup, layerPropertyVmFactory, propertyInputService)
|
||||||
|
{
|
||||||
|
LayerEffect = layerEffect;
|
||||||
|
}
|
||||||
|
|
||||||
public ObservableCollection<ViewModelBase> Children { get; }
|
public ObservableCollection<ViewModelBase> Children { get; }
|
||||||
public LayerPropertyGroup LayerPropertyGroup { get; }
|
public LayerPropertyGroup LayerPropertyGroup { get; }
|
||||||
|
public BaseLayerBrush? LayerBrush { get; }
|
||||||
|
public BaseLayerEffect? LayerEffect { get; }
|
||||||
|
|
||||||
public TreeGroupViewModel TreeGroupViewModel { get; }
|
public TreeGroupViewModel TreeGroupViewModel { get; }
|
||||||
|
|
||||||
public bool IsVisible
|
public bool IsVisible
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
using ReactiveUI;
|
using Artemis.Core;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree;
|
namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree;
|
||||||
|
|
||||||
public interface ITreePropertyViewModel : IReactiveObject
|
public interface ITreePropertyViewModel : IReactiveObject
|
||||||
{
|
{
|
||||||
|
ILayerProperty BaseLayerProperty { get; }
|
||||||
bool HasDataBinding { get; }
|
bool HasDataBinding { get; }
|
||||||
double GetDepth();
|
double GetDepth();
|
||||||
}
|
}
|
||||||
@ -70,24 +70,24 @@
|
|||||||
<Grid IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.LayerBrushRoot}}"
|
<Grid IsVisible="{Binding GroupType, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static viewModel:LayerPropertyGroupType.LayerBrushRoot}}"
|
||||||
ColumnDefinitions="Auto,Auto,Auto,*">
|
ColumnDefinitions="Auto,Auto,Auto,*">
|
||||||
<controls:ArtemisIcon Grid.Column="0"
|
<controls:ArtemisIcon Grid.Column="0"
|
||||||
Icon="{Binding LayerPropertyGroup.LayerBrush.Descriptor.Icon}"
|
Icon="{Binding LayerBrush.Descriptor.Icon}"
|
||||||
Width="16"
|
Width="16"
|
||||||
Height="16"
|
Height="16"
|
||||||
Margin="0 5 5 0" />
|
Margin="0 5 5 0" />
|
||||||
<TextBlock Grid.Column="1"
|
<TextBlock Grid.Column="1"
|
||||||
ToolTip.Tip="{Binding LayerPropertyGroup.LayerBrush.Descriptor.Description}"
|
ToolTip.Tip="{Binding LayerBrush.Descriptor.Description}"
|
||||||
Margin="0 5 0 0">
|
Margin="0 5 0 0">
|
||||||
Brush - 
|
Brush - 
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Grid.Column="2"
|
<TextBlock Grid.Column="2"
|
||||||
Text="{Binding LayerPropertyGroup.LayerBrush.Descriptor.DisplayName}"
|
Text="{Binding LayerBrush.Descriptor.DisplayName}"
|
||||||
ToolTip.Tip="{Binding LayerPropertyGroup.LayerBrush.Descriptor.Description}"
|
ToolTip.Tip="{Binding LayerBrush.Descriptor.Description}"
|
||||||
Margin="0 5 0 0" />
|
Margin="0 5 0 0" />
|
||||||
|
|
||||||
<StackPanel Grid.Column="3"
|
<StackPanel Grid.Column="3"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
IsVisible="{Binding LayerPropertyGroup.LayerBrush.ConfigurationDialog, Converter={x:Static ObjectConverters.IsNotNull}}">
|
IsVisible="{Binding LayerBrush.ConfigurationDialog, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
<TextBlock VerticalAlignment="Center">Extra options available!</TextBlock>
|
<TextBlock VerticalAlignment="Center">Extra options available!</TextBlock>
|
||||||
<avalonia:MaterialIcon Kind="ChevronRight" VerticalAlignment="Center">
|
<avalonia:MaterialIcon Kind="ChevronRight" VerticalAlignment="Center">
|
||||||
<avalonia:MaterialIcon.RenderTransform>
|
<avalonia:MaterialIcon.RenderTransform>
|
||||||
@ -108,31 +108,31 @@
|
|||||||
<controls:ArtemisIcon
|
<controls:ArtemisIcon
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Cursor="SizeNorthSouth"
|
Cursor="SizeNorthSouth"
|
||||||
Icon="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Icon}"
|
Icon="{Binding LayerEffect.Descriptor.Icon}"
|
||||||
Width="16"
|
Width="16"
|
||||||
Height="16"
|
Height="16"
|
||||||
Margin="0 5 5 0"
|
Margin="0 5 5 0"
|
||||||
Background="Transparent" />
|
Background="Transparent" />
|
||||||
<TextBlock Grid.Column="1" ToolTip.Tip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}" Margin="0 5 0 0">
|
<TextBlock Grid.Column="1" ToolTip.Tip="{Binding LayerEffect.Descriptor.Description}" Margin="0 5 0 0">
|
||||||
Effect
|
Effect
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Grid.Column="2"
|
<TextBlock Grid.Column="2"
|
||||||
ToolTip.Tip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}"
|
ToolTip.Tip="{Binding LayerEffect.Descriptor.Description}"
|
||||||
Margin="3 5">
|
Margin="3 5">
|
||||||
-
|
-
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
<!-- Show either the descriptors display name or, if set, the effect name -->
|
<!-- Show either the descriptors display name or, if set, the effect name -->
|
||||||
<TextBlock Grid.Column="3"
|
<TextBlock Grid.Column="3"
|
||||||
Text="{Binding LayerPropertyGroup.LayerEffect.Descriptor.DisplayName}"
|
Text="{Binding LayerEffect.Descriptor.DisplayName}"
|
||||||
ToolTip.Tip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}"
|
ToolTip.Tip="{Binding LayerEffect.Descriptor.Description}"
|
||||||
Margin="0 5"
|
Margin="0 5"
|
||||||
IsVisible="{Binding !LayerPropertyGroup.LayerEffect.Name, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
IsVisible="{Binding !LayerEffect.Name, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||||
<TextBlock Grid.Column="4"
|
<TextBlock Grid.Column="4"
|
||||||
Text="{Binding LayerPropertyGroup.LayerEffect.Name}"
|
Text="{Binding LayerEffect.Name}"
|
||||||
ToolTip.Tip="{Binding LayerPropertyGroup.LayerEffect.Descriptor.Description}"
|
ToolTip.Tip="{Binding LayerEffect.Descriptor.Description}"
|
||||||
Margin="0 5"
|
Margin="0 5"
|
||||||
IsVisible="{Binding LayerPropertyGroup.LayerEffect.Name, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
IsVisible="{Binding LayerEffect.Name, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||||
|
|
||||||
<StackPanel Grid.Column="5" Orientation="Horizontal">
|
<StackPanel Grid.Column="5" Orientation="Horizontal">
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
@ -140,7 +140,7 @@
|
|||||||
ToolTip.Tip="Toggle suspended state"
|
ToolTip.Tip="Toggle suspended state"
|
||||||
Width="18"
|
Width="18"
|
||||||
Height="18"
|
Height="18"
|
||||||
IsChecked="{Binding !LayerPropertyGroup.LayerEffect.Suspended}"
|
IsChecked="{Binding !LayerEffect.Suspended}"
|
||||||
VerticalAlignment="Center" Padding="-25"
|
VerticalAlignment="Center" Padding="-25"
|
||||||
Margin="5 0"
|
Margin="5 0"
|
||||||
Command="{Binding SuspendedToggled}">
|
Command="{Binding SuspendedToggled}">
|
||||||
@ -160,7 +160,7 @@
|
|||||||
Height="24"
|
Height="24"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Command="{Binding OpenEffectSettings}"
|
Command="{Binding OpenEffectSettings}"
|
||||||
IsVisible="{Binding LayerPropertyGroup.LayerEffect.ConfigurationDialog, Converter={x:Static ObjectConverters.IsNotNull}}">
|
IsVisible="{Binding LayerEffect.ConfigurationDialog, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
<avalonia:MaterialIcon Kind="Settings" Height="16" Width="16" />
|
<avalonia:MaterialIcon Kind="Settings" Height="16" Width="16" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button Classes="icon-button"
|
<Button Classes="icon-button"
|
||||||
|
|||||||
@ -46,14 +46,16 @@ public class TreeGroupViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
public ProfileElementPropertyGroupViewModel ProfileElementPropertyGroupViewModel { get; }
|
public ProfileElementPropertyGroupViewModel ProfileElementPropertyGroupViewModel { get; }
|
||||||
public LayerPropertyGroup LayerPropertyGroup => ProfileElementPropertyGroupViewModel.LayerPropertyGroup;
|
public LayerPropertyGroup LayerPropertyGroup => ProfileElementPropertyGroupViewModel.LayerPropertyGroup;
|
||||||
|
public BaseLayerBrush? LayerBrush => ProfileElementPropertyGroupViewModel.LayerBrush;
|
||||||
|
public BaseLayerEffect? LayerEffect => ProfileElementPropertyGroupViewModel.LayerEffect;
|
||||||
|
|
||||||
public ObservableCollection<ViewModelBase>? Children => ProfileElementPropertyGroupViewModel.IsExpanded ? ProfileElementPropertyGroupViewModel.Children : null;
|
public ObservableCollection<ViewModelBase>? Children => ProfileElementPropertyGroupViewModel.IsExpanded ? ProfileElementPropertyGroupViewModel.Children : null;
|
||||||
|
|
||||||
public LayerPropertyGroupType GroupType { get; private set; }
|
public LayerPropertyGroupType GroupType { get; private set; }
|
||||||
|
|
||||||
public async Task OpenBrushSettings()
|
public async Task OpenBrushSettings()
|
||||||
{
|
{
|
||||||
BaseLayerBrush? layerBrush = LayerPropertyGroup.LayerBrush;
|
if (LayerBrush?.ConfigurationDialog is not LayerBrushConfigurationDialog configurationViewModel)
|
||||||
if (layerBrush?.ConfigurationDialog is not LayerBrushConfigurationDialog configurationViewModel)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -65,8 +67,9 @@ public class TreeGroupViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
// Find the BaseLayerBrush parameter, it is required by the base constructor so its there for sure
|
// Find the BaseLayerBrush parameter, it is required by the base constructor so its there for sure
|
||||||
ParameterInfo brushParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerBrush).IsAssignableFrom(p.ParameterType));
|
ParameterInfo brushParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerBrush).IsAssignableFrom(p.ParameterType));
|
||||||
ConstructorArgument argument = new(brushParameter.Name!, layerBrush);
|
ConstructorArgument argument = new(brushParameter.Name!, LayerBrush);
|
||||||
BrushConfigurationViewModel viewModel = (BrushConfigurationViewModel) layerBrush.Descriptor.Provider.Plugin.Kernel!.Get(configurationViewModel.Type, argument);
|
BrushConfigurationViewModel viewModel =
|
||||||
|
(BrushConfigurationViewModel) LayerBrush.Descriptor.Provider.Plugin.Kernel!.Get(configurationViewModel.Type, argument);
|
||||||
|
|
||||||
_brushConfigurationWindowViewModel = new BrushConfigurationWindowViewModel(viewModel, configurationViewModel);
|
_brushConfigurationWindowViewModel = new BrushConfigurationWindowViewModel(viewModel, configurationViewModel);
|
||||||
await _windowService.ShowDialogAsync(_brushConfigurationWindowViewModel);
|
await _windowService.ShowDialogAsync(_brushConfigurationWindowViewModel);
|
||||||
@ -82,8 +85,7 @@ public class TreeGroupViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
public async Task OpenEffectSettings()
|
public async Task OpenEffectSettings()
|
||||||
{
|
{
|
||||||
BaseLayerEffect? layerEffect = LayerPropertyGroup.LayerEffect;
|
if (LayerEffect?.ConfigurationDialog is not LayerEffectConfigurationDialog configurationViewModel)
|
||||||
if (layerEffect?.ConfigurationDialog is not LayerEffectConfigurationDialog configurationViewModel)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -95,8 +97,9 @@ public class TreeGroupViewModel : ActivatableViewModelBase
|
|||||||
|
|
||||||
// Find the BaseLayerEffect parameter, it is required by the base constructor so its there for sure
|
// Find the BaseLayerEffect parameter, it is required by the base constructor so its there for sure
|
||||||
ParameterInfo effectParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerEffect).IsAssignableFrom(p.ParameterType));
|
ParameterInfo effectParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerEffect).IsAssignableFrom(p.ParameterType));
|
||||||
ConstructorArgument argument = new(effectParameter.Name!, layerEffect);
|
ConstructorArgument argument = new(effectParameter.Name!, LayerEffect);
|
||||||
EffectConfigurationViewModel viewModel = (EffectConfigurationViewModel) layerEffect.Descriptor.Provider.Plugin.Kernel!.Get(configurationViewModel.Type, argument);
|
EffectConfigurationViewModel viewModel =
|
||||||
|
(EffectConfigurationViewModel) LayerEffect.Descriptor.Provider.Plugin.Kernel!.Get(configurationViewModel.Type, argument);
|
||||||
|
|
||||||
_effectConfigurationWindowViewModel = new EffectConfigurationWindowViewModel(viewModel, configurationViewModel);
|
_effectConfigurationWindowViewModel = new EffectConfigurationWindowViewModel(viewModel, configurationViewModel);
|
||||||
await _windowService.ShowDialogAsync(_effectConfigurationWindowViewModel);
|
await _windowService.ShowDialogAsync(_effectConfigurationWindowViewModel);
|
||||||
@ -145,9 +148,9 @@ public class TreeGroupViewModel : ActivatableViewModelBase
|
|||||||
GroupType = LayerPropertyGroupType.General;
|
GroupType = LayerPropertyGroupType.General;
|
||||||
else if (LayerPropertyGroup is LayerTransformProperties)
|
else if (LayerPropertyGroup is LayerTransformProperties)
|
||||||
GroupType = LayerPropertyGroupType.Transform;
|
GroupType = LayerPropertyGroupType.Transform;
|
||||||
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerBrush != null)
|
else if (LayerPropertyGroup.Parent == null && ProfileElementPropertyGroupViewModel.LayerBrush != null)
|
||||||
GroupType = LayerPropertyGroupType.LayerBrushRoot;
|
GroupType = LayerPropertyGroupType.LayerBrushRoot;
|
||||||
else if (LayerPropertyGroup.Parent == null && LayerPropertyGroup.LayerEffect != null)
|
else if (LayerPropertyGroup.Parent == null && ProfileElementPropertyGroupViewModel.LayerEffect != null)
|
||||||
GroupType = LayerPropertyGroupType.LayerEffectRoot;
|
GroupType = LayerPropertyGroupType.LayerEffectRoot;
|
||||||
else
|
else
|
||||||
GroupType = LayerPropertyGroupType.None;
|
GroupType = LayerPropertyGroupType.None;
|
||||||
|
|||||||
@ -1,13 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Mixins;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree
|
namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree
|
||||||
{
|
{
|
||||||
public partial class TreePropertyView : ReactiveUserControl<IActivatableViewModel>
|
public partial class TreePropertyView : ReactiveUserControl<ITreePropertyViewModel>
|
||||||
{
|
{
|
||||||
public TreePropertyView()
|
public TreePropertyView()
|
||||||
{
|
{
|
||||||
|
this.WhenActivated(d =>
|
||||||
|
{
|
||||||
|
if (ViewModel != null)
|
||||||
|
{
|
||||||
|
Observable.FromEventPattern<LayerPropertyEventArgs>(e => ViewModel.BaseLayerProperty.CurrentValueSet += e, e => ViewModel.BaseLayerProperty.CurrentValueSet -= e)
|
||||||
|
.Subscribe(_ => this.BringIntoView())
|
||||||
|
.DisposeWith(d);
|
||||||
|
}
|
||||||
|
});
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,4 +30,4 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.Tree
|
|||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -16,6 +16,8 @@ internal class TreePropertyViewModel<T> : ActivatableViewModelBase, ITreePropert
|
|||||||
public LayerProperty<T> LayerProperty { get; }
|
public LayerProperty<T> LayerProperty { get; }
|
||||||
public ProfileElementPropertyViewModel ProfileElementPropertyViewModel { get; }
|
public ProfileElementPropertyViewModel ProfileElementPropertyViewModel { get; }
|
||||||
public PropertyInputViewModel<T>? PropertyInputViewModel { get; }
|
public PropertyInputViewModel<T>? PropertyInputViewModel { get; }
|
||||||
|
|
||||||
|
public ILayerProperty BaseLayerProperty => LayerProperty;
|
||||||
public bool HasDataBinding => LayerProperty.HasDataBinding;
|
public bool HasDataBinding => LayerProperty.HasDataBinding;
|
||||||
|
|
||||||
public double GetDepth()
|
public double GetDepth()
|
||||||
|
|||||||
@ -37,8 +37,8 @@
|
|||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
<Grid ColumnDefinitions="4*,Auto,*" Classes="editor-grid">
|
<Grid ColumnDefinitions="4*,Auto,*" Classes="editor-grid">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<ContentControl Content="{Binding MenuBarViewModel}"></ContentControl>
|
<ContentControl Content="{Binding MenuBarViewModel}"></ContentControl>
|
||||||
<Grid Grid.Column="0" RowDefinitions="3*,Auto,*">
|
<Grid Grid.Column="0" RowDefinitions="3*,Auto,*">
|
||||||
@ -66,8 +66,8 @@
|
|||||||
|
|
||||||
<GridSplitter Grid.Row="1" Classes="editor-grid-splitter-horizontal" />
|
<GridSplitter Grid.Row="1" Classes="editor-grid-splitter-horizontal" />
|
||||||
|
|
||||||
<Border Grid.Row="2" Classes="card card-condensed" Margin="4">
|
<Border Grid.Row="2" Classes="card card-condensed" Margin="4" Padding="0" ClipToBounds="True">
|
||||||
<ContentControl Content="{Binding ProfileElementPropertiesViewModel}"></ContentControl>
|
<ContentControl Content="{Binding ProfileElementPropertiesViewModel}"/>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@ -85,6 +85,6 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<ContentControl Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Content="{Binding StatusBarViewModel}"></ContentControl>
|
<ContentControl Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Content="{Binding StatusBarViewModel}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -126,7 +126,7 @@ namespace Artemis.UI.Screens.Root
|
|||||||
{
|
{
|
||||||
_trayIcon = new TrayIcon
|
_trayIcon = new TrayIcon
|
||||||
{
|
{
|
||||||
Icon = new WindowIcon(_assetLoader.Open(new Uri("avares://Artemis.UI/Assets/Images/Logo/bow.ico"))),
|
Icon = new WindowIcon(_assetLoader.Open(new Uri("avares://Artemis.UI/Assets/Images/Logo/application.ico"))),
|
||||||
Command = ReactiveCommand.Create(OpenMainWindow)
|
Command = ReactiveCommand.Create(OpenMainWindow)
|
||||||
};
|
};
|
||||||
_trayIcon.Menu = (NativeMenu?) Application.Current!.FindResource("TrayIconMenu");
|
_trayIcon.Menu = (NativeMenu?) Application.Current!.FindResource("TrayIconMenu");
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
xmlns:svg="clr-namespace:Avalonia.Svg.Skia;assembly=Avalonia.Svg.Skia"
|
xmlns:svg="clr-namespace:Avalonia.Svg.Skia;assembly=Avalonia.Svg.Skia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Root.SplashView"
|
x:Class="Artemis.UI.Screens.Root.SplashView"
|
||||||
Icon="/Assets/Images/Logo/bow.ico"
|
Icon="/Assets/Images/Logo/application.ico"
|
||||||
Title="Artemis 2.0"
|
Title="Artemis 2.0"
|
||||||
Height="450"
|
Height="450"
|
||||||
Width="400"
|
Width="400"
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="850"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="850"
|
||||||
x:Class="Artemis.UI.Screens.Sidebar.ProfileConfigurationEditView"
|
x:Class="Artemis.UI.Screens.Sidebar.ProfileConfigurationEditView"
|
||||||
Title="{Binding DisplayName}"
|
Title="{Binding DisplayName}"
|
||||||
Icon="/Assets/Images/Logo/bow.ico"
|
Icon="/Assets/Images/Logo/application.ico"
|
||||||
Width="800"
|
Width="800"
|
||||||
MinWidth="420"
|
MinWidth="420"
|
||||||
Height="850">
|
Height="850">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user