diff --git a/src/.idea/.idea.Artemis/.idea/avalonia.xml b/src/.idea/.idea.Artemis/.idea/avalonia.xml
index cdb3f426a..663884d07 100644
--- a/src/.idea/.idea.Artemis/.idea/avalonia.xml
+++ b/src/.idea/.idea.Artemis/.idea/avalonia.xml
@@ -14,6 +14,13 @@
+
+
+
+
+
+
+
diff --git a/src/.idea/.idea.Artemis/.idea/misc.xml b/src/.idea/.idea.Artemis/.idea/misc.xml
new file mode 100644
index 000000000..283b9b4d4
--- /dev/null
+++ b/src/.idea/.idea.Artemis/.idea/misc.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.Core/Extensions/TypeExtensions.cs b/src/Artemis.Core/Extensions/TypeExtensions.cs
index e8c25e05a..748e4dc51 100644
--- a/src/Artemis.Core/Extensions/TypeExtensions.cs
+++ b/src/Artemis.Core/Extensions/TypeExtensions.cs
@@ -230,7 +230,7 @@ namespace Artemis.Core
}
if (genericType.IsInterface)
- foreach (var i in typeToCheck.GetInterfaces())
+ foreach (Type i in typeToCheck.GetInterfaces())
if (i.IsOfGenericType(genericType, out concreteGenericType))
return true;
diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index 8ffbaec64..afc95727b 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -227,19 +227,29 @@ namespace Artemis.Core
///
public override void Enable()
{
- if (Enabled)
- return;
-
+ // No checks here, effects will do their own checks to ensure they never enable twice
+ // Also not enabling children, they'll enable themselves during their own Update
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
baseLayerEffect.InternalEnable();
+ Enabled = true;
+ }
+
+ ///
+ public override void Disable()
+ {
+ // No checks here, effects will do their own checks to ensure they never disable twice
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
+ baseLayerEffect.InternalDisable();
+
+ // Disabling children since their Update won't get called with their parent disabled
foreach (ProfileElement profileElement in Children)
{
- if (profileElement is RenderProfileElement renderProfileElement && renderProfileElement.ShouldBeEnabled)
- renderProfileElement.Enable();
+ if (profileElement is RenderProfileElement renderProfileElement)
+ renderProfileElement.Disable();
}
- Enabled = true;
+ Enabled = false;
}
///
@@ -250,24 +260,6 @@ namespace Artemis.Core
baseLayerEffect.InternalUpdate(Timeline); ;
}
- ///
- public override void Disable()
- {
- if (!Enabled)
- return;
-
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
- baseLayerEffect.InternalDisable();
-
- foreach (ProfileElement profileElement in Children)
- {
- if (profileElement is RenderProfileElement renderProfileElement)
- renderProfileElement.Disable();
- }
-
- Enabled = false;
- }
-
///
/// Occurs when a property affecting the rendering properties of this folder has been updated
///
@@ -308,7 +300,6 @@ namespace Artemis.Core
internal override void Load()
{
- ExpandedPropertyGroups.AddRange(FolderEntity.ExpandedPropertyGroups);
Reset();
// Load child folders
@@ -340,8 +331,6 @@ namespace Artemis.Core
FolderEntity.Suspended = Suspended;
FolderEntity.ProfileId = Profile.EntityId;
- FolderEntity.ExpandedPropertyGroups.Clear();
- FolderEntity.ExpandedPropertyGroups.AddRange(ExpandedPropertyGroups);
SaveRenderElement();
}
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 79099830f..4153c6cd8 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -296,7 +296,6 @@ namespace Artemis.Core
Suspended = LayerEntity.Suspended;
Order = LayerEntity.Order;
- ExpandedPropertyGroups.AddRange(LayerEntity.ExpandedPropertyGroups);
LoadRenderElement();
Adapter.Load();
}
@@ -313,8 +312,6 @@ namespace Artemis.Core
LayerEntity.Suspended = Suspended;
LayerEntity.Name = Name;
LayerEntity.ProfileId = Profile.EntityId;
- LayerEntity.ExpandedPropertyGroups.Clear();
- LayerEntity.ExpandedPropertyGroups.AddRange(ExpandedPropertyGroups);
General.ApplyToEntity();
Transform.ApplyToEntity();
@@ -382,7 +379,7 @@ namespace Artemis.Core
UpdateDisplayCondition();
UpdateTimeline(deltaTime);
-
+
if (ShouldBeEnabled)
Enable();
else if (Timeline.IsFinished && !_renderCopies.Any())
@@ -505,9 +502,7 @@ namespace Artemis.Core
///
public override void Enable()
{
- if (Enabled)
- return;
-
+ // No checks here, the brush and effects will do their own checks to ensure they never enable twice
bool tryOrBreak = TryOrBreak(() => LayerBrush?.InternalEnable(), "Failed to enable layer brush");
if (!tryOrBreak)
return;
@@ -519,10 +514,21 @@ namespace Artemis.Core
}, "Failed to enable one or more effects");
if (!tryOrBreak)
return;
-
+
Enabled = true;
}
+ ///
+ public override void Disable()
+ {
+ // No checks here, the brush and effects will do their own checks to ensure they never disable twice
+ LayerBrush?.InternalDisable();
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
+ baseLayerEffect.InternalDisable();
+
+ Enabled = false;
+ }
+
///
public override void OverrideTimelineAndApply(TimeSpan position)
{
@@ -536,19 +542,6 @@ namespace Artemis.Core
baseLayerEffect.InternalUpdate(Timeline);
}
- ///
- public override void Disable()
- {
- if (!Enabled)
- return;
-
- LayerBrush?.InternalDisable();
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
- baseLayerEffect.InternalDisable();
-
- Enabled = false;
- }
-
///
public override void Reset()
{
@@ -822,6 +815,7 @@ namespace Artemis.Core
General.ShapeType.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
General.BlendMode.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
Transform.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
+ LayerBrush?.Update(0);
OnLayerBrushUpdated();
ClearBrokenState("Failed to initialize layer brush");
diff --git a/src/Artemis.Core/Models/Profile/LayerEffectPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerEffectPropertyGroup.cs
new file mode 100644
index 000000000..5e1b6abb7
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/LayerEffectPropertyGroup.cs
@@ -0,0 +1,25 @@
+namespace Artemis.Core
+{
+ ///
+ /// Represents a property group on a layer
+ ///
+ /// Note: You cannot initialize property groups yourself. If properly placed and annotated, the Artemis core will
+ /// initialize these for you.
+ ///
+ ///
+ public abstract class LayerEffectPropertyGroup : LayerPropertyGroup
+ {
+ ///
+ /// Whether or not this layer effect is enabled
+ ///
+ [PropertyDescription(Name = "Enabled", Description = "Whether or not this layer effect is enabled")]
+ public BoolLayerProperty IsEnabled { get; set; } = null!;
+
+ internal void InitializeIsEnabled()
+ {
+ IsEnabled.DefaultValue = true;
+ if (!IsEnabled.IsLoadedFromStorage)
+ IsEnabled.SetCurrentValue(true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
index f671d6a07..fa1e96f0e 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
@@ -203,7 +203,7 @@ namespace Artemis.Core
/// or existing keyframe.
///
/// The keyframe if one was created or updated.
- public LayerPropertyKeyframe? SetCurrentValue(T value, TimeSpan? time)
+ public LayerPropertyKeyframe? SetCurrentValue(T value, TimeSpan? time = null)
{
if (_disposed)
throw new ObjectDisposedException("LayerProperty");
diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
index 3cdbeec17..6a80f1ac8 100644
--- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
+++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
@@ -249,6 +249,15 @@ namespace Artemis.Core
layerPropertyGroup.Update(timeline);
}
+ internal void MoveLayerProperty(ILayerProperty layerProperty, int index)
+ {
+ if (!_layerProperties.Contains(layerProperty))
+ return;
+
+ _layerProperties.Remove(layerProperty);
+ _layerProperties.Insert(index, layerProperty);
+ }
+
internal virtual void OnVisibilityChanged()
{
VisibilityChanged?.Invoke(this, EventArgs.Empty);
@@ -292,7 +301,7 @@ namespace Artemis.Core
throw new ArtemisPluginException($"Property with PropertyGroupDescription attribute must be of type LayerPropertyGroup: {propertyGroupDescription.Identifier}");
if (!(Activator.CreateInstance(propertyInfo.PropertyType) is LayerPropertyGroup instance))
throw new ArtemisPluginException($"Failed to create instance of layer property group: {propertyGroupDescription.Identifier}");
-
+
PropertyGroupEntity entity = GetPropertyGroupEntity(propertyGroupDescription.Identifier);
instance.Initialize(ProfileElement, this, propertyGroupDescription, entity);
diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
index 9064c1978..97fc1489e 100644
--- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
@@ -4,393 +4,393 @@ using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.LayerEffects;
using Artemis.Core.LayerEffects.Placeholder;
-using Artemis.Core.Properties;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
using SkiaSharp;
-namespace Artemis.Core
+namespace Artemis.Core;
+
+///
+/// Represents an element of a that has advanced rendering capabilities
+///
+public abstract class RenderProfileElement : ProfileElement
{
- ///
- /// Represents an element of a that has advanced rendering capabilities
- ///
- public abstract class RenderProfileElement : ProfileElement
+ private SKRectI _bounds;
+ private SKPath? _path;
+
+ internal RenderProfileElement(ProfileElement parent, Profile profile) : base(profile)
{
- private SKRectI _bounds;
- private SKPath? _path;
-
- internal RenderProfileElement(ProfileElement parent, Profile profile) : base(profile)
- {
- Timeline = new Timeline();
- ExpandedPropertyGroups = new List();
- LayerEffectsList = new List();
- LayerEffects = new ReadOnlyCollection(LayerEffectsList);
- Parent = parent ?? throw new ArgumentNullException(nameof(parent));
- _displayCondition = new AlwaysOnCondition(this);
-
- LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
- LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
- }
-
- ///
- /// Gets a boolean indicating whether this render element and its layers/brushes are enabled
- ///
- public bool Enabled { get; protected set; }
-
- ///
- /// Gets a boolean indicating whether this render element and its layers/brushes should be enabled
- ///
- public abstract bool ShouldBeEnabled { get; }
-
- ///
- /// Creates a list of all layer properties present on this render element
- ///
- /// A list of all layer properties present on this render element
- public abstract List GetAllLayerProperties();
-
- ///
- /// Occurs when a layer effect has been added or removed to this render element
- ///
- public event EventHandler? LayerEffectsUpdated;
-
- ///
- protected override void Dispose(bool disposing)
- {
- LayerEffectStore.LayerEffectAdded -= LayerEffectStoreOnLayerEffectAdded;
- LayerEffectStore.LayerEffectRemoved -= LayerEffectStoreOnLayerEffectRemoved;
-
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
- baseLayerEffect.Dispose();
-
- if (DisplayCondition is IDisposable disposable)
- disposable.Dispose();
-
- base.Dispose(disposing);
- }
-
- internal void LoadRenderElement()
- {
- Timeline = RenderElementEntity.Timeline != null
- ? new Timeline(RenderElementEntity.Timeline)
- : new Timeline();
-
- DisplayCondition = RenderElementEntity.DisplayCondition switch
- {
- AlwaysOnConditionEntity entity => new AlwaysOnCondition(entity, this),
- PlayOnceConditionEntity entity => new PlayOnceCondition(entity, this),
- StaticConditionEntity entity => new StaticCondition(entity, this),
- EventConditionEntity entity => new EventCondition(entity, this),
- _ => DisplayCondition
- };
-
- ActivateEffects();
- }
-
- internal void SaveRenderElement()
- {
- RenderElementEntity.LayerEffects.Clear();
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
- {
- baseLayerEffect.Save();
- RenderElementEntity.LayerEffects.Add(baseLayerEffect.LayerEffectEntity);
- }
-
- // Condition
- DisplayCondition?.Save();
- RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
-
- // Timeline
- RenderElementEntity.Timeline = Timeline?.Entity;
- Timeline?.Save();
- }
-
- internal void LoadNodeScript()
- {
- if (DisplayCondition is INodeScriptCondition scriptCondition)
- scriptCondition.LoadNodeScript();
-
- foreach (ILayerProperty layerProperty in GetAllLayerProperties())
- layerProperty.BaseDataBinding.LoadNodeScript();
- }
-
- internal void OnLayerEffectsUpdated()
- {
- LayerEffectsUpdated?.Invoke(this, EventArgs.Empty);
- }
-
- #region Timeline
-
- ///
- /// Gets the timeline associated with this render element
- ///
- public Timeline Timeline { get; private set; }
-
- ///
- /// Updates the according to the provided and current display condition
- ///
- protected void UpdateTimeline(double deltaTime)
- {
- DisplayCondition.UpdateTimeline(deltaTime);
- }
-
- #endregion
-
- #region Properties
-
- internal abstract RenderElementEntity RenderElementEntity { get; }
-
- ///
- /// Gets the parent of this element
- ///
- public new ProfileElement Parent
- {
- get => base.Parent!;
- internal set
- {
- base.Parent = value;
- OnPropertyChanged(nameof(Parent));
- }
- }
-
- ///
- /// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
- /// clipped.
- ///
- public SKPath? Path
- {
- get => _path;
- protected set
- {
- SetAndNotify(ref _path, value);
- // I can't really be sure about the performance impact of calling Bounds often but
- // SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
- Bounds = SKRectI.Round(value?.Bounds ?? SKRect.Empty);
- }
- }
-
- ///
- /// The bounds of this entity
- ///
- public SKRectI Bounds
- {
- get => _bounds;
- private set => SetAndNotify(ref _bounds, value);
- }
-
-
- #region Property group expansion
-
- internal List ExpandedPropertyGroups;
-
- ///
- /// Determines whether the provided property group is expanded
- ///
- /// The property group to check
- /// A boolean indicating whether the provided property group is expanded
- public bool IsPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup)
- {
- return ExpandedPropertyGroups.Contains(layerPropertyGroup.Path);
- }
-
- ///
- /// Expands or collapses the provided property group
- ///
- /// The group to expand or collapse
- /// Whether to expand or collapse the property group
- public void SetPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup, bool expanded)
- {
- if (!expanded && IsPropertyGroupExpanded(layerPropertyGroup))
- ExpandedPropertyGroups.Remove(layerPropertyGroup.Path);
- else if (expanded && !IsPropertyGroupExpanded(layerPropertyGroup))
- ExpandedPropertyGroups.Add(layerPropertyGroup.Path);
- }
-
- #endregion
-
- #endregion
-
- #region State
-
- ///
- /// Enables the render element and its brushes and effects
- ///
- public abstract void Disable();
-
- ///
- /// Disables the render element and its brushes and effects
- ///
- public abstract void Enable();
-
- #endregion
-
- #region Effect management
-
- internal readonly List LayerEffectsList;
-
- ///
- /// Gets a read-only collection of the layer effects on this entity
- ///
- public ReadOnlyCollection LayerEffects { get; }
-
- ///
- /// Adds a the layer effect described inthe provided
- ///
- public void AddLayerEffect(LayerEffectDescriptor descriptor)
- {
- if (descriptor == null)
- throw new ArgumentNullException(nameof(descriptor));
-
- LayerEffectEntity entity = new()
- {
- Id = Guid.NewGuid(),
- Suspended = false,
- Order = LayerEffects.Count + 1
- };
- descriptor.CreateInstance(this, entity);
-
- OrderEffects();
- OnLayerEffectsUpdated();
- }
-
- ///
- /// Removes the provided layer
- ///
- ///
- public void RemoveLayerEffect([NotNull] BaseLayerEffect effect)
- {
- if (effect == null) throw new ArgumentNullException(nameof(effect));
-
- // Remove the effect from the layer and dispose it
- LayerEffectsList.Remove(effect);
- effect.Dispose();
-
- // Update the order on the remaining effects
- OrderEffects();
- OnLayerEffectsUpdated();
- }
-
- private void OrderEffects()
- {
- int index = 0;
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects.OrderBy(e => e.Order))
- {
- baseLayerEffect.Order = Order = index + 1;
- index++;
- }
-
- LayerEffectsList.Sort((a, b) => a.Order.CompareTo(b.Order));
- }
-
- internal void ActivateEffects()
- {
- foreach (LayerEffectEntity layerEffectEntity in RenderElementEntity.LayerEffects)
- {
- // If there is a non-placeholder existing effect, skip this entity
- BaseLayerEffect? existing = LayerEffectsList.FirstOrDefault(e => e.LayerEffectEntity.Id == layerEffectEntity.Id);
- if (existing != null && existing.Descriptor.PlaceholderFor == null)
- continue;
-
- LayerEffectDescriptor? descriptor = LayerEffectStore.Get(layerEffectEntity.ProviderId, layerEffectEntity.EffectType)?.LayerEffectDescriptor;
- if (descriptor != null)
- {
- // If a descriptor is found but there is an existing placeholder, remove the placeholder
- if (existing != null)
- {
- LayerEffectsList.Remove(existing);
- existing.Dispose();
- }
-
- // Create an instance with the descriptor
- descriptor.CreateInstance(this, layerEffectEntity);
- }
- else if (existing == null)
- {
- // If no descriptor was found and there was no existing placeholder, create a placeholder
- descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffectEntity.ProviderId);
- descriptor.CreateInstance(this, layerEffectEntity);
- }
- }
-
- OrderEffects();
- }
-
-
- internal void ActivateLayerEffect(BaseLayerEffect layerEffect)
- {
- LayerEffectsList.Add(layerEffect);
- OnLayerEffectsUpdated();
- }
-
- private void LayerEffectStoreOnLayerEffectRemoved(object? sender, LayerEffectStoreEvent e)
- {
- // If effects provided by the plugin are on the element, replace them with placeholders
- List pluginEffects = LayerEffectsList.Where(ef => ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
- foreach (BaseLayerEffect pluginEffect in pluginEffects)
- {
- LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.LayerEffectEntity.Id);
- LayerEffectsList.Remove(pluginEffect);
- pluginEffect.Dispose();
-
- LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(pluginEffect.ProviderId);
- descriptor.CreateInstance(this, entity);
- }
- }
-
- private void LayerEffectStoreOnLayerEffectAdded(object? sender, LayerEffectStoreEvent e)
- {
- if (RenderElementEntity.LayerEffects.Any(ef => ef.ProviderId == e.Registration.PluginFeature.Id))
- ActivateEffects();
- }
-
- #endregion
-
- #region Conditions
-
- ///
- /// Gets whether the display conditions applied to this layer where met or not during last update
- /// Always true if the layer has no display conditions
- ///
- public bool DisplayConditionMet
- {
- get => _displayConditionMet;
- protected set => SetAndNotify(ref _displayConditionMet, value);
- }
-
- private bool _displayConditionMet;
-
- ///
- /// Gets or sets the display condition used to determine whether this element is active or not
- ///
- public ICondition DisplayCondition
- {
- get => _displayCondition;
- set => SetAndNotify(ref _displayCondition, value);
- }
-
- private ICondition _displayCondition;
-
- ///
- /// Evaluates the display conditions on this element and applies any required changes to the
- ///
- public void UpdateDisplayCondition()
- {
- if (Suspended)
- {
- DisplayConditionMet = false;
- return;
- }
-
- DisplayCondition.Update();
- DisplayConditionMet = DisplayCondition.IsMet;
- }
-
- #endregion
-
- ///
- /// Overrides the main timeline to the specified time and clears any extra time lines
- ///
- /// The position to set the timeline to
- public abstract void OverrideTimelineAndApply(TimeSpan position);
+ _layerEffects = new List();
+ _displayCondition = new AlwaysOnCondition(this);
+ Timeline = new Timeline();
+ LayerEffects = new ReadOnlyCollection(_layerEffects);
+ Parent = parent ?? throw new ArgumentNullException(nameof(parent));
+
+ LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
+ LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
}
+
+ ///
+ /// Gets a boolean indicating whether this render element and its layers/brushes are enabled
+ ///
+ public bool Enabled { get; protected set; }
+
+ ///
+ /// Gets a boolean indicating whether this render element and its layers/brushes should be enabled
+ ///
+ public abstract bool ShouldBeEnabled { get; }
+
+ ///
+ /// Creates a list of all layer properties present on this render element
+ ///
+ /// A list of all layer properties present on this render element
+ public abstract List GetAllLayerProperties();
+
+ ///
+ /// Occurs when a layer effect has been added or removed to this render element
+ ///
+ public event EventHandler? LayerEffectsUpdated;
+
+ ///
+ /// Overrides the main timeline to the specified time and clears any extra time lines
+ ///
+ /// The position to set the timeline to
+ public abstract void OverrideTimelineAndApply(TimeSpan position);
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ LayerEffectStore.LayerEffectAdded -= LayerEffectStoreOnLayerEffectAdded;
+ LayerEffectStore.LayerEffectRemoved -= LayerEffectStoreOnLayerEffectRemoved;
+
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
+ baseLayerEffect.Dispose();
+
+ if (DisplayCondition is IDisposable disposable)
+ disposable.Dispose();
+
+ base.Dispose(disposing);
+ }
+
+ internal void LoadRenderElement()
+ {
+ Timeline = RenderElementEntity.Timeline != null
+ ? new Timeline(RenderElementEntity.Timeline)
+ : new Timeline();
+
+ DisplayCondition = RenderElementEntity.DisplayCondition switch
+ {
+ AlwaysOnConditionEntity entity => new AlwaysOnCondition(entity, this),
+ PlayOnceConditionEntity entity => new PlayOnceCondition(entity, this),
+ StaticConditionEntity entity => new StaticCondition(entity, this),
+ EventConditionEntity entity => new EventCondition(entity, this),
+ _ => DisplayCondition
+ };
+
+ LoadLayerEffects();
+ }
+
+ internal void SaveRenderElement()
+ {
+ RenderElementEntity.LayerEffects.Clear();
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
+ {
+ baseLayerEffect.Save();
+ RenderElementEntity.LayerEffects.Add(baseLayerEffect.LayerEffectEntity);
+ }
+
+ // Condition
+ DisplayCondition?.Save();
+ RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
+
+ // Timeline
+ RenderElementEntity.Timeline = Timeline?.Entity;
+ Timeline?.Save();
+ }
+
+ internal void LoadNodeScript()
+ {
+ if (DisplayCondition is INodeScriptCondition scriptCondition)
+ scriptCondition.LoadNodeScript();
+
+ foreach (ILayerProperty layerProperty in GetAllLayerProperties())
+ layerProperty.BaseDataBinding.LoadNodeScript();
+ }
+
+ private void OnLayerEffectsUpdated()
+ {
+ LayerEffectsUpdated?.Invoke(this, EventArgs.Empty);
+ }
+
+ #region Timeline
+
+ ///
+ /// Gets the timeline associated with this render element
+ ///
+ public Timeline Timeline { get; private set; }
+
+ ///
+ /// Updates the according to the provided and current display
+ /// condition
+ ///
+ protected void UpdateTimeline(double deltaTime)
+ {
+ DisplayCondition.UpdateTimeline(deltaTime);
+ }
+
+ #endregion
+
+ #region Properties
+
+ internal abstract RenderElementEntity RenderElementEntity { get; }
+
+ ///
+ /// Gets the parent of this element
+ ///
+ public new ProfileElement Parent
+ {
+ get => base.Parent!;
+ internal set
+ {
+ base.Parent = value;
+ OnPropertyChanged(nameof(Parent));
+ }
+ }
+
+ ///
+ /// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
+ /// clipped.
+ ///
+ public SKPath? Path
+ {
+ get => _path;
+ protected set
+ {
+ SetAndNotify(ref _path, value);
+ // I can't really be sure about the performance impact of calling Bounds often but
+ // SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
+ Bounds = SKRectI.Round(value?.Bounds ?? SKRect.Empty);
+ }
+ }
+
+ ///
+ /// The bounds of this entity
+ ///
+ public SKRectI Bounds
+ {
+ get => _bounds;
+ private set => SetAndNotify(ref _bounds, value);
+ }
+
+ #endregion
+
+ #region State
+
+ ///
+ /// Enables the render element and its brushes and effects
+ ///
+ public abstract void Disable();
+
+ ///
+ /// Disables the render element and its brushes and effects
+ ///
+ public abstract void Enable();
+
+ #endregion
+
+ #region Effect management
+
+ private readonly List _layerEffects;
+
+ ///
+ /// Gets a read-only collection of the layer effects on this entity
+ ///
+ public ReadOnlyCollection LayerEffects { get; }
+
+ ///
+ /// Adds a the provided layer effect to the render profile element
+ ///
+ /// The effect to add.
+ public void AddLayerEffect(BaseLayerEffect layerEffect)
+ {
+ if (layerEffect == null)
+ throw new ArgumentNullException(nameof(layerEffect));
+
+ // Ensure something needs to be done
+ if (_layerEffects.Contains(layerEffect))
+ return;
+
+ // Make sure the layer effect is tied to this element
+ layerEffect.ProfileElement = this;
+ _layerEffects.Add(layerEffect);
+
+ // Update the order on the effects
+ OrderEffects();
+ OnLayerEffectsUpdated();
+ }
+
+ ///
+ /// Removes the provided layer effect.
+ ///
+ /// The effect to remove.
+ public void RemoveLayerEffect(BaseLayerEffect layerEffect)
+ {
+ if (layerEffect == null)
+ throw new ArgumentNullException(nameof(layerEffect));
+
+ // Ensure something needs to be done
+ if (!_layerEffects.Contains(layerEffect))
+ return;
+
+ // Remove the effect from the layer
+ _layerEffects.Remove(layerEffect);
+
+ // Update the order on the remaining effects
+ OrderEffects();
+ OnLayerEffectsUpdated();
+ }
+
+ private void LoadLayerEffects()
+ {
+ foreach (BaseLayerEffect baseLayerEffect in _layerEffects)
+ baseLayerEffect.Dispose();
+ _layerEffects.Clear();
+
+ foreach (LayerEffectEntity layerEffectEntity in RenderElementEntity.LayerEffects.OrderBy(e => e.Order))
+ LoadLayerEffect(layerEffectEntity);
+ }
+
+ private void LoadLayerEffect(LayerEffectEntity layerEffectEntity)
+ {
+ LayerEffectDescriptor? descriptor = LayerEffectStore.Get(layerEffectEntity.ProviderId, layerEffectEntity.EffectType)?.LayerEffectDescriptor;
+ BaseLayerEffect layerEffect;
+ // Create an instance with the descriptor
+ if (descriptor != null)
+ {
+ layerEffect = descriptor.CreateInstance(this, layerEffectEntity);
+ }
+ // If no descriptor was found and there was no existing placeholder, create a placeholder
+ else
+ {
+ descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffectEntity.ProviderId);
+ layerEffect = descriptor.CreateInstance(this, layerEffectEntity);
+ }
+
+ _layerEffects.Add(layerEffect);
+ }
+
+ private void ReplaceLayerEffectWithPlaceholder(BaseLayerEffect layerEffect)
+ {
+ int index = _layerEffects.IndexOf(layerEffect);
+ if (index == -1)
+ return;
+
+ LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffect.ProviderId);
+ BaseLayerEffect placeholder = descriptor.CreateInstance(this, layerEffect.LayerEffectEntity);
+ _layerEffects[index] = placeholder;
+ layerEffect.Dispose();
+ OnLayerEffectsUpdated();
+ }
+
+ private void ReplacePlaceholderWithLayerEffect(PlaceholderLayerEffect placeholder)
+ {
+ int index = _layerEffects.IndexOf(placeholder);
+ if (index == -1)
+ return;
+
+ LayerEffectDescriptor? descriptor = LayerEffectStore.Get(placeholder.OriginalEntity.ProviderId, placeholder.PlaceholderFor)?.LayerEffectDescriptor;
+ if (descriptor == null)
+ throw new ArtemisCoreException("Can't replace a placeholder effect because the real effect isn't available.");
+
+ BaseLayerEffect layerEffect = descriptor.CreateInstance(this, placeholder.OriginalEntity);
+ _layerEffects[index] = layerEffect;
+ placeholder.Dispose();
+ OnLayerEffectsUpdated();
+ }
+
+ private void OrderEffects()
+ {
+ int index = 0;
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects.OrderBy(e => e.Order))
+ {
+ baseLayerEffect.Order = Order = index + 1;
+ index++;
+ }
+
+ _layerEffects.Sort((a, b) => a.Order.CompareTo(b.Order));
+ }
+
+ private void LayerEffectStoreOnLayerEffectRemoved(object? sender, LayerEffectStoreEvent e)
+ {
+ // Find effects that just got disabled and replace them with placeholders
+ List affectedLayerEffects = _layerEffects.Where(ef => ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
+
+ if (!affectedLayerEffects.Any())
+ return;
+
+ foreach (BaseLayerEffect baseLayerEffect in affectedLayerEffects)
+ ReplaceLayerEffectWithPlaceholder(baseLayerEffect);
+ OnLayerEffectsUpdated();
+ }
+
+ private void LayerEffectStoreOnLayerEffectAdded(object? sender, LayerEffectStoreEvent e)
+ {
+ // Find placeholders that just got enabled and replace them with real effects
+ List affectedPlaceholders = LayerEffects
+ .Where(l => l is PlaceholderLayerEffect ph && ph.OriginalEntity.ProviderId == e.Registration.PluginFeature.Id)
+ .Cast()
+ .ToList();
+
+ if (!affectedPlaceholders.Any())
+ return;
+
+ foreach (PlaceholderLayerEffect placeholderLayerEffect in affectedPlaceholders)
+ ReplacePlaceholderWithLayerEffect(placeholderLayerEffect);
+ OnLayerEffectsUpdated();
+ }
+
+ #endregion
+
+ #region Conditions
+
+ ///
+ /// Gets whether the display conditions applied to this layer where met or not during last update
+ /// Always true if the layer has no display conditions
+ ///
+ public bool DisplayConditionMet
+ {
+ get => _displayConditionMet;
+ private set => SetAndNotify(ref _displayConditionMet, value);
+ }
+
+ private bool _displayConditionMet;
+
+ ///
+ /// Gets or sets the display condition used to determine whether this element is active or not
+ ///
+ public ICondition DisplayCondition
+ {
+ get => _displayCondition;
+ set => SetAndNotify(ref _displayCondition, value);
+ }
+
+ private ICondition _displayCondition;
+
+ ///
+ /// Evaluates the display conditions on this element and applies any required changes to the
+ ///
+ public void UpdateDisplayCondition()
+ {
+ if (Suspended)
+ {
+ DisplayConditionMet = false;
+ return;
+ }
+
+ DisplayCondition.Update();
+ DisplayConditionMet = DisplayCondition.IsMet;
+ }
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
index 8c6dc49db..aa8991159 100644
--- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
+++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
@@ -451,7 +451,7 @@ namespace Artemis.Core
});
DeviceEntity.InputMappings.Clear();
- foreach (var (original, mapped) in InputMappings)
+ foreach ((ArtemisLed? original, ArtemisLed? mapped) in InputMappings)
DeviceEntity.InputMappings.Add(new InputMappingEntity {OriginalLedId = (int) original.RgbLed.Id, MappedLedId = (int) mapped.RgbLed.Id});
DeviceEntity.Categories.Clear();
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs
index fca826e2d..48dc97915 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs
@@ -6,7 +6,7 @@ namespace Artemis.Core.LayerBrushes
///
/// For internal use only, please use or or instead
///
- public abstract class PropertiesLayerBrush : BaseLayerBrush where T : LayerPropertyGroup
+ public abstract class PropertiesLayerBrush : BaseLayerBrush where T : LayerPropertyGroup, new()
{
private T _properties = null!;
@@ -35,7 +35,7 @@ namespace Artemis.Core.LayerBrushes
internal void InitializeProperties(PropertyGroupEntity? propertyGroupEntity)
{
- Properties = Activator.CreateInstance();
+ Properties = new T();
PropertyGroupDescriptionAttribute groupDescription = new() {Identifier = "Brush", Name = Descriptor.DisplayName, Description = Descriptor.Description};
Properties.Initialize(Layer, null, groupDescription, propertyGroupEntity);
PropertiesInitialized = true;
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrush.cs
index 749d64bfa..9cfc502b3 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrush.cs
@@ -6,7 +6,7 @@ namespace Artemis.Core.LayerBrushes
/// Represents a brush that renders on a layer
///
/// The type of brush properties
- public abstract class LayerBrush : PropertiesLayerBrush where T : LayerPropertyGroup
+ public abstract class LayerBrush : PropertiesLayerBrush where T : LayerPropertyGroup, new()
{
///
/// Creates a new instance of the class
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushDescriptor.cs b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushDescriptor.cs
index 8aa7026e0..b2fad30cc 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushDescriptor.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushDescriptor.cs
@@ -69,8 +69,6 @@ namespace Artemis.Core.LayerBrushes
brush.LayerBrushEntity = entity ?? new LayerBrushEntity { ProviderId = Provider.Id, BrushType = LayerBrushType.FullName };
brush.Initialize();
- brush.Update(0);
-
return brush;
}
}
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs
index 479738e15..a60db4405 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs
@@ -6,7 +6,7 @@ namespace Artemis.Core.LayerBrushes
/// Represents a brush that renders on a per-layer basis
///
/// The type of brush properties
- public abstract class PerLedLayerBrush : PropertiesLayerBrush where T : LayerPropertyGroup
+ public abstract class PerLedLayerBrush : PropertiesLayerBrush where T : LayerPropertyGroup, new()
{
///
/// Creates a new instance of the class
diff --git a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs
index 24c46589a..495f24dba 100644
--- a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs
+++ b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs
@@ -7,12 +7,11 @@ namespace Artemis.Core.LayerEffects
///
/// For internal use only, please use instead
///
- public abstract class BaseLayerEffect : BreakableModel, IDisposable
+ public abstract class BaseLayerEffect : BreakableModel, IDisposable, IStorageModel
{
private ILayerEffectConfigurationDialog? _configurationDialog;
private LayerEffectDescriptor _descriptor;
private bool _suspended;
- private Guid _entityId;
private bool _hasBeenRenamed;
private string _name;
private int _order;
@@ -51,15 +50,6 @@ namespace Artemis.Core.LayerEffects
set => SetAndNotify(ref _name, value);
}
- ///
- /// Gets or sets the suspended state, if suspended the effect is skipped in render and update
- ///
- public bool Suspended
- {
- get => _suspended;
- set => SetAndNotify(ref _suspended, value);
- }
-
///
/// Gets or sets whether the effect has been renamed by the user, if true consider refraining from changing the name
/// programatically
@@ -105,13 +95,18 @@ namespace Artemis.Core.LayerEffects
///
/// Gets a reference to the layer property group without knowing it's type
///
- public virtual LayerPropertyGroup? BaseProperties => null;
+ public virtual LayerEffectPropertyGroup? BaseProperties => null;
///
/// Gets a boolean indicating whether the layer effect is enabled or not
///
public bool Enabled { get; private set; }
+ ///
+ /// Gets a boolean indicating whether the layer effect is suspended or not
+ ///
+ public bool Suspended => BaseProperties is not {PropertiesInitialized: true} || !BaseProperties.IsEnabled;
+
#region IDisposable
///
@@ -224,11 +219,20 @@ namespace Artemis.Core.LayerEffects
#endregion
+ ///
+ public void Load()
+ {
+ Name = LayerEffectEntity.Name;
+ HasBeenRenamed = LayerEffectEntity.HasBeenRenamed;
+ Order = LayerEffectEntity.Order;
+ }
+
+ ///
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.ProviderId = Descriptor.Provider.Id;
+ LayerEffectEntity.EffectType = GetType().FullName;
LayerEffectEntity.Name = Name;
- LayerEffectEntity.Suspended = Suspended;
LayerEffectEntity.HasBeenRenamed = HasBeenRenamed;
LayerEffectEntity.Order = Order;
diff --git a/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs
index 8916758e7..87259d44b 100644
--- a/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs
+++ b/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs
@@ -6,7 +6,7 @@ namespace Artemis.Core.LayerEffects
/// Represents an effect that applies preprocessing and/or postprocessing to a layer
///
///
- public abstract class LayerEffect : BaseLayerEffect where T : LayerPropertyGroup
+ public abstract class LayerEffect : BaseLayerEffect where T : LayerEffectPropertyGroup, new()
{
private T _properties = null!;
@@ -16,7 +16,7 @@ namespace Artemis.Core.LayerEffects
public bool PropertiesInitialized { get; internal set; }
///
- public override LayerPropertyGroup BaseProperties => Properties;
+ public override LayerEffectPropertyGroup BaseProperties => Properties;
///
/// Gets the properties of this effect.
@@ -32,19 +32,22 @@ namespace Artemis.Core.LayerEffects
}
internal set => _properties = value;
}
-
- internal void InitializeProperties()
- {
- Properties = Activator.CreateInstance();
- Properties.Initialize(ProfileElement, null, new PropertyGroupDescriptionAttribute(){Identifier = "LayerEffect"}, LayerEffectEntity.PropertyGroup);
- PropertiesInitialized = true;
-
- EnableLayerEffect();
- }
-
+
internal override void Initialize()
{
InitializeProperties();
}
+
+ private void InitializeProperties()
+ {
+ Properties = new T();
+ Properties.Initialize(ProfileElement, null, new PropertyGroupDescriptionAttribute {Identifier = "LayerEffect"}, LayerEffectEntity.PropertyGroup);
+
+ // Initialize will call PopulateDefaults but that is for plugin developers so can't rely on that to default IsEnabled to true
+ Properties.InitializeIsEnabled();
+ PropertiesInitialized = true;
+
+ EnableLayerEffect();
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs b/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs
index 34f501845..59fa31b5c 100644
--- a/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs
+++ b/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs
@@ -1,116 +1,111 @@
using System;
-using System.Linq;
using Artemis.Core.LayerEffects.Placeholder;
using Artemis.Storage.Entities.Profile;
using Ninject;
-namespace Artemis.Core.LayerEffects
+namespace Artemis.Core.LayerEffects;
+
+///
+/// A class that describes a layer effect
+///
+public class LayerEffectDescriptor
{
- ///
- /// A class that describes a layer effect
- ///
- public class LayerEffectDescriptor
+ internal LayerEffectDescriptor(string displayName, string description, string icon, Type layerEffectType, LayerEffectProvider provider)
{
- internal LayerEffectDescriptor(string displayName, string description, string icon, Type? layerEffectType, LayerEffectProvider provider)
+ DisplayName = displayName;
+ Description = description;
+ Icon = icon;
+ LayerEffectType = layerEffectType ?? throw new ArgumentNullException(nameof(layerEffectType));
+ Provider = provider ?? throw new ArgumentNullException(nameof(provider));
+ }
+
+ internal LayerEffectDescriptor(string placeholderFor, LayerEffectProvider provider)
+ {
+ PlaceholderFor = placeholderFor ?? throw new ArgumentNullException(nameof(placeholderFor));
+ Provider = provider ?? throw new ArgumentNullException(nameof(provider));
+ DisplayName = "Missing effect";
+ Description = "This effect could not be loaded";
+ Icon = "FileQuestion";
+ }
+
+ ///
+ /// The name that is displayed in the UI
+ ///
+ public string DisplayName { get; }
+
+ ///
+ /// The description that is displayed in the UI
+ ///
+ public string Description { get; }
+
+ ///
+ /// The Material icon to display in the UI, a full reference can be found
+ /// here
+ ///
+ public string Icon { get; }
+
+ ///
+ /// The type of the layer effect
+ ///
+ public Type? LayerEffectType { get; }
+
+ ///
+ /// The plugin that provided this
+ ///
+ public LayerEffectProvider Provider { get; }
+
+ ///
+ /// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder
+ ///
+ public string? PlaceholderFor { get; }
+
+ ///
+ /// Creates an instance of the described effect and applies it to the render element
+ ///
+ public BaseLayerEffect CreateInstance(RenderProfileElement renderElement, LayerEffectEntity? entity)
+ {
+ if (PlaceholderFor != null)
{
- DisplayName = displayName;
- Description = description;
- Icon = icon;
- LayerEffectType = layerEffectType;
- Provider = provider;
- }
-
- ///
- /// The name that is displayed in the UI
- ///
- public string DisplayName { get; }
-
- ///
- /// The description that is displayed in the UI
- ///
- public string Description { get; }
-
- ///
- /// The Material icon to display in the UI, a full reference can be found
- /// here
- ///
- public string Icon { get; }
-
- ///
- /// The type of the layer effect
- ///
- public Type? LayerEffectType { get; }
-
- ///
- /// The plugin that provided this
- ///
- public LayerEffectProvider Provider { get; }
-
- ///
- /// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder
- ///
- public string? PlaceholderFor { get; internal set; }
-
- ///
- /// Creates an instance of the described effect and applies it to the render element
- ///
- internal void CreateInstance(RenderProfileElement renderElement, LayerEffectEntity? entity)
- {
- 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");
-
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;
- }
+ throw new ArtemisCoreException("Cannot create a placeholder for a layer effect that wasn't loaded from an entity");
- if (PlaceholderFor != null)
- {
- CreatePlaceHolderInstance(renderElement, entity);
- return;
- }
-
- BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType);
- effect.ProfileElement = renderElement;
- effect.LayerEffectEntity = entity;
- effect.Order = entity.Order;
- effect.Name = entity.Name;
- effect.Suspended = entity.Suspended;
- effect.Descriptor = this;
-
- effect.Initialize();
- effect.Update(0);
-
- renderElement.ActivateLayerEffect(effect);
+ return CreatePlaceHolderInstance(renderElement, entity);
}
- private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
+ 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);
+ effect.ProfileElement = renderElement;
+ effect.Descriptor = this;
+ if (entity != null)
{
- if (PlaceholderFor == null)
- throw new ArtemisCoreException("Cannot create a placeholder instance using a layer effect descriptor that is not a placeholder for anything");
- PlaceholderLayerEffect effect = new(entity, PlaceholderFor)
- {
- ProfileElement = renderElement,
- Descriptor = this
- };
+ effect.LayerEffectEntity = entity;
+ effect.Load();
effect.Initialize();
- renderElement.ActivateLayerEffect(effect);
-
- if (renderElement.ShouldBeEnabled)
- effect.InternalEnable();
}
+ else
+ {
+ effect.LayerEffectEntity = new LayerEffectEntity();
+ effect.Initialize();
+ effect.Save();
+ }
+
+ return effect;
+ }
+
+ private BaseLayerEffect CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
+ {
+ if (PlaceholderFor == null)
+ throw new ArtemisCoreException("Cannot create a placeholder instance using a layer effect descriptor that is not a placeholder for anything");
+
+ PlaceholderLayerEffect effect = new(entity, PlaceholderFor)
+ {
+ ProfileElement = renderElement,
+ Descriptor = this
+ };
+ effect.Initialize();
+
+ return effect;
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs
index a1ec92371..c9b187088 100644
--- a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs
+++ b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs
@@ -16,7 +16,6 @@ namespace Artemis.Core.LayerEffects.Placeholder
LayerEffectEntity = originalEntity;
Order = OriginalEntity.Order;
Name = OriginalEntity.Name;
- Suspended = OriginalEntity.Suspended;
HasBeenRenamed = OriginalEntity.HasBeenRenamed;
}
@@ -58,7 +57,7 @@ namespace Artemis.Core.LayerEffects.Placeholder
///
/// This is in place so that the UI has something to show
///
- internal class PlaceholderProperties : LayerPropertyGroup
+ internal class PlaceholderProperties : LayerEffectPropertyGroup
{
protected override void PopulateDefaults()
{
diff --git a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffectDescriptor.cs b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffectDescriptor.cs
index 374340bb1..a5496d986 100644
--- a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffectDescriptor.cs
+++ b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffectDescriptor.cs
@@ -4,11 +4,7 @@
{
public static LayerEffectDescriptor Create(string missingProviderId)
{
- LayerEffectDescriptor descriptor = new("Missing effect", "This effect could not be loaded", "FileQuestion", null, Constants.EffectPlaceholderPlugin)
- {
- PlaceholderFor = missingProviderId
- };
-
+ LayerEffectDescriptor descriptor = new(missingProviderId, Constants.EffectPlaceholderPlugin);
return descriptor;
}
}
diff --git a/src/Artemis.Core/Plugins/Modules/Module.cs b/src/Artemis.Core/Plugins/Modules/Module.cs
index d6dbfff46..5b62963a2 100644
--- a/src/Artemis.Core/Plugins/Modules/Module.cs
+++ b/src/Artemis.Core/Plugins/Modules/Module.cs
@@ -12,7 +12,7 @@ namespace Artemis.Core.Modules
///
/// Allows you to add new data to the Artemis data model
///
- public abstract class Module : Module where T : DataModel
+ public abstract class Module : Module where T : DataModel, new()
{
///
/// The data model driving this module
@@ -79,7 +79,7 @@ namespace Artemis.Core.Modules
internal override void InternalEnable()
{
- DataModel = Activator.CreateInstance();
+ DataModel = new T();
DataModel.Module = this;
DataModel.DataModelDescription = GetDataModelDescription();
base.InternalEnable();
@@ -310,7 +310,7 @@ namespace Artemis.Core.Modules
///
internal override void InternalEnable()
{
- foreach ((DefaultCategoryName categoryName, var path) in _pendingDefaultProfilePaths)
+ foreach ((DefaultCategoryName categoryName, string? path) in _pendingDefaultProfilePaths)
AddDefaultProfile(categoryName, path);
_pendingDefaultProfilePaths.Clear();
diff --git a/src/Artemis.Core/Plugins/Settings/PluginSettings.cs b/src/Artemis.Core/Plugins/Settings/PluginSettings.cs
index 203b6185b..dbfdb860f 100644
--- a/src/Artemis.Core/Plugins/Settings/PluginSettings.cs
+++ b/src/Artemis.Core/Plugins/Settings/PluginSettings.cs
@@ -71,7 +71,7 @@ namespace Artemis.Core
///
public void SaveAllSettings()
{
- foreach (var (_, pluginSetting) in _settingEntities)
+ foreach ((string _, IPluginSetting? pluginSetting) in _settingEntities)
pluginSetting.Save();
}
diff --git a/src/Artemis.Core/Services/Input/InputService.cs b/src/Artemis.Core/Services/Input/InputService.cs
index f47ac5d78..17082f7df 100644
--- a/src/Artemis.Core/Services/Input/InputService.cs
+++ b/src/Artemis.Core/Services/Input/InputService.cs
@@ -301,7 +301,7 @@ namespace Artemis.Core.Services
public void ReleaseAll()
{
- foreach (var (device, keys) in _pressedKeys.ToList())
+ foreach ((ArtemisDevice? device, HashSet? keys) in _pressedKeys.ToList())
{
foreach (KeyboardKey keyboardKey in keys)
{
diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/DataModelJsonPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/DataModelJsonPluginEndPoint.cs
index 70a019331..5b65b0cf2 100644
--- a/src/Artemis.Core/Services/WebServer/EndPoints/DataModelJsonPluginEndPoint.cs
+++ b/src/Artemis.Core/Services/WebServer/EndPoints/DataModelJsonPluginEndPoint.cs
@@ -12,7 +12,7 @@ namespace Artemis.Core.Services
/// or .
/// Note: Both will be deserialized and serialized respectively using JSON.
///
- public class DataModelJsonPluginEndPoint : PluginEndPoint where T : DataModel
+ public class DataModelJsonPluginEndPoint : PluginEndPoint where T : DataModel, new()
{
private readonly Module _module;
diff --git a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs
index 7c8e2124f..6a8fe71ef 100644
--- a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs
+++ b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs
@@ -51,7 +51,7 @@ namespace Artemis.Core.Services
/// The module whose datamodel to apply the received JSON to
/// The name of the end point, must be unique
/// The resulting end point
- DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(Module module, string endPointName) where T : DataModel;
+ DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(Module module, string endPointName) where T : DataModel, new();
///
/// Adds a new endpoint for the given plugin feature receiving an a .
diff --git a/src/Artemis.Core/Services/WebServer/WebServerService.cs b/src/Artemis.Core/Services/WebServer/WebServerService.cs
index 0d7654812..2b450ec78 100644
--- a/src/Artemis.Core/Services/WebServer/WebServerService.cs
+++ b/src/Artemis.Core/Services/WebServer/WebServerService.cs
@@ -50,7 +50,7 @@ namespace Artemis.Core.Services
.WithModule(PluginsModule);
// Add registered modules
- foreach (var webModule in _modules)
+ foreach (WebModuleRegistration? webModule in _modules)
server = server.WithModule(webModule.CreateInstance());
server = server
@@ -132,7 +132,7 @@ namespace Artemis.Core.Services
return endPoint;
}
- public DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(Module module, string endPointName) where T : DataModel
+ public DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(Module module, string endPointName) where T : DataModel, new()
{
if (module == null) throw new ArgumentNullException(nameof(module));
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
@@ -141,7 +141,7 @@ namespace Artemis.Core.Services
return endPoint;
}
- private void HandleDataModelRequest(Module module, T value) where T : DataModel
+ private void HandleDataModelRequest(Module module, T value) where T : DataModel, new()
{
}
diff --git a/src/Artemis.Core/VisualScripting/Internal/DataBindingExitNode.cs b/src/Artemis.Core/VisualScripting/Internal/DataBindingExitNode.cs
index 224e61e0f..2ca393ca6 100644
--- a/src/Artemis.Core/VisualScripting/Internal/DataBindingExitNode.cs
+++ b/src/Artemis.Core/VisualScripting/Internal/DataBindingExitNode.cs
@@ -24,7 +24,7 @@ namespace Artemis.Core.Internal
public override void Evaluate()
{
- foreach (var (property, inputPin) in _propertyPins)
+ foreach ((IDataBindingProperty? property, InputPin? inputPin) in _propertyPins)
{
if (inputPin.ConnectedTo.Any())
_propertyValues[property] = inputPin.Value!;
@@ -35,7 +35,7 @@ namespace Artemis.Core.Internal
public void ApplyToDataBinding()
{
- foreach (var (property, pendingValue) in _propertyValues)
+ foreach ((IDataBindingProperty? property, object? pendingValue) in _propertyValues)
property.SetValue(pendingValue);
}
diff --git a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
index 26f1d9c53..6eb99c5d8 100644
--- a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
@@ -12,11 +12,8 @@ namespace Artemis.Storage.Entities.Profile.Abstract
public List LayerEffects { get; set; }
public List PropertyEntities { get; set; }
- public List ExpandedPropertyGroups { get; set; }
public IConditionEntity DisplayCondition { get; set; }
public TimelineEntity Timeline { get; set; }
-
- public NodeScriptEntity NodeScript { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/FolderEntity.cs b/src/Artemis.Storage/Entities/Profile/FolderEntity.cs
index 0f6cdde1b..a9d25396b 100644
--- a/src/Artemis.Storage/Entities/Profile/FolderEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/FolderEntity.cs
@@ -11,7 +11,6 @@ namespace Artemis.Storage.Entities.Profile
{
PropertyEntities = new List();
LayerEffects = new List();
- ExpandedPropertyGroups = new List();
}
public int Order { get; set; }
diff --git a/src/Artemis.Storage/Entities/Profile/LayerEffectEntity.cs b/src/Artemis.Storage/Entities/Profile/LayerEffectEntity.cs
index 236d1d2fb..b3b0c5187 100644
--- a/src/Artemis.Storage/Entities/Profile/LayerEffectEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/LayerEffectEntity.cs
@@ -5,11 +5,9 @@ namespace Artemis.Storage.Entities.Profile
{
public class LayerEffectEntity
{
- public Guid Id { get; set; }
public string ProviderId { get; set; }
public string EffectType { get; set; }
public string Name { get; set; }
- public bool Suspended { get; set; }
public bool HasBeenRenamed { get; set; }
public int Order { get; set; }
diff --git a/src/Artemis.Storage/Entities/Profile/LayerEntity.cs b/src/Artemis.Storage/Entities/Profile/LayerEntity.cs
index 1c8ff29ea..ffe08feb0 100644
--- a/src/Artemis.Storage/Entities/Profile/LayerEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/LayerEntity.cs
@@ -14,7 +14,6 @@ namespace Artemis.Storage.Entities.Profile
AdaptionHints = new List();
PropertyEntities = new List();
LayerEffects = new List();
- ExpandedPropertyGroups = new List();
}
public int Order { get; set; }
diff --git a/src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs b/src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs
index d019c1667..699b25c40 100644
--- a/src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs
+++ b/src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs
@@ -17,15 +17,17 @@ namespace Artemis.Storage.Migrations
foreach (FolderEntity profileEntityFolder in profileEntity.Folders)
{
profileEntityFolder.Suspended = false;
- foreach (LayerEffectEntity layerEffectEntity in profileEntityFolder.LayerEffects)
- layerEffectEntity.Suspended = false;
+ // Commented out during Avalonia port when Suspended was moved into the LayerEffect's LayerProperties
+ // foreach (LayerEffectEntity layerEffectEntity in profileEntityFolder.LayerEffects)
+ // layerEffectEntity.Suspended = false;
}
foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
{
profileEntityLayer.Suspended = false;
- foreach (LayerEffectEntity layerEffectEntity in profileEntityLayer.LayerEffects)
- layerEffectEntity.Suspended = false;
+ // Commented out during Avalonia port when Suspended was moved into the LayerEffect's LayerProperties
+ // foreach (LayerEffectEntity layerEffectEntity in profileEntityLayer.LayerEffects)
+ // layerEffectEntity.Suspended = false;
}
repository.Upsert(profileEntity);
diff --git a/src/Artemis.UI.Linux/packages.lock.json b/src/Artemis.UI.Linux/packages.lock.json
index 078afba5d..e49cf2d22 100644
--- a/src/Artemis.UI.Linux/packages.lock.json
+++ b/src/Artemis.UI.Linux/packages.lock.json
@@ -222,8 +222,8 @@
},
"FluentAvaloniaUI": {
"type": "Transitive",
- "resolved": "1.3.0",
- "contentHash": "xzcsuOswakMpz/EdA59NEOgaCtZ/9zsd5QWTB0YYQqSv1GF95Uk2aMVtO5gtfNrCT4lZvGNWVf3HGjYz9cHovQ==",
+ "resolved": "1.3.4",
+ "contentHash": "INZBxCGyt4Dzm0IaNXoXuU08VUQ62FbeMHPH7MO5W1WeVdU5N8+1YTYfqYAA66twa5wba8MYqezXWhoU8Hq0DQ==",
"dependencies": {
"Avalonia": "0.10.13",
"Avalonia.Desktop": "0.10.13",
@@ -1780,7 +1780,7 @@
"Avalonia.Svg.Skia": "0.10.12",
"Avalonia.Xaml.Behaviors": "0.10.13.2",
"DynamicData": "7.5.4",
- "FluentAvaloniaUI": "1.3.0",
+ "FluentAvaloniaUI": "1.3.4",
"Flurl.Http": "3.2.0",
"Live.Avalonia": "1.3.1",
"Material.Icons.Avalonia": "1.0.2",
@@ -1801,7 +1801,7 @@
"Avalonia.Svg.Skia": "0.10.12",
"Avalonia.Xaml.Behaviors": "0.10.13.2",
"DynamicData": "7.5.4",
- "FluentAvaloniaUI": "1.3.0",
+ "FluentAvaloniaUI": "1.3.4",
"Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease7",
"ReactiveUI": "17.1.50",
diff --git a/src/Artemis.UI.MacOS/packages.lock.json b/src/Artemis.UI.MacOS/packages.lock.json
index 078afba5d..e49cf2d22 100644
--- a/src/Artemis.UI.MacOS/packages.lock.json
+++ b/src/Artemis.UI.MacOS/packages.lock.json
@@ -222,8 +222,8 @@
},
"FluentAvaloniaUI": {
"type": "Transitive",
- "resolved": "1.3.0",
- "contentHash": "xzcsuOswakMpz/EdA59NEOgaCtZ/9zsd5QWTB0YYQqSv1GF95Uk2aMVtO5gtfNrCT4lZvGNWVf3HGjYz9cHovQ==",
+ "resolved": "1.3.4",
+ "contentHash": "INZBxCGyt4Dzm0IaNXoXuU08VUQ62FbeMHPH7MO5W1WeVdU5N8+1YTYfqYAA66twa5wba8MYqezXWhoU8Hq0DQ==",
"dependencies": {
"Avalonia": "0.10.13",
"Avalonia.Desktop": "0.10.13",
@@ -1780,7 +1780,7 @@
"Avalonia.Svg.Skia": "0.10.12",
"Avalonia.Xaml.Behaviors": "0.10.13.2",
"DynamicData": "7.5.4",
- "FluentAvaloniaUI": "1.3.0",
+ "FluentAvaloniaUI": "1.3.4",
"Flurl.Http": "3.2.0",
"Live.Avalonia": "1.3.1",
"Material.Icons.Avalonia": "1.0.2",
@@ -1801,7 +1801,7 @@
"Avalonia.Svg.Skia": "0.10.12",
"Avalonia.Xaml.Behaviors": "0.10.13.2",
"DynamicData": "7.5.4",
- "FluentAvaloniaUI": "1.3.0",
+ "FluentAvaloniaUI": "1.3.4",
"Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease7",
"ReactiveUI": "17.1.50",
diff --git a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
index a55751eb4..221832a3e 100644
--- a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
+++ b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
@@ -23,19 +23,13 @@
-
+
-
-
- $(DefaultXamlRuntime)
- MSBuild:Compile
-
-
diff --git a/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml b/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml
index 56dcf901e..293135dfd 100644
--- a/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml
+++ b/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml
@@ -4,5 +4,4 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Shared.ArtemisIcon">
- Welcome to Avalonia!
diff --git a/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs b/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs
index 7805c02a8..75bea347f 100644
--- a/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs
+++ b/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs
@@ -53,7 +53,7 @@ namespace Artemis.UI.Shared
{
SvgSource source = new();
source.Load(iconString);
- Content = new SvgImage {Source = source};
+ Content = new Image {Source = new SvgImage {Source = source}};
}
// An URI pointing to a different kind of image
else
@@ -79,10 +79,8 @@ namespace Artemis.UI.Shared
private void OnDetachedFromLogicalTree(object? sender, LogicalTreeAttachmentEventArgs e)
{
- if (Content is SvgImage svgImage)
- svgImage.Source?.Dispose();
- else if (Content is Image image)
- ((Bitmap) image.Source).Dispose();
+ if (Content is Image image && image.Source is IDisposable disposable)
+ disposable.Dispose();
}
private void InitializeComponent()
diff --git a/src/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayView.xaml b/src/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayView.xaml
deleted file mode 100644
index b722b7d59..000000000
--- a/src/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayView.xaml
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/AddLayerEffect.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/AddLayerEffect.cs
new file mode 100644
index 000000000..feedd6543
--- /dev/null
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/AddLayerEffect.cs
@@ -0,0 +1,48 @@
+using System;
+using Artemis.Core;
+using Artemis.Core.LayerEffects;
+
+namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
+
+///
+/// Represents a profile editor command that can be used to add a layer effect to a profile element.
+///
+public class AddLayerEffect : IProfileEditorCommand, IDisposable
+{
+ private readonly RenderProfileElement _renderProfileElement;
+ private readonly BaseLayerEffect _layerEffect;
+ private bool _executed;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public AddLayerEffect(RenderProfileElement renderProfileElement, BaseLayerEffect layerEffect)
+ {
+ _renderProfileElement = renderProfileElement;
+ _layerEffect = layerEffect;
+ }
+
+ ///
+ public string DisplayName => "Add layer effect";
+
+ ///
+ public void Execute()
+ {
+ _renderProfileElement.AddLayerEffect(_layerEffect);
+ _executed = true;
+ }
+
+ ///
+ public void Undo()
+ {
+ _renderProfileElement.RemoveLayerEffect(_layerEffect);
+ _executed = false;
+ }
+
+ ///
+ public void Dispose()
+ {
+ if (!_executed)
+ _layerEffect.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/packages.lock.json b/src/Artemis.UI.Shared/packages.lock.json
index aaa59d395..0dd5d6365 100644
--- a/src/Artemis.UI.Shared/packages.lock.json
+++ b/src/Artemis.UI.Shared/packages.lock.json
@@ -62,9 +62,9 @@
},
"FluentAvaloniaUI": {
"type": "Direct",
- "requested": "[1.3.0, )",
- "resolved": "1.3.0",
- "contentHash": "xzcsuOswakMpz/EdA59NEOgaCtZ/9zsd5QWTB0YYQqSv1GF95Uk2aMVtO5gtfNrCT4lZvGNWVf3HGjYz9cHovQ==",
+ "requested": "[1.3.4, )",
+ "resolved": "1.3.4",
+ "contentHash": "INZBxCGyt4Dzm0IaNXoXuU08VUQ62FbeMHPH7MO5W1WeVdU5N8+1YTYfqYAA66twa5wba8MYqezXWhoU8Hq0DQ==",
"dependencies": {
"Avalonia": "0.10.13",
"Avalonia.Desktop": "0.10.13",
diff --git a/src/Artemis.UI.Windows/packages.lock.json b/src/Artemis.UI.Windows/packages.lock.json
index 51d6f98cb..6a5f3484a 100644
--- a/src/Artemis.UI.Windows/packages.lock.json
+++ b/src/Artemis.UI.Windows/packages.lock.json
@@ -238,8 +238,8 @@
},
"FluentAvaloniaUI": {
"type": "Transitive",
- "resolved": "1.3.0",
- "contentHash": "xzcsuOswakMpz/EdA59NEOgaCtZ/9zsd5QWTB0YYQqSv1GF95Uk2aMVtO5gtfNrCT4lZvGNWVf3HGjYz9cHovQ==",
+ "resolved": "1.3.4",
+ "contentHash": "INZBxCGyt4Dzm0IaNXoXuU08VUQ62FbeMHPH7MO5W1WeVdU5N8+1YTYfqYAA66twa5wba8MYqezXWhoU8Hq0DQ==",
"dependencies": {
"Avalonia": "0.10.13",
"Avalonia.Desktop": "0.10.13",
@@ -1796,7 +1796,7 @@
"Avalonia.Svg.Skia": "0.10.12",
"Avalonia.Xaml.Behaviors": "0.10.13.2",
"DynamicData": "7.5.4",
- "FluentAvaloniaUI": "1.3.0",
+ "FluentAvaloniaUI": "1.3.4",
"Flurl.Http": "3.2.0",
"Live.Avalonia": "1.3.1",
"Material.Icons.Avalonia": "1.0.2",
@@ -1817,7 +1817,7 @@
"Avalonia.Svg.Skia": "0.10.12",
"Avalonia.Xaml.Behaviors": "0.10.13.2",
"DynamicData": "7.5.4",
- "FluentAvaloniaUI": "1.3.0",
+ "FluentAvaloniaUI": "1.3.4",
"Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease7",
"ReactiveUI": "17.1.50",
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index c750a6d1a..af2c19e4d 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/src/Artemis.UI/ArtemisBootstrapper.cs b/src/Artemis.UI/ArtemisBootstrapper.cs
index 0764be57d..5eac6489d 100644
--- a/src/Artemis.UI/ArtemisBootstrapper.cs
+++ b/src/Artemis.UI/ArtemisBootstrapper.cs
@@ -42,8 +42,6 @@ public static class ArtemisBootstrapper
_kernel.Load(modules);
_kernel.UseNinjectDependencyResolver();
- DataModelPicker.DataModelUIService = _kernel.Get();
-
return _kernel;
}
@@ -62,6 +60,7 @@ public static class ArtemisBootstrapper
_application.DataContext = rootViewModel;
RxApp.DefaultExceptionHandler = Observer.Create(DisplayUnhandledException);
+ DataModelPicker.DataModelUIService = _kernel.Get();
}
private static void DisplayUnhandledException(Exception exception)
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputViewModel.cs
index 0fe7783a7..fbde8d7a9 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputViewModel.cs
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputViewModel.cs
@@ -1,11 +1,7 @@
-using System;
-using System.Collections.Specialized;
-using System.Linq;
-using Artemis.Core;
+using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Artemis.UI.Shared.Services.PropertyInput;
-using Avalonia.Media;
using ReactiveUI;
namespace Artemis.UI.DefaultTypes.PropertyInput;
@@ -60,14 +56,10 @@ public class ColorGradientPropertyInputViewModel : PropertyInputViewModel(LayerProperty, ColorGradient, _originalGradient, Time));
- }
_originalGradient = null;
}
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputViewModel.cs
index fae919a27..a5c38997e 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputViewModel.cs
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputViewModel.cs
@@ -1,4 +1,5 @@
-using Artemis.Core;
+using System;
+using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.PropertyInput;
using ReactiveUI.Validation.Extensions;
@@ -11,10 +12,10 @@ public class FloatPropertyInputViewModel : PropertyInputViewModel
: base(layerProperty, profileEditorService, propertyInputService)
{
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
- this.ValidationRule(vm => vm.InputValue, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue,
+ this.ValidationRule(vm => vm.InputValue, i => i >= Convert.ToSingle(LayerProperty.PropertyDescription.MinInputValue),
$"Value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
- this.ValidationRule(vm => vm.InputValue, i => i <= (float) LayerProperty.PropertyDescription.MaxInputValue,
+ this.ValidationRule(vm => vm.InputValue, i => i <= Convert.ToSingle(LayerProperty.PropertyDescription.MaxInputValue),
$"Value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs
index 16df98f6e..2bfbbb04e 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs
@@ -1,4 +1,5 @@
-using Artemis.Core;
+using System;
+using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.PropertyInput;
using ReactiveUI;
@@ -16,17 +17,17 @@ public class FloatRangePropertyInputViewModel : PropertyInputViewModel vm.Start, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue,
+ this.ValidationRule(vm => vm.Start, i => i >= Convert.ToSingle(LayerProperty.PropertyDescription.MinInputValue),
$"Start value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
- this.ValidationRule(vm => vm.End, i => i >= (float) LayerProperty.PropertyDescription.MinInputValue,
+ this.ValidationRule(vm => vm.End, i => i >= Convert.ToSingle(LayerProperty.PropertyDescription.MinInputValue),
$"End value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
}
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
{
- this.ValidationRule(vm => vm.Start, i => i < (float) LayerProperty.PropertyDescription.MaxInputValue,
+ this.ValidationRule(vm => vm.Start, i => i < Convert.ToSingle(LayerProperty.PropertyDescription.MaxInputValue),
$"Start value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
- this.ValidationRule(vm => vm.End, i => i < (float) LayerProperty.PropertyDescription.MaxInputValue,
+ this.ValidationRule(vm => vm.End, i => i < Convert.ToSingle(LayerProperty.PropertyDescription.MaxInputValue),
$"End value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
}
}
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputViewModel.cs
index 92f02cfa6..95e16ed4f 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputViewModel.cs
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputViewModel.cs
@@ -1,4 +1,5 @@
-using Artemis.Core;
+using System;
+using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.PropertyInput;
using ReactiveUI.Validation.Extensions;
@@ -11,10 +12,10 @@ public class IntPropertyInputViewModel : PropertyInputViewModel
: base(layerProperty, profileEditorService, propertyInputService)
{
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
- this.ValidationRule(vm => vm.InputValue, i => i >= (int) LayerProperty.PropertyDescription.MinInputValue,
+ this.ValidationRule(vm => vm.InputValue, i => i >= Convert.ToInt32(LayerProperty.PropertyDescription.MinInputValue),
$"Value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
- this.ValidationRule(vm => vm.InputValue, i => i < (int) LayerProperty.PropertyDescription.MaxInputValue,
+ this.ValidationRule(vm => vm.InputValue, i => i < Convert.ToInt32(LayerProperty.PropertyDescription.MaxInputValue),
$"Value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs
index 3e548ceaf..9fb62c16b 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs
@@ -1,4 +1,5 @@
-using Artemis.Core;
+using System;
+using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.PropertyInput;
using ReactiveUI;
@@ -16,17 +17,17 @@ public class IntRangePropertyInputViewModel : PropertyInputViewModel
if (LayerProperty.PropertyDescription.MinInputValue.IsNumber())
{
- this.ValidationRule(vm => vm.Start, i => i >= (int) LayerProperty.PropertyDescription.MinInputValue,
+ this.ValidationRule(vm => vm.Start, i => i >= Convert.ToInt32(LayerProperty.PropertyDescription.MinInputValue),
$"Start value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
- this.ValidationRule(vm => vm.End, i => i >= (int)LayerProperty.PropertyDescription.MinInputValue,
+ this.ValidationRule(vm => vm.End, i => i >= Convert.ToInt32(LayerProperty.PropertyDescription.MinInputValue),
$"End value must be equal to or greater than {LayerProperty.PropertyDescription.MinInputValue}.");
}
if (LayerProperty.PropertyDescription.MaxInputValue.IsNumber())
{
- this.ValidationRule(vm => vm.Start, i => i < (int)LayerProperty.PropertyDescription.MaxInputValue,
+ this.ValidationRule(vm => vm.Start, i => i < Convert.ToInt32(LayerProperty.PropertyDescription.MaxInputValue),
$"Start value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
- this.ValidationRule(vm => vm.End, i => i < (int) LayerProperty.PropertyDescription.MaxInputValue,
+ this.ValidationRule(vm => vm.End, i => i < Convert.ToInt32(LayerProperty.PropertyDescription.MaxInputValue),
$"End value must be smaller than {LayerProperty.PropertyDescription.MaxInputValue}.");
}
}
diff --git a/src/Artemis.UI/Screens/Device/DeviceSettingsViewModel.cs b/src/Artemis.UI/Screens/Device/DeviceSettingsViewModel.cs
index 34d4c882c..dd861e0f1 100644
--- a/src/Artemis.UI/Screens/Device/DeviceSettingsViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/DeviceSettingsViewModel.cs
@@ -77,7 +77,7 @@ namespace Artemis.UI.Screens.Device
await _windowService.CreateContentDialog()
.WithTitle($"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input")
- .WithViewModel(out var viewModel, ("device", Device))
+ .WithViewModel(out DeviceDetectInputViewModel? viewModel, ("device", Device))
.WithCloseButtonText("Cancel")
.ShowAsync();
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml
new file mode 100644
index 000000000..3fec4881e
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ None of the effects match your search
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml.cs
new file mode 100644
index 000000000..77b18f314
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectView.axaml.cs
@@ -0,0 +1,28 @@
+using Artemis.Core.LayerEffects;
+using Avalonia;
+using Avalonia.Input;
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+
+namespace Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
+
+public class AddEffectView : ReactiveUserControl
+{
+ public AddEffectView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
+ {
+ if (sender is not IDataContextProvider {DataContext: LayerEffectDescriptor descriptor} || ViewModel == null)
+ return;
+
+ ViewModel?.AddLayerEffect(descriptor);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectViewModel.cs
new file mode 100644
index 000000000..3193351f0
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/AddEffectViewModel.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Reactive.Linq;
+using Artemis.Core;
+using Artemis.Core.LayerEffects;
+using Artemis.Core.Services;
+using Artemis.UI.Shared;
+using Artemis.UI.Shared.Services.ProfileEditor;
+using Artemis.UI.Shared.Services.ProfileEditor.Commands;
+using DynamicData;
+using ReactiveUI;
+
+namespace Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
+
+public class AddEffectViewModel : ContentDialogViewModelBase
+{
+ private readonly RenderProfileElement _renderProfileElement;
+ private readonly IProfileEditorService _profileEditorService;
+ private string? _searchText;
+
+ public AddEffectViewModel(RenderProfileElement renderProfileElement, IProfileEditorService profileEditorService, ILayerEffectService layerEffectService)
+ {
+ _renderProfileElement = renderProfileElement;
+ _profileEditorService = profileEditorService;
+
+ SourceList layerEffectSourceList = new();
+ layerEffectSourceList.AddRange(layerEffectService.GetLayerEffects());
+ IObservable> layerEffectFilter = this.WhenAnyValue(vm => vm.SearchText).Select(CreatePredicate);
+
+ layerEffectSourceList.Connect()
+ .Filter(layerEffectFilter)
+ .Bind(out ReadOnlyObservableCollection layerEffectDescriptors)
+ .Subscribe();
+ LayerEffectDescriptors = layerEffectDescriptors;
+ }
+
+ public ReadOnlyObservableCollection LayerEffectDescriptors { get; }
+
+ public string? SearchText
+ {
+ get => _searchText;
+ set => this.RaiseAndSetIfChanged(ref _searchText, value);
+ }
+
+ public void AddLayerEffect(LayerEffectDescriptor descriptor)
+ {
+ BaseLayerEffect layerEffect = descriptor.CreateInstance(_renderProfileElement, null);
+ _profileEditorService.ExecuteCommand(new AddLayerEffect(_renderProfileElement, layerEffect));
+ ContentDialog?.Hide();
+ }
+
+ private Func CreatePredicate(string? search)
+ {
+ if (string.IsNullOrWhiteSpace(search))
+ return _ => true;
+
+ search = search.Trim();
+ return data => data.DisplayName.Contains(search, StringComparison.InvariantCultureIgnoreCase) ||
+ data.Description.Contains(search, StringComparison.InvariantCultureIgnoreCase);
+ }
+}
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesView.axaml
index b6ead192e..7d755037a 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesView.axaml
@@ -13,7 +13,7 @@
-
+
@@ -29,13 +29,22 @@
Name="TreeScrollViewer"
Offset="{CompiledBinding #TimelineScrollViewer.Offset, Mode=OneWay}"
Background="{DynamicResource CardStrokeColorDefaultSolidBrush}">
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -45,12 +54,12 @@
Background="Transparent"
Margin="0 0 -5 0" />
-
+ Content="{CompiledBinding DataBindingViewModel}" />
-
-
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesViewModel.cs
index bfd8b37d5..25ef9fc7d 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertiesViewModel.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
+using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
@@ -12,8 +13,12 @@ using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Playback;
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
+using Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
+using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Shared;
+using Artemis.UI.Shared.Services;
+using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.ProfileEditor;
using ReactiveUI;
@@ -23,6 +28,8 @@ public class PropertiesViewModel : ActivatableViewModelBase
{
private readonly Dictionary _cachedPropertyViewModels;
private readonly IDataBindingVmFactory _dataBindingVmFactory;
+ private readonly IWindowService _windowService;
+ private readonly ILayerEffectService _layerEffectService;
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
private readonly IProfileEditorService _profileEditorService;
private readonly ISettingsService _settingsService;
@@ -38,18 +45,22 @@ public class PropertiesViewModel : ActivatableViewModelBase
ISettingsService settingsService,
ILayerPropertyVmFactory layerPropertyVmFactory,
IDataBindingVmFactory dataBindingVmFactory,
+ IWindowService windowService,
+ ILayerEffectService layerEffectService,
PlaybackViewModel playbackViewModel)
{
_profileEditorService = profileEditorService;
_settingsService = settingsService;
_layerPropertyVmFactory = layerPropertyVmFactory;
_dataBindingVmFactory = dataBindingVmFactory;
+ _windowService = windowService;
+ _layerEffectService = layerEffectService;
_cachedPropertyViewModels = new Dictionary();
PropertyGroupViewModels = new ObservableCollection();
PlaybackViewModel = playbackViewModel;
TimelineViewModel = layerPropertyVmFactory.TimelineViewModel(PropertyGroupViewModels);
-
+ AddEffect = ReactiveCommand.CreateFromTask(ExecuteAddEffect);
// React to service profile element changes as long as the VM is active
this.WhenActivated(d =>
{
@@ -86,6 +97,7 @@ public class PropertiesViewModel : ActivatableViewModelBase
public ObservableCollection PropertyGroupViewModels { get; }
public PlaybackViewModel PlaybackViewModel { get; }
public TimelineViewModel TimelineViewModel { get; }
+ public ReactiveCommand AddEffect { get; }
public DataBindingViewModel? DataBindingViewModel
{
@@ -102,6 +114,17 @@ public class PropertiesViewModel : ActivatableViewModelBase
public IObservable Playing => _profileEditorService.Playing;
public PluginSetting PropertiesTreeWidth => _settingsService.GetSetting("ProfileEditor.PropertiesTreeWidth", 500.0);
+ private async Task ExecuteAddEffect()
+ {
+ if (ProfileElement == null)
+ return;
+
+ await _windowService.CreateContentDialog()
+ .WithTitle("Add layer effect")
+ .WithViewModel(out AddEffectViewModel _, ("renderProfileElement", ProfileElement))
+ .WithCloseButtonText("Cancel")
+ .ShowAsync();
+ }
private void UpdatePropertyGroups()
{
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs
index edccd7921..83da67450 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs
@@ -38,18 +38,36 @@ public class PropertyGroupViewModel : ViewModelBase, IDisposable
PopulateChildren();
}
- public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService,
- BaseLayerBrush layerBrush)
- : this(layerPropertyGroup, layerPropertyVmFactory, propertyInputService)
+ public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService, BaseLayerBrush layerBrush)
{
+ _layerPropertyVmFactory = layerPropertyVmFactory;
+ _propertyInputService = propertyInputService;
LayerBrush = layerBrush;
+ Children = new ObservableCollection();
+ LayerPropertyGroup = layerPropertyGroup;
+ TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this);
+ TimelineGroupViewModel = layerPropertyVmFactory.TimelineGroupViewModel(this);
+
+ LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
+ _isVisible = !LayerPropertyGroup.IsHidden;
+
+ PopulateChildren();
}
- public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService,
- BaseLayerEffect layerEffect)
- : this(layerPropertyGroup, layerPropertyVmFactory, propertyInputService)
+ public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService, BaseLayerEffect layerEffect)
{
+ _layerPropertyVmFactory = layerPropertyVmFactory;
+ _propertyInputService = propertyInputService;
LayerEffect = layerEffect;
+ Children = new ObservableCollection();
+ LayerPropertyGroup = layerPropertyGroup;
+ TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this);
+ TimelineGroupViewModel = layerPropertyVmFactory.TimelineGroupViewModel(this);
+
+ LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
+ _isVisible = !LayerPropertyGroup.IsHidden;
+
+ PopulateChildren();
}
public ObservableCollection Children { get; }
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml
index 9fdd258eb..0d84c6054 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml
@@ -53,7 +53,7 @@
-
+
General
@@ -61,22 +61,23 @@
-
+
Transform
+ Margin="0 0 5 0" />
- Brush -
+ Margin="0 5 5 0">
+ Brush -
-
Effect
@@ -133,18 +134,23 @@
Margin="0 5"
IsVisible="{Binding LayerEffect.Name, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
-
-
-
-
+
+
+