1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Event condition - Simplify to only one event per layer

This commit is contained in:
Robert 2021-09-15 22:35:34 +02:00
parent d657e7844e
commit 00125d1478
24 changed files with 350 additions and 617 deletions

View File

@ -1,35 +1,35 @@
using System; using System;
using System.Linq; using System.Linq;
using Artemis.Core.Internal; using Artemis.Core.Internal;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions; using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core namespace Artemis.Core
{ {
public class EventCondition : CorePropertyChanged, IDisposable, IStorageModel public class EventCondition : CorePropertyChanged, INodeScriptCondition
{ {
private readonly string _displayName; private readonly string _displayName;
private readonly object? _context; private readonly EventConditionEntity _entity;
private DateTime _lastProcessedTrigger; private EventDefaultNode _eventNode;
private TimeLineEventOverlapMode _eventOverlapMode;
private DataModelPath? _eventPath; private DataModelPath? _eventPath;
private DateTime _lastProcessedTrigger;
internal EventCondition(string displayName, object? context) public EventCondition(ProfileElement profileElement)
{ {
_displayName = displayName; _entity = new EventConditionEntity();
_context = context; _displayName = profileElement.GetType().Name;
Entity = new EventConditionEntity(); ProfileElement = profileElement;
Script = new NodeScript<bool>($"Activate {displayName}", $"Whether or not the event should activate the {displayName}", context); Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", profileElement.Profile);
UpdateEventNode();
} }
internal EventCondition(EventConditionEntity entity, string displayName, object? context) internal EventCondition(EventConditionEntity entity, ProfileElement profileElement)
{ {
_displayName = displayName; _entity = entity;
_context = context; _displayName = profileElement.GetType().Name;
Entity = entity;
Script = null!;
ProfileElement = profileElement;
Load(); Load();
} }
@ -47,36 +47,14 @@ namespace Artemis.Core
get => _eventPath; get => _eventPath;
} }
internal EventConditionEntity Entity { get; }
internal bool Evaluate() /// <summary>
/// Gets or sets how the condition behaves when events trigger before the timeline finishes
/// </summary>
public TimeLineEventOverlapMode EventOverlapMode
{ {
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger) get => _eventOverlapMode;
return false; set => SetAndNotify(ref _eventOverlapMode, value);
// TODO: Place dataModelEvent.LastEventArgumentsUntyped; in the start node
Script.Run();
_lastProcessedTrigger = dataModelEvent.LastTrigger;
return Script.Result;
}
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
Script.Dispose();
EventPath?.Dispose();
}
#endregion
internal void LoadNodeScript()
{
Script.Load();
UpdateEventNode();
} }
/// <summary> /// <summary>
@ -87,33 +65,120 @@ namespace Artemis.Core
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent) if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent)
return; return;
if (Script.Nodes.FirstOrDefault(n => n is EventStartNode) is EventStartNode existing) if (Script.Nodes.FirstOrDefault(n => n is EventDefaultNode) is EventDefaultNode existing)
{
existing.UpdateDataModelEvent(dataModelEvent); existing.UpdateDataModelEvent(dataModelEvent);
_eventNode = existing;
}
else else
{ {
EventStartNode node = new(); _eventNode = new EventDefaultNode();
node.UpdateDataModelEvent(dataModelEvent); _eventNode.UpdateDataModelEvent(dataModelEvent);
Script.AddNode(node); }
if (_eventNode.Pins.Any() && !Script.Nodes.Contains(_eventNode))
Script.AddNode(_eventNode);
else
Script.RemoveNode(_eventNode);
}
private bool Evaluate()
{
if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger)
return false;
_lastProcessedTrigger = dataModelEvent.LastTrigger;
if (!Script.ExitNodeConnected)
return true;
Script.Run();
return Script.Result;
}
/// <inheritdoc />
public IConditionEntity Entity => _entity;
/// <inheritdoc />
public ProfileElement ProfileElement { get; }
/// <inheritdoc />
public bool IsMet { get; private set; }
/// <inheritdoc />
public void Update()
{
if (EventOverlapMode == TimeLineEventOverlapMode.Toggle)
{
if (Evaluate())
IsMet = !IsMet;
}
else
{
IsMet = Evaluate();
} }
} }
#region Implementation of IStorageModel /// <inheritdoc />
public void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline)
{
if (!isMet)
{
if (EventOverlapMode == TimeLineEventOverlapMode.Toggle)
timeline.JumpToEnd();
return;
}
// Event overlap mode doesn't apply in this case
if (timeline.IsFinished)
{
timeline.JumpToStart();
return;
}
// If the timeline was already running, look at the event overlap mode
if (EventOverlapMode == TimeLineEventOverlapMode.Restart)
timeline.JumpToStart();
else if (EventOverlapMode == TimeLineEventOverlapMode.Copy)
timeline.AddExtraTimeline();
else if (EventOverlapMode == TimeLineEventOverlapMode.Toggle && !wasMet)
timeline.JumpToStart();
// The remaining overlap mode is 'ignore' which requires no further action
}
/// <inheritdoc />
public void Dispose()
{
Script.Dispose();
EventPath?.Dispose();
}
#region Storage
/// <inheritdoc /> /// <inheritdoc />
public void Load() public void Load()
{ {
EventPath = new DataModelPath(null, Entity.EventPath); EventOverlapMode = (TimeLineEventOverlapMode) _entity.EventOverlapMode;
Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", Entity.Script, _context); Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile);
UpdateEventNode(); EventPath = new DataModelPath(_entity.EventPath);
} }
/// <inheritdoc /> /// <inheritdoc />
public void Save() public void Save()
{ {
EventPath?.Save(); _entity.EventOverlapMode = (int) EventOverlapMode;
Entity.EventPath = EventPath?.Entity;
Script.Save(); Script.Save();
Entity.Script = Script.Entity; _entity.Script = Script.Entity;
EventPath?.Save();
_entity.EventPath = EventPath?.Entity;
}
/// <inheritdoc />
public void LoadNodeScript()
{
Script.Load();
UpdateEventNode();
Script.LoadConnections();
} }
#endregion #endregion

View File

@ -1,175 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core
{
public class EventsCondition : CorePropertyChanged, INodeScriptCondition
{
private readonly EventsConditionEntity _entity;
private readonly List<EventCondition> _eventsList;
private TimeLineEventOverlapMode _eventOverlapMode;
public EventsCondition(ProfileElement profileElement)
{
_entity = new EventsConditionEntity();
_eventsList = new List<EventCondition>();
ProfileElement = profileElement;
Events = new ReadOnlyCollection<EventCondition>(_eventsList);
}
internal EventsCondition(EventsConditionEntity entity, ProfileElement profileElement)
{
_entity = entity;
_eventsList = new List<EventCondition>();
ProfileElement = profileElement;
Events = new ReadOnlyCollection<EventCondition>(_eventsList);
Load();
}
/// <summary>
/// Gets a list of events this condition reacts to
/// </summary>
public IReadOnlyCollection<EventCondition> Events { get; }
/// <summary>
/// Gets or sets how the condition behaves when events trigger before the timeline finishes
/// </summary>
public TimeLineEventOverlapMode EventOverlapMode
{
get => _eventOverlapMode;
set => SetAndNotify(ref _eventOverlapMode, value);
}
/// <inheritdoc />
public IConditionEntity Entity => _entity;
/// <inheritdoc />
public ProfileElement ProfileElement { get; }
/// <inheritdoc />
public bool IsMet { get; private set; }
/// <summary>
/// Adds a new event condition
/// </summary>
/// <returns>The newly created event condition</returns>
public EventCondition AddEventCondition()
{
EventCondition eventCondition = new(ProfileElement.GetType().Name.ToLower(), ProfileElement.Profile);
lock (_eventsList)
{
_eventsList.Add(eventCondition);
}
return eventCondition;
}
/// <summary>
/// Removes the provided event condition
/// </summary>
/// <param name="eventCondition">The event condition to remove</param>
public void RemoveEventCondition(EventCondition eventCondition)
{
lock (_eventsList)
{
_eventsList.Remove(eventCondition);
}
}
/// <inheritdoc />
public void Update()
{
lock (_eventsList)
{
if (EventOverlapMode == TimeLineEventOverlapMode.Toggle)
{
if (_eventsList.Any(c => c.Evaluate()))
IsMet = !IsMet;
}
else
{
IsMet = _eventsList.Any(c => c.Evaluate());
}
}
}
/// <inheritdoc />
public void ApplyToTimeline(bool isMet, bool wasMet, Timeline timeline)
{
if (!isMet)
return;
// Event overlap mode doesn't apply in this case
if (timeline.IsFinished)
{
timeline.JumpToStart();
return;
}
// If the timeline was already running, look at the event overlap mode
if (EventOverlapMode == TimeLineEventOverlapMode.Restart)
timeline.JumpToStart();
else if (EventOverlapMode == TimeLineEventOverlapMode.Copy)
timeline.AddExtraTimeline();
else if (EventOverlapMode == TimeLineEventOverlapMode.Toggle && !wasMet)
timeline.JumpToStart();
// The remaining overlap mode is 'ignore' which requires no further action
}
/// <inheritdoc />
public void Dispose()
{
foreach (EventCondition eventCondition in Events)
eventCondition.Dispose();
}
#region Storage
/// <inheritdoc />
public void Load()
{
EventOverlapMode = (TimeLineEventOverlapMode) _entity.EventOverlapMode;
lock (_eventsList)
{
_eventsList.Clear();
foreach (EventConditionEntity eventCondition in _entity.Events)
_eventsList.Add(new EventCondition(eventCondition, ProfileElement.GetType().Name.ToLower(), ProfileElement.Profile));
}
}
/// <inheritdoc />
public void Save()
{
_entity.EventOverlapMode = (int) EventOverlapMode;
_entity.Events.Clear();
lock (_eventsList)
{
foreach (EventCondition eventCondition in _eventsList)
{
eventCondition.Save();
_entity.Events.Add(eventCondition.Entity);
}
}
}
/// <inheritdoc />
public void LoadNodeScript()
{
lock (_eventsList)
{
foreach (EventCondition eventCondition in _eventsList)
eventCondition.LoadNodeScript();
}
}
#endregion
}
}

View File

@ -1,6 +1,4 @@
using System; using Artemis.Storage.Entities.Profile.Abstract;
using System.Linq;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions; using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core namespace Artemis.Core
@ -8,19 +6,21 @@ namespace Artemis.Core
public class StaticCondition : CorePropertyChanged, INodeScriptCondition public class StaticCondition : CorePropertyChanged, INodeScriptCondition
{ {
private readonly StaticConditionEntity _entity; private readonly StaticConditionEntity _entity;
private readonly string _displayName;
public StaticCondition(ProfileElement profileElement) public StaticCondition(ProfileElement profileElement)
{ {
_entity = new StaticConditionEntity(); _entity = new StaticConditionEntity();
_displayName = profileElement.GetType().Name;
ProfileElement = profileElement; ProfileElement = profileElement;
string typeDisplayName = profileElement.GetType().Name.ToLower(); Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not this {_displayName} should be active", profileElement.Profile);
Script = new NodeScript<bool>($"Activate {typeDisplayName}", $"Whether or not this {typeDisplayName} should be active", profileElement.Profile);
} }
internal StaticCondition(StaticConditionEntity entity, ProfileElement profileElement) internal StaticCondition(StaticConditionEntity entity, ProfileElement profileElement)
{ {
_entity = entity; _entity = entity;
_displayName = profileElement.GetType().Name;
ProfileElement = profileElement; ProfileElement = profileElement;
Script = null!; Script = null!;
@ -45,7 +45,7 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public void Update() public void Update()
{ {
if (!Script.HasNodes) if (!Script.ExitNodeConnected)
{ {
IsMet = true; IsMet = true;
return; return;
@ -64,23 +64,18 @@ namespace Artemis.Core
timeline.JumpToEndSegment(); timeline.JumpToEndSegment();
} }
#region IDisposable
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
Script.Dispose(); Script.Dispose();
} }
#endregion
#region Storage #region Storage
/// <inheritdoc /> /// <inheritdoc />
public void Load() public void Load()
{ {
string typeDisplayName = ProfileElement.GetType().Name.ToLower(); Script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not this {_displayName} should be active", _entity.Script, ProfileElement.Profile);
Script = new NodeScript<bool>($"Activate {typeDisplayName}", $"Whether or not this {typeDisplayName} should be active", _entity.Script, ProfileElement.Profile);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -12,13 +12,14 @@ namespace Artemis.Core
private readonly List<IDataBindingProperty> _properties = new(); private readonly List<IDataBindingProperty> _properties = new();
private bool _disposed; private bool _disposed;
private bool _isEnabled; private bool _isEnabled;
private DataBindingNodeScript<TLayerProperty> _script;
internal DataBinding(LayerProperty<TLayerProperty> layerProperty) internal DataBinding(LayerProperty<TLayerProperty> layerProperty)
{ {
LayerProperty = layerProperty; LayerProperty = layerProperty;
Entity = new DataBindingEntity(); Entity = new DataBindingEntity();
Script = new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile); _script = new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile);
Save(); Save();
} }
@ -28,7 +29,7 @@ namespace Artemis.Core
LayerProperty = layerProperty; LayerProperty = layerProperty;
Entity = entity; Entity = entity;
Script = new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile); _script = new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile);
// Load will add children so be initialized before that // Load will add children so be initialized before that
Load(); Load();
@ -42,7 +43,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the script used to populate the data binding /// Gets the script used to populate the data binding
/// </summary> /// </summary>
public DataBindingNodeScript<TLayerProperty> Script { get; private set; } public INodeScript Script => _script;
/// <summary> /// <summary>
/// Gets the data binding entity this data binding uses for persistent storage /// Gets the data binding entity this data binding uses for persistent storage
@ -183,7 +184,7 @@ namespace Artemis.Core
if (!IsEnabled) if (!IsEnabled)
return; return;
Script.DataBindingExitNode.ApplyToDataBinding(); _script.DataBindingExitNode.ApplyToDataBinding();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -220,8 +221,8 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public void LoadNodeScript() public void LoadNodeScript()
{ {
Script.Dispose(); _script.Dispose();
Script = Entity.NodeScript != null _script = Entity.NodeScript != null
? new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, Entity.NodeScript, LayerProperty.ProfileElement.Profile) ? new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, Entity.NodeScript, LayerProperty.ProfileElement.Profile)
: new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile); : new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile);
} }
@ -232,9 +233,9 @@ namespace Artemis.Core
if (_disposed) if (_disposed)
throw new ObjectDisposedException("DataBinding"); throw new ObjectDisposedException("DataBinding");
Script.Save(); _script.Save();
Entity.IsEnabled = IsEnabled; Entity.IsEnabled = IsEnabled;
Entity.NodeScript = Script.Entity.Nodes.Any() ? Script.Entity : null; Entity.NodeScript = _script.Entity.Nodes.Any() ? _script.Entity : null;
} }
#endregion #endregion

View File

@ -75,11 +75,9 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="DataModelPath" /> class based on a <see cref="DataModelPathEntity" /> /// Creates a new instance of the <see cref="DataModelPath" /> class based on a <see cref="DataModelPathEntity" />
/// </summary> /// </summary>
/// <param name="target"></param>
/// <param name="entity"></param> /// <param name="entity"></param>
public DataModelPath(DataModel? target, DataModelPathEntity entity) public DataModelPath(DataModelPathEntity entity)
{ {
Target = target;
Path = entity.Path; Path = entity.Path;
Entity = entity; Entity = entity;

View File

@ -7,6 +7,7 @@ using Artemis.Core.LayerEffects.Placeholder;
using Artemis.Core.Properties; using Artemis.Core.Properties;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract; using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
using SkiaSharp; using SkiaSharp;
namespace Artemis.Core namespace Artemis.Core
@ -72,6 +73,13 @@ namespace Artemis.Core
? new Timeline(RenderElementEntity.Timeline) ? new Timeline(RenderElementEntity.Timeline)
: new Timeline(); : new Timeline();
DisplayCondition = RenderElementEntity.DisplayCondition switch
{
StaticConditionEntity staticConditionEntity => new StaticCondition(staticConditionEntity, this),
EventConditionEntity eventConditionEntity => new EventCondition(eventConditionEntity, this),
_ => DisplayCondition
};
ActivateEffects(); ActivateEffects();
} }
@ -130,10 +138,10 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void UpdateTimeline(double deltaTime) public void UpdateTimeline(double deltaTime)
{ {
// TODO: Move to conditions
// The play mode dictates whether to stick to the main segment unless the display conditions contains events // The play mode dictates whether to stick to the main segment unless the display conditions contains events
bool stickToMainSegment = (Timeline.PlayMode == TimelinePlayMode.Repeat || Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle) && DisplayConditionMet; bool stickToMainSegment = Timeline.PlayMode == TimelinePlayMode.Repeat && DisplayConditionMet;
// if (DisplayCondition != null && DisplayCondition.ContainsEvents && Timeline.EventOverlapMode != TimeLineEventOverlapMode.Toggle)
// stickToMainSegment = false;
Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment); Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment);
} }

View File

@ -13,7 +13,7 @@ namespace Artemis.Core
public class Timeline : CorePropertyChanged, IStorageModel public class Timeline : CorePropertyChanged, IStorageModel
{ {
private const int MaxExtraTimelines = 15; private const int MaxExtraTimelines = 15;
private readonly object _lock = new (); private readonly object _lock = new();
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="Timeline" /> class /// Creates a new instance of the <see cref="Timeline" /> class
@ -24,7 +24,7 @@ namespace Artemis.Core
MainSegmentLength = TimeSpan.FromSeconds(5); MainSegmentLength = TimeSpan.FromSeconds(5);
_extraTimelines = new List<Timeline>(); _extraTimelines = new List<Timeline>();
ExtraTimelines = new(_extraTimelines); ExtraTimelines = new ReadOnlyCollection<Timeline>(_extraTimelines);
Save(); Save();
} }
@ -33,7 +33,7 @@ namespace Artemis.Core
{ {
Entity = entity; Entity = entity;
_extraTimelines = new List<Timeline>(); _extraTimelines = new List<Timeline>();
ExtraTimelines = new(_extraTimelines); ExtraTimelines = new ReadOnlyCollection<Timeline>(_extraTimelines);
Load(); Load();
} }
@ -47,7 +47,7 @@ namespace Artemis.Core
EndSegmentLength = Parent.EndSegmentLength; EndSegmentLength = Parent.EndSegmentLength;
_extraTimelines = new List<Timeline>(); _extraTimelines = new List<Timeline>();
ExtraTimelines = new(_extraTimelines); ExtraTimelines = new ReadOnlyCollection<Timeline>(_extraTimelines);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -140,16 +140,6 @@ namespace Artemis.Core
set => SetAndNotify(ref _stopMode, value); set => SetAndNotify(ref _stopMode, value);
} }
/// <summary>
/// Gets or sets the mode in which the render element responds to display condition events being fired before the
/// timeline finished
/// </summary>
public TimeLineEventOverlapMode EventOverlapMode
{
get => _eventOverlapMode;
set => SetAndNotify(ref _eventOverlapMode, value);
}
/// <summary> /// <summary>
/// Gets a list of extra copies of the timeline applied to this timeline /// Gets a list of extra copies of the timeline applied to this timeline
/// </summary> /// </summary>
@ -445,7 +435,8 @@ namespace Artemis.Core
EndSegmentLength = Entity.EndSegmentLength; EndSegmentLength = Entity.EndSegmentLength;
PlayMode = (TimelinePlayMode) Entity.PlayMode; PlayMode = (TimelinePlayMode) Entity.PlayMode;
StopMode = (TimelineStopMode) Entity.StopMode; StopMode = (TimelineStopMode) Entity.StopMode;
EventOverlapMode = (TimeLineEventOverlapMode) Entity.EventOverlapMode;
JumpToEnd();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -456,7 +447,6 @@ namespace Artemis.Core
Entity.EndSegmentLength = EndSegmentLength; Entity.EndSegmentLength = EndSegmentLength;
Entity.PlayMode = (int) PlayMode; Entity.PlayMode = (int) PlayMode;
Entity.StopMode = (int) StopMode; Entity.StopMode = (int) StopMode;
Entity.EventOverlapMode = (int) EventOverlapMode;
} }
#endregion #endregion

View File

@ -171,7 +171,7 @@ namespace Artemis.Core
if (_disposed) if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration"); throw new ObjectDisposedException("ProfileConfiguration");
if (!ActivationCondition.HasNodes) if (!ActivationCondition.ExitNodeConnected)
ActivationConditionMet = true; ActivationConditionMet = true;
else else
{ {

View File

@ -1,15 +1,19 @@
using System; using System;
using System.Linq;
using Artemis.Core.Internal; using Artemis.Core.Internal;
using Artemis.Storage.Entities.Profile.Nodes; using Artemis.Storage.Entities.Profile.Nodes;
namespace Artemis.Core namespace Artemis.Core
{ {
public class DataBindingNodeScript<TLayerProperty> : NodeScript internal class DataBindingNodeScript<TLayerProperty> : NodeScript
{ {
#region Properties & Fields #region Properties & Fields
internal DataBindingExitNode<TLayerProperty> DataBindingExitNode { get; } internal DataBindingExitNode<TLayerProperty> DataBindingExitNode { get; }
/// <inheritdoc />
public override bool ExitNodeConnected => DataBindingExitNode.Pins.Any(p => p.ConnectedTo.Any());
/// <inheritdoc /> /// <inheritdoc />
public override Type ResultType => typeof(object); public override Type ResultType => typeof(object);

View File

@ -9,7 +9,6 @@ namespace Artemis.Core
{ {
string Name { get; } string Name { get; }
string Description { get; } string Description { get; }
bool HasNodes { get; }
IEnumerable<INode> Nodes { get; } IEnumerable<INode> Nodes { get; }

View File

@ -1,20 +1,23 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Artemis.Core.Modules;
using Humanizer; using Humanizer;
namespace Artemis.Core.Internal namespace Artemis.Core.Internal
{ {
internal class EventStartNode : Node internal class EventDefaultNode : Node
{ {
private IDataModelEvent? _dataModelEvent;
private readonly Dictionary<PropertyInfo, OutputPin> _propertyPins; private readonly Dictionary<PropertyInfo, OutputPin> _propertyPins;
private IDataModelEvent? _dataModelEvent;
public EventStartNode() : base("Event Arguments", "Contains the event arguments that triggered the evaluation") public EventDefaultNode() : base("Event Arguments", "Contains the event arguments that triggered the evaluation")
{ {
_propertyPins = new Dictionary<PropertyInfo, OutputPin>(); _propertyPins = new Dictionary<PropertyInfo, OutputPin>();
} }
public override bool IsDefaultNode => true;
public void UpdateDataModelEvent(IDataModelEvent dataModelEvent) public void UpdateDataModelEvent(IDataModelEvent dataModelEvent)
{ {
if (_dataModelEvent == dataModelEvent) if (_dataModelEvent == dataModelEvent)
@ -25,13 +28,11 @@ namespace Artemis.Core.Internal
_propertyPins.Clear(); _propertyPins.Clear();
_dataModelEvent = dataModelEvent; _dataModelEvent = dataModelEvent;
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))))
_propertyPins.Add(propertyInfo, CreateOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize())); _propertyPins.Add(propertyInfo, CreateOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
} }
#region Overrides of Node
/// <inheritdoc />
public override void Evaluate() public override void Evaluate()
{ {
if (_dataModelEvent?.LastEventArgumentsUntyped == null) if (_dataModelEvent?.LastEventArgumentsUntyped == null)
@ -43,7 +44,5 @@ namespace Artemis.Core.Internal
outputPin.Value = propertyInfo.GetValue(_dataModelEvent.LastEventArgumentsUntyped) ?? outputPin.Type.GetDefault()!; outputPin.Value = propertyInfo.GetValue(_dataModelEvent.LastEventArgumentsUntyped) ?? outputPin.Type.GetDefault()!;
} }
} }
#endregion
} }
} }

View File

@ -25,12 +25,12 @@ namespace Artemis.Core
public string Name { get; } public string Name { get; }
public string Description { get; } public string Description { get; }
public bool HasNodes => _nodes.Count > 1;
private readonly List<INode> _nodes = new(); private readonly List<INode> _nodes = new();
public IEnumerable<INode> Nodes => new ReadOnlyCollection<INode>(_nodes); public IEnumerable<INode> Nodes => new ReadOnlyCollection<INode>(_nodes);
protected INode ExitNode { get; set; } protected INode ExitNode { get; set; }
public abstract bool ExitNodeConnected { get; }
public abstract Type ResultType { get; } public abstract Type ResultType { get; }
public object? Context { get; set; } public object? Context { get; set; }
@ -149,7 +149,7 @@ namespace Artemis.Core
{ {
IPinCollection? collection = node.PinCollections.FirstOrDefault(c => c.Name == entityNodePinCollection.Name && IPinCollection? collection = node.PinCollections.FirstOrDefault(c => c.Name == entityNodePinCollection.Name &&
c.Type.Name == entityNodePinCollection.Type && c.Type.Name == entityNodePinCollection.Type &&
(int)c.Direction == entityNodePinCollection.Direction); (int) c.Direction == entityNodePinCollection.Direction);
if (collection == null) if (collection == null)
continue; continue;
@ -231,7 +231,7 @@ namespace Artemis.Core
{ {
Name = nodePinCollection.Name, Name = nodePinCollection.Name,
Type = nodePinCollection.Type.Name, Type = nodePinCollection.Type.Name,
Direction = (int)nodePinCollection.Direction, Direction = (int) nodePinCollection.Direction,
Amount = nodePinCollection.Count() Amount = nodePinCollection.Count()
}); });
} }
@ -301,8 +301,9 @@ namespace Artemis.Core
{ {
#region Properties & Fields #region Properties & Fields
public T Result => ((ExitNode<T>)ExitNode).Value; public T Result => ((ExitNode<T>) ExitNode).Value;
public override bool ExitNodeConnected => ((ExitNode<T>) ExitNode).Input.ConnectedTo.Any();
public override Type ResultType => typeof(T); public override Type ResultType => typeof(T);
#endregion #endregion

View File

@ -1,17 +1,11 @@
using System.Collections.Generic; using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Nodes; using Artemis.Storage.Entities.Profile.Nodes;
namespace Artemis.Storage.Entities.Profile.Conditions namespace Artemis.Storage.Entities.Profile.Conditions
{ {
public class EventsConditionEntity : IConditionEntity public class EventConditionEntity : IConditionEntity
{ {
public int EventOverlapMode { get; set; } public int EventOverlapMode { get; set; }
public List<EventConditionEntity> Events { get; set; } = new();
}
public class EventConditionEntity
{
public DataModelPathEntity EventPath { get; set; } public DataModelPathEntity EventPath { get; set; }
public NodeScriptEntity Script { get; set; } public NodeScriptEntity Script { get; set; }
} }

View File

@ -10,6 +10,5 @@ namespace Artemis.Storage.Entities.Profile
public int PlayMode { get; set; } public int PlayMode { get; set; }
public int StopMode { get; set; } public int StopMode { get; set; }
public int EventOverlapMode { get; set; }
} }
} }

View File

@ -97,7 +97,6 @@ namespace Artemis.UI.Ninject.Factories
public interface IConditionVmFactory : IVmFactory public interface IConditionVmFactory : IVmFactory
{ {
StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition); StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition);
EventsConditionViewModel EventsConditionViewModel(EventsCondition eventsCondition);
EventConditionViewModel EventConditionViewModel(EventCondition eventCondition); EventConditionViewModel EventConditionViewModel(EventCondition eventCondition);
} }

View File

@ -81,13 +81,13 @@
<RadioButton.ToolTip> <RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40"> <ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock> <TextBlock>
A condition that checks whenever one or more events fire and acts in a configurable way A condition triggers on a selectable event
</TextBlock> </TextBlock>
</ToolTip> </ToolTip>
</RadioButton.ToolTip> </RadioButton.ToolTip>
<TextBlock VerticalAlignment="Center" FontSize="12"> <TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="LightningBolt" VerticalAlignment="Center" Margin="0 0 2 -3" /> <materialDesign:PackIcon Kind="LightningBolt" VerticalAlignment="Center" Margin="0 0 2 -3" />
EVENTS EVENT
</TextBlock> </TextBlock>
</RadioButton> </RadioButton>
</Grid> </Grid>

View File

@ -50,7 +50,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
if (DisplayConditionType == DisplayConditionType.Static) if (DisplayConditionType == DisplayConditionType.Static)
_profileEditorService.SelectedProfileElement.ChangeDisplayCondition(new StaticCondition(_profileEditorService.SelectedProfileElement)); _profileEditorService.SelectedProfileElement.ChangeDisplayCondition(new StaticCondition(_profileEditorService.SelectedProfileElement));
else if (DisplayConditionType == DisplayConditionType.Events) else if (DisplayConditionType == DisplayConditionType.Events)
_profileEditorService.SelectedProfileElement.ChangeDisplayCondition(new EventsCondition(_profileEditorService.SelectedProfileElement)); _profileEditorService.SelectedProfileElement.ChangeDisplayCondition(new EventCondition(_profileEditorService.SelectedProfileElement));
else else
_profileEditorService.SelectedProfileElement.ChangeDisplayCondition(null); _profileEditorService.SelectedProfileElement.ChangeDisplayCondition(null);
@ -71,9 +71,9 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
ActiveItem = _conditionVmFactory.StaticConditionViewModel(staticCondition); ActiveItem = _conditionVmFactory.StaticConditionViewModel(staticCondition);
_displayConditionType = DisplayConditionType.Static; _displayConditionType = DisplayConditionType.Static;
} }
else if (renderProfileElement.DisplayCondition is EventsCondition eventsCondition) else if (renderProfileElement.DisplayCondition is EventCondition eventsCondition)
{ {
ActiveItem = _conditionVmFactory.EventsConditionViewModel(eventsCondition); ActiveItem = _conditionVmFactory.EventConditionViewModel(eventsCondition);
_displayConditionType = DisplayConditionType.Events; _displayConditionType = DisplayConditionType.Events;
} }
else else

View File

@ -4,43 +4,45 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event" xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting" xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:shared="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared" xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:controls1="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:EventConditionViewModel}}"> d:DataContext="{d:DesignInstance {x:Type local:EventConditionViewModel}}">
<UserControl.Resources>
<converters:ComparisonConverter x:Key="ComparisonConverter" />
</UserControl.Resources>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.RowDefinitions>
<ColumnDefinition /> <RowDefinition Height="*" />
<ColumnDefinition /> <RowDefinition Height="22" />
</Grid.ColumnDefinitions> <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" VerticalAlignment="Stretch">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<shared:DataModelPicker Grid.Row="0" <controls1:DataModelPicker Grid.Row="0"
Grid.Column="0" Margin="0 0 0 5"
Margin="0 5"
ButtonBrush="DarkGoldenrod" ButtonBrush="DarkGoldenrod"
DataModelPath="{Binding EventCondition.EventPath}" DataModelPath="{Binding EventCondition.EventPath}"
DataModelPathSelected="{s:Action DataModelPathSelected}" DataModelPathSelected="{s:Action DataModelPathSelected}"
FilterTypes="{Binding FilterTypes}" FilterTypes="{Binding FilterTypes}"
ShowFullPath="True"
Modules="{Binding Modules}" /> Modules="{Binding Modules}" />
<Button Grid.Row="0"
Grid.Column="1"
Margin="0 5"
Command="{s:Action AddEvent}"
s:View.ActionTarget="{Binding Parent}"
Content="ADD EVENT" />
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" MouseUp="{s:Action ScriptGridMouseUp}" Cursor="Hand"> <Grid Grid.Row="1" MouseUp="{s:Action ScriptGridMouseUp}" Cursor="Hand">
<controls:VisualScriptPresenter Script="{Binding EventCondition.Script}" AutoFitScript="True" /> <controls:VisualScriptPresenter Script="{Binding EventCondition.Script}" AutoFitScript="True" />
<Border VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <Border VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Border.Background> <Border.Background>
<SolidColorBrush Color="{Binding Color, Source={StaticResource MaterialDesignPaper}}" Opacity="0.75" /> <SolidColorBrush Color="{Binding Color, Source={StaticResource MaterialDesignCardBackground}}" Opacity="0.75" />
</Border.Background> </Border.Background>
<Border.Style> <Border.Style>
<Style> <Style>
@ -64,18 +66,101 @@
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</Border.Style> </Border.Style>
<Grid HorizontalAlignment="Center"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<ColumnDefinition /> <TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center">
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center" Grid.Column="0">
Click to edit script Click to edit script
</TextBlock> </TextBlock>
<materialDesign:PackIcon Kind="OpenInNew" Margin="10 " Width="30" Height="30" Grid.Column="1" VerticalAlignment="Center" /> <materialDesign:PackIcon Kind="OpenInNew" Margin="10 " Width="30" Height="30" VerticalAlignment="Center" />
</Grid> </StackPanel>
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" TextWrapping="Wrap" TextAlignment="Center">
Scripts with nothing connected to their end-node are always true.
</TextBlock>
</StackPanel>
</Border> </Border>
</Grid> </Grid>
</Grid> </Grid>
<!-- Trigger mode -->
<TextBlock Grid.Row="1" Grid.Column="0" Text="Trigger mode" VerticalAlignment="Center">
<TextBlock.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-30">
<TextBlock>
Configure how the layer should act when the event(s) trigger
</TextBlock>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
<materialDesign:ColorZone Grid.Row="2" Grid.Column="0" Mode="Standard" CornerRadius="3">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Restart}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" Margin="-3 0 0 -3" />
RESTART
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Stop the current run and restart the timeline
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
<RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Toggle}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="TrafficLight" VerticalAlignment="Center" Margin="-3 0 0 -3" />
TOGGLE
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Repeat the timeline until the event fires again
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
<RadioButton Grid.Column="2"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Ignore}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="EarHearingOff" VerticalAlignment="Center" Margin="-3 0 0 -3" />
IGNORE
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Ignore subsequent event fires until the timeline finishes
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
<RadioButton Grid.Column="3"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Copy}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="ContentCopy" VerticalAlignment="Center" Margin="-3 0 0 -3" />
COPY
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Play another copy of the timeline on top of the current run
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
</Grid>
</UserControl> </UserControl>

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Windows.Input; using System.Windows.Input;
using Artemis.Core; using Artemis.Core;
@ -13,33 +12,31 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event
{ {
public class EventConditionViewModel : Screen public class EventConditionViewModel : Screen
{ {
private readonly INodeVmFactory _nodeVmFactory;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly IWindowManager _windowManager; private readonly IWindowManager _windowManager;
private readonly INodeVmFactory _nodeVmFactory;
public EventConditionViewModel(EventCondition eventCondition, IWindowManager windowManager, INodeVmFactory nodeVmFactory, IProfileEditorService profileEditorService) public EventConditionViewModel(EventCondition eventCondition, IProfileEditorService profileEditorService, IWindowManager windowManager, INodeVmFactory nodeVmFactory)
{ {
_profileEditorService = profileEditorService;
_windowManager = windowManager; _windowManager = windowManager;
_nodeVmFactory = nodeVmFactory; _nodeVmFactory = nodeVmFactory;
_profileEditorService = profileEditorService;
EventCondition = eventCondition; EventCondition = eventCondition;
DisplayName = EventCondition.EventPath?.Segments.LastOrDefault()?.GetPropertyDescription()?.Name ?? "Invalid event";
FilterTypes = new BindableCollection<Type> {typeof(IDataModelEvent) };
Modules = new BindableCollection<Module>();
if (_profileEditorService.SelectedProfileConfiguration?.Module != null)
Modules.Add(_profileEditorService.SelectedProfileConfiguration.Module);
} }
public EventCondition EventCondition { get; } public EventCondition EventCondition { get; }
public BindableCollection<Type> FilterTypes { get; } public BindableCollection<Type> FilterTypes { get; }
public BindableCollection<Module> Modules { get; } public BindableCollection<Module> Modules { get; }
public bool CanDeleteEvent => ((EventsConditionViewModel) Parent).Items.Count > 1;
public void DeleteEvent() public TimeLineEventOverlapMode EventOverlapMode
{ {
((EventsConditionViewModel) Parent).DeleteEvent(this); get => EventCondition.EventOverlapMode;
set
{
if (EventCondition.EventOverlapMode == value) return;
EventCondition.EventOverlapMode = value;
_profileEditorService.SaveSelectedProfileElement();
}
} }
public void DataModelPathSelected(object sender, DataModelSelectedEventArgs e) public void DataModelPathSelected(object sender, DataModelSelectedEventArgs e)
@ -56,28 +53,5 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event
_windowManager.ShowDialog(_nodeVmFactory.NodeScriptWindowViewModel(EventCondition.Script)); _windowManager.ShowDialog(_nodeVmFactory.NodeScriptWindowViewModel(EventCondition.Script));
_profileEditorService.SaveSelectedProfileElement(); _profileEditorService.SaveSelectedProfileElement();
} }
#region Overrides of Screen
/// <inheritdoc />
protected override void OnInitialActivate()
{
((EventsConditionViewModel) Parent).Items.CollectionChanged += ItemsOnCollectionChanged;
base.OnInitialActivate();
}
/// <inheritdoc />
protected override void OnClose()
{
((EventsConditionViewModel) Parent).Items.CollectionChanged -= ItemsOnCollectionChanged;
base.OnClose();
}
#endregion
private void ItemsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
NotifyOfPropertyChange(nameof(CanDeleteEvent));
}
} }
} }

View File

@ -1,139 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event.EventsConditionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:EventsConditionViewModel}}">
<UserControl.Resources>
<converters:ComparisonConverter x:Key="ComparisonConverter" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="22" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TabControl Grid.Row="0"
VerticalAlignment="Stretch"
ItemsSource="{Binding Items}"
SelectedItem="{Binding ActiveItem}"
Style="{StaticResource MaterialDesignTabControl}">
<TabControl.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="True" Margin="-15 0">
<Button DockPanel.Dock="Right"
Style="{StaticResource MaterialDesignIconForegroundButton}"
Command="{s:Action DeleteEvent}"
s:View.ActionTarget="{Binding}"
CommandParameter="{Binding}"
Width="25"
Height="25">
<materialDesign:PackIcon Kind="Close" Width="15" Height="15" Foreground="{DynamicResource MaterialDesignBody}" />
</Button>
<Label Content="{Binding DisplayName}" DockPanel.Dock="Left" />
</DockPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding IsAsync=True}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False"
TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<!-- Trigger mode -->
<TextBlock Grid.Row="1" Grid.Column="0" Text="Trigger mode" VerticalAlignment="Center">
<TextBlock.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-30">
<TextBlock>
Configure how the layer should act when the event(s) trigger
</TextBlock>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
<materialDesign:ColorZone Grid.Row="2" Grid.Column="0" Mode="Standard" CornerRadius="3">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Restart}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" Margin="-3 0 0 -3" />
RESTART
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Stop the current run and restart the timeline
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
<RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Toggle}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="TrafficLight" VerticalAlignment="Center" Margin="-3 0 0 -3" />
TOGGLE
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Repeat the timeline until the event fires again
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
<RadioButton Grid.Column="2"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Ignore}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="EarHearingOff" VerticalAlignment="Center" Margin="-3 0 0 -3" />
IGNORE
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Ignore subsequent event fires until the timeline finishes
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
<RadioButton Grid.Column="3"
Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding Path=EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Copy}}">
<TextBlock VerticalAlignment="Center" FontSize="12">
<materialDesign:PackIcon Kind="ContentCopy" VerticalAlignment="Center" Margin="-3 0 0 -3" />
COPY
</TextBlock>
<RadioButton.ToolTip>
<ToolTip Placement="Center" VerticalOffset="-40">
<TextBlock>
Play another copy of the timeline on top of the current run
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
</Grid>
</materialDesign:ColorZone>
</Grid>
</UserControl>

View File

@ -1,65 +0,0 @@
using System.Linq;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared.Services;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions.Event
{
public class EventsConditionViewModel : Conductor<EventConditionViewModel>.Collection.OneActive
{
private readonly IConditionVmFactory _conditionVmFactory;
private readonly IProfileEditorService _profileEditorService;
public EventsConditionViewModel(EventsCondition eventsCondition, IConditionVmFactory conditionVmFactory, IProfileEditorService profileEditorService)
{
_conditionVmFactory = conditionVmFactory;
_profileEditorService = profileEditorService;
EventsCondition = eventsCondition;
}
public EventsCondition EventsCondition { get; }
public TimeLineEventOverlapMode EventOverlapMode
{
get => EventsCondition.EventOverlapMode;
set
{
if (EventsCondition.EventOverlapMode == value) return;
EventsCondition.EventOverlapMode = value;
_profileEditorService.SaveSelectedProfileElement();
}
}
public void AddEvent()
{
EventCondition eventCondition = EventsCondition.AddEventCondition();
Items.Add(_conditionVmFactory.EventConditionViewModel(eventCondition));
_profileEditorService.SaveSelectedProfileElement();
}
public void DeleteEvent(EventConditionViewModel eventConditionViewModel)
{
EventsCondition.RemoveEventCondition(eventConditionViewModel.EventCondition);
Items.Remove(eventConditionViewModel);
_profileEditorService.SaveSelectedProfileElement();
}
#region Overrides of Screen
/// <inheritdoc />
protected override void OnInitialActivate()
{
if (!EventsCondition.Events.Any())
EventsCondition.AddEventCondition();
foreach (EventCondition eventCondition in EventsCondition.Events)
Items.Add(_conditionVmFactory.EventConditionViewModel(eventCondition));
base.OnInitialActivate();
}
#endregion
}
}

View File

@ -49,16 +49,17 @@
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</Border.Style> </Border.Style>
<Grid HorizontalAlignment="Center"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<ColumnDefinition /> <TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center">
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" VerticalAlignment="Center" Grid.Column="0">
Click to edit script Click to edit script
</TextBlock> </TextBlock>
<materialDesign:PackIcon Kind="OpenInNew" Margin="10 " Width="30" Height="30" Grid.Column="1" VerticalAlignment="Center" /> <materialDesign:PackIcon Kind="OpenInNew" Margin="10 " Width="30" Height="30" VerticalAlignment="Center" />
</Grid> </StackPanel>
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" TextWrapping="Wrap" TextAlignment="Center">
Scripts with nothing connected to their end-node are always true.
</TextBlock>
</StackPanel>
</Border> </Border>
</Grid> </Grid>
<Grid Grid.Row="1"> <Grid Grid.Row="1">

View File

@ -31,7 +31,7 @@ namespace Artemis.VisualScripting.Nodes
if (Storage is not DataModelPathEntity pathEntity) if (Storage is not DataModelPathEntity pathEntity)
return; return;
DataModelPath = new DataModelPath(null, pathEntity); DataModelPath = new DataModelPath(pathEntity);
DataModelPath.PathValidated += DataModelPathOnPathValidated; DataModelPath.PathValidated += DataModelPathOnPathValidated;
} }

View File

@ -32,7 +32,7 @@ namespace Artemis.VisualScripting.Nodes
if (Storage is not DataModelPathEntity pathEntity) if (Storage is not DataModelPathEntity pathEntity)
return; return;
DataModelPath = new DataModelPath(null, pathEntity); DataModelPath = new DataModelPath(pathEntity);
DataModelPath.PathValidated += DataModelPathOnPathValidated; DataModelPath.PathValidated += DataModelPathOnPathValidated;
UpdateOutputPin(false); UpdateOutputPin(false);