diff --git a/src/Artemis.Core/Models/Profile/Conditions/AlwaysOnCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/AlwaysOnCondition.cs
new file mode 100644
index 000000000..90c0a790a
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/Conditions/AlwaysOnCondition.cs
@@ -0,0 +1,78 @@
+using System;
+using Artemis.Storage.Entities.Profile.Abstract;
+using Artemis.Storage.Entities.Profile.Conditions;
+
+namespace Artemis.Core
+{
+ public class AlwaysOnCondition : ICondition
+ {
+ public AlwaysOnCondition(RenderProfileElement profileElement)
+ {
+ ProfileElement = profileElement;
+ Entity = new AlwaysOnConditionEntity();
+ }
+
+ public AlwaysOnCondition(AlwaysOnConditionEntity alwaysOnConditionEntity, RenderProfileElement profileElement)
+ {
+ ProfileElement = profileElement;
+ Entity = alwaysOnConditionEntity;
+ }
+
+ #region Implementation of IDisposable
+
+ ///
+ public void Dispose()
+ {
+ }
+
+ #endregion
+
+ #region Implementation of IStorageModel
+
+ ///
+ public void Load()
+ {
+ }
+
+ ///
+ public void Save()
+ {
+ }
+
+ #endregion
+
+ #region Implementation of ICondition
+
+ ///
+ public IConditionEntity Entity { get; }
+
+ ///
+ public RenderProfileElement ProfileElement { get; }
+
+ ///
+ public bool IsMet { get; private set; }
+
+ ///
+ public void Update()
+ {
+ if (ProfileElement.Parent is RenderProfileElement parent)
+ IsMet = parent.DisplayConditionMet;
+ else
+ IsMet = true;
+ }
+
+ ///
+ public void UpdateTimeline(double deltaTime)
+ {
+ ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), true);
+ }
+
+ ///
+ public void OverrideTimeline(TimeSpan position)
+ {
+ ProfileElement.Timeline.Override(position, position > ProfileElement.Timeline.Length);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs
index ecf83c4d1..6c8fd2c9a 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs
@@ -4,211 +4,262 @@ using Artemis.Core.Internal;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
-namespace Artemis.Core
+namespace Artemis.Core;
+
+///
+/// Represents a condition that is based on a
+///
+public class EventCondition : CorePropertyChanged, INodeScriptCondition
+{
+ private readonly string _displayName;
+ private readonly EventConditionEntity _entity;
+ private readonly EventDefaultNode _eventNode;
+ private DataModelPath? _eventPath;
+ private DateTime _lastProcessedTrigger;
+ private EventOverlapMode _overlapMode;
+ private NodeScript _script;
+ private EventTriggerMode _triggerMode;
+ private bool _wasMet;
+
+ ///
+ /// Creates a new instance of the class
+ ///
+ public EventCondition(RenderProfileElement profileElement)
+ {
+ ProfileElement = profileElement;
+
+ _entity = new EventConditionEntity();
+ _displayName = profileElement.GetType().Name;
+ _eventNode = new EventDefaultNode {X = -300};
+ _script = new NodeScript($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
+ }
+
+ internal EventCondition(EventConditionEntity entity, RenderProfileElement profileElement)
+ {
+ ProfileElement = profileElement;
+
+ _entity = entity;
+ _displayName = profileElement.GetType().Name;
+ _eventNode = new EventDefaultNode();
+ _script = null!;
+
+ Load();
+ }
+
+ ///
+ /// Gets the script that drives the event condition
+ ///
+ public NodeScript Script
+ {
+ get => _script;
+ set => SetAndNotify(ref _script, value);
+ }
+
+ ///
+ /// Gets or sets the path to the event that drives this event condition
+ ///
+ public DataModelPath? EventPath
+ {
+ get => _eventPath;
+ set => SetAndNotify(ref _eventPath, value);
+ }
+
+ ///
+ /// Gets or sets how the condition behaves when the event fires.
+ ///
+ public EventTriggerMode TriggerMode
+ {
+ get => _triggerMode;
+ set => SetAndNotify(ref _triggerMode, value);
+ }
+
+ ///
+ /// Gets or sets how the condition behaves when events trigger before the timeline finishes in the
+ /// event trigger mode.
+ ///
+ public EventOverlapMode OverlapMode
+ {
+ get => _overlapMode;
+ set => SetAndNotify(ref _overlapMode, value);
+ }
+
+ ///
+ /// Updates the event node, applying the selected event
+ ///
+ public void UpdateEventNode()
+ {
+ IDataModelEvent? dataModelEvent = EventPath?.GetValue() as IDataModelEvent;
+ _eventNode.CreatePins(dataModelEvent);
+
+ if (dataModelEvent != null && !Script.Nodes.Contains(_eventNode))
+ Script.AddNode(_eventNode);
+ else if (dataModelEvent == null && Script.Nodes.Contains(_eventNode))
+ Script.RemoveNode(_eventNode);
+ }
+
+ ///
+ /// Gets the start node of the event script, if any
+ ///
+ /// The start node of the event script, if any.
+ public INode GetStartNode()
+ {
+ return _eventNode;
+ }
+
+ private bool Evaluate()
+ {
+ if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger)
+ return false;
+
+ _lastProcessedTrigger = dataModelEvent.LastTrigger;
+
+ if (!Script.ExitNodeConnected)
+ return true;
+
+ Script.Run();
+ return Script.Result;
+ }
+
+ ///
+ public IConditionEntity Entity => _entity;
+
+ ///
+ public RenderProfileElement ProfileElement { get; }
+
+ ///
+ public bool IsMet { get; private set; }
+
+ ///
+ public void Update()
+ {
+ _wasMet = IsMet;
+ if (TriggerMode == EventTriggerMode.Toggle)
+ {
+ if (Evaluate())
+ IsMet = !IsMet;
+ }
+ else
+ {
+ IsMet = Evaluate();
+ }
+ }
+
+ ///
+ public void UpdateTimeline(double deltaTime)
+ {
+ if (TriggerMode == EventTriggerMode.Toggle)
+ {
+ if (!IsMet && _wasMet)
+ ProfileElement.Timeline.JumpToEnd();
+ else if (IsMet && !_wasMet)
+ ProfileElement.Timeline.JumpToStart();
+ }
+ else
+ {
+ if (IsMet && ProfileElement.Timeline.IsFinished)
+ {
+ ProfileElement.Timeline.JumpToStart();
+ }
+ else if (IsMet)
+ {
+ if (OverlapMode == EventOverlapMode.Restart)
+ ProfileElement.Timeline.JumpToStart();
+ else if (OverlapMode == EventOverlapMode.Copy && ProfileElement is Layer layer)
+ layer.CreateCopyAsChild();
+ }
+ }
+
+ ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), TriggerMode == EventTriggerMode.Toggle);
+ }
+
+ ///
+ public void OverrideTimeline(TimeSpan position)
+ {
+ ProfileElement.Timeline.Override(position, TriggerMode == EventTriggerMode.Toggle && position > ProfileElement.Timeline.Length);
+ }
+
+ ///
+ public void Dispose()
+ {
+ Script?.Dispose();
+ EventPath?.Dispose();
+ }
+
+ #region Storage
+
+ ///
+ public void Load()
+ {
+ TriggerMode = (EventTriggerMode) _entity.TriggerMode;
+ OverlapMode = (EventOverlapMode) _entity.OverlapMode;
+
+ if (_entity.EventPath != null)
+ EventPath = new DataModelPath(_entity.EventPath);
+
+ Script = _entity.Script != null
+ ? new NodeScript($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile)
+ : new NodeScript($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
+ }
+
+ ///
+ public void Save()
+ {
+ _entity.TriggerMode = (int) TriggerMode;
+ _entity.OverlapMode = (int) OverlapMode;
+ Script.Save();
+ _entity.Script = Script?.Entity;
+ EventPath?.Save();
+ _entity.EventPath = EventPath?.Entity;
+ }
+
+ ///
+ public INodeScript NodeScript => Script;
+
+ ///
+ public void LoadNodeScript()
+ {
+ Script.Load();
+ UpdateEventNode();
+ Script.LoadConnections();
+ }
+
+ #endregion
+}
+
+///
+/// Represents a mode for render elements to start their timeline when display conditions events are fired.
+///
+public enum EventTriggerMode
{
///
- /// Represents a condition that is based on a
+ /// Play the timeline once.
///
- public class EventCondition : CorePropertyChanged, INodeScriptCondition
- {
- private readonly string _displayName;
- private readonly EventConditionEntity _entity;
- private EventDefaultNode? _eventNode;
- private TimeLineEventOverlapMode _eventOverlapMode;
- private DataModelPath? _eventPath;
- private DateTime _lastProcessedTrigger;
- private NodeScript? _script;
+ Play,
- ///
- /// Creates a new instance of the class
- ///
- public EventCondition(ProfileElement profileElement)
- {
- _entity = new EventConditionEntity();
- _displayName = profileElement.GetType().Name;
+ ///
+ /// Toggle repeating the timeline.
+ ///
+ Toggle
+}
- ProfileElement = profileElement;
- }
+///
+/// Represents a mode for render elements to configure the behaviour of events that overlap i.e. trigger again before
+/// the timeline finishes.
+///
+public enum EventOverlapMode
+{
+ ///
+ /// Stop the current run and restart the timeline
+ ///
+ Restart,
- internal EventCondition(EventConditionEntity entity, ProfileElement profileElement)
- {
- _entity = entity;
- _displayName = profileElement.GetType().Name;
+ ///
+ /// Ignore subsequent event fires until the timeline finishes
+ ///
+ Ignore,
- ProfileElement = profileElement;
- Load();
- }
-
- ///
- /// Gets the script that drives the event condition
- ///
- public NodeScript? Script
- {
- get => _script;
- set => SetAndNotify(ref _script, value);
- }
-
- ///
- /// Gets or sets the path to the event that drives this event condition
- ///
- public DataModelPath? EventPath
- {
- get => _eventPath;
- set => SetAndNotify(ref _eventPath, value);
- }
-
- ///
- /// Gets or sets how the condition behaves when events trigger before the timeline finishes
- ///
- public TimeLineEventOverlapMode EventOverlapMode
- {
- get => _eventOverlapMode;
- set => SetAndNotify(ref _eventOverlapMode, value);
- }
-
- ///
- /// Updates the event node, applying the selected event
- ///
- public void UpdateEventNode()
- {
- if (Script == null || EventPath?.GetValue() is not IDataModelEvent dataModelEvent)
- return;
-
- if (Script.Nodes.FirstOrDefault(n => n is EventDefaultNode) is EventDefaultNode existing)
- {
- existing.UpdateDataModelEvent(dataModelEvent);
- _eventNode = existing;
- }
- else
- {
- _eventNode = new EventDefaultNode {X = -300};
- _eventNode.UpdateDataModelEvent(dataModelEvent);
- }
-
- if (_eventNode.Pins.Any() && !Script.Nodes.Contains(_eventNode))
- Script.AddNode(_eventNode);
- else
- Script.RemoveNode(_eventNode);
- }
-
- ///
- /// Updates the with a new empty node script
- ///
- public void CreateEmptyNodeScript()
- {
- Script?.Dispose();
- Script = new NodeScript($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
- UpdateEventNode();
- }
-
- private bool Evaluate()
- {
- if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger)
- return false;
-
- _lastProcessedTrigger = dataModelEvent.LastTrigger;
-
- if (Script == null)
- return true;
-
- Script.Run();
- return Script.Result;
- }
-
- ///
- public IConditionEntity Entity => _entity;
-
- ///
- public ProfileElement ProfileElement { get; }
-
- ///
- public bool IsMet { get; private set; }
-
- ///
- public void Update()
- {
- if (EventOverlapMode == TimeLineEventOverlapMode.Toggle)
- {
- if (Evaluate())
- IsMet = !IsMet;
- }
- else
- {
- IsMet = Evaluate();
- }
- }
-
- ///
- public void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline)
- {
- if (!isMet)
- {
- if (EventOverlapMode == TimeLineEventOverlapMode.Toggle)
- timeline.JumpToEnd();
- return;
- }
-
- // Event overlap mode doesn't apply in this case
- if (timeline.IsFinished)
- {
- timeline.JumpToStart();
- return;
- }
-
- // If the timeline was already running, look at the event overlap mode
- if (EventOverlapMode == TimeLineEventOverlapMode.Restart)
- timeline.JumpToStart();
- else if (EventOverlapMode == TimeLineEventOverlapMode.Copy && ProfileElement is Layer layer)
- layer.CreateCopyAsChild();
- else if (EventOverlapMode == TimeLineEventOverlapMode.Toggle && !wasMet)
- timeline.JumpToStart();
-
- // The remaining overlap mode is 'ignore' which requires no further action
- }
-
- ///
- public void Dispose()
- {
- Script?.Dispose();
- EventPath?.Dispose();
- }
-
- #region Storage
-
- ///
- public void Load()
- {
- EventOverlapMode = (TimeLineEventOverlapMode) _entity.EventOverlapMode;
- if (_entity.Script != null)
- Script = new NodeScript($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile);
- if (_entity.EventPath != null)
- EventPath = new DataModelPath(_entity.EventPath);
- }
-
- ///
- public void Save()
- {
- _entity.EventOverlapMode = (int) EventOverlapMode;
- Script?.Save();
- _entity.Script = Script?.Entity;
- EventPath?.Save();
- _entity.EventPath = EventPath?.Entity;
- }
-
- ///
- public INodeScript NodeScript => Script;
-
- ///
- public void LoadNodeScript()
- {
- if (Script == null)
- return;
-
- Script.Load();
- UpdateEventNode();
- Script.LoadConnections();
- }
-
- #endregion
- }
+ ///
+ /// Play another copy of the timeline on top of the current run
+ ///
+ Copy
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/ICondition.cs b/src/Artemis.Core/Models/Profile/Conditions/ICondition.cs
index 2f4df80f7..a56394c49 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/ICondition.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/ICondition.cs
@@ -16,7 +16,7 @@ public interface ICondition : IDisposable, IStorageModel
///
/// Gets the profile element this condition applies to
///
- public ProfileElement ProfileElement { get; }
+ public RenderProfileElement ProfileElement { get; }
///
/// Gets a boolean indicating whether the condition is currently met
@@ -30,12 +30,11 @@ public interface ICondition : IDisposable, IStorageModel
void Update();
///
- /// Applies the display condition to the provided timeline
+ /// Updates the timeline according to the provided as the display condition sees fit.
///
- ///
- ///
- /// The timeline to apply the display condition to
- void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline);
+ void UpdateTimeline(double deltaTime);
+
+ void OverrideTimeline(TimeSpan position);
}
///
diff --git a/src/Artemis.Core/Models/Profile/Conditions/PlayOnceCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/PlayOnceCondition.cs
new file mode 100644
index 000000000..cd33f2d91
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/Conditions/PlayOnceCondition.cs
@@ -0,0 +1,78 @@
+using System;
+using Artemis.Storage.Entities.Profile.Abstract;
+using Artemis.Storage.Entities.Profile.Conditions;
+
+namespace Artemis.Core
+{
+ public class PlayOnceCondition : ICondition
+ {
+ public PlayOnceCondition(RenderProfileElement profileElement)
+ {
+ ProfileElement = profileElement;
+ Entity = new PlayOnceConditionEntity();
+ }
+
+ public PlayOnceCondition(PlayOnceConditionEntity entity, RenderProfileElement profileElement)
+ {
+ ProfileElement = profileElement;
+ Entity = entity;
+ }
+
+ #region Implementation of IDisposable
+
+ ///
+ public void Dispose()
+ {
+ }
+
+ #endregion
+
+ #region Implementation of IStorageModel
+
+ ///
+ public void Load()
+ {
+ }
+
+ ///
+ public void Save()
+ {
+ }
+
+ #endregion
+
+ #region Implementation of ICondition
+
+ ///
+ public IConditionEntity Entity { get; }
+
+ ///
+ public RenderProfileElement ProfileElement { get; }
+
+ ///
+ public bool IsMet { get; private set; }
+
+ ///
+ public void Update()
+ {
+ if (ProfileElement.Parent is RenderProfileElement parent)
+ IsMet = parent.DisplayConditionMet;
+ else
+ IsMet = true;
+ }
+
+ ///
+ public void UpdateTimeline(double deltaTime)
+ {
+ ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), false);
+ }
+
+ ///
+ public void OverrideTimeline(TimeSpan position)
+ {
+ ProfileElement.Timeline.Override(position, false);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/StaticCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/StaticCondition.cs
index 028745bef..af47599ef 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/StaticCondition.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/StaticCondition.cs
@@ -1,4 +1,5 @@
-using Artemis.Storage.Entities.Profile.Abstract;
+using System;
+using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core
@@ -10,11 +11,14 @@ namespace Artemis.Core
{
private readonly string _displayName;
private readonly StaticConditionEntity _entity;
+ private StaticPlayMode _playMode;
+ private StaticStopMode _stopMode;
+ private bool _wasMet;
///
/// Creates a new instance of the class
///
- public StaticCondition(ProfileElement profileElement)
+ public StaticCondition(RenderProfileElement profileElement)
{
_entity = new StaticConditionEntity();
_displayName = profileElement.GetType().Name;
@@ -23,7 +27,7 @@ namespace Artemis.Core
Script = new NodeScript($"Activate {_displayName}", $"Whether or not this {_displayName} should be active", profileElement.Profile);
}
- internal StaticCondition(StaticConditionEntity entity, ProfileElement profileElement)
+ internal StaticCondition(StaticConditionEntity entity, RenderProfileElement profileElement)
{
_entity = entity;
_displayName = profileElement.GetType().Name;
@@ -43,31 +47,66 @@ namespace Artemis.Core
public IConditionEntity Entity => _entity;
///
- public ProfileElement ProfileElement { get; }
+ public RenderProfileElement ProfileElement { get; }
///
public bool IsMet { get; private set; }
- ///
- public void Update()
+ ///
+ /// Gets or sets the mode in which the render element starts its timeline when display conditions are met
+ ///
+ public StaticPlayMode PlayMode
{
- if (!Script.ExitNodeConnected)
- {
- IsMet = true;
- return;
- }
+ get => _playMode;
+ set => SetAndNotify(ref _playMode, value);
+ }
- Script.Run();
- IsMet = Script.Result;
+ ///
+ /// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
+ ///
+ public StaticStopMode StopMode
+ {
+ get => _stopMode;
+ set => SetAndNotify(ref _stopMode, value);
}
///
- public void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline)
+ public void Update()
{
- if (isMet && !wasMet && timeline.IsFinished)
- timeline.JumpToStart();
- else if (!isMet && wasMet && timeline.StopMode == TimelineStopMode.SkipToEnd)
- timeline.JumpToEndSegment();
+ _wasMet = IsMet;
+
+ // No need to run the script if the parent isn't met anyway
+ bool parentConditionMet = ProfileElement.Parent is not RenderProfileElement renderProfileElement || renderProfileElement.DisplayConditionMet;
+ if (!parentConditionMet)
+ {
+ IsMet = false;
+ return;
+ }
+
+ if (!Script.ExitNodeConnected)
+ IsMet = true;
+ else
+ {
+ Script.Run();
+ IsMet = Script.Result;
+ }
+ }
+
+ ///
+ public void UpdateTimeline(double deltaTime)
+ {
+ if (IsMet && !_wasMet && ProfileElement.Timeline.IsFinished)
+ ProfileElement.Timeline.JumpToStart();
+ else if (!IsMet && _wasMet && StopMode == StaticStopMode.SkipToEnd)
+ ProfileElement.Timeline.JumpToEndSegment();
+
+ ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), PlayMode == StaticPlayMode.Repeat && IsMet);
+ }
+
+ ///
+ public void OverrideTimeline(TimeSpan position)
+ {
+ ProfileElement.Timeline.Override(position, PlayMode == StaticPlayMode.Repeat && position > ProfileElement.Timeline.Length);
}
///
@@ -81,12 +120,18 @@ namespace Artemis.Core
///
public void Load()
{
+ PlayMode = (StaticPlayMode) _entity.PlayMode;
+ StopMode = (StaticStopMode) _entity.StopMode;
+
Script = new NodeScript($"Activate {_displayName}", $"Whether or not this {_displayName} should be active", _entity.Script, ProfileElement.Profile);
}
///
public void Save()
{
+ _entity.PlayMode = (int) PlayMode;
+ _entity.StopMode = (int) StopMode;
+
Script.Save();
_entity.Script = Script.Entity;
}
@@ -102,4 +147,36 @@ namespace Artemis.Core
#endregion
}
+
+ ///
+ /// Represents a mode for render elements to start their timeline when display conditions are met
+ ///
+ public enum StaticPlayMode
+ {
+ ///
+ /// Continue repeating the main segment of the timeline while the condition is met
+ ///
+ Repeat,
+
+ ///
+ /// Only play the timeline once when the condition is met
+ ///
+ Once
+ }
+
+ ///
+ /// Represents a mode for render elements to stop their timeline when display conditions are no longer met
+ ///
+ public enum StaticStopMode
+ {
+ ///
+ /// When conditions are no longer met, finish the the current run of the main timeline
+ ///
+ Finish,
+
+ ///
+ /// When conditions are no longer met, skip to the end segment of the timeline
+ ///
+ SkipToEnd
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index 463ddfbb4..c42df2b5b 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -237,11 +237,11 @@ namespace Artemis.Core
}
///
- public override void OverrideTimelineAndApply(TimeSpan position, bool stickToMainSegment)
+ public override void OverrideTimelineAndApply(TimeSpan position)
{
- Timeline.Override(position, stickToMainSegment);
+ DisplayCondition.OverrideTimeline(position);
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
- baseLayerEffect.InternalUpdate(Timeline);
+ baseLayerEffect.InternalUpdate(Timeline); ;
}
///
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 21a8a113f..3afb7b74f 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -476,9 +476,9 @@ namespace Artemis.Core
}
///
- public override void OverrideTimelineAndApply(TimeSpan position, bool stickToMainSegment)
+ public override void OverrideTimelineAndApply(TimeSpan position)
{
- Timeline.Override(position, stickToMainSegment);
+ DisplayCondition.OverrideTimeline(position);
General.Update(Timeline);
Transform.Update(Timeline);
diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
index 619f10fba..9064c1978 100644
--- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
@@ -27,6 +27,7 @@ namespace Artemis.Core
LayerEffectsList = new List();
LayerEffects = new ReadOnlyCollection(LayerEffectsList);
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
+ _displayCondition = new AlwaysOnCondition(this);
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
@@ -52,7 +53,7 @@ namespace Artemis.Core
/// Occurs when a layer effect has been added or removed to this render element
///
public event EventHandler? LayerEffectsUpdated;
-
+
///
protected override void Dispose(bool disposing)
{
@@ -76,8 +77,10 @@ namespace Artemis.Core
DisplayCondition = RenderElementEntity.DisplayCondition switch
{
- StaticConditionEntity staticConditionEntity => new StaticCondition(staticConditionEntity, this),
- EventConditionEntity eventConditionEntity => new EventCondition(eventConditionEntity, this),
+ 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
};
@@ -124,17 +127,11 @@ namespace Artemis.Core
public Timeline Timeline { get; private set; }
///
- /// Updates the according to the provided and current display
- /// condition status
+ /// Updates the according to the provided and current display condition
///
protected void UpdateTimeline(double deltaTime)
{
- // TODO: Move to conditions
-
- // The play mode dictates whether to stick to the main segment unless the display conditions contains events
- bool stickToMainSegment = Timeline.PlayMode == TimelinePlayMode.Repeat && DisplayConditionMet;
-
- Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment);
+ DisplayCondition.UpdateTimeline(deltaTime);
}
#endregion
@@ -317,7 +314,7 @@ namespace Artemis.Core
OrderEffects();
}
-
+
internal void ActivateLayerEffect(BaseLayerEffect layerEffect)
{
@@ -365,13 +362,13 @@ namespace Artemis.Core
///
/// Gets or sets the display condition used to determine whether this element is active or not
///
- public ICondition? DisplayCondition
+ public ICondition DisplayCondition
{
get => _displayCondition;
set => SetAndNotify(ref _displayCondition, value);
}
- private ICondition? _displayCondition;
+ private ICondition _displayCondition;
///
/// Evaluates the display conditions on this element and applies any required changes to the
@@ -384,14 +381,7 @@ namespace Artemis.Core
return;
}
- if (DisplayCondition == null)
- {
- DisplayConditionMet = true;
- return;
- }
-
DisplayCondition.Update();
- DisplayCondition.ApplyToTimeline(DisplayCondition.IsMet, DisplayConditionMet, Timeline);
DisplayConditionMet = DisplayCondition.IsMet;
}
@@ -401,7 +391,6 @@ namespace Artemis.Core
/// Overrides the main timeline to the specified time and clears any extra time lines
///
/// The position to set the timeline to
- /// Whether to stick to the main segment, wrapping around if needed
- public abstract void OverrideTimelineAndApply(TimeSpan position, bool stickToMainSegment);
+ public abstract void OverrideTimelineAndApply(TimeSpan position);
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Timeline.cs b/src/Artemis.Core/Models/Profile/Timeline.cs
index 606f0ba8b..016ea1ecb 100644
--- a/src/Artemis.Core/Models/Profile/Timeline.cs
+++ b/src/Artemis.Core/Models/Profile/Timeline.cs
@@ -41,8 +41,7 @@ public class Timeline : CorePropertyChanged, IStorageModel
private TimeSpan _position;
private TimeSpan _lastDelta;
- private TimelinePlayMode _playMode;
- private TimelineStopMode _stopMode;
+
private TimeSpan _startSegmentLength;
private TimeSpan _mainSegmentLength;
private TimeSpan _endSegmentLength;
@@ -67,23 +66,7 @@ public class Timeline : CorePropertyChanged, IStorageModel
private set => SetAndNotify(ref _lastDelta, value);
}
- ///
- /// Gets or sets the mode in which the render element starts its timeline when display conditions are met
- ///
- public TimelinePlayMode PlayMode
- {
- get => _playMode;
- set => SetAndNotify(ref _playMode, value);
- }
- ///
- /// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
- ///
- public TimelineStopMode StopMode
- {
- get => _stopMode;
- set => SetAndNotify(ref _stopMode, value);
- }
///
/// Gets a boolean indicating whether the timeline has finished its run
@@ -388,8 +371,6 @@ public class Timeline : CorePropertyChanged, IStorageModel
StartSegmentLength = Entity.StartSegmentLength;
MainSegmentLength = Entity.MainSegmentLength;
EndSegmentLength = Entity.EndSegmentLength;
- PlayMode = (TimelinePlayMode) Entity.PlayMode;
- StopMode = (TimelineStopMode) Entity.StopMode;
JumpToEnd();
}
@@ -400,8 +381,6 @@ public class Timeline : CorePropertyChanged, IStorageModel
Entity.StartSegmentLength = StartSegmentLength;
Entity.MainSegmentLength = MainSegmentLength;
Entity.EndSegmentLength = EndSegmentLength;
- Entity.PlayMode = (int) PlayMode;
- Entity.StopMode = (int) StopMode;
}
#endregion
@@ -413,61 +392,3 @@ internal enum TimelineSegment
Main,
End
}
-
-///
-/// Represents a mode for render elements to start their timeline when display conditions are met
-///
-public enum TimelinePlayMode
-{
- ///
- /// Continue repeating the main segment of the timeline while the condition is met
- ///
- Repeat,
-
- ///
- /// Only play the timeline once when the condition is met
- ///
- Once
-}
-
-///
-/// Represents a mode for render elements to stop their timeline when display conditions are no longer met
-///
-public enum TimelineStopMode
-{
- ///
- /// When conditions are no longer met, finish the the current run of the main timeline
- ///
- Finish,
-
- ///
- /// When conditions are no longer met, skip to the end segment of the timeline
- ///
- SkipToEnd
-}
-
-///
-/// Represents a mode for render elements to start their timeline when display conditions events are fired
-///
-public enum TimeLineEventOverlapMode
-{
- ///
- /// Stop the current run and restart the timeline
- ///
- Restart,
-
- ///
- /// Ignore subsequent event fires until the timeline finishes
- ///
- Ignore,
-
- ///
- /// Play another copy of the timeline on top of the current run
- ///
- Copy,
-
- ///
- /// Repeat the timeline until the event fires again
- ///
- Toggle
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/VisualScripting/Internal/EventDefaultNode.cs b/src/Artemis.Core/VisualScripting/Internal/EventDefaultNode.cs
index b5ee6e227..ef7f2ffb9 100644
--- a/src/Artemis.Core/VisualScripting/Internal/EventDefaultNode.cs
+++ b/src/Artemis.Core/VisualScripting/Internal/EventDefaultNode.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Artemis.Core.Modules;
@@ -9,6 +10,7 @@ namespace Artemis.Core.Internal
internal class EventDefaultNode : Node
{
private readonly Dictionary _propertyPins;
+ private readonly List _pinBucket = new();
private IDataModelEvent? _dataModelEvent;
public EventDefaultNode() : base("Event Arguments", "Contains the event arguments that triggered the evaluation")
@@ -18,19 +20,22 @@ namespace Artemis.Core.Internal
public override bool IsDefaultNode => true;
- public void UpdateDataModelEvent(IDataModelEvent dataModelEvent)
+ public void CreatePins(IDataModelEvent? dataModelEvent)
{
if (_dataModelEvent == dataModelEvent)
return;
- foreach (var (_, outputPin) in _propertyPins)
- RemovePin(outputPin);
+ while (Pins.Any())
+ RemovePin((Pin) Pins.First());
_propertyPins.Clear();
_dataModelEvent = dataModelEvent;
+ if (dataModelEvent == null)
+ return;
+
foreach (PropertyInfo propertyInfo in dataModelEvent.ArgumentsType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
- .Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof(DataModelIgnoreAttribute))))
- _propertyPins.Add(propertyInfo, CreateOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
+ .Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof(DataModelIgnoreAttribute))))
+ _propertyPins.Add(propertyInfo, CreateOrAddOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
}
public override void Evaluate()
@@ -38,11 +43,38 @@ namespace Artemis.Core.Internal
if (_dataModelEvent?.LastEventArgumentsUntyped == null)
return;
- foreach (var (propertyInfo, outputPin) in _propertyPins)
+ foreach ((PropertyInfo propertyInfo, OutputPin outputPin) in _propertyPins)
{
if (outputPin.ConnectedTo.Any())
outputPin.Value = propertyInfo.GetValue(_dataModelEvent.LastEventArgumentsUntyped) ?? outputPin.Type.GetDefault()!;
}
}
+
+ ///
+ /// Creates or adds an input pin to the node using a bucket.
+ /// The bucket might grow a bit over time as the user edits the node but pins won't get lost, enabling undo/redo in the
+ /// editor.
+ ///
+ private OutputPin CreateOrAddOutputPin(Type valueType, string displayName)
+ {
+ // Grab the first pin from the bucket that isn't on the node yet
+ OutputPin? pin = _pinBucket.FirstOrDefault(p => !Pins.Contains(p));
+
+ // If there is none, create a new one and add it to the bucket
+ if (pin == null)
+ {
+ pin = CreateOutputPin(valueType, displayName);
+ _pinBucket.Add(pin);
+ }
+ // If there was a pin in the bucket, update it's type and display name and reuse it
+ else
+ {
+ pin.ChangeType(valueType);
+ pin.Name = displayName;
+ AddPin(pin);
+ }
+
+ return pin;
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/VisualScripting/Node.cs b/src/Artemis.Core/VisualScripting/Node.cs
index a9bdfb8f2..4622a1441 100644
--- a/src/Artemis.Core/VisualScripting/Node.cs
+++ b/src/Artemis.Core/VisualScripting/Node.cs
@@ -280,7 +280,7 @@ public abstract class Node : CorePropertyChanged, INode
/// Serializes the object into a string
///
/// The serialized object
- internal virtual string SerializeStorage()
+ public virtual string SerializeStorage()
{
return string.Empty;
}
@@ -289,7 +289,7 @@ public abstract class Node : CorePropertyChanged, INode
/// Deserializes the object and sets it
///
/// The serialized object
- internal virtual void DeserializeStorage(string serialized)
+ public virtual void DeserializeStorage(string serialized)
{
}
@@ -333,12 +333,14 @@ public abstract class Node : Node
///
public event EventHandler? StorageModified;
- internal override string SerializeStorage()
+ ///
+ public override string SerializeStorage()
{
return CoreJson.SerializeObject(Storage, true);
}
- internal override void DeserializeStorage(string serialized)
+ ///
+ public override void DeserializeStorage(string serialized)
{
Storage = CoreJson.DeserializeObject(serialized) ?? default(TStorage);
}
diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/AlwaysOnConditionEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/AlwaysOnConditionEntity.cs
new file mode 100644
index 000000000..31793708b
--- /dev/null
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/AlwaysOnConditionEntity.cs
@@ -0,0 +1,10 @@
+using Artemis.Storage.Entities.Profile.Abstract;
+using Artemis.Storage.Entities.Profile.Nodes;
+
+namespace Artemis.Storage.Entities.Profile.Conditions
+{
+ public class AlwaysOnConditionEntity : IConditionEntity
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/EventConditionEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/EventConditionEntity.cs
index 19b9587dc..c7e83890a 100644
--- a/src/Artemis.Storage/Entities/Profile/Conditions/EventConditionEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/EventConditionEntity.cs
@@ -5,7 +5,8 @@ namespace Artemis.Storage.Entities.Profile.Conditions
{
public class EventConditionEntity : IConditionEntity
{
- public int EventOverlapMode { get; set; }
+ public int TriggerMode { get; set; }
+ public int OverlapMode { get; set; }
public DataModelPathEntity EventPath { get; set; }
public NodeScriptEntity Script { get; set; }
}
diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/PlayOnceConditionEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/PlayOnceConditionEntity.cs
new file mode 100644
index 000000000..0107523a5
--- /dev/null
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/PlayOnceConditionEntity.cs
@@ -0,0 +1,9 @@
+using Artemis.Storage.Entities.Profile.Abstract;
+using Artemis.Storage.Entities.Profile.Nodes;
+
+namespace Artemis.Storage.Entities.Profile.Conditions
+{
+ public class PlayOnceConditionEntity : IConditionEntity
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/StaticConditionEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/StaticConditionEntity.cs
index 38a938c9a..edf362006 100644
--- a/src/Artemis.Storage/Entities/Profile/Conditions/StaticConditionEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/StaticConditionEntity.cs
@@ -5,6 +5,8 @@ namespace Artemis.Storage.Entities.Profile.Conditions
{
public class StaticConditionEntity : IConditionEntity
{
+ public int PlayMode { get; set; }
+ public int StopMode { get; set; }
public NodeScriptEntity Script { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/TimelineEntity.cs b/src/Artemis.Storage/Entities/Profile/TimelineEntity.cs
index 46dd7984b..790c86309 100644
--- a/src/Artemis.Storage/Entities/Profile/TimelineEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/TimelineEntity.cs
@@ -7,8 +7,5 @@ namespace Artemis.Storage.Entities.Profile
public TimeSpan StartSegmentLength { get; set; }
public TimeSpan MainSegmentLength { get; set; }
public TimeSpan EndSegmentLength { get; set; }
-
- public int PlayMode { get; set; }
- public int StopMode { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/DeleteKeyframe.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/DeleteKeyframe.cs
index eacb82fce..da36e77a9 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/DeleteKeyframe.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/DeleteKeyframe.cs
@@ -10,7 +10,7 @@ public class DeleteKeyframe : IProfileEditorCommand
private readonly ILayerPropertyKeyframe _keyframe;
///
- /// Creates a new instance of the class.
+ /// Creates a new instance of the class.
///
public DeleteKeyframe(ILayerPropertyKeyframe keyframe)
{
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventConditionPath.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventConditionPath.cs
new file mode 100644
index 000000000..b44d8516a
--- /dev/null
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventConditionPath.cs
@@ -0,0 +1,73 @@
+using System;
+using Artemis.Core;
+using Artemis.UI.Shared.Services.NodeEditor;
+
+namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
+
+///
+/// Represents a profile editor command that can be used to update an event condition's trigger mode.
+///
+public class UpdateEventConditionPath : IProfileEditorCommand, IDisposable
+{
+ private readonly EventCondition _eventCondition;
+ private readonly DataModelPath? _value;
+ private readonly DataModelPath? _oldValue;
+ private readonly NodeConnectionStore? _store;
+ private bool _executed;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public UpdateEventConditionPath(EventCondition eventCondition, DataModelPath? value)
+ {
+ _eventCondition = eventCondition;
+ _value = value;
+ _oldValue = eventCondition.EventPath;
+
+ INode? startNode = eventCondition.GetStartNode();
+ if (startNode != null)
+ _store = new NodeConnectionStore(startNode);
+ }
+
+ ///
+ public string DisplayName => "Update event path";
+
+ ///
+ public void Execute()
+ {
+ // Store old connections
+ _store?.Store();
+
+ // Change the end node
+ _eventCondition.EventPath = _value;
+ _eventCondition.UpdateEventNode();
+
+ _executed = true;
+ }
+
+ ///
+ public void Undo()
+ {
+ // Change the end node
+ _eventCondition.EventPath = _oldValue;
+ _eventCondition.UpdateEventNode();
+
+ // Restore old connections
+ _store?.Restore();
+
+ _executed = false;
+ }
+
+ #region IDisposable
+
+ ///
+ public void Dispose()
+ {
+ if (_executed)
+ _oldValue?.Dispose();
+ else
+ _value?.Dispose();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventOverlapMode.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventOverlapMode.cs
new file mode 100644
index 000000000..49aa59e10
--- /dev/null
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventOverlapMode.cs
@@ -0,0 +1,38 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
+
+///
+/// Represents a profile editor command that can be used to update an event condition's overlap mode.
+///
+public class UpdateEventOverlapMode : IProfileEditorCommand
+{
+ private readonly EventCondition _eventCondition;
+ private readonly EventOverlapMode _value;
+ private readonly EventOverlapMode _oldValue;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public UpdateEventOverlapMode(EventCondition eventCondition, EventOverlapMode value)
+ {
+ _eventCondition = eventCondition;
+ _value = value;
+ _oldValue = eventCondition.OverlapMode;
+ }
+
+ ///
+ public string DisplayName => "Update event overlap mode";
+
+ ///
+ public void Execute()
+ {
+ _eventCondition.OverlapMode = _value;
+ }
+
+ ///
+ public void Undo()
+ {
+ _eventCondition.OverlapMode = _oldValue;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventTriggerMode.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventTriggerMode.cs
new file mode 100644
index 000000000..b25e514ae
--- /dev/null
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventTriggerMode.cs
@@ -0,0 +1,38 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
+
+///
+/// Represents a profile editor command that can be used to update an event condition's trigger mode.
+///
+public class UpdateEventTriggerMode : IProfileEditorCommand
+{
+ private readonly EventCondition _eventCondition;
+ private readonly EventTriggerMode _oldValue;
+ private readonly EventTriggerMode _value;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public UpdateEventTriggerMode(EventCondition eventCondition, EventTriggerMode value)
+ {
+ _eventCondition = eventCondition;
+ _value = value;
+ _oldValue = eventCondition.TriggerMode;
+ }
+
+ ///
+ public string DisplayName => "Update event trigger mode";
+
+ ///
+ public void Execute()
+ {
+ _eventCondition.TriggerMode = _value;
+ }
+
+ ///
+ public void Undo()
+ {
+ _eventCondition.TriggerMode = _oldValue;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateStaticPlayMode.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateStaticPlayMode.cs
new file mode 100644
index 000000000..acf7e1d1a
--- /dev/null
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateStaticPlayMode.cs
@@ -0,0 +1,38 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
+
+///
+/// Represents a profile editor command that can be used to update an static condition's play mode.
+///
+public class UpdateStaticPlayMode : IProfileEditorCommand
+{
+ private readonly StaticCondition _staticCondition;
+ private readonly StaticPlayMode _value;
+ private readonly StaticPlayMode _oldValue;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public UpdateStaticPlayMode(StaticCondition staticCondition, StaticPlayMode value)
+ {
+ _staticCondition = staticCondition;
+ _value = value;
+ _oldValue = staticCondition.PlayMode;
+ }
+
+ ///
+ public string DisplayName => "Update condition play mode";
+
+ ///
+ public void Execute()
+ {
+ _staticCondition.PlayMode = _value;
+ }
+
+ ///
+ public void Undo()
+ {
+ _staticCondition.PlayMode = _oldValue;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateStaticStopMode.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateStaticStopMode.cs
new file mode 100644
index 000000000..12641d95f
--- /dev/null
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateStaticStopMode.cs
@@ -0,0 +1,38 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
+
+///
+/// Represents a profile editor command that can be used to update an static condition's play mode.
+///
+public class UpdateStaticStopMode : IProfileEditorCommand
+{
+ private readonly StaticCondition _staticCondition;
+ private readonly StaticStopMode _value;
+ private readonly StaticStopMode _oldValue;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public UpdateStaticStopMode(StaticCondition staticCondition, StaticStopMode value)
+ {
+ _staticCondition = staticCondition;
+ _value = value;
+ _oldValue = staticCondition.StopMode;
+ }
+
+ ///
+ public string DisplayName => "Update condition stop mode";
+
+ ///
+ public void Execute()
+ {
+ _staticCondition.StopMode = _value;
+ }
+
+ ///
+ public void Undo()
+ {
+ _staticCondition.StopMode = _oldValue;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs
index a00c8b674..9634b3287 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs
@@ -96,8 +96,7 @@ internal class ProfileEditorService : IProfileEditorService
else
{
renderElement.Enable();
- bool stickToMainSegment = (renderElement != _profileElementSubject.Value || renderElement.Timeline.Length < time) && renderElement.Timeline.PlayMode == TimelinePlayMode.Repeat;
- renderElement.OverrideTimelineAndApply(time, stickToMainSegment);
+ renderElement.OverrideTimelineAndApply(time);
foreach (ProfileElement child in renderElement.Children)
TickProfileElement(child, time);
diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs
index d9af870e0..9759807b8 100644
--- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs
+++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs
@@ -5,6 +5,7 @@ using Artemis.Core.LayerEffects;
using Artemis.UI.Screens.Device;
using Artemis.UI.Screens.Plugins;
using Artemis.UI.Screens.ProfileEditor;
+using Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
using Artemis.UI.Screens.ProfileEditor.ProfileTree;
using Artemis.UI.Screens.ProfileEditor.Properties;
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
@@ -52,7 +53,7 @@ namespace Artemis.UI.Ninject.Factories
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(SidebarViewModel sidebarViewModel, ProfileConfiguration profileConfiguration);
}
- public interface SurfaceVmFactory : IVmFactory
+ public interface ISurfaceVmFactory : IVmFactory
{
SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device);
ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device);
@@ -108,4 +109,12 @@ namespace Artemis.UI.Ninject.Factories
InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel);
OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel);
}
+
+ public interface IConditionVmFactory : IVmFactory
+ {
+ AlwaysOnConditionViewModel AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition);
+ PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition);
+ StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition);
+ EventConditionViewModel EventConditionViewModel(EventCondition eventCondition);
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypeViewModel.cs
index 5faaf0773..20683fb03 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypeViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypeViewModel.cs
@@ -1,19 +1,18 @@
using System;
using Artemis.UI.Shared;
-namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition
-{
- public class ConditionTypeViewModel : ViewModelBase
- {
- public ConditionTypeViewModel(string name, string description, Type? conditionType)
- {
- Name = name;
- Description = description;
- ConditionType = conditionType;
- }
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition;
- public string Name { get; }
- public string Description { get; }
- public Type? ConditionType { get; }
+public class ConditionTypeViewModel : ViewModelBase
+{
+ public ConditionTypeViewModel(string name, string description, Type conditionType)
+ {
+ Name = name;
+ Description = description;
+ ConditionType = conditionType;
}
+
+ public string Name { get; }
+ public string Description { get; }
+ public Type ConditionType { get; }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/AlwaysOnConditionView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/AlwaysOnConditionView.axaml
new file mode 100644
index 000000000..e703f4359
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/AlwaysOnConditionView.axaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ After playing the start segment, the main segment is endlessly repeated.
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/AlwaysOnConditionView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/AlwaysOnConditionView.axaml.cs
new file mode 100644
index 000000000..99520854f
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/AlwaysOnConditionView.axaml.cs
@@ -0,0 +1,17 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
+
+public class AlwaysOnConditionView : UserControl
+{
+ public AlwaysOnConditionView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/AlwaysOnConditionViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/AlwaysOnConditionViewModel.cs
new file mode 100644
index 000000000..94e6eeb55
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/AlwaysOnConditionViewModel.cs
@@ -0,0 +1,14 @@
+using Artemis.Core;
+using Artemis.UI.Shared;
+
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
+
+public class AlwaysOnConditionViewModel : ViewModelBase
+{
+ private readonly AlwaysOnCondition _alwaysOnCondition;
+
+ public AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition)
+ {
+ _alwaysOnCondition = alwaysOnCondition;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/EventConditionView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/EventConditionView.axaml
new file mode 100644
index 000000000..09363a0de
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/EventConditionView.axaml
@@ -0,0 +1,59 @@
+
+
+
+
+
+ Triggered by event
+
+
+ When the event fires..
+
+
+ Play the timeline once
+
+
+ Toggle the element on or off
+
+
+
+ And if already playing..
+
+
+ Restart the timeline
+
+
+ Play a second copy
+
+
+ Do nothing
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/EventConditionView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/EventConditionView.axaml.cs
new file mode 100644
index 000000000..b9bfdfd71
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/EventConditionView.axaml.cs
@@ -0,0 +1,17 @@
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
+
+public class EventConditionView : ReactiveUserControl
+{
+ public EventConditionView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/EventConditionViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/EventConditionViewModel.cs
new file mode 100644
index 000000000..144f5ae33
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/EventConditionViewModel.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Reactive;
+using System.Reactive.Linq;
+using System.Threading.Tasks;
+using Artemis.Core;
+using Artemis.UI.Screens.VisualScripting;
+using Artemis.UI.Shared;
+using Artemis.UI.Shared.Services;
+using Artemis.UI.Shared.Services.ProfileEditor;
+using Artemis.UI.Shared.Services.ProfileEditor.Commands;
+using Avalonia.Controls.Mixins;
+using ReactiveUI;
+
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
+
+public class EventConditionViewModel : ActivatableViewModelBase
+{
+ private readonly EventCondition _eventCondition;
+ private readonly IProfileEditorService _profileEditorService;
+ private readonly ObservableAsPropertyHelper _showOverlapOptions;
+ private readonly IWindowService _windowService;
+ private ObservableAsPropertyHelper? _eventPath;
+ private ObservableAsPropertyHelper? _selectedOverlapMode;
+ private ObservableAsPropertyHelper? _selectedTriggerMode;
+
+ public EventConditionViewModel(EventCondition eventCondition, IProfileEditorService profileEditorService, IWindowService windowService)
+ {
+ _eventCondition = eventCondition;
+ _profileEditorService = profileEditorService;
+ _windowService = windowService;
+ _showOverlapOptions = this.WhenAnyValue(vm => vm.SelectedTriggerMode)
+ .Select(m => m == 0)
+ .ToProperty(this, vm => vm.ShowOverlapOptions);
+
+ this.WhenActivated(d =>
+ {
+ _eventPath = eventCondition.WhenAnyValue(c => c.EventPath).ToProperty(this, vm => vm.EventPath).DisposeWith(d);
+ _selectedTriggerMode = eventCondition.WhenAnyValue(c => c.TriggerMode).Select(m => (int) m).ToProperty(this, vm => vm.SelectedTriggerMode).DisposeWith(d);
+ _selectedOverlapMode = eventCondition.WhenAnyValue(c => c.OverlapMode).Select(m => (int) m).ToProperty(this, vm => vm.SelectedOverlapMode).DisposeWith(d);
+ });
+
+ OpenEditor = ReactiveCommand.CreateFromTask(ExecuteOpenEditor);
+ }
+
+ public ObservableCollection FilterTypes { get; } = new() {typeof(IDataModelEvent)};
+ public ReactiveCommand OpenEditor { get; }
+ public bool ShowOverlapOptions => _showOverlapOptions.Value;
+ public bool IsConditionForLayer => _eventCondition.ProfileElement is Layer;
+
+ public DataModelPath? EventPath
+ {
+ get => _eventPath?.Value != null ? new DataModelPath(_eventPath.Value) : null;
+ set => _profileEditorService.ExecuteCommand(new UpdateEventConditionPath(_eventCondition, value != null ? new DataModelPath(value) : null));
+ }
+
+ public int SelectedTriggerMode
+ {
+ get => _selectedTriggerMode?.Value ?? 0;
+ set => _profileEditorService.ExecuteCommand(new UpdateEventTriggerMode(_eventCondition, (EventTriggerMode) value));
+ }
+
+ public int SelectedOverlapMode
+ {
+ get => _selectedOverlapMode?.Value ?? 0;
+ set => _profileEditorService.ExecuteCommand(new UpdateEventOverlapMode(_eventCondition, (EventOverlapMode) value));
+ }
+
+ private async Task ExecuteOpenEditor()
+ {
+ await _windowService.ShowDialogAsync(("nodeScript", _eventCondition.NodeScript));
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/PlayOnceConditionView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/PlayOnceConditionView.axaml
new file mode 100644
index 000000000..174c61a60
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/PlayOnceConditionView.axaml
@@ -0,0 +1,15 @@
+
+
+
+
+ Every segment of the timeline is played once.
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/PlayOnceConditionView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/PlayOnceConditionView.axaml.cs
new file mode 100644
index 000000000..39b606054
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/PlayOnceConditionView.axaml.cs
@@ -0,0 +1,17 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
+
+public class PlayOnceConditionView : UserControl
+{
+ public PlayOnceConditionView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/PlayOnceConditionViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/PlayOnceConditionViewModel.cs
new file mode 100644
index 000000000..59fbf90ed
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/PlayOnceConditionViewModel.cs
@@ -0,0 +1,14 @@
+using Artemis.Core;
+using Artemis.UI.Shared;
+
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
+
+public class PlayOnceConditionViewModel : ViewModelBase
+{
+ private readonly PlayOnceCondition _playOnceCondition;
+
+ public PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition)
+ {
+ _playOnceCondition = playOnceCondition;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/StaticConditionView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/StaticConditionView.axaml
new file mode 100644
index 000000000..f4cd60f7b
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/StaticConditionView.axaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+ While condition is met..
+
+ Repeat the main segment
+ Play the main segment once
+
+
+ And when no longer met..
+
+ Finish the main segment
+ Skip forward to the end segment
+
+
+
+
+
+ The start- and end-segments are played once each time the condition is met.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/StaticConditionView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/StaticConditionView.axaml.cs
new file mode 100644
index 000000000..83855685c
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/StaticConditionView.axaml.cs
@@ -0,0 +1,17 @@
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
+
+public class StaticConditionView : ReactiveUserControl
+{
+ public StaticConditionView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/StaticConditionViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/StaticConditionViewModel.cs
new file mode 100644
index 000000000..c6e7dadd7
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/ConditionTypes/StaticConditionViewModel.cs
@@ -0,0 +1,56 @@
+using System.Reactive;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Threading.Tasks;
+using Artemis.Core;
+using Artemis.UI.Screens.VisualScripting;
+using Artemis.UI.Shared;
+using Artemis.UI.Shared.Services;
+using Artemis.UI.Shared.Services.ProfileEditor;
+using Artemis.UI.Shared.Services.ProfileEditor.Commands;
+using ReactiveUI;
+
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
+
+public class StaticConditionViewModel : ActivatableViewModelBase
+{
+ private readonly IProfileEditorService _profileEditorService;
+ private readonly StaticCondition _staticCondition;
+ private readonly IWindowService _windowService;
+ private ObservableAsPropertyHelper? _selectedPlayMode;
+ private ObservableAsPropertyHelper? _selectedStopMode;
+
+ public StaticConditionViewModel(StaticCondition staticCondition, IProfileEditorService profileEditorService, IWindowService windowService)
+ {
+ _staticCondition = staticCondition;
+ _profileEditorService = profileEditorService;
+ _windowService = windowService;
+
+ this.WhenActivated(d =>
+ {
+ _selectedPlayMode = staticCondition.WhenAnyValue(c => c.PlayMode).Select(m => (int) m).ToProperty(this, vm => vm.SelectedPlayMode).DisposeWith(d);
+ _selectedStopMode = staticCondition.WhenAnyValue(c => c.StopMode).Select(m => (int) m).ToProperty(this, vm => vm.SelectedStopMode).DisposeWith(d);
+ });
+
+ OpenEditor = ReactiveCommand.CreateFromTask(ExecuteOpenEditor);
+ }
+
+ public ReactiveCommand OpenEditor { get; }
+
+ public int SelectedPlayMode
+ {
+ get => _selectedPlayMode?.Value ?? 0;
+ set => _profileEditorService.ExecuteCommand(new UpdateStaticPlayMode(_staticCondition, (StaticPlayMode) value));
+ }
+
+ public int SelectedStopMode
+ {
+ get => _selectedStopMode?.Value ?? 0;
+ set => _profileEditorService.ExecuteCommand(new UpdateStaticStopMode(_staticCondition, (StaticStopMode) value));
+ }
+
+ private async Task ExecuteOpenEditor()
+ {
+ await _windowService.ShowDialogAsync(("nodeScript", _staticCondition.NodeScript));
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptView.axaml
index eb186053f..fd8a5ca96 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptView.axaml
@@ -3,8 +3,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:displayCondition="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayCondition"
- xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
- xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.Controls.DataModelPicker;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="650"
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayCondition.DisplayConditionScriptView"
x:DataType="displayCondition:DisplayConditionScriptViewModel">
@@ -20,19 +18,14 @@
-
-
-
-
-
- Condition type
+
+ Activation type
@@ -44,64 +37,7 @@
-
-
-
- Triggered by event
-
-
- When the event fires..
-
-
- Play the timeline once
-
-
- Toggle the element on or off
-
-
-
- And if already playing..
-
-
- Restart the timeline
-
-
- Play a second copy
-
-
- Do nothing
-
-
-
-
-
-
- While condition is met..
-
- Play the main segment once
- Repeat the main segment
-
-
- And when no longer met..
-
- Finish the main segment
- Skip forward to the end segment
-
-
-
-
-
- The start- and end-segments are always played once.
-
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptView.axaml.cs
index f493916d0..730ad046a 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptView.axaml.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptView.axaml.cs
@@ -1,18 +1,17 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
-namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition
-{
- public partial class DisplayConditionScriptView : ReactiveUserControl
- {
- public DisplayConditionScriptView()
- {
- InitializeComponent();
- }
+namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition;
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+public class DisplayConditionScriptView : ReactiveUserControl
+{
+ public DisplayConditionScriptView()
+ {
+ InitializeComponent();
}
-}
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptViewModel.cs
index 98d94ee2a..3568cfff7 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/DisplayCondition/DisplayConditionScriptViewModel.cs
@@ -1,13 +1,9 @@
-using System;
-using System.Collections.ObjectModel;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
-using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
-using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Shared;
-using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Avalonia.Controls.Mixins;
@@ -17,81 +13,79 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition;
public class DisplayConditionScriptViewModel : ActivatableViewModelBase
{
+ private readonly ObservableAsPropertyHelper _conditionViewModel;
+ private readonly IConditionVmFactory _conditionVmFactory;
private readonly IProfileEditorService _profileEditorService;
- private readonly ObservableAsPropertyHelper _showEventOptions;
- private readonly ObservableAsPropertyHelper _showStaticOptions;
- private readonly IWindowService _windowService;
- private ObservableAsPropertyHelper? _nodeScriptViewModel;
- private RenderProfileElement? _profileElement;
+ private ObservableAsPropertyHelper? _profileElement;
private ObservableAsPropertyHelper? _selectedConditionTypeViewModel;
- public DisplayConditionScriptViewModel(IProfileEditorService profileEditorService, INodeVmFactory nodeVmFactory, IWindowService windowService)
+ public DisplayConditionScriptViewModel(IProfileEditorService profileEditorService, IConditionVmFactory conditionVmFactory)
{
_profileEditorService = profileEditorService;
- _windowService = windowService;
+ _conditionVmFactory = conditionVmFactory;
ConditionTypeViewModels = new ObservableCollection
{
- new("None", "The element is always active.", null),
- new("Regular", "The element is activated when the provided visual script ends in true.", typeof(StaticCondition)),
- new("Event", "The element is activated when the selected event fires.\r\n" +
- "Events that contain data can conditionally trigger the layer using a visual script.", typeof(EventCondition))
+ new("Always", "The element is always active.", typeof(AlwaysOnCondition)),
+ new("Once", "The element is shown once until its timeline is finished.", typeof(PlayOnceCondition)),
+ new("Conditional", "The element is activated when the provided visual script ends in true.", typeof(StaticCondition)),
+ new("On event", "The element is activated when the selected event fires.\r\n" +
+ "Events that contain data can conditionally trigger the layer using a visual script.", typeof(EventCondition))
};
this.WhenActivated(d =>
{
- profileEditorService.ProfileElement.Subscribe(p => _profileElement = p).DisposeWith(d);
-
- _nodeScriptViewModel = profileEditorService.ProfileElement
- .Select(p => p?.WhenAnyValue(element => element.DisplayCondition) ?? Observable.Never())
- .Switch()
- .Select(c => c is INodeScriptCondition {NodeScript: NodeScript nodeScript} ? nodeVmFactory.NodeScriptViewModel(nodeScript, true) : null)
- .ToProperty(this, vm => vm.NodeScriptViewModel)
- .DisposeWith(d);
+ _profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d);
_selectedConditionTypeViewModel = profileEditorService.ProfileElement
- .Select(p => p?.WhenAnyValue(element => element.DisplayCondition) ?? Observable.Never())
+ .Select(p => p?.WhenAnyValue(element => element.DisplayCondition) ?? Observable.Return(null))
.Switch()
.Select(c => c != null ? ConditionTypeViewModels.FirstOrDefault(vm => vm.ConditionType == c.GetType()) : null)
.ToProperty(this, vm => vm.SelectedConditionTypeViewModel)
.DisposeWith(d);
});
- _showStaticOptions = this.WhenAnyValue(vm => vm.SelectedConditionTypeViewModel)
- .Select(c => c != null && c.ConditionType == typeof(StaticCondition))
- .ToProperty(this, vm => vm.ShowStaticOptions);
- _showEventOptions = this.WhenAnyValue(vm => vm.SelectedConditionTypeViewModel)
- .Select(c => c != null && c.ConditionType == typeof(EventCondition))
- .ToProperty(this, vm => vm.ShowEventOptions);
+ _conditionViewModel = this.WhenAnyValue(vm => vm.SelectedConditionTypeViewModel).Select(_ => CreateConditionViewModel()).ToProperty(this, vm => vm.ConditionViewModel);
}
- public NodeScriptViewModel? NodeScriptViewModel => _nodeScriptViewModel?.Value;
+ public RenderProfileElement? ProfileElement => _profileElement?.Value;
+ public ViewModelBase? ConditionViewModel => _conditionViewModel.Value;
public ObservableCollection ConditionTypeViewModels { get; }
public ConditionTypeViewModel? SelectedConditionTypeViewModel
{
get => _selectedConditionTypeViewModel?.Value;
- set
- {
- if (_profileElement == null)
- return;
-
- ICondition? condition = null;
- if (value?.ConditionType == typeof(StaticCondition))
- condition = new StaticCondition(_profileElement);
- else if (value?.ConditionType == typeof(EventCondition))
- condition = new EventCondition(_profileElement);
-
- _profileEditorService.ExecuteCommand(new ChangeConditionType(_profileElement, condition));
- }
+ set => ApplyConditionType(value);
}
- public bool ShowStaticOptions => _showStaticOptions.Value;
- public bool ShowEventOptions => _showEventOptions.Value;
-
-
- public async Task OpenEditor()
+ private ViewModelBase? CreateConditionViewModel()
{
- if (_profileElement?.DisplayCondition is StaticCondition staticCondition)
- await _windowService.ShowDialogAsync(("nodeScript", staticCondition.Script));
+ if (ProfileElement == null)
+ return null;
+
+ if (ProfileElement.DisplayCondition is AlwaysOnCondition alwaysOnCondition)
+ return _conditionVmFactory.AlwaysOnConditionViewModel(alwaysOnCondition);
+ if (ProfileElement.DisplayCondition is PlayOnceCondition playOnceCondition)
+ return _conditionVmFactory.PlayOnceConditionViewModel(playOnceCondition);
+ if (ProfileElement.DisplayCondition is StaticCondition staticCondition)
+ return _conditionVmFactory.StaticConditionViewModel(staticCondition);
+ if (ProfileElement.DisplayCondition is EventCondition eventCondition)
+ return _conditionVmFactory.EventConditionViewModel(eventCondition);
+
+ return null;
+ }
+
+ private void ApplyConditionType(ConditionTypeViewModel? value)
+ {
+ if (ProfileElement == null || value == null || ProfileElement.DisplayCondition.GetType() == value.ConditionType)
+ return;
+
+ if (value.ConditionType == typeof(AlwaysOnCondition))
+ _profileEditorService.ExecuteCommand(new ChangeConditionType(ProfileElement, new AlwaysOnCondition(ProfileElement)));
+ if (value.ConditionType == typeof(PlayOnceCondition))
+ _profileEditorService.ExecuteCommand(new ChangeConditionType(ProfileElement, new PlayOnceCondition(ProfileElement)));
+ if (value.ConditionType == typeof(StaticCondition))
+ _profileEditorService.ExecuteCommand(new ChangeConditionType(ProfileElement, new StaticCondition(ProfileElement)));
+ if (value.ConditionType == typeof(EventCondition))
+ _profileEditorService.ExecuteCommand(new ChangeConditionType(ProfileElement, new EventCondition(ProfileElement)));
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentView.axaml
index 9884a7533..8e1ac2788 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentView.axaml
@@ -46,13 +46,6 @@
Command="{Binding DisableSegment}">
-
-
-