mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Core - Refactored display conditions
Profile editor - Added UIs for each condition type
This commit is contained in:
parent
66ea718316
commit
3dfc25b092
@ -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
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of IStorageModel
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of ICondition
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IConditionEntity Entity { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public RenderProfileElement ProfileElement { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsMet { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (ProfileElement.Parent is RenderProfileElement parent)
|
||||||
|
IsMet = parent.DisplayConditionMet;
|
||||||
|
else
|
||||||
|
IsMet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void UpdateTimeline(double deltaTime)
|
||||||
|
{
|
||||||
|
ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OverrideTimeline(TimeSpan position)
|
||||||
|
{
|
||||||
|
ProfileElement.Timeline.Override(position, position > ProfileElement.Timeline.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,45 +4,52 @@ using Artemis.Core.Internal;
|
|||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
using Artemis.Storage.Entities.Profile.Conditions;
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a condition that is based on a <see cref="DataModelEvent" />
|
||||||
|
/// </summary>
|
||||||
|
public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Represents a condition that is based on a <see cref="DataModelEvent" />
|
|
||||||
/// </summary>
|
|
||||||
public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
|
||||||
{
|
|
||||||
private readonly string _displayName;
|
private readonly string _displayName;
|
||||||
private readonly EventConditionEntity _entity;
|
private readonly EventConditionEntity _entity;
|
||||||
private EventDefaultNode? _eventNode;
|
private readonly EventDefaultNode _eventNode;
|
||||||
private TimeLineEventOverlapMode _eventOverlapMode;
|
|
||||||
private DataModelPath? _eventPath;
|
private DataModelPath? _eventPath;
|
||||||
private DateTime _lastProcessedTrigger;
|
private DateTime _lastProcessedTrigger;
|
||||||
private NodeScript<bool>? _script;
|
private EventOverlapMode _overlapMode;
|
||||||
|
private NodeScript<bool> _script;
|
||||||
|
private EventTriggerMode _triggerMode;
|
||||||
|
private bool _wasMet;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="EventCondition" /> class
|
/// Creates a new instance of the <see cref="EventCondition" /> class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EventCondition(ProfileElement profileElement)
|
public EventCondition(RenderProfileElement profileElement)
|
||||||
{
|
{
|
||||||
|
ProfileElement = profileElement;
|
||||||
|
|
||||||
_entity = new EventConditionEntity();
|
_entity = new EventConditionEntity();
|
||||||
_displayName = profileElement.GetType().Name;
|
_displayName = profileElement.GetType().Name;
|
||||||
|
_eventNode = new EventDefaultNode {X = -300};
|
||||||
ProfileElement = profileElement;
|
_script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal EventCondition(EventConditionEntity entity, ProfileElement profileElement)
|
internal EventCondition(EventConditionEntity entity, RenderProfileElement profileElement)
|
||||||
{
|
{
|
||||||
|
ProfileElement = profileElement;
|
||||||
|
|
||||||
_entity = entity;
|
_entity = entity;
|
||||||
_displayName = profileElement.GetType().Name;
|
_displayName = profileElement.GetType().Name;
|
||||||
|
_eventNode = new EventDefaultNode();
|
||||||
|
_script = null!;
|
||||||
|
|
||||||
ProfileElement = profileElement;
|
|
||||||
Load();
|
Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the script that drives the event condition
|
/// Gets the script that drives the event condition
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NodeScript<bool>? Script
|
public NodeScript<bool> Script
|
||||||
{
|
{
|
||||||
get => _script;
|
get => _script;
|
||||||
set => SetAndNotify(ref _script, value);
|
set => SetAndNotify(ref _script, value);
|
||||||
@ -58,12 +65,22 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets how the condition behaves when events trigger before the timeline finishes
|
/// Gets or sets how the condition behaves when the event fires.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeLineEventOverlapMode EventOverlapMode
|
public EventTriggerMode TriggerMode
|
||||||
{
|
{
|
||||||
get => _eventOverlapMode;
|
get => _triggerMode;
|
||||||
set => SetAndNotify(ref _eventOverlapMode, value);
|
set => SetAndNotify(ref _triggerMode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets how the condition behaves when events trigger before the timeline finishes in the
|
||||||
|
/// <see cref="EventTriggerMode.Play" /> event trigger mode.
|
||||||
|
/// </summary>
|
||||||
|
public EventOverlapMode OverlapMode
|
||||||
|
{
|
||||||
|
get => _overlapMode;
|
||||||
|
set => SetAndNotify(ref _overlapMode, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -71,34 +88,22 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void UpdateEventNode()
|
public void UpdateEventNode()
|
||||||
{
|
{
|
||||||
if (Script == null || EventPath?.GetValue() is not IDataModelEvent dataModelEvent)
|
IDataModelEvent? dataModelEvent = EventPath?.GetValue() as IDataModelEvent;
|
||||||
return;
|
_eventNode.CreatePins(dataModelEvent);
|
||||||
|
|
||||||
if (Script.Nodes.FirstOrDefault(n => n is EventDefaultNode) is EventDefaultNode existing)
|
if (dataModelEvent != null && !Script.Nodes.Contains(_eventNode))
|
||||||
{
|
|
||||||
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);
|
Script.AddNode(_eventNode);
|
||||||
else
|
else if (dataModelEvent == null && Script.Nodes.Contains(_eventNode))
|
||||||
Script.RemoveNode(_eventNode);
|
Script.RemoveNode(_eventNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the <see cref="Script" /> with a new empty node script
|
/// Gets the start node of the event script, if any
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void CreateEmptyNodeScript()
|
/// <returns>The start node of the event script, if any.</returns>
|
||||||
|
public INode GetStartNode()
|
||||||
{
|
{
|
||||||
Script?.Dispose();
|
return _eventNode;
|
||||||
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
|
||||||
UpdateEventNode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Evaluate()
|
private bool Evaluate()
|
||||||
@ -108,7 +113,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
_lastProcessedTrigger = dataModelEvent.LastTrigger;
|
_lastProcessedTrigger = dataModelEvent.LastTrigger;
|
||||||
|
|
||||||
if (Script == null)
|
if (!Script.ExitNodeConnected)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Script.Run();
|
Script.Run();
|
||||||
@ -119,7 +124,7 @@ namespace Artemis.Core
|
|||||||
public IConditionEntity Entity => _entity;
|
public IConditionEntity Entity => _entity;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ProfileElement ProfileElement { get; }
|
public RenderProfileElement ProfileElement { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsMet { get; private set; }
|
public bool IsMet { get; private set; }
|
||||||
@ -127,7 +132,8 @@ namespace Artemis.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
if (EventOverlapMode == TimeLineEventOverlapMode.Toggle)
|
_wasMet = IsMet;
|
||||||
|
if (TriggerMode == EventTriggerMode.Toggle)
|
||||||
{
|
{
|
||||||
if (Evaluate())
|
if (Evaluate())
|
||||||
IsMet = !IsMet;
|
IsMet = !IsMet;
|
||||||
@ -139,31 +145,37 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline)
|
public void UpdateTimeline(double deltaTime)
|
||||||
{
|
{
|
||||||
if (!isMet)
|
if (TriggerMode == EventTriggerMode.Toggle)
|
||||||
{
|
{
|
||||||
if (EventOverlapMode == TimeLineEventOverlapMode.Toggle)
|
if (!IsMet && _wasMet)
|
||||||
timeline.JumpToEnd();
|
ProfileElement.Timeline.JumpToEnd();
|
||||||
return;
|
else if (IsMet && !_wasMet)
|
||||||
|
ProfileElement.Timeline.JumpToStart();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Event overlap mode doesn't apply in this case
|
|
||||||
if (timeline.IsFinished)
|
|
||||||
{
|
{
|
||||||
timeline.JumpToStart();
|
if (IsMet && ProfileElement.Timeline.IsFinished)
|
||||||
return;
|
{
|
||||||
|
ProfileElement.Timeline.JumpToStart();
|
||||||
}
|
}
|
||||||
|
else if (IsMet)
|
||||||
// If the timeline was already running, look at the event overlap mode
|
{
|
||||||
if (EventOverlapMode == TimeLineEventOverlapMode.Restart)
|
if (OverlapMode == EventOverlapMode.Restart)
|
||||||
timeline.JumpToStart();
|
ProfileElement.Timeline.JumpToStart();
|
||||||
else if (EventOverlapMode == TimeLineEventOverlapMode.Copy && ProfileElement is Layer layer)
|
else if (OverlapMode == EventOverlapMode.Copy && ProfileElement is Layer layer)
|
||||||
layer.CreateCopyAsChild();
|
layer.CreateCopyAsChild();
|
||||||
else if (EventOverlapMode == TimeLineEventOverlapMode.Toggle && !wasMet)
|
}
|
||||||
timeline.JumpToStart();
|
}
|
||||||
|
|
||||||
// The remaining overlap mode is 'ignore' which requires no further action
|
ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), TriggerMode == EventTriggerMode.Toggle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OverrideTimeline(TimeSpan position)
|
||||||
|
{
|
||||||
|
ProfileElement.Timeline.Override(position, TriggerMode == EventTriggerMode.Toggle && position > ProfileElement.Timeline.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -178,18 +190,23 @@ namespace Artemis.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Load()
|
public void Load()
|
||||||
{
|
{
|
||||||
EventOverlapMode = (TimeLineEventOverlapMode) _entity.EventOverlapMode;
|
TriggerMode = (EventTriggerMode) _entity.TriggerMode;
|
||||||
if (_entity.Script != null)
|
OverlapMode = (EventOverlapMode) _entity.OverlapMode;
|
||||||
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile);
|
|
||||||
if (_entity.EventPath != null)
|
if (_entity.EventPath != null)
|
||||||
EventPath = new DataModelPath(_entity.EventPath);
|
EventPath = new DataModelPath(_entity.EventPath);
|
||||||
|
|
||||||
|
Script = _entity.Script != null
|
||||||
|
? new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile)
|
||||||
|
: new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Save()
|
public void Save()
|
||||||
{
|
{
|
||||||
_entity.EventOverlapMode = (int) EventOverlapMode;
|
_entity.TriggerMode = (int) TriggerMode;
|
||||||
Script?.Save();
|
_entity.OverlapMode = (int) OverlapMode;
|
||||||
|
Script.Save();
|
||||||
_entity.Script = Script?.Entity;
|
_entity.Script = Script?.Entity;
|
||||||
EventPath?.Save();
|
EventPath?.Save();
|
||||||
_entity.EventPath = EventPath?.Entity;
|
_entity.EventPath = EventPath?.Entity;
|
||||||
@ -201,14 +218,48 @@ namespace Artemis.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void LoadNodeScript()
|
public void LoadNodeScript()
|
||||||
{
|
{
|
||||||
if (Script == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Script.Load();
|
Script.Load();
|
||||||
UpdateEventNode();
|
UpdateEventNode();
|
||||||
Script.LoadConnections();
|
Script.LoadConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a mode for render elements to start their timeline when display conditions events are fired.
|
||||||
|
/// </summary>
|
||||||
|
public enum EventTriggerMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Play the timeline once.
|
||||||
|
/// </summary>
|
||||||
|
Play,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggle repeating the timeline.
|
||||||
|
/// </summary>
|
||||||
|
Toggle
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a mode for render elements to configure the behaviour of events that overlap i.e. trigger again before
|
||||||
|
/// the timeline finishes.
|
||||||
|
/// </summary>
|
||||||
|
public enum EventOverlapMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stop the current run and restart the timeline
|
||||||
|
/// </summary>
|
||||||
|
Restart,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ignore subsequent event fires until the timeline finishes
|
||||||
|
/// </summary>
|
||||||
|
Ignore,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Play another copy of the timeline on top of the current run
|
||||||
|
/// </summary>
|
||||||
|
Copy
|
||||||
}
|
}
|
||||||
@ -16,7 +16,7 @@ public interface ICondition : IDisposable, IStorageModel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the profile element this condition applies to
|
/// Gets the profile element this condition applies to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ProfileElement ProfileElement { get; }
|
public RenderProfileElement ProfileElement { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether the condition is currently met
|
/// Gets a boolean indicating whether the condition is currently met
|
||||||
@ -30,12 +30,11 @@ public interface ICondition : IDisposable, IStorageModel
|
|||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies the display condition to the provided timeline
|
/// Updates the timeline according to the provided <paramref name="deltaTime" /> as the display condition sees fit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="isMet"></param>
|
void UpdateTimeline(double deltaTime);
|
||||||
/// <param name="wasMet"></param>
|
|
||||||
/// <param name="timeline">The timeline to apply the display condition to</param>
|
void OverrideTimeline(TimeSpan position);
|
||||||
void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of IStorageModel
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of ICondition
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IConditionEntity Entity { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public RenderProfileElement ProfileElement { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsMet { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (ProfileElement.Parent is RenderProfileElement parent)
|
||||||
|
IsMet = parent.DisplayConditionMet;
|
||||||
|
else
|
||||||
|
IsMet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void UpdateTimeline(double deltaTime)
|
||||||
|
{
|
||||||
|
ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OverrideTimeline(TimeSpan position)
|
||||||
|
{
|
||||||
|
ProfileElement.Timeline.Override(position, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using System;
|
||||||
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
using Artemis.Storage.Entities.Profile.Conditions;
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
@ -10,11 +11,14 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
private readonly string _displayName;
|
private readonly string _displayName;
|
||||||
private readonly StaticConditionEntity _entity;
|
private readonly StaticConditionEntity _entity;
|
||||||
|
private StaticPlayMode _playMode;
|
||||||
|
private StaticStopMode _stopMode;
|
||||||
|
private bool _wasMet;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="StaticCondition" /> class
|
/// Creates a new instance of the <see cref="StaticCondition" /> class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StaticCondition(ProfileElement profileElement)
|
public StaticCondition(RenderProfileElement profileElement)
|
||||||
{
|
{
|
||||||
_entity = new StaticConditionEntity();
|
_entity = new StaticConditionEntity();
|
||||||
_displayName = profileElement.GetType().Name;
|
_displayName = profileElement.GetType().Name;
|
||||||
@ -23,7 +27,7 @@ namespace Artemis.Core
|
|||||||
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not this {_displayName} should be active", profileElement.Profile);
|
Script = new NodeScript<bool>($"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;
|
_entity = entity;
|
||||||
_displayName = profileElement.GetType().Name;
|
_displayName = profileElement.GetType().Name;
|
||||||
@ -43,31 +47,66 @@ namespace Artemis.Core
|
|||||||
public IConditionEntity Entity => _entity;
|
public IConditionEntity Entity => _entity;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ProfileElement ProfileElement { get; }
|
public RenderProfileElement ProfileElement { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsMet { get; private set; }
|
public bool IsMet { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the mode in which the render element starts its timeline when display conditions are met
|
||||||
|
/// </summary>
|
||||||
|
public StaticPlayMode PlayMode
|
||||||
|
{
|
||||||
|
get => _playMode;
|
||||||
|
set => SetAndNotify(ref _playMode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
|
||||||
|
/// </summary>
|
||||||
|
public StaticStopMode StopMode
|
||||||
|
{
|
||||||
|
get => _stopMode;
|
||||||
|
set => SetAndNotify(ref _stopMode, value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
if (!Script.ExitNodeConnected)
|
_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 = true;
|
IsMet = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Script.ExitNodeConnected)
|
||||||
|
IsMet = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
Script.Run();
|
Script.Run();
|
||||||
IsMet = Script.Result;
|
IsMet = Script.Result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline)
|
public void UpdateTimeline(double deltaTime)
|
||||||
{
|
{
|
||||||
if (isMet && !wasMet && timeline.IsFinished)
|
if (IsMet && !_wasMet && ProfileElement.Timeline.IsFinished)
|
||||||
timeline.JumpToStart();
|
ProfileElement.Timeline.JumpToStart();
|
||||||
else if (!isMet && wasMet && timeline.StopMode == TimelineStopMode.SkipToEnd)
|
else if (!IsMet && _wasMet && StopMode == StaticStopMode.SkipToEnd)
|
||||||
timeline.JumpToEndSegment();
|
ProfileElement.Timeline.JumpToEndSegment();
|
||||||
|
|
||||||
|
ProfileElement.Timeline.Update(TimeSpan.FromSeconds(deltaTime), PlayMode == StaticPlayMode.Repeat && IsMet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OverrideTimeline(TimeSpan position)
|
||||||
|
{
|
||||||
|
ProfileElement.Timeline.Override(position, PlayMode == StaticPlayMode.Repeat && position > ProfileElement.Timeline.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -81,12 +120,18 @@ namespace Artemis.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Load()
|
public void Load()
|
||||||
{
|
{
|
||||||
|
PlayMode = (StaticPlayMode) _entity.PlayMode;
|
||||||
|
StopMode = (StaticStopMode) _entity.StopMode;
|
||||||
|
|
||||||
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not this {_displayName} should be active", _entity.Script, ProfileElement.Profile);
|
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not this {_displayName} should be active", _entity.Script, ProfileElement.Profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Save()
|
public void Save()
|
||||||
{
|
{
|
||||||
|
_entity.PlayMode = (int) PlayMode;
|
||||||
|
_entity.StopMode = (int) StopMode;
|
||||||
|
|
||||||
Script.Save();
|
Script.Save();
|
||||||
_entity.Script = Script.Entity;
|
_entity.Script = Script.Entity;
|
||||||
}
|
}
|
||||||
@ -102,4 +147,36 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a mode for render elements to start their timeline when display conditions are met
|
||||||
|
/// </summary>
|
||||||
|
public enum StaticPlayMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Continue repeating the main segment of the timeline while the condition is met
|
||||||
|
/// </summary>
|
||||||
|
Repeat,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Only play the timeline once when the condition is met
|
||||||
|
/// </summary>
|
||||||
|
Once
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a mode for render elements to stop their timeline when display conditions are no longer met
|
||||||
|
/// </summary>
|
||||||
|
public enum StaticStopMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// When conditions are no longer met, finish the the current run of the main timeline
|
||||||
|
/// </summary>
|
||||||
|
Finish,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When conditions are no longer met, skip to the end segment of the timeline
|
||||||
|
/// </summary>
|
||||||
|
SkipToEnd
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -237,11 +237,11 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
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))
|
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
|
||||||
baseLayerEffect.InternalUpdate(Timeline);
|
baseLayerEffect.InternalUpdate(Timeline); ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@ -476,9 +476,9 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OverrideTimelineAndApply(TimeSpan position, bool stickToMainSegment)
|
public override void OverrideTimelineAndApply(TimeSpan position)
|
||||||
{
|
{
|
||||||
Timeline.Override(position, stickToMainSegment);
|
DisplayCondition.OverrideTimeline(position);
|
||||||
|
|
||||||
General.Update(Timeline);
|
General.Update(Timeline);
|
||||||
Transform.Update(Timeline);
|
Transform.Update(Timeline);
|
||||||
|
|||||||
@ -27,6 +27,7 @@ namespace Artemis.Core
|
|||||||
LayerEffectsList = new List<BaseLayerEffect>();
|
LayerEffectsList = new List<BaseLayerEffect>();
|
||||||
LayerEffects = new ReadOnlyCollection<BaseLayerEffect>(LayerEffectsList);
|
LayerEffects = new ReadOnlyCollection<BaseLayerEffect>(LayerEffectsList);
|
||||||
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||||
|
_displayCondition = new AlwaysOnCondition(this);
|
||||||
|
|
||||||
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
||||||
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
||||||
@ -76,8 +77,10 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
DisplayCondition = RenderElementEntity.DisplayCondition switch
|
DisplayCondition = RenderElementEntity.DisplayCondition switch
|
||||||
{
|
{
|
||||||
StaticConditionEntity staticConditionEntity => new StaticCondition(staticConditionEntity, this),
|
AlwaysOnConditionEntity entity => new AlwaysOnCondition(entity, this),
|
||||||
EventConditionEntity eventConditionEntity => new EventCondition(eventConditionEntity, this),
|
PlayOnceConditionEntity entity => new PlayOnceCondition(entity, this),
|
||||||
|
StaticConditionEntity entity => new StaticCondition(entity, this),
|
||||||
|
EventConditionEntity entity => new EventCondition(entity, this),
|
||||||
_ => DisplayCondition
|
_ => DisplayCondition
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -124,17 +127,11 @@ namespace Artemis.Core
|
|||||||
public Timeline Timeline { get; private set; }
|
public Timeline Timeline { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the <see cref="Timeline" /> according to the provided <paramref name="deltaTime" /> and current display
|
/// Updates the <see cref="Timeline" /> according to the provided <paramref name="deltaTime" /> and current display condition
|
||||||
/// condition status
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void UpdateTimeline(double deltaTime)
|
protected void UpdateTimeline(double deltaTime)
|
||||||
{
|
{
|
||||||
// TODO: Move to conditions
|
DisplayCondition.UpdateTimeline(deltaTime);
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -365,13 +362,13 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the display condition used to determine whether this element is active or not
|
/// Gets or sets the display condition used to determine whether this element is active or not
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ICondition? DisplayCondition
|
public ICondition DisplayCondition
|
||||||
{
|
{
|
||||||
get => _displayCondition;
|
get => _displayCondition;
|
||||||
set => SetAndNotify(ref _displayCondition, value);
|
set => SetAndNotify(ref _displayCondition, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ICondition? _displayCondition;
|
private ICondition _displayCondition;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evaluates the display conditions on this element and applies any required changes to the <see cref="Timeline" />
|
/// Evaluates the display conditions on this element and applies any required changes to the <see cref="Timeline" />
|
||||||
@ -384,14 +381,7 @@ namespace Artemis.Core
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DisplayCondition == null)
|
|
||||||
{
|
|
||||||
DisplayConditionMet = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DisplayCondition.Update();
|
DisplayCondition.Update();
|
||||||
DisplayCondition.ApplyToTimeline(DisplayCondition.IsMet, DisplayConditionMet, Timeline);
|
|
||||||
DisplayConditionMet = DisplayCondition.IsMet;
|
DisplayConditionMet = DisplayCondition.IsMet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +391,6 @@ namespace Artemis.Core
|
|||||||
/// Overrides the main timeline to the specified time and clears any extra time lines
|
/// Overrides the main timeline to the specified time and clears any extra time lines
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="position">The position to set the timeline to</param>
|
/// <param name="position">The position to set the timeline to</param>
|
||||||
/// <param name="stickToMainSegment">Whether to stick to the main segment, wrapping around if needed</param>
|
public abstract void OverrideTimelineAndApply(TimeSpan position);
|
||||||
public abstract void OverrideTimelineAndApply(TimeSpan position, bool stickToMainSegment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,8 +41,7 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
|||||||
|
|
||||||
private TimeSpan _position;
|
private TimeSpan _position;
|
||||||
private TimeSpan _lastDelta;
|
private TimeSpan _lastDelta;
|
||||||
private TimelinePlayMode _playMode;
|
|
||||||
private TimelineStopMode _stopMode;
|
|
||||||
private TimeSpan _startSegmentLength;
|
private TimeSpan _startSegmentLength;
|
||||||
private TimeSpan _mainSegmentLength;
|
private TimeSpan _mainSegmentLength;
|
||||||
private TimeSpan _endSegmentLength;
|
private TimeSpan _endSegmentLength;
|
||||||
@ -67,23 +66,7 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
|||||||
private set => SetAndNotify(ref _lastDelta, value);
|
private set => SetAndNotify(ref _lastDelta, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the mode in which the render element starts its timeline when display conditions are met
|
|
||||||
/// </summary>
|
|
||||||
public TimelinePlayMode PlayMode
|
|
||||||
{
|
|
||||||
get => _playMode;
|
|
||||||
set => SetAndNotify(ref _playMode, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
|
|
||||||
/// </summary>
|
|
||||||
public TimelineStopMode StopMode
|
|
||||||
{
|
|
||||||
get => _stopMode;
|
|
||||||
set => SetAndNotify(ref _stopMode, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether the timeline has finished its run
|
/// Gets a boolean indicating whether the timeline has finished its run
|
||||||
@ -388,8 +371,6 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
|||||||
StartSegmentLength = Entity.StartSegmentLength;
|
StartSegmentLength = Entity.StartSegmentLength;
|
||||||
MainSegmentLength = Entity.MainSegmentLength;
|
MainSegmentLength = Entity.MainSegmentLength;
|
||||||
EndSegmentLength = Entity.EndSegmentLength;
|
EndSegmentLength = Entity.EndSegmentLength;
|
||||||
PlayMode = (TimelinePlayMode) Entity.PlayMode;
|
|
||||||
StopMode = (TimelineStopMode) Entity.StopMode;
|
|
||||||
|
|
||||||
JumpToEnd();
|
JumpToEnd();
|
||||||
}
|
}
|
||||||
@ -400,8 +381,6 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
|||||||
Entity.StartSegmentLength = StartSegmentLength;
|
Entity.StartSegmentLength = StartSegmentLength;
|
||||||
Entity.MainSegmentLength = MainSegmentLength;
|
Entity.MainSegmentLength = MainSegmentLength;
|
||||||
Entity.EndSegmentLength = EndSegmentLength;
|
Entity.EndSegmentLength = EndSegmentLength;
|
||||||
Entity.PlayMode = (int) PlayMode;
|
|
||||||
Entity.StopMode = (int) StopMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -413,61 +392,3 @@ internal enum TimelineSegment
|
|||||||
Main,
|
Main,
|
||||||
End
|
End
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a mode for render elements to start their timeline when display conditions are met
|
|
||||||
/// </summary>
|
|
||||||
public enum TimelinePlayMode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Continue repeating the main segment of the timeline while the condition is met
|
|
||||||
/// </summary>
|
|
||||||
Repeat,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Only play the timeline once when the condition is met
|
|
||||||
/// </summary>
|
|
||||||
Once
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a mode for render elements to stop their timeline when display conditions are no longer met
|
|
||||||
/// </summary>
|
|
||||||
public enum TimelineStopMode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// When conditions are no longer met, finish the the current run of the main timeline
|
|
||||||
/// </summary>
|
|
||||||
Finish,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When conditions are no longer met, skip to the end segment of the timeline
|
|
||||||
/// </summary>
|
|
||||||
SkipToEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a mode for render elements to start their timeline when display conditions events are fired
|
|
||||||
/// </summary>
|
|
||||||
public enum TimeLineEventOverlapMode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stop the current run and restart the timeline
|
|
||||||
/// </summary>
|
|
||||||
Restart,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ignore subsequent event fires until the timeline finishes
|
|
||||||
/// </summary>
|
|
||||||
Ignore,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Play another copy of the timeline on top of the current run
|
|
||||||
/// </summary>
|
|
||||||
Copy,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Repeat the timeline until the event fires again
|
|
||||||
/// </summary>
|
|
||||||
Toggle
|
|
||||||
}
|
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
@ -9,6 +10,7 @@ namespace Artemis.Core.Internal
|
|||||||
internal class EventDefaultNode : Node
|
internal class EventDefaultNode : Node
|
||||||
{
|
{
|
||||||
private readonly Dictionary<PropertyInfo, OutputPin> _propertyPins;
|
private readonly Dictionary<PropertyInfo, OutputPin> _propertyPins;
|
||||||
|
private readonly List<OutputPin> _pinBucket = new();
|
||||||
private IDataModelEvent? _dataModelEvent;
|
private IDataModelEvent? _dataModelEvent;
|
||||||
|
|
||||||
public EventDefaultNode() : base("Event Arguments", "Contains the event arguments that triggered the evaluation")
|
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 override bool IsDefaultNode => true;
|
||||||
|
|
||||||
public void UpdateDataModelEvent(IDataModelEvent dataModelEvent)
|
public void CreatePins(IDataModelEvent? dataModelEvent)
|
||||||
{
|
{
|
||||||
if (_dataModelEvent == dataModelEvent)
|
if (_dataModelEvent == dataModelEvent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var (_, outputPin) in _propertyPins)
|
while (Pins.Any())
|
||||||
RemovePin(outputPin);
|
RemovePin((Pin) Pins.First());
|
||||||
_propertyPins.Clear();
|
_propertyPins.Clear();
|
||||||
|
|
||||||
_dataModelEvent = dataModelEvent;
|
_dataModelEvent = dataModelEvent;
|
||||||
|
if (dataModelEvent == null)
|
||||||
|
return;
|
||||||
|
|
||||||
foreach (PropertyInfo propertyInfo in dataModelEvent.ArgumentsType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
foreach (PropertyInfo propertyInfo in dataModelEvent.ArgumentsType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||||
.Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof(DataModelIgnoreAttribute))))
|
.Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof(DataModelIgnoreAttribute))))
|
||||||
_propertyPins.Add(propertyInfo, CreateOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
|
_propertyPins.Add(propertyInfo, CreateOrAddOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Evaluate()
|
public override void Evaluate()
|
||||||
@ -38,11 +43,38 @@ namespace Artemis.Core.Internal
|
|||||||
if (_dataModelEvent?.LastEventArgumentsUntyped == null)
|
if (_dataModelEvent?.LastEventArgumentsUntyped == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var (propertyInfo, outputPin) in _propertyPins)
|
foreach ((PropertyInfo propertyInfo, OutputPin outputPin) in _propertyPins)
|
||||||
{
|
{
|
||||||
if (outputPin.ConnectedTo.Any())
|
if (outputPin.ConnectedTo.Any())
|
||||||
outputPin.Value = propertyInfo.GetValue(_dataModelEvent.LastEventArgumentsUntyped) ?? outputPin.Type.GetDefault()!;
|
outputPin.Value = propertyInfo.GetValue(_dataModelEvent.LastEventArgumentsUntyped) ?? outputPin.Type.GetDefault()!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,7 +280,7 @@ public abstract class Node : CorePropertyChanged, INode
|
|||||||
/// Serializes the <see cref="Storage" /> object into a string
|
/// Serializes the <see cref="Storage" /> object into a string
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The serialized object</returns>
|
/// <returns>The serialized object</returns>
|
||||||
internal virtual string SerializeStorage()
|
public virtual string SerializeStorage()
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
@ -289,7 +289,7 @@ public abstract class Node : CorePropertyChanged, INode
|
|||||||
/// Deserializes the <see cref="Storage" /> object and sets it
|
/// Deserializes the <see cref="Storage" /> object and sets it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serialized">The serialized object</param>
|
/// <param name="serialized">The serialized object</param>
|
||||||
internal virtual void DeserializeStorage(string serialized)
|
public virtual void DeserializeStorage(string serialized)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,12 +333,14 @@ public abstract class Node<TStorage> : Node
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler? StorageModified;
|
public event EventHandler? StorageModified;
|
||||||
|
|
||||||
internal override string SerializeStorage()
|
/// <inheritdoc />
|
||||||
|
public override string SerializeStorage()
|
||||||
{
|
{
|
||||||
return CoreJson.SerializeObject(Storage, true);
|
return CoreJson.SerializeObject(Storage, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void DeserializeStorage(string serialized)
|
/// <inheritdoc />
|
||||||
|
public override void DeserializeStorage(string serialized)
|
||||||
{
|
{
|
||||||
Storage = CoreJson.DeserializeObject<TStorage>(serialized) ?? default(TStorage);
|
Storage = CoreJson.DeserializeObject<TStorage>(serialized) ?? default(TStorage);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,7 +5,8 @@ namespace Artemis.Storage.Entities.Profile.Conditions
|
|||||||
{
|
{
|
||||||
public class EventConditionEntity : IConditionEntity
|
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 DataModelPathEntity EventPath { get; set; }
|
||||||
public NodeScriptEntity Script { get; set; }
|
public NodeScriptEntity Script { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,8 @@ namespace Artemis.Storage.Entities.Profile.Conditions
|
|||||||
{
|
{
|
||||||
public class StaticConditionEntity : IConditionEntity
|
public class StaticConditionEntity : IConditionEntity
|
||||||
{
|
{
|
||||||
|
public int PlayMode { get; set; }
|
||||||
|
public int StopMode { get; set; }
|
||||||
public NodeScriptEntity Script { get; set; }
|
public NodeScriptEntity Script { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,8 +7,5 @@ namespace Artemis.Storage.Entities.Profile
|
|||||||
public TimeSpan StartSegmentLength { get; set; }
|
public TimeSpan StartSegmentLength { get; set; }
|
||||||
public TimeSpan MainSegmentLength { get; set; }
|
public TimeSpan MainSegmentLength { get; set; }
|
||||||
public TimeSpan EndSegmentLength { get; set; }
|
public TimeSpan EndSegmentLength { get; set; }
|
||||||
|
|
||||||
public int PlayMode { get; set; }
|
|
||||||
public int StopMode { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10,7 +10,7 @@ public class DeleteKeyframe : IProfileEditorCommand
|
|||||||
private readonly ILayerPropertyKeyframe _keyframe;
|
private readonly ILayerPropertyKeyframe _keyframe;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="DeleteKeyframe{T}" /> class.
|
/// Creates a new instance of the <see cref="DeleteKeyframe" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DeleteKeyframe(ILayerPropertyKeyframe keyframe)
|
public DeleteKeyframe(ILayerPropertyKeyframe keyframe)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared.Services.NodeEditor;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a profile editor command that can be used to update an event condition's trigger mode.
|
||||||
|
/// </summary>
|
||||||
|
public class UpdateEventConditionPath : IProfileEditorCommand, IDisposable
|
||||||
|
{
|
||||||
|
private readonly EventCondition _eventCondition;
|
||||||
|
private readonly DataModelPath? _value;
|
||||||
|
private readonly DataModelPath? _oldValue;
|
||||||
|
private readonly NodeConnectionStore? _store;
|
||||||
|
private bool _executed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="UpdateEventTriggerMode" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public UpdateEventConditionPath(EventCondition eventCondition, DataModelPath? value)
|
||||||
|
{
|
||||||
|
_eventCondition = eventCondition;
|
||||||
|
_value = value;
|
||||||
|
_oldValue = eventCondition.EventPath;
|
||||||
|
|
||||||
|
INode? startNode = eventCondition.GetStartNode();
|
||||||
|
if (startNode != null)
|
||||||
|
_store = new NodeConnectionStore(startNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string DisplayName => "Update event path";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
// Store old connections
|
||||||
|
_store?.Store();
|
||||||
|
|
||||||
|
// Change the end node
|
||||||
|
_eventCondition.EventPath = _value;
|
||||||
|
_eventCondition.UpdateEventNode();
|
||||||
|
|
||||||
|
_executed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
// Change the end node
|
||||||
|
_eventCondition.EventPath = _oldValue;
|
||||||
|
_eventCondition.UpdateEventNode();
|
||||||
|
|
||||||
|
// Restore old connections
|
||||||
|
_store?.Restore();
|
||||||
|
|
||||||
|
_executed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_executed)
|
||||||
|
_oldValue?.Dispose();
|
||||||
|
else
|
||||||
|
_value?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a profile editor command that can be used to update an event condition's overlap mode.
|
||||||
|
/// </summary>
|
||||||
|
public class UpdateEventOverlapMode : IProfileEditorCommand
|
||||||
|
{
|
||||||
|
private readonly EventCondition _eventCondition;
|
||||||
|
private readonly EventOverlapMode _value;
|
||||||
|
private readonly EventOverlapMode _oldValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="UpdateEventOverlapMode" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public UpdateEventOverlapMode(EventCondition eventCondition, EventOverlapMode value)
|
||||||
|
{
|
||||||
|
_eventCondition = eventCondition;
|
||||||
|
_value = value;
|
||||||
|
_oldValue = eventCondition.OverlapMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string DisplayName => "Update event overlap mode";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
_eventCondition.OverlapMode = _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
_eventCondition.OverlapMode = _oldValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a profile editor command that can be used to update an event condition's trigger mode.
|
||||||
|
/// </summary>
|
||||||
|
public class UpdateEventTriggerMode : IProfileEditorCommand
|
||||||
|
{
|
||||||
|
private readonly EventCondition _eventCondition;
|
||||||
|
private readonly EventTriggerMode _oldValue;
|
||||||
|
private readonly EventTriggerMode _value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="UpdateEventTriggerMode" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public UpdateEventTriggerMode(EventCondition eventCondition, EventTriggerMode value)
|
||||||
|
{
|
||||||
|
_eventCondition = eventCondition;
|
||||||
|
_value = value;
|
||||||
|
_oldValue = eventCondition.TriggerMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string DisplayName => "Update event trigger mode";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
_eventCondition.TriggerMode = _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
_eventCondition.TriggerMode = _oldValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a profile editor command that can be used to update an static condition's play mode.
|
||||||
|
/// </summary>
|
||||||
|
public class UpdateStaticPlayMode : IProfileEditorCommand
|
||||||
|
{
|
||||||
|
private readonly StaticCondition _staticCondition;
|
||||||
|
private readonly StaticPlayMode _value;
|
||||||
|
private readonly StaticPlayMode _oldValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="UpdateEventTriggerMode" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public UpdateStaticPlayMode(StaticCondition staticCondition, StaticPlayMode value)
|
||||||
|
{
|
||||||
|
_staticCondition = staticCondition;
|
||||||
|
_value = value;
|
||||||
|
_oldValue = staticCondition.PlayMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string DisplayName => "Update condition play mode";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
_staticCondition.PlayMode = _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
_staticCondition.PlayMode = _oldValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a profile editor command that can be used to update an static condition's play mode.
|
||||||
|
/// </summary>
|
||||||
|
public class UpdateStaticStopMode : IProfileEditorCommand
|
||||||
|
{
|
||||||
|
private readonly StaticCondition _staticCondition;
|
||||||
|
private readonly StaticStopMode _value;
|
||||||
|
private readonly StaticStopMode _oldValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="UpdateEventTriggerMode" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public UpdateStaticStopMode(StaticCondition staticCondition, StaticStopMode value)
|
||||||
|
{
|
||||||
|
_staticCondition = staticCondition;
|
||||||
|
_value = value;
|
||||||
|
_oldValue = staticCondition.StopMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string DisplayName => "Update condition stop mode";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
_staticCondition.StopMode = _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
_staticCondition.StopMode = _oldValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -96,8 +96,7 @@ internal class ProfileEditorService : IProfileEditorService
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
renderElement.Enable();
|
renderElement.Enable();
|
||||||
bool stickToMainSegment = (renderElement != _profileElementSubject.Value || renderElement.Timeline.Length < time) && renderElement.Timeline.PlayMode == TimelinePlayMode.Repeat;
|
renderElement.OverrideTimelineAndApply(time);
|
||||||
renderElement.OverrideTimelineAndApply(time, stickToMainSegment);
|
|
||||||
|
|
||||||
foreach (ProfileElement child in renderElement.Children)
|
foreach (ProfileElement child in renderElement.Children)
|
||||||
TickProfileElement(child, time);
|
TickProfileElement(child, time);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ using Artemis.Core.LayerEffects;
|
|||||||
using Artemis.UI.Screens.Device;
|
using Artemis.UI.Screens.Device;
|
||||||
using Artemis.UI.Screens.Plugins;
|
using Artemis.UI.Screens.Plugins;
|
||||||
using Artemis.UI.Screens.ProfileEditor;
|
using Artemis.UI.Screens.ProfileEditor;
|
||||||
|
using Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
|
||||||
using Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
using Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
||||||
using Artemis.UI.Screens.ProfileEditor.Properties;
|
using Artemis.UI.Screens.ProfileEditor.Properties;
|
||||||
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
|
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
|
||||||
@ -52,7 +53,7 @@ namespace Artemis.UI.Ninject.Factories
|
|||||||
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(SidebarViewModel sidebarViewModel, ProfileConfiguration profileConfiguration);
|
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(SidebarViewModel sidebarViewModel, ProfileConfiguration profileConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface SurfaceVmFactory : IVmFactory
|
public interface ISurfaceVmFactory : IVmFactory
|
||||||
{
|
{
|
||||||
SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device);
|
SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device);
|
||||||
ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device);
|
ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device);
|
||||||
@ -108,4 +109,12 @@ namespace Artemis.UI.Ninject.Factories
|
|||||||
InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel);
|
InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel);
|
||||||
OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition
|
namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition;
|
||||||
|
|
||||||
|
public class ConditionTypeViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
public class ConditionTypeViewModel : ViewModelBase
|
public ConditionTypeViewModel(string name, string description, Type conditionType)
|
||||||
{
|
|
||||||
public ConditionTypeViewModel(string name, string description, Type? conditionType)
|
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Description = description;
|
Description = description;
|
||||||
@ -14,6 +14,5 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition
|
|||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
public Type? ConditionType { get; }
|
public Type ConditionType { get; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes.AlwaysOnConditionView">
|
||||||
|
|
||||||
|
<DockPanel VerticalAlignment="Top" Margin="0 5">
|
||||||
|
<avalonia:MaterialIcon Kind="InfoCircle" Margin="5 0"/>
|
||||||
|
<TextBlock Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||||
|
TextWrapping="Wrap">
|
||||||
|
After playing the start segment, the main segment is endlessly repeated.
|
||||||
|
</TextBlock>
|
||||||
|
</DockPanel>
|
||||||
|
</UserControl>
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.Controls.DataModelPicker;assembly=Artemis.UI.Shared"
|
||||||
|
xmlns:conditionTypes="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes.EventConditionView"
|
||||||
|
x:DataType="conditionTypes:EventConditionViewModel">
|
||||||
|
<DockPanel HorizontalAlignment="Stretch">
|
||||||
|
<DockPanel.Styles>
|
||||||
|
<Style Selector="DockPanel > TextBlock">
|
||||||
|
<Setter Property="Margin" Value="0 5" />
|
||||||
|
</Style>
|
||||||
|
</DockPanel.Styles>
|
||||||
|
<TextBlock DockPanel.Dock="Top">Triggered by event</TextBlock>
|
||||||
|
<dataModelPicker:DataModelPickerButton Placeholder="Select an event"
|
||||||
|
DockPanel.Dock="Top"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
FilterTypes="{CompiledBinding FilterTypes}"
|
||||||
|
DataModelPath="{CompiledBinding EventPath}"/>
|
||||||
|
|
||||||
|
<TextBlock DockPanel.Dock="Top">When the event fires..</TextBlock>
|
||||||
|
<ComboBox PlaceholderText="Select a play mode" HorizontalAlignment="Stretch" DockPanel.Dock="Top" SelectedIndex="{CompiledBinding SelectedTriggerMode}">
|
||||||
|
<ComboBoxItem>
|
||||||
|
Play the timeline once
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
Toggle the element on or off
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<TextBlock DockPanel.Dock="Top" IsVisible="{CompiledBinding ShowOverlapOptions}">And if already playing..</TextBlock>
|
||||||
|
<ComboBox PlaceholderText="Select a play mode"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
DockPanel.Dock="Top"
|
||||||
|
SelectedIndex="{CompiledBinding SelectedOverlapMode}"
|
||||||
|
IsVisible="{CompiledBinding ShowOverlapOptions}">
|
||||||
|
<ComboBoxItem>
|
||||||
|
Restart the timeline
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem IsEnabled="{CompiledBinding IsConditionForLayer}">
|
||||||
|
Play a second copy
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
Do nothing
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<Button DockPanel.Dock="Bottom"
|
||||||
|
ToolTip.Tip="Open editor"
|
||||||
|
Margin="0 15 0 5"
|
||||||
|
Command="{CompiledBinding OpenEditor}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Bottom">
|
||||||
|
Edit condition script
|
||||||
|
</Button>
|
||||||
|
</DockPanel>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
|
||||||
|
|
||||||
|
public class EventConditionView : ReactiveUserControl<EventConditionViewModel>
|
||||||
|
{
|
||||||
|
public EventConditionView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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<bool> _showOverlapOptions;
|
||||||
|
private readonly IWindowService _windowService;
|
||||||
|
private ObservableAsPropertyHelper<DataModelPath?>? _eventPath;
|
||||||
|
private ObservableAsPropertyHelper<int>? _selectedOverlapMode;
|
||||||
|
private ObservableAsPropertyHelper<int>? _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<Type> FilterTypes { get; } = new() {typeof(IDataModelEvent)};
|
||||||
|
public ReactiveCommand<Unit, Unit> 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<NodeScriptWindowViewModel, bool>(("nodeScript", _eventCondition.NodeScript));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes.PlayOnceConditionView">
|
||||||
|
<DockPanel VerticalAlignment="Top" Margin="0 5">
|
||||||
|
<avalonia:MaterialIcon Kind="InfoCircle" Margin="5 0"/>
|
||||||
|
<TextBlock Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||||
|
TextWrapping="Wrap">
|
||||||
|
Every segment of the timeline is played once.
|
||||||
|
</TextBlock>
|
||||||
|
</DockPanel>
|
||||||
|
</UserControl>
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:conditionTypes="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes.StaticConditionView"
|
||||||
|
x:DataType="conditionTypes:StaticConditionViewModel">
|
||||||
|
<DockPanel HorizontalAlignment="Stretch">
|
||||||
|
<DockPanel.Styles>
|
||||||
|
<Style Selector="DockPanel > TextBlock">
|
||||||
|
<Setter Property="Margin" Value="0 5" />
|
||||||
|
</Style>
|
||||||
|
</DockPanel.Styles>
|
||||||
|
<TextBlock DockPanel.Dock="Top">While condition is met..</TextBlock>
|
||||||
|
<ComboBox DockPanel.Dock="Top" PlaceholderText="Select a play mode" HorizontalAlignment="Stretch" SelectedIndex="{CompiledBinding SelectedPlayMode}">
|
||||||
|
<ComboBoxItem>Repeat the main segment</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Play the main segment once</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<TextBlock DockPanel.Dock="Top">And when no longer met..</TextBlock>
|
||||||
|
<ComboBox DockPanel.Dock="Top" PlaceholderText="Select a stop mode" HorizontalAlignment="Stretch" SelectedIndex="{CompiledBinding SelectedStopMode}">
|
||||||
|
<ComboBoxItem>Finish the main segment</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Skip forward to the end segment</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<DockPanel DockPanel.Dock="Top" Margin="0 5">
|
||||||
|
<avalonia:MaterialIcon Kind="InfoCircle" Margin="5 0" />
|
||||||
|
<TextBlock Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||||
|
TextWrapping="Wrap">
|
||||||
|
The start- and end-segments are played once each time the condition is met.
|
||||||
|
</TextBlock>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<Button DockPanel.Dock="Bottom"
|
||||||
|
ToolTip.Tip="Open editor"
|
||||||
|
Margin="0 15 0 5"
|
||||||
|
Command="{CompiledBinding OpenEditor}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Bottom">
|
||||||
|
Edit condition script
|
||||||
|
</Button>
|
||||||
|
</DockPanel>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition.ConditionTypes;
|
||||||
|
|
||||||
|
public class StaticConditionView : ReactiveUserControl<StaticConditionViewModel>
|
||||||
|
{
|
||||||
|
public StaticConditionView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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<int>? _selectedPlayMode;
|
||||||
|
private ObservableAsPropertyHelper<int>? _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<Unit, Unit> 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<NodeScriptWindowViewModel, bool>(("nodeScript", _staticCondition.NodeScript));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,8 +3,6 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:displayCondition="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayCondition"
|
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"
|
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="650"
|
||||||
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayCondition.DisplayConditionScriptView"
|
x:Class="Artemis.UI.Screens.ProfileEditor.DisplayCondition.DisplayConditionScriptView"
|
||||||
x:DataType="displayCondition:DisplayConditionScriptViewModel">
|
x:DataType="displayCondition:DisplayConditionScriptViewModel">
|
||||||
@ -20,19 +18,14 @@
|
|||||||
</Style>
|
</Style>
|
||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<DockPanel LastChildFill="False">
|
<Grid RowDefinitions="Auto,Auto,*">
|
||||||
<DockPanel.Styles>
|
<TextBlock Grid.Row="0" Margin="0 5">Activation type</TextBlock>
|
||||||
<Style Selector="DockPanel > TextBlock">
|
|
||||||
<Setter Property="Margin" Value="0 5"></Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
</DockPanel.Styles>
|
|
||||||
<TextBlock DockPanel.Dock="Top">Condition type</TextBlock>
|
|
||||||
<ComboBox Name="ConditionType"
|
<ComboBox Name="ConditionType"
|
||||||
DockPanel.Dock="Top"
|
Grid.Row="1"
|
||||||
Classes="condition-type"
|
Classes="condition-type"
|
||||||
PlaceholderText="Select a condition type"
|
PlaceholderText="Select an activation type"
|
||||||
Items="{CompiledBinding ConditionTypeViewModels}"
|
Items="{CompiledBinding ConditionTypeViewModels}"
|
||||||
|
IsEnabled="{CompiledBinding ProfileElement, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||||
SelectedItem="{CompiledBinding SelectedConditionTypeViewModel}"
|
SelectedItem="{CompiledBinding SelectedConditionTypeViewModel}"
|
||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Stretch">
|
||||||
<ComboBox.ItemTemplate>
|
<ComboBox.ItemTemplate>
|
||||||
@ -44,64 +37,7 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
<ContentControl Grid.Row="2" Content="{CompiledBinding ConditionViewModel}" />
|
||||||
<!-- Event options -->
|
</Grid>
|
||||||
<DockPanel IsVisible="{CompiledBinding ShowEventOptions}" DockPanel.Dock="Top" HorizontalAlignment="Stretch">
|
|
||||||
<TextBlock DockPanel.Dock="Top">Triggered by event</TextBlock>
|
|
||||||
<dataModelPicker:DataModelPickerButton Placeholder="Select an event"
|
|
||||||
DockPanel.Dock="Top"
|
|
||||||
HorizontalAlignment="Stretch" />
|
|
||||||
|
|
||||||
<TextBlock DockPanel.Dock="Top">When the event fires..</TextBlock>
|
|
||||||
<ComboBox PlaceholderText="Select a play mode" HorizontalAlignment="Stretch" DockPanel.Dock="Top">
|
|
||||||
<ComboBoxItem>
|
|
||||||
Play the timeline once
|
|
||||||
</ComboBoxItem>
|
|
||||||
<ComboBoxItem>
|
|
||||||
Toggle the element on or off
|
|
||||||
</ComboBoxItem>
|
|
||||||
</ComboBox>
|
|
||||||
|
|
||||||
<TextBlock DockPanel.Dock="Top">And if already playing..</TextBlock>
|
|
||||||
<ComboBox PlaceholderText="Select a play mode" HorizontalAlignment="Stretch" DockPanel.Dock="Top">
|
|
||||||
<ComboBoxItem>
|
|
||||||
Restart the timeline
|
|
||||||
</ComboBoxItem>
|
|
||||||
<ComboBoxItem>
|
|
||||||
Play a second copy
|
|
||||||
</ComboBoxItem>
|
|
||||||
<ComboBoxItem>
|
|
||||||
Do nothing
|
|
||||||
</ComboBoxItem>
|
|
||||||
</ComboBox>
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
<!-- Static options -->
|
|
||||||
<DockPanel IsVisible="{CompiledBinding ShowStaticOptions}" HorizontalAlignment="Stretch" DockPanel.Dock="Top">
|
|
||||||
<TextBlock DockPanel.Dock="Top">While condition is met..</TextBlock>
|
|
||||||
<ComboBox DockPanel.Dock="Top" PlaceholderText="Select a play mode" HorizontalAlignment="Stretch">
|
|
||||||
<ComboBoxItem>Play the main segment once</ComboBoxItem>
|
|
||||||
<ComboBoxItem>Repeat the main segment</ComboBoxItem>
|
|
||||||
</ComboBox>
|
|
||||||
|
|
||||||
<TextBlock DockPanel.Dock="Top">And when no longer met..</TextBlock>
|
|
||||||
<ComboBox DockPanel.Dock="Top" PlaceholderText="Select a stop mode" HorizontalAlignment="Stretch">
|
|
||||||
<ComboBoxItem>Finish the main segment</ComboBoxItem>
|
|
||||||
<ComboBoxItem>Skip forward to the end segment</ComboBoxItem>
|
|
||||||
</ComboBox>
|
|
||||||
|
|
||||||
<DockPanel DockPanel.Dock="Top" Margin="0 5">
|
|
||||||
<avalonia:MaterialIcon Kind="InfoCircle" Margin="5 0"></avalonia:MaterialIcon>
|
|
||||||
<TextBlock Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
|
||||||
TextWrapping="Wrap">
|
|
||||||
The start- and end-segments are always played once.
|
|
||||||
</TextBlock>
|
|
||||||
</DockPanel>
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
<Button DockPanel.Dock="Bottom" ToolTip.Tip="Open editor" Margin="0 15 0 5" Command="{Binding OpenEditor}" HorizontalAlignment="Stretch">
|
|
||||||
Edit condition script
|
|
||||||
</Button>
|
|
||||||
</DockPanel>
|
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,10 +1,10 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition
|
namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition;
|
||||||
|
|
||||||
|
public class DisplayConditionScriptView : ReactiveUserControl<DisplayConditionScriptViewModel>
|
||||||
{
|
{
|
||||||
public partial class DisplayConditionScriptView : ReactiveUserControl<DisplayConditionScriptViewModel>
|
|
||||||
{
|
|
||||||
public DisplayConditionScriptView()
|
public DisplayConditionScriptView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -14,5 +14,4 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition
|
|||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,13 +1,9 @@
|
|||||||
using System;
|
using System.Collections.ObjectModel;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Screens.VisualScripting;
|
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||||
using Avalonia.Controls.Mixins;
|
using Avalonia.Controls.Mixins;
|
||||||
@ -17,81 +13,79 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayCondition;
|
|||||||
|
|
||||||
public class DisplayConditionScriptViewModel : ActivatableViewModelBase
|
public class DisplayConditionScriptViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
|
private readonly ObservableAsPropertyHelper<ViewModelBase?> _conditionViewModel;
|
||||||
|
private readonly IConditionVmFactory _conditionVmFactory;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
private readonly ObservableAsPropertyHelper<bool> _showEventOptions;
|
private ObservableAsPropertyHelper<RenderProfileElement?>? _profileElement;
|
||||||
private readonly ObservableAsPropertyHelper<bool> _showStaticOptions;
|
|
||||||
private readonly IWindowService _windowService;
|
|
||||||
private ObservableAsPropertyHelper<NodeScriptViewModel?>? _nodeScriptViewModel;
|
|
||||||
private RenderProfileElement? _profileElement;
|
|
||||||
private ObservableAsPropertyHelper<ConditionTypeViewModel?>? _selectedConditionTypeViewModel;
|
private ObservableAsPropertyHelper<ConditionTypeViewModel?>? _selectedConditionTypeViewModel;
|
||||||
|
|
||||||
public DisplayConditionScriptViewModel(IProfileEditorService profileEditorService, INodeVmFactory nodeVmFactory, IWindowService windowService)
|
public DisplayConditionScriptViewModel(IProfileEditorService profileEditorService, IConditionVmFactory conditionVmFactory)
|
||||||
{
|
{
|
||||||
_profileEditorService = profileEditorService;
|
_profileEditorService = profileEditorService;
|
||||||
_windowService = windowService;
|
_conditionVmFactory = conditionVmFactory;
|
||||||
|
|
||||||
ConditionTypeViewModels = new ObservableCollection<ConditionTypeViewModel>
|
ConditionTypeViewModels = new ObservableCollection<ConditionTypeViewModel>
|
||||||
{
|
{
|
||||||
new("None", "The element is always active.", null),
|
new("Always", "The element is always active.", typeof(AlwaysOnCondition)),
|
||||||
new("Regular", "The element is activated when the provided visual script ends in true.", typeof(StaticCondition)),
|
new("Once", "The element is shown once until its timeline is finished.", typeof(PlayOnceCondition)),
|
||||||
new("Event", "The element is activated when the selected event fires.\r\n" +
|
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))
|
"Events that contain data can conditionally trigger the layer using a visual script.", typeof(EventCondition))
|
||||||
};
|
};
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
profileEditorService.ProfileElement.Subscribe(p => _profileElement = p).DisposeWith(d);
|
_profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d);
|
||||||
|
|
||||||
_nodeScriptViewModel = profileEditorService.ProfileElement
|
|
||||||
.Select(p => p?.WhenAnyValue(element => element.DisplayCondition) ?? Observable.Never<ICondition?>())
|
|
||||||
.Switch()
|
|
||||||
.Select(c => c is INodeScriptCondition {NodeScript: NodeScript nodeScript} ? nodeVmFactory.NodeScriptViewModel(nodeScript, true) : null)
|
|
||||||
.ToProperty(this, vm => vm.NodeScriptViewModel)
|
|
||||||
.DisposeWith(d);
|
|
||||||
_selectedConditionTypeViewModel = profileEditorService.ProfileElement
|
_selectedConditionTypeViewModel = profileEditorService.ProfileElement
|
||||||
.Select(p => p?.WhenAnyValue(element => element.DisplayCondition) ?? Observable.Never<ICondition?>())
|
.Select(p => p?.WhenAnyValue(element => element.DisplayCondition) ?? Observable.Return<ICondition?>(null))
|
||||||
.Switch()
|
.Switch()
|
||||||
.Select(c => c != null ? ConditionTypeViewModels.FirstOrDefault(vm => vm.ConditionType == c.GetType()) : null)
|
.Select(c => c != null ? ConditionTypeViewModels.FirstOrDefault(vm => vm.ConditionType == c.GetType()) : null)
|
||||||
.ToProperty(this, vm => vm.SelectedConditionTypeViewModel)
|
.ToProperty(this, vm => vm.SelectedConditionTypeViewModel)
|
||||||
.DisposeWith(d);
|
.DisposeWith(d);
|
||||||
});
|
});
|
||||||
|
|
||||||
_showStaticOptions = this.WhenAnyValue(vm => vm.SelectedConditionTypeViewModel)
|
_conditionViewModel = this.WhenAnyValue(vm => vm.SelectedConditionTypeViewModel).Select(_ => CreateConditionViewModel()).ToProperty(this, vm => vm.ConditionViewModel);
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeScriptViewModel? NodeScriptViewModel => _nodeScriptViewModel?.Value;
|
public RenderProfileElement? ProfileElement => _profileElement?.Value;
|
||||||
|
public ViewModelBase? ConditionViewModel => _conditionViewModel.Value;
|
||||||
public ObservableCollection<ConditionTypeViewModel> ConditionTypeViewModels { get; }
|
public ObservableCollection<ConditionTypeViewModel> ConditionTypeViewModels { get; }
|
||||||
|
|
||||||
public ConditionTypeViewModel? SelectedConditionTypeViewModel
|
public ConditionTypeViewModel? SelectedConditionTypeViewModel
|
||||||
{
|
{
|
||||||
get => _selectedConditionTypeViewModel?.Value;
|
get => _selectedConditionTypeViewModel?.Value;
|
||||||
set
|
set => ApplyConditionType(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ViewModelBase? CreateConditionViewModel()
|
||||||
{
|
{
|
||||||
if (_profileElement == null)
|
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;
|
return;
|
||||||
|
|
||||||
ICondition? condition = null;
|
if (value.ConditionType == typeof(AlwaysOnCondition))
|
||||||
if (value?.ConditionType == typeof(StaticCondition))
|
_profileEditorService.ExecuteCommand(new ChangeConditionType(ProfileElement, new AlwaysOnCondition(ProfileElement)));
|
||||||
condition = new StaticCondition(_profileElement);
|
if (value.ConditionType == typeof(PlayOnceCondition))
|
||||||
else if (value?.ConditionType == typeof(EventCondition))
|
_profileEditorService.ExecuteCommand(new ChangeConditionType(ProfileElement, new PlayOnceCondition(ProfileElement)));
|
||||||
condition = new EventCondition(_profileElement);
|
if (value.ConditionType == typeof(StaticCondition))
|
||||||
|
_profileEditorService.ExecuteCommand(new ChangeConditionType(ProfileElement, new StaticCondition(ProfileElement)));
|
||||||
_profileEditorService.ExecuteCommand(new ChangeConditionType(_profileElement, condition));
|
if (value.ConditionType == typeof(EventCondition))
|
||||||
}
|
_profileEditorService.ExecuteCommand(new ChangeConditionType(ProfileElement, new EventCondition(ProfileElement)));
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShowStaticOptions => _showStaticOptions.Value;
|
|
||||||
public bool ShowEventOptions => _showEventOptions.Value;
|
|
||||||
|
|
||||||
|
|
||||||
public async Task OpenEditor()
|
|
||||||
{
|
|
||||||
if (_profileElement?.DisplayCondition is StaticCondition staticCondition)
|
|
||||||
await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(("nodeScript", staticCondition.Script));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,13 +46,6 @@
|
|||||||
Command="{Binding DisableSegment}">
|
Command="{Binding DisableSegment}">
|
||||||
<avalonia:MaterialIcon Kind="CloseCircle" />
|
<avalonia:MaterialIcon Kind="CloseCircle" />
|
||||||
</Button>
|
</Button>
|
||||||
<ToggleButton Name="SegmentRepeat"
|
|
||||||
Classes="icon-button icon-button-small"
|
|
||||||
ToolTip.Tip="Repeat this segment"
|
|
||||||
IsChecked="{CompiledBinding RepeatSegment}"
|
|
||||||
Padding="0">
|
|
||||||
<avalonia:MaterialIcon Kind="Repeat" />
|
|
||||||
</ToggleButton>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Button Name="AddEndSegment"
|
<Button Name="AddEndSegment"
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reactive;
|
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
@ -13,12 +10,11 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments;
|
|||||||
|
|
||||||
public class MainSegmentViewModel : TimelineSegmentViewModel
|
public class MainSegmentViewModel : TimelineSegmentViewModel
|
||||||
{
|
{
|
||||||
private RenderProfileElement? _profileElement;
|
private readonly ObservableAsPropertyHelper<double> _width;
|
||||||
private ObservableAsPropertyHelper<double>? _start;
|
|
||||||
private ObservableAsPropertyHelper<double>? _end;
|
private ObservableAsPropertyHelper<double>? _end;
|
||||||
private ObservableAsPropertyHelper<string?>? _endTimestamp;
|
private ObservableAsPropertyHelper<string?>? _endTimestamp;
|
||||||
private ObservableAsPropertyHelper<bool>? _repeatSegment;
|
private RenderProfileElement? _profileElement;
|
||||||
private readonly ObservableAsPropertyHelper<double> _width;
|
private ObservableAsPropertyHelper<double>? _start;
|
||||||
|
|
||||||
public MainSegmentViewModel(IProfileEditorService profileEditorService) : base(profileEditorService)
|
public MainSegmentViewModel(IProfileEditorService profileEditorService) : base(profileEditorService)
|
||||||
{
|
{
|
||||||
@ -44,12 +40,6 @@ public class MainSegmentViewModel : TimelineSegmentViewModel
|
|||||||
.Select(p => $"{Math.Floor(p.TotalSeconds):00}.{p.Milliseconds:000}")
|
.Select(p => $"{Math.Floor(p.TotalSeconds):00}.{p.Milliseconds:000}")
|
||||||
.ToProperty(this, vm => vm.EndTimestamp)
|
.ToProperty(this, vm => vm.EndTimestamp)
|
||||||
.DisposeWith(d);
|
.DisposeWith(d);
|
||||||
_repeatSegment = profileEditorService.ProfileElement
|
|
||||||
.Select(p => p?.WhenAnyValue(element => element.Timeline.PlayMode) ?? Observable.Never<TimelinePlayMode>())
|
|
||||||
.Switch()
|
|
||||||
.Select(p => p == TimelinePlayMode.Repeat)
|
|
||||||
.ToProperty(this, vm => vm.RepeatSegment)
|
|
||||||
.DisposeWith(d);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
_width = this.WhenAnyValue(vm => vm.StartX, vm => vm.EndX).Select(t => t.Item2 - t.Item1).ToProperty(this, vm => vm.Width);
|
_width = this.WhenAnyValue(vm => vm.StartX, vm => vm.EndX).Select(t => t.Item2 - t.Item1).ToProperty(this, vm => vm.Width);
|
||||||
@ -73,10 +63,4 @@ public class MainSegmentViewModel : TimelineSegmentViewModel
|
|||||||
public override double Width => _width.Value;
|
public override double Width => _width.Value;
|
||||||
public override string? EndTimestamp => _endTimestamp?.Value;
|
public override string? EndTimestamp => _endTimestamp?.Value;
|
||||||
public override ResizeTimelineSegment.SegmentType Type => ResizeTimelineSegment.SegmentType.Main;
|
public override ResizeTimelineSegment.SegmentType Type => ResizeTimelineSegment.SegmentType.Main;
|
||||||
|
|
||||||
public bool RepeatSegment
|
|
||||||
{
|
|
||||||
get => _repeatSegment?.Value ?? false;
|
|
||||||
set => throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -22,7 +22,7 @@ namespace Artemis.UI.Screens.SurfaceEditor
|
|||||||
|
|
||||||
public SurfaceEditorViewModel(IScreen hostScreen,
|
public SurfaceEditorViewModel(IScreen hostScreen,
|
||||||
IRgbService rgbService,
|
IRgbService rgbService,
|
||||||
SurfaceVmFactory surfaceVmFactory,
|
ISurfaceVmFactory surfaceVmFactory,
|
||||||
ISettingsService settingsService) : base(hostScreen, "surface-editor")
|
ISettingsService settingsService) : base(hostScreen, "surface-editor")
|
||||||
{
|
{
|
||||||
_rgbService = rgbService;
|
_rgbService = rgbService;
|
||||||
|
|||||||
@ -3,9 +3,30 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting"
|
xmlns:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting"
|
||||||
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.VisualScripting.NodeScriptWindowView"
|
x:Class="Artemis.UI.Screens.VisualScripting.NodeScriptWindowView"
|
||||||
x:DataType="visualScripting:NodeScriptWindowViewModel"
|
x:DataType="visualScripting:NodeScriptWindowViewModel"
|
||||||
Title="VisualEditorWindowView">
|
Title="Artemis | Visual Script Editor"
|
||||||
|
Width="1200"
|
||||||
|
Height="800">
|
||||||
|
<Grid Margin="15" ColumnDefinitions="*,*" RowDefinitions="Auto,*">
|
||||||
|
<StackPanel Grid.Row="0">
|
||||||
|
<TextBlock Classes="h4" Text="{CompiledBinding NodeScript.Name}"/>
|
||||||
|
<TextBlock Classes="subtitle" Margin="0 0 0 10" Text="{CompiledBinding NodeScript.Description}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<controls:HyperlinkButton Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/profiles/conditions">
|
||||||
|
Learn more about visual scripts
|
||||||
|
</controls:HyperlinkButton>
|
||||||
|
|
||||||
|
<Border Classes="card" Grid.Row="1" Grid.ColumnSpan="2">
|
||||||
<ContentControl Content="{CompiledBinding NodeScriptViewModel}" />
|
<ContentControl Content="{CompiledBinding NodeScriptViewModel}" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</Window>
|
</Window>
|
||||||
@ -5,7 +5,7 @@ using Artemis.VisualScripting.Nodes.Operators.Screens;
|
|||||||
namespace Artemis.VisualScripting.Nodes.Operators;
|
namespace Artemis.VisualScripting.Nodes.Operators;
|
||||||
|
|
||||||
[Node("Enum Equals", "Determines the equality between an input and a selected enum value", "Operators", InputType = typeof(Enum), OutputType = typeof(bool))]
|
[Node("Enum Equals", "Determines the equality between an input and a selected enum value", "Operators", InputType = typeof(Enum), OutputType = typeof(bool))]
|
||||||
public class EnumEqualsNode : Node<Enum, EnumEqualsNodeCustomViewModel>
|
public class EnumEqualsNode : Node<int, EnumEqualsNodeCustomViewModel>
|
||||||
{
|
{
|
||||||
public EnumEqualsNode() : base("Enum Equals", "Determines the equality between an input and a selected enum value")
|
public EnumEqualsNode() : base("Enum Equals", "Determines the equality between an input and a selected enum value")
|
||||||
{
|
{
|
||||||
@ -17,20 +17,18 @@ public class EnumEqualsNode : Node<Enum, EnumEqualsNodeCustomViewModel>
|
|||||||
|
|
||||||
private void InputPinOnPinConnected(object? sender, SingleValueEventArgs<IPin> e)
|
private void InputPinOnPinConnected(object? sender, SingleValueEventArgs<IPin> e)
|
||||||
{
|
{
|
||||||
if (Storage?.GetType() != InputPin.ConnectedTo.First().Type)
|
Storage = 0;
|
||||||
Storage = Enum.GetValues(InputPin.ConnectedTo.First().Type).Cast<Enum>().FirstOrDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputPin<Enum> InputPin { get; }
|
public InputPin<Enum> InputPin { get; }
|
||||||
public OutputPin<bool> OutputPin { get; }
|
public OutputPin<bool> OutputPin { get; }
|
||||||
|
|
||||||
#region Overrides of Node
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Evaluate()
|
public override void Evaluate()
|
||||||
{
|
{
|
||||||
OutputPin.Value = InputPin.Value != null && InputPin.Value.Equals(Storage);
|
if (InputPin.Value == null)
|
||||||
|
OutputPin.Value = false;
|
||||||
|
else
|
||||||
|
OutputPin.Value = Convert.ToInt32(InputPin.Value) == Storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
@ -8,7 +8,7 @@
|
|||||||
x:DataType="screens:EnumEqualsNodeCustomViewModel">
|
x:DataType="screens:EnumEqualsNodeCustomViewModel">
|
||||||
<ComboBox IsEnabled="{CompiledBinding EnumValues.Count}"
|
<ComboBox IsEnabled="{CompiledBinding EnumValues.Count}"
|
||||||
Items="{CompiledBinding EnumValues}"
|
Items="{CompiledBinding EnumValues}"
|
||||||
SelectedItem="{CompiledBinding CurrentValue}"
|
SelectedIndex="{CompiledBinding CurrentValue}"
|
||||||
PlaceholderText="Connect a pin..."
|
PlaceholderText="Connect a pin..."
|
||||||
Classes="condensed"
|
Classes="condensed"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center" />
|
||||||
|
|||||||
@ -33,11 +33,7 @@ public class EnumEqualsNodeCustomViewModel : CustomNodeViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(x => _node.InputPin.PinConnected += x, x => _node.InputPin.PinConnected -= x)
|
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(x => _node.InputPin.PinConnected += x, x => _node.InputPin.PinConnected -= x)
|
||||||
.Subscribe(p =>
|
.Subscribe(p => EnumValues.AddRange(Enum.GetValues(p.EventArgs.Value.Type).Cast<Enum>()))
|
||||||
{
|
|
||||||
EnumValues.AddRange(Enum.GetValues(p.EventArgs.Value.Type).Cast<Enum>());
|
|
||||||
this.RaisePropertyChanged(nameof(CurrentValue));
|
|
||||||
})
|
|
||||||
.DisposeWith(d);
|
.DisposeWith(d);
|
||||||
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(x => _node.InputPin.PinDisconnected += x, x => _node.InputPin.PinDisconnected -= x)
|
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(x => _node.InputPin.PinDisconnected += x, x => _node.InputPin.PinDisconnected -= x)
|
||||||
.Subscribe(_ => EnumValues.Clear())
|
.Subscribe(_ => EnumValues.Clear())
|
||||||
@ -47,13 +43,13 @@ public class EnumEqualsNodeCustomViewModel : CustomNodeViewModel
|
|||||||
|
|
||||||
public ObservableCollection<Enum> EnumValues { get; } = new();
|
public ObservableCollection<Enum> EnumValues { get; } = new();
|
||||||
|
|
||||||
public Enum? CurrentValue
|
public int CurrentValue
|
||||||
{
|
{
|
||||||
get => _node.Storage;
|
get => _node.Storage;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value != null && !Equals(_node.Storage, value))
|
if (!Equals(_node.Storage, value))
|
||||||
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<Enum>(_node, value));
|
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<int>(_node, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user