diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings
index 535046f29..13a2166e3 100644
--- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings
+++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings
@@ -75,6 +75,9 @@
True
True
True
+ True
+ True
+ True
True
True
True
diff --git a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs
index 8c54d17f5..26c262cd0 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using Artemis.Core.Internal;
+using Artemis.Core.VisualScripting.Internal;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
@@ -13,13 +14,15 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
{
private readonly string _displayName;
private readonly EventConditionEntity _entity;
- private EventDefaultNode _eventNode;
+ private IEventConditionNode _startNode;
private DataModelPath? _eventPath;
- private DateTime _lastProcessedTrigger;
- private EventOverlapMode _overlapMode;
private NodeScript _script;
- private EventTriggerMode _triggerMode;
private bool _wasMet;
+ private DateTime _lastProcessedTrigger;
+ private object? _lastProcessedValue;
+ private EventOverlapMode _overlapMode;
+ private EventTriggerMode _triggerMode;
+ private EventToggleOffMode _toggleOffMode;
///
/// Creates a new instance of the class
@@ -30,7 +33,7 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
_entity = new EventConditionEntity();
_displayName = profileElement.GetType().Name;
- _eventNode = new EventDefaultNode {X = -300};
+ _startNode = new EventConditionEventStartNode {X = -300};
_script = new NodeScript($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
}
@@ -40,7 +43,7 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
_entity = entity;
_displayName = profileElement.GetType().Name;
- _eventNode = new EventDefaultNode();
+ _startNode = new EventConditionEventStartNode();
_script = null!;
Load();
@@ -83,18 +86,70 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
set => SetAndNotify(ref _overlapMode, value);
}
+ ///
+ /// Gets or sets the mode for render elements when toggling off the event when using .
+ ///
+ public EventToggleOffMode ToggleOffMode
+ {
+ get => _toggleOffMode;
+ set => SetAndNotify(ref _toggleOffMode, value);
+ }
+
///
/// Updates the event node, applying the selected event
///
public void UpdateEventNode()
{
- IDataModelEvent? dataModelEvent = EventPath?.GetValue() as IDataModelEvent;
- _eventNode.CreatePins(dataModelEvent);
+ if (EventPath == null)
+ return;
- if (dataModelEvent != null && !Script.Nodes.Contains(_eventNode))
- Script.AddNode(_eventNode);
- else if (dataModelEvent == null && Script.Nodes.Contains(_eventNode))
- Script.RemoveNode(_eventNode);
+ Type? pathType = EventPath.GetPropertyType();
+ if (pathType == null)
+ return;
+
+ // Create an event node if the path type is a data model event
+ if (pathType.IsAssignableTo(typeof(IDataModelEvent)))
+ {
+ EventConditionEventStartNode eventNode;
+ // Ensure the start node is an event node
+ if (_startNode is not EventConditionEventStartNode node)
+ {
+ eventNode = new EventConditionEventStartNode();
+ ReplaceStartNode(eventNode);
+ _startNode = eventNode;
+ }
+ else
+ eventNode = node;
+
+ IDataModelEvent? dataModelEvent = EventPath?.GetValue() as IDataModelEvent;
+ eventNode.CreatePins(dataModelEvent);
+ }
+ // Create a value changed node if the path type is a regular value
+ else
+ {
+ // Ensure the start nod is a value changed node
+ EventConditionValueChangedStartNode valueChangedNode;
+ // Ensure the start node is an event node
+ if (_startNode is not EventConditionValueChangedStartNode node)
+ {
+ valueChangedNode = new EventConditionValueChangedStartNode();
+ ReplaceStartNode(valueChangedNode);
+ }
+ else
+ valueChangedNode = node;
+
+ valueChangedNode.UpdateOutputPins(EventPath);
+ }
+ }
+
+ private void ReplaceStartNode(IEventConditionNode newStartNode)
+ {
+ if (Script.Nodes.Contains(_startNode))
+ Script.RemoveNode(_startNode);
+
+ _startNode = newStartNode;
+ if (!Script.Nodes.Contains(_startNode))
+ Script.AddNode(_startNode);
}
///
@@ -103,15 +158,30 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
/// The start node of the event script, if any.
public INode GetStartNode()
{
- return _eventNode;
+ return _startNode;
}
private bool Evaluate()
{
- if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger)
+ if (EventPath == null)
return false;
- _lastProcessedTrigger = dataModelEvent.LastTrigger;
+ object? value = EventPath.GetValue();
+ if (_startNode is EventConditionEventStartNode)
+ {
+ if (value is not IDataModelEvent dataModelEvent || dataModelEvent.LastTrigger <= _lastProcessedTrigger)
+ return false;
+
+ _lastProcessedTrigger = dataModelEvent.LastTrigger;
+ }
+ else if (_startNode is EventConditionValueChangedStartNode valueChangedNode)
+ {
+ if (Equals(value, _lastProcessedValue))
+ return false;
+
+ valueChangedNode.UpdateValues(value, _lastProcessedValue);
+ _lastProcessedValue = value;
+ }
if (!Script.ExitNodeConnected)
return true;
@@ -151,6 +221,8 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
{
if (IsMet && !_wasMet)
ProfileElement.Timeline.JumpToStart();
+ if (!IsMet && _wasMet && ToggleOffMode == EventToggleOffMode.SkipToEnd)
+ ProfileElement.Timeline.JumpToEndSegment();
}
else
{
@@ -191,6 +263,7 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
{
TriggerMode = (EventTriggerMode) _entity.TriggerMode;
OverlapMode = (EventOverlapMode) _entity.OverlapMode;
+ ToggleOffMode = (EventToggleOffMode) _entity.ToggleOffMode;
if (_entity.EventPath != null)
EventPath = new DataModelPath(_entity.EventPath);
@@ -206,6 +279,8 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
{
_entity.TriggerMode = (int) TriggerMode;
_entity.OverlapMode = (int) OverlapMode;
+ _entity.ToggleOffMode = (int) ToggleOffMode;
+
Script.Save();
_entity.Script = Script?.Entity;
EventPath?.Save();
@@ -221,9 +296,9 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
Script.Load();
// The load action may have created an event node, use that one over the one we have here
- INode? existingEventNode = Script.Nodes.FirstOrDefault(n => n.Id == EventDefaultNode.NodeId);
+ INode? existingEventNode = Script.Nodes.FirstOrDefault(n => n.Id == EventConditionEventStartNode.NodeId || n.Id == EventConditionValueChangedStartNode.NodeId);
if (existingEventNode != null)
- _eventNode = (EventDefaultNode) existingEventNode;
+ _startNode = (IEventConditionNode) existingEventNode;
UpdateEventNode();
Script.LoadConnections();
@@ -268,4 +343,20 @@ public enum EventOverlapMode
/// Ignore subsequent event fires until the timeline finishes
///
Ignore
+}
+
+///
+/// Represents a mode for render elements when toggling off the event when using .
+///
+public enum EventToggleOffMode
+{
+ ///
+ /// When the event toggles the condition off, finish the the current run of the main timeline
+ ///
+ Finish,
+
+ ///
+ /// When the event toggles the condition off, skip to the end segment of the timeline
+ ///
+ SkipToEnd
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEvent.cs b/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEvent.cs
deleted file mode 100644
index 9bb880516..000000000
--- a/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEvent.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Artemis.Core
-{
- internal class DataModelValueChangedEvent : IDataModelEvent
- {
- public DataModelValueChangedEvent(DataModelPath path)
- {
- Path = path;
- }
-
- public DataModelPath Path { get; }
- public T? LastValue { get; private set; }
- public T? CurrentValue { get; private set; }
- public DateTime LastTrigger { get; private set; }
- public TimeSpan TimeSinceLastTrigger => DateTime.Now - LastTrigger;
- public int TriggerCount { get; private set; }
- public Type ArgumentsType { get; } = typeof(DataModelValueChangedEventArgs);
- public string TriggerPastParticiple => "changed";
- public bool TrackHistory { get; set; } = false;
- public DataModelEventArgs? LastEventArgumentsUntyped { get; private set; }
- public List EventArgumentsHistoryUntyped { get; } = new();
-
- public void Update()
- {
- if (!Path.IsValid)
- return;
-
- object? value = Path.GetValue();
- if (value != null)
- CurrentValue = (T?) value;
- else
- CurrentValue = default;
-
- if (!Equals(LastValue, CurrentValue))
- Trigger();
-
- LastValue = CurrentValue;
- }
-
- public void Reset()
- {
- TriggerCount = 0;
- }
-
- private void Trigger()
- {
- LastEventArgumentsUntyped = new DataModelValueChangedEventArgs(CurrentValue, LastValue);
- LastTrigger = DateTime.Now;
- TriggerCount++;
-
- OnEventTriggered();
- }
-
- #region Events
-
- public event EventHandler? EventTriggered;
-
- internal virtual void OnEventTriggered()
- {
- EventTriggered?.Invoke(this, EventArgs.Empty);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEventArgs.cs b/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEventArgs.cs
deleted file mode 100644
index b25976a61..000000000
--- a/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEventArgs.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Artemis.Core.Modules;
-
-namespace Artemis.Core
-{
- internal class DataModelValueChangedEventArgs : DataModelEventArgs
- {
- public DataModelValueChangedEventArgs(T? currentValue, T? previousValue)
- {
- CurrentValue = currentValue;
- PreviousValue = previousValue;
- }
-
- [DataModelProperty(Description = "The current value of the property")]
- public T? CurrentValue { get; }
- [DataModelProperty(Description = "The previous value of the property")]
- public T? PreviousValue { get; }
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/ProfileElement.cs b/src/Artemis.Core/Models/Profile/ProfileElement.cs
index 60ecda321..2f70ff8f8 100644
--- a/src/Artemis.Core/Models/Profile/ProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/ProfileElement.cs
@@ -334,20 +334,18 @@ namespace Artemis.Core
/// Explorer
///
/// The resulting name i.e. New layer or New layer (2)
- public string GetNewLayerName()
+ public string GetNewLayerName(string baseName = "New layer")
{
- if (!Children.Any(c => c is Layer))
- return "New layer";
+ if (!Children.Any(c => c is Layer && c.Name == baseName))
+ return baseName;
- // Grab existing unnamed layers and get the first available number
- // Looks slow but it's not https://stackoverflow.com/a/8865806/5015269
- Regex regex = new(@"New layer \((\d+)\)");
- int firstAvailable = Enumerable.Range(1, int.MaxValue)
- .Except(Children.Where(c => c is Layer && c.Name != null && regex.IsMatch(c.Name))
- .Select(c => int.Parse(regex.Match(c.Name!).Groups[1].Value))
- .OrderBy(i => i))
- .First();
- return $"New layer ({firstAvailable})";
+ int current = 2;
+ while (true)
+ {
+ if (Children.All(c => c is Layer && c.Name != $"{baseName} ({current})"))
+ return $"{baseName} ({current})";
+ current++;
+ }
}
///
@@ -355,20 +353,18 @@ namespace Artemis.Core
/// in Explorer
///
/// The resulting name i.e. New folder or New folder (2)
- public string GetNewFolderName()
+ public string GetNewFolderName(string baseName = "New folder")
{
- if (!Children.Any(c => c is Folder))
- return "New folder";
+ if (!Children.Any(c => c is Folder && c.Name == baseName))
+ return baseName;
- // Grab existing unnamed layers and get the first available number
- // Looks slow but it's not https://stackoverflow.com/a/8865806/5015269
- Regex regex = new(@"New folder \((\d+)\)");
- int firstAvailable = Enumerable.Range(1, int.MaxValue)
- .Except(Children.Where(c => c is Folder && c.Name != null && regex.IsMatch(c.Name))
- .Select(c => int.Parse(regex.Match(c.Name!).Groups[1].Value))
- .OrderBy(i => i))
- .First();
- return $"New folder ({firstAvailable})";
+ int current = 2;
+ while (true)
+ {
+ if (Children.All(c => c is Folder && c.Name != $"{baseName} ({current})"))
+ return $"{baseName} ({current})";
+ current++;
+ }
}
#endregion
diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs
index 15a9a71a5..ed00c54fe 100644
--- a/src/Artemis.Core/Services/CoreService.cs
+++ b/src/Artemis.Core/Services/CoreService.cs
@@ -38,13 +38,14 @@ namespace Artemis.Core.Services
// ReSharper disable UnusedParameter.Local
public CoreService(IKernel kernel,
ILogger logger,
- StorageMigrationService _, // injected to ensure migration runs early
+ StorageMigrationService _1, // injected to ensure migration runs early
ISettingsService settingsService,
IPluginManagementService pluginManagementService,
IRgbService rgbService,
IProfileService profileService,
IModuleService moduleService,
- IScriptingService scriptingService)
+ IScriptingService scriptingService,
+ IProcessMonitorService _2)
{
Kernel = kernel;
Constants.CorePlugin.Kernel = kernel;
diff --git a/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs b/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs
index bd1451101..3f272c348 100644
--- a/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs
+++ b/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs
@@ -11,7 +11,6 @@ namespace Artemis.Core.Services
internal class ProcessMonitorService : IProcessMonitorService
{
private readonly ILogger _logger;
- private readonly Timer _processScanTimer;
private readonly ProcessComparer _comparer;
private Process[] _lastScannedProcesses;
@@ -19,16 +18,15 @@ namespace Artemis.Core.Services
{
_logger = logger;
_lastScannedProcesses = Process.GetProcesses();
- _processScanTimer = new Timer(1000);
- _processScanTimer.Elapsed += OnTimerElapsed;
- _processScanTimer.Start();
+ Timer processScanTimer = new(1000);
+ processScanTimer.Elapsed += OnTimerElapsed;
+ processScanTimer.Start();
_comparer = new ProcessComparer();
ProcessActivationRequirement.ProcessMonitorService = this;
}
public event EventHandler? ProcessStarted;
-
public event EventHandler? ProcessStopped;
public IEnumerable GetRunningProcesses()
@@ -36,7 +34,7 @@ namespace Artemis.Core.Services
return _lastScannedProcesses;
}
- private void OnTimerElapsed(object sender, ElapsedEventArgs e)
+ private void OnTimerElapsed(object? sender, ElapsedEventArgs e)
{
Process[] newProcesses = Process.GetProcesses();
foreach (Process startedProcess in newProcesses.Except(_lastScannedProcesses, _comparer))
@@ -64,7 +62,7 @@ namespace Artemis.Core.Services
return x.Id == y.Id && x.ProcessName == y.ProcessName && x.SessionId == y.SessionId;
}
- public int GetHashCode(Process obj)
+ public int GetHashCode(Process? obj)
{
if (obj == null) return 0;
return obj.Id;
diff --git a/src/Artemis.Core/VisualScripting/Internal/EventConditionEventStartNode.cs b/src/Artemis.Core/VisualScripting/Internal/EventConditionEventStartNode.cs
new file mode 100644
index 000000000..c169e0555
--- /dev/null
+++ b/src/Artemis.Core/VisualScripting/Internal/EventConditionEventStartNode.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Artemis.Core.Modules;
+using Artemis.Core.VisualScripting.Internal;
+using Humanizer;
+
+namespace Artemis.Core.Internal;
+
+internal class EventConditionEventStartNode : DefaultNode, IEventConditionNode
+{
+ internal static readonly Guid NodeId = new("278735FE-69E9-4A73-A6B8-59E83EE19305");
+ private readonly List _pinBucket = new();
+ private readonly Dictionary _propertyPins;
+ private IDataModelEvent? _dataModelEvent;
+
+ public EventConditionEventStartNode() : base(NodeId, "Event Arguments", "Contains the event arguments that triggered the evaluation")
+ {
+ _propertyPins = new Dictionary();
+ }
+
+ public void CreatePins(IDataModelEvent? dataModelEvent)
+ {
+ if (_dataModelEvent == dataModelEvent)
+ return;
+
+ while (Pins.Any())
+ RemovePin((Pin) Pins.First());
+ _propertyPins.Clear();
+
+ _dataModelEvent = dataModelEvent;
+ if (dataModelEvent == null)
+ return;
+
+ foreach (PropertyInfo propertyInfo in dataModelEvent.ArgumentsType
+ .GetProperties(BindingFlags.Instance | BindingFlags.Public)
+ .Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof(DataModelIgnoreAttribute))))
+ _propertyPins.Add(propertyInfo, CreateOrAddOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
+ }
+
+ public override void Evaluate()
+ {
+ if (_dataModelEvent?.LastEventArgumentsUntyped == null)
+ return;
+
+ foreach ((PropertyInfo propertyInfo, OutputPin outputPin) in _propertyPins)
+ {
+ if (outputPin.ConnectedTo.Any())
+ outputPin.Value = propertyInfo.GetValue(_dataModelEvent.LastEventArgumentsUntyped) ?? outputPin.Type.GetDefault()!;
+ }
+ }
+
+ ///
+ /// Creates or adds an input pin to the node using a bucket.
+ /// The bucket might grow a bit over time as the user edits the node but pins won't get lost, enabling undo/redo in the
+ /// editor.
+ ///
+ private OutputPin CreateOrAddOutputPin(Type valueType, string displayName)
+ {
+ // Grab the first pin from the bucket that isn't on the node yet
+ OutputPin? pin = _pinBucket.FirstOrDefault(p => !Pins.Contains(p));
+
+ // If there is none, create a new one and add it to the bucket
+ if (pin == null)
+ {
+ pin = CreateOutputPin(valueType, displayName);
+ _pinBucket.Add(pin);
+ }
+ // If there was a pin in the bucket, update it's type and display name and reuse it
+ else
+ {
+ pin.ChangeType(valueType);
+ pin.Name = displayName;
+ AddPin(pin);
+ }
+
+ return pin;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/VisualScripting/Internal/EventConditionValueChangedStartNode.cs b/src/Artemis.Core/VisualScripting/Internal/EventConditionValueChangedStartNode.cs
new file mode 100644
index 000000000..5bbf1d77b
--- /dev/null
+++ b/src/Artemis.Core/VisualScripting/Internal/EventConditionValueChangedStartNode.cs
@@ -0,0 +1,46 @@
+using System;
+using Artemis.Core.VisualScripting.Internal;
+
+namespace Artemis.Core.Internal;
+
+internal class EventConditionValueChangedStartNode : DefaultNode, IEventConditionNode
+{
+ internal static readonly Guid NodeId = new("F9A270DB-A231-4800-BAB3-DC1F96856756");
+ private object? _newValue;
+ private object? _oldValue;
+
+ public EventConditionValueChangedStartNode() : base(NodeId, "Changed values", "Contains the old and new values of the property chat was changed.")
+ {
+ NewValue = CreateOutputPin(typeof(object), "New value");
+ OldValue = CreateOutputPin(typeof(object), "Old value");
+ }
+
+ public OutputPin NewValue { get; }
+ public OutputPin OldValue { get; }
+
+ public void UpdateOutputPins(DataModelPath dataModelPath)
+ {
+ Type? type = dataModelPath?.GetPropertyType();
+ if (Numeric.IsTypeCompatible(type))
+ type = typeof(Numeric);
+ type ??= typeof(object);
+
+ if (NewValue.Type != type)
+ NewValue.ChangeType(type);
+ if (OldValue.Type != type)
+ OldValue.ChangeType(type);
+ }
+
+ public void UpdateValues(object? newValue, object? oldValue)
+ {
+ _newValue = newValue;
+ _oldValue = oldValue;
+ }
+
+ ///
+ public override void Evaluate()
+ {
+ NewValue.Value = _newValue;
+ OldValue.Value = _oldValue;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/VisualScripting/Internal/EventDefaultNode.cs b/src/Artemis.Core/VisualScripting/Internal/EventDefaultNode.cs
deleted file mode 100644
index 4e35b99e6..000000000
--- a/src/Artemis.Core/VisualScripting/Internal/EventDefaultNode.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using Artemis.Core.Modules;
-using Humanizer;
-
-namespace Artemis.Core.Internal
-{
- internal class EventDefaultNode : DefaultNode
- {
- internal static readonly Guid NodeId = new("278735FE-69E9-4A73-A6B8-59E83EE19305");
- private readonly Dictionary _propertyPins;
- private readonly List _pinBucket = new();
- private IDataModelEvent? _dataModelEvent;
-
- public EventDefaultNode() : base(NodeId, "Event Arguments", "Contains the event arguments that triggered the evaluation")
- {
- _propertyPins = new Dictionary();
- }
-
- public void CreatePins(IDataModelEvent? dataModelEvent)
- {
- if (_dataModelEvent == dataModelEvent)
- return;
-
- while (Pins.Any())
- RemovePin((Pin) Pins.First());
- _propertyPins.Clear();
-
- _dataModelEvent = dataModelEvent;
- if (dataModelEvent == null)
- return;
-
- foreach (PropertyInfo propertyInfo in dataModelEvent.ArgumentsType
- .GetProperties(BindingFlags.Instance | BindingFlags.Public)
- .Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof(DataModelIgnoreAttribute))))
- {
- _propertyPins.Add(propertyInfo, CreateOrAddOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
- }
- }
-
- public override void Evaluate()
- {
- if (_dataModelEvent?.LastEventArgumentsUntyped == null)
- return;
-
- foreach ((PropertyInfo propertyInfo, OutputPin outputPin) in _propertyPins)
- {
- if (outputPin.ConnectedTo.Any())
- outputPin.Value = propertyInfo.GetValue(_dataModelEvent.LastEventArgumentsUntyped) ?? outputPin.Type.GetDefault()!;
- }
- }
-
- ///
- /// Creates or adds an input pin to the node using a bucket.
- /// The bucket might grow a bit over time as the user edits the node but pins won't get lost, enabling undo/redo in the
- /// editor.
- ///
- private OutputPin CreateOrAddOutputPin(Type valueType, string displayName)
- {
- // Grab the first pin from the bucket that isn't on the node yet
- OutputPin? pin = _pinBucket.FirstOrDefault(p => !Pins.Contains(p));
-
- // If there is none, create a new one and add it to the bucket
- if (pin == null)
- {
- pin = CreateOutputPin(valueType, displayName);
- _pinBucket.Add(pin);
- }
- // If there was a pin in the bucket, update it's type and display name and reuse it
- else
- {
- pin.ChangeType(valueType);
- pin.Name = displayName;
- AddPin(pin);
- }
-
- return pin;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/VisualScripting/Internal/IEventConditionNode.cs b/src/Artemis.Core/VisualScripting/Internal/IEventConditionNode.cs
new file mode 100644
index 000000000..b5436aea7
--- /dev/null
+++ b/src/Artemis.Core/VisualScripting/Internal/IEventConditionNode.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Artemis.Core.VisualScripting.Internal
+{
+ internal interface IEventConditionNode : INode
+ {
+ }
+}
diff --git a/src/Artemis.Storage/Entities/Profile/Conditions/EventConditionEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/EventConditionEntity.cs
index c7e83890a..93bc4990a 100644
--- a/src/Artemis.Storage/Entities/Profile/Conditions/EventConditionEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/EventConditionEntity.cs
@@ -7,6 +7,7 @@ namespace Artemis.Storage.Entities.Profile.Conditions
{
public int TriggerMode { get; set; }
public int OverlapMode { get; set; }
+ public int ToggleOffMode { get; set; }
public DataModelPathEntity EventPath { get; set; }
public NodeScriptEntity Script { get; set; }
}
diff --git a/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml b/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml
index 4245d6b76..56dcf901e 100644
--- a/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml
+++ b/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml
@@ -3,6 +3,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Shared.Controls.ArtemisIcon">
+ x:Class="Artemis.UI.Shared.ArtemisIcon">
Welcome to Avalonia!
diff --git a/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs b/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs
index a115738d5..7805c02a8 100644
--- a/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs
+++ b/src/Artemis.UI.Shared/Controls/ArtemisIcon.axaml.cs
@@ -8,7 +8,7 @@ using Avalonia.Svg.Skia;
using Material.Icons;
using Material.Icons.Avalonia;
-namespace Artemis.UI.Shared.Controls
+namespace Artemis.UI.Shared
{
///
/// Represents a control that can display an arbitrary kind of icon.
diff --git a/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs b/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs
index 520a54a12..bb343bec1 100644
--- a/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs
+++ b/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPicker.cs
@@ -13,10 +13,11 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
+using Avalonia.Threading;
using Material.Icons.Avalonia;
using ReactiveUI;
-namespace Artemis.UI.Shared.Controls.DataModelPicker;
+namespace Artemis.UI.Shared.DataModelPicker;
///
/// Represents a data model picker picker that can be used to select a data model path.
@@ -52,29 +53,32 @@ public class DataModelPicker : TemplatedControl
public static readonly StyledProperty DataModelViewModelProperty =
AvaloniaProperty.Register(nameof(DataModelViewModel));
- ///
- /// A list of data model view models to show
- ///
- public static readonly StyledProperty?> ExtraDataModelViewModelsProperty =
- AvaloniaProperty.Register?>(nameof(ExtraDataModelViewModels), new ObservableCollection());
-
///
/// A list of types to filter the selectable paths on.
///
public static readonly StyledProperty?> FilterTypesProperty =
AvaloniaProperty.Register?>(nameof(FilterTypes), new ObservableCollection());
- private MaterialIcon? _currentPathIcon;
- private TextBlock? _currentPathDisplay;
+ ///
+ /// Gets or sets a boolean indicating whether the picker is in event picker mode.
+ /// When event children aren't selectable and non-events are described as "{PropertyName}
+ /// changed".
+ ///
+ public static readonly StyledProperty IsEventPickerProperty =
+ AvaloniaProperty.Register(nameof(IsEventPicker));
+
private TextBlock? _currentPathDescription;
+ private TextBlock? _currentPathDisplay;
+
+ private MaterialIcon? _currentPathIcon;
private TreeView? _dataModelTreeView;
+ private DispatcherTimer? _updateTimer;
static DataModelPicker()
{
ModulesProperty.Changed.Subscribe(ModulesChanged);
DataModelPathProperty.Changed.Subscribe(DataModelPathPropertyChanged);
DataModelViewModelProperty.Changed.Subscribe(DataModelViewModelPropertyChanged);
- ExtraDataModelViewModelsProperty.Changed.Subscribe(ExtraDataModelViewModelsChanged);
}
///
@@ -125,16 +129,7 @@ public class DataModelPicker : TemplatedControl
get => GetValue(DataModelViewModelProperty);
private set => SetValue(DataModelViewModelProperty, value);
}
-
- ///
- /// A list of data model view models to show.
- ///
- public ObservableCollection? ExtraDataModelViewModels
- {
- get => GetValue(ExtraDataModelViewModelsProperty);
- set => SetValue(ExtraDataModelViewModelsProperty, value);
- }
-
+
///
/// A list of types to filter the selectable paths on.
///
@@ -144,6 +139,17 @@ public class DataModelPicker : TemplatedControl
set => SetValue(FilterTypesProperty, value);
}
+ ///
+ /// Gets or sets a boolean indicating whether the picker is in event picker mode.
+ /// When event children aren't selectable and non-events are described as "{PropertyName}
+ /// changed".
+ ///
+ public bool IsEventPicker
+ {
+ get => GetValue(IsEventPickerProperty);
+ set => SetValue(IsEventPickerProperty, value);
+ }
+
///
/// Occurs when a new path has been selected
///
@@ -158,42 +164,6 @@ public class DataModelPicker : TemplatedControl
DataModelPathSelected?.Invoke(this, e);
}
- #region Overrides of TemplatedControl
-
- ///
- protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
- {
- if (_dataModelTreeView != null)
- _dataModelTreeView.SelectionChanged -= DataModelTreeViewOnSelectionChanged;
-
- _currentPathIcon = e.NameScope.Find("CurrentPathIcon");
- _currentPathDisplay = e.NameScope.Find("CurrentPathDisplay");
- _currentPathDescription = e.NameScope.Find("CurrentPathDescription");
- _dataModelTreeView = e.NameScope.Find("DataModelTreeView");
-
- if (_dataModelTreeView != null)
- _dataModelTreeView.SelectionChanged += DataModelTreeViewOnSelectionChanged;
- }
-
- #region Overrides of Visual
-
- ///
- protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
- {
- GetDataModel();
- UpdateCurrentPath(true);
- }
-
- ///
- protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
- {
- DataModelViewModel?.Dispose();
- }
-
- #endregion
-
- #endregion
-
private static void ModulesChanged(AvaloniaPropertyChangedEventArgs?> e)
{
if (e.Sender is DataModelPicker dataModelPicker)
@@ -212,11 +182,6 @@ public class DataModelPicker : TemplatedControl
e.OldValue.Value.Dispose();
}
- private static void ExtraDataModelViewModelsChanged(AvaloniaPropertyChangedEventArgs?> e)
- {
- // TODO, the original did nothing here either and I can't remember why
- }
-
private void ExecuteSelectPropertyCommand(DataModelVisualizationViewModel selected)
{
if (selected.DataModelPath == null)
@@ -228,6 +193,14 @@ public class DataModelPicker : TemplatedControl
OnDataModelPathSelected(new DataModelSelectedEventArgs(DataModelPath));
}
+ private void Update(object? sender, EventArgs e)
+ {
+ if (DataModelUIService == null)
+ return;
+
+ DataModelViewModel?.Update(DataModelUIService, new DataModelUpdateConfiguration(!IsEventPicker));
+ }
+
private void GetDataModel()
{
if (DataModelUIService == null)
@@ -251,10 +224,11 @@ public class DataModelPicker : TemplatedControl
private void DataModelOnUpdateRequested(object? sender, EventArgs e)
{
- DataModelViewModel?.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes);
- if (ExtraDataModelViewModels == null) return;
- foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels)
- extraDataModelViewModel.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes);
+ if (DataModelUIService == null || DataModelViewModel == null)
+ return;
+
+ DataModelViewModel.Update(DataModelUIService, new DataModelUpdateConfiguration(IsEventPicker));
+ DataModelViewModel.ApplyTypeFilter(true, FilterTypes?.ToArray() ?? Type.EmptyTypes);
}
private void DataModelTreeViewOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
@@ -295,6 +269,45 @@ public class DataModelPicker : TemplatedControl
_currentPathDescription.Text = DataModelPath.GetPropertyDescription()?.Description;
if (_currentPathIcon != null)
_currentPathIcon.Kind = DataModelPath.GetPropertyType().GetTypeIcon();
-
}
+
+ #region Overrides of TemplatedControl
+
+ ///
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ if (_dataModelTreeView != null)
+ _dataModelTreeView.SelectionChanged -= DataModelTreeViewOnSelectionChanged;
+
+ _currentPathIcon = e.NameScope.Find("CurrentPathIcon");
+ _currentPathDisplay = e.NameScope.Find("CurrentPathDisplay");
+ _currentPathDescription = e.NameScope.Find("CurrentPathDescription");
+ _dataModelTreeView = e.NameScope.Find("DataModelTreeView");
+
+ if (_dataModelTreeView != null)
+ _dataModelTreeView.SelectionChanged += DataModelTreeViewOnSelectionChanged;
+ }
+
+ #region Overrides of Visual
+
+ ///
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ GetDataModel();
+ UpdateCurrentPath(true);
+ _updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(200), DispatcherPriority.Normal, Update);
+ _updateTimer.Start();
+ }
+
+ ///
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ DataModelViewModel?.Dispose();
+ _updateTimer?.Stop();
+ _updateTimer = null;
+ }
+
+ #endregion
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPickerButton.cs b/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPickerButton.cs
index 2955c92f4..825105231 100644
--- a/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPickerButton.cs
+++ b/src/Artemis.UI.Shared/Controls/DataModelPicker/DataModelPickerButton.cs
@@ -3,8 +3,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core;
using Artemis.Core.Modules;
-using Artemis.UI.Shared.Controls.Flyouts;
-using Artemis.UI.Shared.DataModelVisualization.Shared;
+using Artemis.UI.Shared.Flyouts;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
@@ -13,7 +12,7 @@ using Avalonia.Interactivity;
using Avalonia.Threading;
using FluentAvalonia.Core;
-namespace Artemis.UI.Shared.Controls.DataModelPicker;
+namespace Artemis.UI.Shared.DataModelPicker;
///
/// Represents a button that can be used to pick a data model path in a flyout.
@@ -68,18 +67,20 @@ public class DataModelPickerButton : TemplatedControl
public static readonly StyledProperty?> ModulesProperty =
AvaloniaProperty.Register?>(nameof(Modules), new ObservableCollection());
- ///
- /// A list of data model view models to show
- ///
- public static readonly StyledProperty?> ExtraDataModelViewModelsProperty =
- AvaloniaProperty.Register?>(nameof(ExtraDataModelViewModels), new ObservableCollection());
-
///
/// A list of types to filter the selectable paths on.
///
public static readonly StyledProperty?> FilterTypesProperty =
AvaloniaProperty.Register?>(nameof(FilterTypes), new ObservableCollection());
+ ///
+ /// Gets or sets a boolean indicating whether the picker is in event picker mode.
+ /// When event children aren't selectable and non-events are described as "{PropertyName}
+ /// changed".
+ ///
+ public static readonly StyledProperty IsEventPickerProperty =
+ AvaloniaProperty.Register(nameof(IsEventPicker));
+
private bool _attached;
private bool _flyoutActive;
private Button? _button;
@@ -165,15 +166,6 @@ public class DataModelPickerButton : TemplatedControl
set => SetValue(ModulesProperty, value);
}
- ///
- /// A list of data model view models to show.
- ///
- public ObservableCollection? ExtraDataModelViewModels
- {
- get => GetValue(ExtraDataModelViewModelsProperty);
- set => SetValue(ExtraDataModelViewModelsProperty, value);
- }
-
///
/// A list of types to filter the selectable paths on.
///
@@ -183,6 +175,17 @@ public class DataModelPickerButton : TemplatedControl
set => SetValue(FilterTypesProperty, value);
}
+ ///
+ /// Gets or sets a boolean indicating whether the picker is in event picker mode.
+ /// When event children aren't selectable and non-events are described as "{PropertyName}
+ /// changed".
+ ///
+ public bool IsEventPicker
+ {
+ get => GetValue(IsEventPickerProperty);
+ set => SetValue(IsEventPickerProperty, value);
+ }
+
///
/// Raised when the flyout opens.
///
@@ -239,14 +242,24 @@ public class DataModelPickerButton : TemplatedControl
}
else
{
+ // If a valid path is selected, gather all its segments and create a visual representation of the path
string? formattedPath = null;
if (DataModelPath != null && DataModelPath.IsValid)
formattedPath = string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription() != null).Select(s => s.GetPropertyDescription()!.Name));
+ // Always show the full path in the tooltip
ToolTip.SetTip(_button, formattedPath);
- _label.Text = ShowFullPath
+
+ // Reuse the tooltip value when showing the full path, otherwise only show the last segment
+ string? labelText = ShowFullPath
? formattedPath
: DataModelPath?.Segments.LastOrDefault()?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier;
+
+ // Add "changed" to the end of the display value if this is an event picker but no event was picked
+ if (IsEventPicker && labelText != null && DataModelPath?.GetPropertyType()?.IsAssignableTo(typeof(IDataModelEvent)) == false)
+ labelText += " changed";
+
+ _label.Text = labelText ?? Placeholder;
}
}
@@ -257,8 +270,8 @@ public class DataModelPickerButton : TemplatedControl
// Logic here is taken from Fluent Avalonia's ColorPicker which also reuses the same control since it's large
_flyout.DataModelPicker.DataModelPath = DataModelPath;
- _flyout.DataModelPicker.ExtraDataModelViewModels = ExtraDataModelViewModels;
_flyout.DataModelPicker.FilterTypes = FilterTypes;
+ _flyout.DataModelPicker.IsEventPicker = IsEventPicker;
_flyout.DataModelPicker.Modules = Modules;
_flyout.DataModelPicker.ShowDataModelValues = ShowDataModelValues;
diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
index b5a944faf..a90ca2550 100644
--- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
+++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
@@ -18,7 +18,7 @@ using Avalonia.Rendering;
using Avalonia.Threading;
using Avalonia.Visuals.Media.Imaging;
-namespace Artemis.UI.Shared.Controls
+namespace Artemis.UI.Shared
{
///
/// Visualizes an with optional per-LED colors
diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs
index 9ccc4bcb6..32b84c1ec 100644
--- a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs
+++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs
@@ -11,7 +11,7 @@ using Color = Avalonia.Media.Color;
using Point = Avalonia.Point;
using SolidColorBrush = Avalonia.Media.SolidColorBrush;
-namespace Artemis.UI.Shared.Controls
+namespace Artemis.UI.Shared
{
internal class DeviceVisualizerLed
{
diff --git a/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml b/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml
index bf79ed332..36ada5068 100644
--- a/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml
+++ b/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml
@@ -3,7 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Shared.Controls.EnumComboBox">
+ x:Class="Artemis.UI.Shared.EnumComboBox">
diff --git a/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml.cs b/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml.cs
index c172e11af..4f0c33187 100644
--- a/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml.cs
+++ b/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml.cs
@@ -8,7 +8,7 @@ using Avalonia.Data;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
-namespace Artemis.UI.Shared.Controls
+namespace Artemis.UI.Shared
{
///
/// Represents a combobox that can display the values of an enum.
diff --git a/src/Artemis.UI.Shared/Controls/Flyouts/DataModelPickerFlyout.cs b/src/Artemis.UI.Shared/Controls/Flyouts/DataModelPickerFlyout.cs
index 8449f3d7f..73bd6efee 100644
--- a/src/Artemis.UI.Shared/Controls/Flyouts/DataModelPickerFlyout.cs
+++ b/src/Artemis.UI.Shared/Controls/Flyouts/DataModelPickerFlyout.cs
@@ -5,7 +5,7 @@ using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Controls.Primitives;
-namespace Artemis.UI.Shared.Controls.Flyouts;
+namespace Artemis.UI.Shared.Flyouts;
///
/// Defines a flyout that hosts a data model picker.
diff --git a/src/Artemis.UI.Shared/Controls/Flyouts/GradientPickerFlyout.cs b/src/Artemis.UI.Shared/Controls/Flyouts/GradientPickerFlyout.cs
index 16a13122b..30258306d 100644
--- a/src/Artemis.UI.Shared/Controls/Flyouts/GradientPickerFlyout.cs
+++ b/src/Artemis.UI.Shared/Controls/Flyouts/GradientPickerFlyout.cs
@@ -1,6 +1,6 @@
using Avalonia.Controls;
-namespace Artemis.UI.Shared.Controls.Flyouts;
+namespace Artemis.UI.Shared.Flyouts;
///
/// Defines a flyout that hosts a gradient picker.
diff --git a/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPicker.cs b/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPicker.cs
index 05d2ca73e..6eef1fb88 100644
--- a/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPicker.cs
+++ b/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPicker.cs
@@ -16,7 +16,7 @@ using FluentAvalonia.UI.Media;
using ReactiveUI;
using Button = Avalonia.Controls.Button;
-namespace Artemis.UI.Shared.Controls.GradientPicker;
+namespace Artemis.UI.Shared.GradientPicker;
///
/// Represents a gradient picker that can be used to edit a gradient.
diff --git a/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPickerButton.cs b/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPickerButton.cs
index b6d149e1e..6a2ae8265 100644
--- a/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPickerButton.cs
+++ b/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPickerButton.cs
@@ -2,7 +2,7 @@
using System.Collections.Specialized;
using System.Linq;
using Artemis.Core;
-using Artemis.UI.Shared.Controls.Flyouts;
+using Artemis.UI.Shared.Flyouts;
using Artemis.UI.Shared.Providers;
using Avalonia;
using Avalonia.Controls;
@@ -13,7 +13,7 @@ using Avalonia.Media;
using FluentAvalonia.Core;
using Button = FluentAvalonia.UI.Controls.Button;
-namespace Artemis.UI.Shared.Controls.GradientPicker;
+namespace Artemis.UI.Shared.GradientPicker;
///
/// Represents a gradient picker box that can be used to edit a gradient
diff --git a/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPickerColorStop.cs b/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPickerColorStop.cs
index de169e523..a13e67bfa 100644
--- a/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPickerColorStop.cs
+++ b/src/Artemis.UI.Shared/Controls/GradientPicker/GradientPickerColorStop.cs
@@ -2,11 +2,10 @@
using Artemis.Core;
using Avalonia;
using Avalonia.Controls;
-using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
-namespace Artemis.UI.Shared.Controls.GradientPicker;
+namespace Artemis.UI.Shared.GradientPicker;
public class GradientPickerColorStop : TemplatedControl
{
diff --git a/src/Artemis.UI.Shared/Controls/HotkeyBox.axaml b/src/Artemis.UI.Shared/Controls/HotkeyBox.axaml
index 29ae38f38..0e53282e1 100644
--- a/src/Artemis.UI.Shared/Controls/HotkeyBox.axaml
+++ b/src/Artemis.UI.Shared/Controls/HotkeyBox.axaml
@@ -2,9 +2,9 @@
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:controls="clr-namespace:Artemis.UI.Shared.Controls"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Shared.Controls.HotkeyBox">
+ x:Class="Artemis.UI.Shared.HotkeyBox">
-
/// Represents a control that can be used to display or edit instances.
diff --git a/src/Artemis.UI.Shared/Controls/NoInputTextBox.cs b/src/Artemis.UI.Shared/Controls/NoInputTextBox.cs
index 6d20bb00e..05859bc49 100644
--- a/src/Artemis.UI.Shared/Controls/NoInputTextBox.cs
+++ b/src/Artemis.UI.Shared/Controls/NoInputTextBox.cs
@@ -3,7 +3,7 @@ using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Styling;
-namespace Artemis.UI.Shared.Controls
+namespace Artemis.UI.Shared
{
internal class NoInputTextBox : TextBox, IStyleable
{
diff --git a/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml b/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml
index 22e77952d..fe83bd1ac 100644
--- a/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml
+++ b/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml
@@ -3,5 +3,5 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Shared.Controls.ProfileConfigurationIcon">
+ x:Class="Artemis.UI.Shared.ProfileConfigurationIcon">
diff --git a/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs b/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs
index b145cb84a..026dbb8e5 100644
--- a/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs
+++ b/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs
@@ -10,7 +10,7 @@ using Avalonia.Svg.Skia;
using Material.Icons;
using Material.Icons.Avalonia;
-namespace Artemis.UI.Shared.Controls
+namespace Artemis.UI.Shared
{
///
/// Represents a control that can display the icon of a specific .
diff --git a/src/Artemis.UI.Shared/Controls/SelectionRectangle.cs b/src/Artemis.UI.Shared/Controls/SelectionRectangle.cs
index 5c43e5cdb..f422b646d 100644
--- a/src/Artemis.UI.Shared/Controls/SelectionRectangle.cs
+++ b/src/Artemis.UI.Shared/Controls/SelectionRectangle.cs
@@ -6,7 +6,7 @@ using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Media;
-namespace Artemis.UI.Shared.Controls;
+namespace Artemis.UI.Shared;
///
/// Visualizes an with optional per-LED colors
diff --git a/src/Artemis.UI.Shared/Events/DataModelSelectedEventArgs.cs b/src/Artemis.UI.Shared/Events/DataModelSelectedEventArgs.cs
index 7f59aedad..35b97d2af 100644
--- a/src/Artemis.UI.Shared/Events/DataModelSelectedEventArgs.cs
+++ b/src/Artemis.UI.Shared/Events/DataModelSelectedEventArgs.cs
@@ -1,6 +1,5 @@
using System;
using Artemis.Core;
-using Artemis.UI.Shared.Controls;
namespace Artemis.UI.Shared.Events
{
diff --git a/src/Artemis.UI.Shared/Events/SelectionRectangleEventArgs.cs b/src/Artemis.UI.Shared/Events/SelectionRectangleEventArgs.cs
index f522cd3fe..1980d5b5c 100644
--- a/src/Artemis.UI.Shared/Events/SelectionRectangleEventArgs.cs
+++ b/src/Artemis.UI.Shared/Events/SelectionRectangleEventArgs.cs
@@ -1,5 +1,4 @@
using System;
-using Artemis.UI.Shared.Controls;
using Avalonia;
using Avalonia.Input;
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventToggleOffMode.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventToggleOffMode.cs
new file mode 100644
index 000000000..b820e07f8
--- /dev/null
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventToggleOffMode.cs
@@ -0,0 +1,38 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
+
+///
+/// Represents a profile editor command that can be used to update an event condition's overlap mode.
+///
+public class UpdateEventToggleOffMode : IProfileEditorCommand
+{
+ private readonly EventCondition _eventCondition;
+ private readonly EventToggleOffMode _value;
+ private readonly EventToggleOffMode _oldValue;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public UpdateEventToggleOffMode(EventCondition eventCondition, EventToggleOffMode value)
+ {
+ _eventCondition = eventCondition;
+ _value = value;
+ _oldValue = eventCondition.ToggleOffMode;
+ }
+
+ ///
+ public string DisplayName => "Update event toggle off mode";
+
+ ///
+ public void Execute()
+ {
+ _eventCondition.ToggleOffMode = _value;
+ }
+
+ ///
+ public void Undo()
+ {
+ _eventCondition.ToggleOffMode = _oldValue;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Styles/Condensed.axaml b/src/Artemis.UI.Shared/Styles/Condensed.axaml
index 78c83629b..287589530 100644
--- a/src/Artemis.UI.Shared/Styles/Condensed.axaml
+++ b/src/Artemis.UI.Shared/Styles/Condensed.axaml
@@ -1,9 +1,9 @@
+ xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.GradientPicker"
+ xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.DataModelPicker"
+ xmlns:controls1="clr-namespace:Artemis.UI.Shared">
diff --git a/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml b/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml
index 4f0c80e28..67ff351ca 100644
--- a/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml
+++ b/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml
@@ -1,9 +1,9 @@
+ xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.DataModelPicker">
@@ -20,7 +20,22 @@
RowDefinitions="*"
MinHeight="38"
IsVisible="{Binding DataModelPath, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static ObjectConverters.IsNotNull}}">
-
+
+
@@ -42,6 +57,7 @@
@@ -58,7 +74,13 @@
-
+
+
+
+
+
diff --git a/src/Artemis.UI.Shared/Styles/Controls/DataModelPickerButton.axaml b/src/Artemis.UI.Shared/Styles/Controls/DataModelPickerButton.axaml
index b573b409b..45885f134 100644
--- a/src/Artemis.UI.Shared/Styles/Controls/DataModelPickerButton.axaml
+++ b/src/Artemis.UI.Shared/Styles/Controls/DataModelPickerButton.axaml
@@ -1,8 +1,8 @@
+ xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.DataModelPicker"
+ xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.GradientPicker">
diff --git a/src/Artemis.UI.Shared/Styles/Controls/GradientPicker.axaml b/src/Artemis.UI.Shared/Styles/Controls/GradientPicker.axaml
index 208eba1da..d10a9fd21 100644
--- a/src/Artemis.UI.Shared/Styles/Controls/GradientPicker.axaml
+++ b/src/Artemis.UI.Shared/Styles/Controls/GradientPicker.axaml
@@ -1,10 +1,10 @@
+ xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters"
+ xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.GradientPicker">
@@ -18,10 +18,10 @@
-
+
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/src/Artemis.UI/ArtemisBootstrapper.cs b/src/Artemis.UI/ArtemisBootstrapper.cs
index 591cde846..0764be57d 100644
--- a/src/Artemis.UI/ArtemisBootstrapper.cs
+++ b/src/Artemis.UI/ArtemisBootstrapper.cs
@@ -5,7 +5,7 @@ using Artemis.Core.Ninject;
using Artemis.UI.Exceptions;
using Artemis.UI.Ninject;
using Artemis.UI.Screens.Root;
-using Artemis.UI.Shared.Controls.DataModelPicker;
+using Artemis.UI.Shared.DataModelPicker;
using Artemis.UI.Shared.Ninject;
using Artemis.UI.Shared.Services;
using Artemis.VisualScripting.Ninject;
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputView.axaml b/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputView.axaml
index 8912a6a9b..d96240a61 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputView.axaml
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputView.axaml
@@ -2,12 +2,12 @@
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:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.DefaultTypes.PropertyInput.BoolPropertyInputView"
x:DataType="propertyInput:BoolPropertyInputViewModel">
-
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml b/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml
index 79b3a3fd2..6a2041784 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml
@@ -2,8 +2,8 @@
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:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
+ xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.GradientPicker;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.DefaultTypes.PropertyInput.ColorGradientPropertyInputView"
x:DataType="propertyInput:ColorGradientPropertyInputViewModel">
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml.cs
index 2288a4821..b14a38ca4 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml.cs
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.axaml.cs
@@ -1,5 +1,5 @@
using System;
-using Artemis.UI.Shared.Controls.GradientPicker;
+using Artemis.UI.Shared.GradientPicker;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/EnumPropertyInputView.axaml b/src/Artemis.UI/DefaultTypes/PropertyInput/EnumPropertyInputView.axaml
index f04e9e77c..7a49d4e22 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/EnumPropertyInputView.axaml
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/EnumPropertyInputView.axaml
@@ -2,8 +2,8 @@
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:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.DefaultTypes.PropertyInput.EnumPropertyInputView">
-
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Extensions/ProfileElementExtensions.cs b/src/Artemis.UI/Extensions/ProfileElementExtensions.cs
new file mode 100644
index 000000000..21f53e40a
--- /dev/null
+++ b/src/Artemis.UI/Extensions/ProfileElementExtensions.cs
@@ -0,0 +1,65 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using Artemis.Core;
+using Artemis.Storage.Entities.Profile;
+using Avalonia;
+using Avalonia.Input;
+using Newtonsoft.Json;
+
+namespace Artemis.UI.Extensions;
+
+///
+/// Provides static extension methods for UI related profile element tasks.
+///
+public static class ProfileElementExtensions
+{
+ public const string ClipboardDataFormat = "Artemis.ProfileElement";
+
+ public static async Task CopyToClipboard(this Folder folder)
+ {
+ if (Application.Current?.Clipboard == null)
+ return;
+
+ DataObject dataObject = new();
+ string copy = CoreJson.SerializeObject(folder.FolderEntity, true);
+ dataObject.Set(ClipboardDataFormat, copy);
+ await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
+ }
+
+ public static async Task CopyToClipboard(this Layer layer)
+ {
+ if (Application.Current?.Clipboard == null)
+ return;
+
+ DataObject dataObject = new();
+ string copy = CoreJson.SerializeObject(layer.LayerEntity, true);
+ dataObject.Set(ClipboardDataFormat, copy);
+ await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
+ }
+
+
+ public static async Task PasteChildFromClipboard(this Folder parent)
+ {
+ if (Application.Current?.Clipboard == null)
+ return null;
+
+ byte[]? bytes = (byte[]?) await Application.Current.Clipboard.GetDataAsync(ClipboardDataFormat);
+ if (bytes == null!)
+ return null;
+
+ object? entity = CoreJson.DeserializeObject(Encoding.Unicode.GetString(bytes), true);
+ switch (entity)
+ {
+ case FolderEntity folderEntity:
+ folderEntity.Id = Guid.NewGuid();
+ return new Folder(parent.Profile, parent, folderEntity);
+ case LayerEntity layerEntity:
+ layerEntity.Id = Guid.NewGuid();
+ return new Layer(parent.Profile, parent, layerEntity);
+ default:
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugPluginView.axaml b/src/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugPluginView.axaml
index e85aa0b4d..e062eff63 100644
--- a/src/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugPluginView.axaml
+++ b/src/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugPluginView.axaml
@@ -2,13 +2,13 @@
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:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Debugger.Performance.PerformanceDebugPluginView">
-
+
diff --git a/src/Artemis.UI/Screens/Device/DevicePropertiesView.axaml b/src/Artemis.UI/Screens/Device/DevicePropertiesView.axaml
index 14c185c15..31070f5e3 100644
--- a/src/Artemis.UI/Screens/Device/DevicePropertiesView.axaml
+++ b/src/Artemis.UI/Screens/Device/DevicePropertiesView.axaml
@@ -2,8 +2,8 @@
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:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
x:Class="Artemis.UI.Screens.Device.DevicePropertiesView"
Icon="/Assets/Images/Logo/application.ico"
@@ -26,7 +26,7 @@
-
-
@@ -23,7 +23,7 @@
-
-
@@ -17,9 +17,9 @@
+ ShowFullPath="{CompiledBinding ShowFullPaths.Value}"
+ IsEventPicker="True"/>
When the event fires..
@@ -48,6 +48,16 @@
+ When toggling off..
+
+ Finish the main segment
+ Skip forward to the end segment
+
+
-
+ LostFocus="InputElement_OnLostFocus" />
- public override bool SupportsChildren => false;
-
- #endregion
+ Layer = layer;
}
+
+ public Layer Layer { get; }
+
+ #region Overrides of TreeItemViewModel
+
+ ///
+ protected override async Task ExecuteDuplicate()
+ {
+ await ProfileEditorService.SaveProfileAsync();
+
+ LayerEntity copy = CoreJson.DeserializeObject(CoreJson.SerializeObject(Layer.LayerEntity, true), true)!;
+ copy.Id = Guid.NewGuid();
+ copy.Name = Layer.Parent.GetNewFolderName(copy.Name + " - copy");
+
+ Layer copied = new(Layer.Profile, Layer.Parent, copy);
+ ProfileEditorService.ExecuteCommand(new AddProfileElement(copied, Layer.Parent, Layer.Order - 1));
+ }
+
+ protected override async Task ExecuteCopy()
+ {
+ await ProfileEditorService.SaveProfileAsync();
+ await Layer.CopyToClipboard();
+ }
+
+ protected override async Task ExecutePaste()
+ {
+ if (Layer.Parent is not Folder parent)
+ return;
+ RenderProfileElement? pasted = await parent.PasteChildFromClipboard();
+ if (pasted == null)
+ return;
+
+ // If the target contains an element with the same name, affix with " - copy"
+ if (parent.Children.Any(c => c.Name == pasted.Name))
+ pasted.Name = parent.GetNewLayerName(pasted.Name + " - copy");
+
+ ProfileEditorService.ExecuteCommand(new AddProfileElement(pasted, parent, Layer.Order - 1));
+ }
+
+ ///
+ public override bool SupportsChildren => false;
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml
index d2499dc9b..6ed6ef893 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeView.axaml
@@ -10,7 +10,7 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileTree.ProfileTreeView"
x:DataType="profileTree:ProfileTreeViewModel">
-
+
@@ -101,7 +101,7 @@
-
+
-
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineView.axaml.cs
index c23f6e109..9b1d11f85 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineView.axaml.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineView.axaml.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
-using Artemis.UI.Shared.Controls;
+using Artemis.UI.Shared;
using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.Extensions;
using Avalonia;
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml
index 999fc6af2..9fdd258eb 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml
@@ -3,11 +3,11 @@
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"
- xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:sharedConverters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:viewModel="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Tree"
xmlns:properties="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Tree.TreeGroupView">
@@ -68,7 +68,7 @@
-
-
-
-
+
-
-
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionRemoveToolView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionRemoveToolView.axaml
index 69dbdc20e..0a3572f7b 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionRemoveToolView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/SelectionRemoveToolView.axaml
@@ -2,21 +2,21 @@
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:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:paz="clr-namespace:Avalonia.Controls.PanAndZoom;assembly=Avalonia.Controls.PanAndZoom"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools.SelectionRemoveToolView"
ClipToBounds="False">
-
-
+
-
-
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml
index 60ef0da43..43fd4a0c0 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml
@@ -4,8 +4,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:paz="clr-namespace:Avalonia.Controls.PanAndZoom;assembly=Avalonia.Controls.PanAndZoom"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
- xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:visualEditor="clr-namespace:Artemis.UI.Screens.ProfileEditor.VisualEditor"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.VisualEditorView"
x:DataType="visualEditor:VisualEditorViewModel">
@@ -53,7 +53,7 @@
-
+
@@ -88,7 +88,11 @@
-
+
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml b/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml
index 1a64ea58b..274a2ed7d 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml
+++ b/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml
@@ -4,8 +4,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:layerBrushes="clr-namespace:Artemis.Core.LayerBrushes;assembly=Artemis.Core"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
- xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:settings="clr-namespace:Artemis.UI.Screens.Settings"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="2400"
x:Class="Artemis.UI.Screens.Settings.GeneralTabView"
x:DataType="settings:GeneralTabViewModel">
@@ -61,7 +61,7 @@
-
+
@@ -76,7 +76,7 @@
-
+
diff --git a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditView.axaml b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditView.axaml
index b79452edb..9ed4ec6dc 100644
--- a/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditView.axaml
+++ b/src/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditView.axaml
@@ -3,12 +3,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
- xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:svg="clr-namespace:Avalonia.Svg.Skia;assembly=Avalonia.Svg.Skia"
xmlns:local="clr-namespace:Artemis.UI.Screens.Sidebar"
xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="850"
x:Class="Artemis.UI.Screens.Sidebar.ProfileConfigurationEditView"
Title="{Binding DisplayName}"
@@ -55,7 +55,7 @@
-
+
@@ -148,7 +148,7 @@
IsChecked="{Binding HotkeyMode, Converter={StaticResource EnumBoolConverter}, ConverterParameter={x:Static core:ProfileConfigurationHotkeyMode.EnableDisable}}" />
-
-
-
+
+
diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml
index 2d75e58eb..530396d61 100644
--- a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml
+++ b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml
@@ -3,7 +3,6 @@
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"
- xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:local="clr-namespace:Artemis.UI.Screens.Sidebar"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Sidebar.SidebarCategoryView">
diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml
index a803394db..86f6e3b05 100644
--- a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml
+++ b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml
@@ -4,8 +4,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
- xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:sidebar="clr-namespace:Artemis.UI.Screens.Sidebar"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Sidebar.SidebarProfileConfigurationView"
x:DataType="sidebar:SidebarProfileConfigurationViewModel">
@@ -68,7 +68,7 @@
-
@@ -20,7 +20,7 @@
-
+
@@ -101,15 +101,15 @@
-
-
+
-
-
+
+
-
-
+
-
-
+
+
diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs b/src/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs
index ed93397ef..ea59272a8 100644
--- a/src/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs
+++ b/src/Artemis.UI/Screens/VisualScripting/NodeScriptView.axaml.cs
@@ -6,7 +6,7 @@ using System.Reactive.Disposables;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Events;
-using Artemis.UI.Shared.Controls;
+using Artemis.UI.Shared;
using Artemis.UI.Shared.Events;
using Avalonia;
using Avalonia.Controls;
@@ -153,7 +153,7 @@ public class NodeScriptView : ReactiveUserControl
private void ZoomBorder_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
- if (!_selectionRectangle.IsSelecting)
+ if (!_selectionRectangle.IsSelecting && e.InitialPressMouseButton == MouseButton.Left)
ViewModel?.ClearNodeSelection();
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/WorkshopView.axaml b/src/Artemis.UI/Screens/Workshop/WorkshopView.axaml
index 6d084802d..ab3ff22da 100644
--- a/src/Artemis.UI/Screens/Workshop/WorkshopView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/WorkshopView.axaml
@@ -3,65 +3,66 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:builders="clr-namespace:Artemis.UI.Shared.Services.Builders;assembly=Artemis.UI.Shared"
- xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:attachedProperties="clr-namespace:Artemis.UI.Shared.AttachedProperties;assembly=Artemis.UI.Shared"
xmlns:workshop="clr-namespace:Artemis.UI.Screens.Workshop"
- xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker;assembly=Artemis.UI.Shared"
- xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.Controls.DataModelPicker;assembly=Artemis.UI.Shared"
- mc:Ignorable="d" d:DesignWidth="800"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
+ xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.GradientPicker;assembly=Artemis.UI.Shared"
+ mc:Ignorable="d" d:DesignWidth="800"
x:Class="Artemis.UI.Screens.Workshop.WorkshopView"
x:DataType="workshop:WorkshopViewModel">
-
-
-
- Nodes tests
-
-
-
-
-
-
- Notification tests
-
-
-
-
+
+
+
+
+ Nodes tests
+
+
+
+
+
+
+ Notification tests
+
+
+
+
-
+
-
+
+
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.VisualScripting/Nodes/Branching/BooleanBranchNode.cs b/src/Artemis.VisualScripting/Nodes/Branching/BooleanBranchNode.cs
new file mode 100644
index 000000000..63e60c2c9
--- /dev/null
+++ b/src/Artemis.VisualScripting/Nodes/Branching/BooleanBranchNode.cs
@@ -0,0 +1,59 @@
+using Artemis.Core;
+using Artemis.Core.Events;
+
+namespace Artemis.VisualScripting.Nodes.Branching;
+
+[Node("Branch", "Forwards one of two values depending on an input boolean", "Branching", InputType = typeof(object), OutputType = typeof(object))]
+public class BooleanBranchNode : Node
+{
+ public BooleanBranchNode() : base("Branch", "Forwards one of two values depending on an input boolean")
+ {
+ BooleanInput = CreateInputPin();
+ TrueInput = CreateInputPin(typeof(object), "True");
+ FalseInput = CreateInputPin(typeof(object), "False");
+
+ Output = CreateOutputPin(typeof(object));
+
+ TrueInput.PinConnected += InputPinConnected;
+ FalseInput.PinConnected += InputPinConnected;
+ TrueInput.PinDisconnected += InputPinDisconnected;
+ FalseInput.PinDisconnected += InputPinDisconnected;
+ }
+
+ private void InputPinConnected(object? sender, SingleValueEventArgs e)
+ {
+ if (TrueInput.ConnectedTo.Any() && !FalseInput.ConnectedTo.Any())
+ ChangeType(TrueInput.ConnectedTo.First().Type);
+ if (FalseInput.ConnectedTo.Any() && !TrueInput.ConnectedTo.Any())
+ ChangeType(FalseInput.ConnectedTo.First().Type);
+ }
+
+ private void InputPinDisconnected(object? sender, SingleValueEventArgs e)
+ {
+ if (!TrueInput.ConnectedTo.Any() && !FalseInput.ConnectedTo.Any())
+ ChangeType(typeof(object));
+ }
+
+ private void ChangeType(Type type)
+ {
+ TrueInput.ChangeType(type);
+ FalseInput.ChangeType(type);
+ Output.ChangeType(type);
+ }
+
+ public InputPin BooleanInput { get; set; }
+ public InputPin TrueInput { get; set; }
+ public InputPin FalseInput { get; set; }
+
+ public OutputPin Output { get; set; }
+
+ #region Overrides of Node
+
+ ///
+ public override void Evaluate()
+ {
+ Output.Value = BooleanInput.Value ? TrueInput.Value : FalseInput.Value;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventNode.cs b/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventNode.cs
index 8c3f1bc45..ad2531da8 100644
--- a/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventNode.cs
+++ b/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventNode.cs
@@ -12,8 +12,9 @@ public class DataModelEventNode : Node _lastTrigger)
- {
- _lastTrigger = dataModelEvent.LastTrigger;
- _currentIndex++;
+ _currentIndex++;
- if (_currentIndex >= CycleValues.Count())
- _currentIndex = 0;
- }
-
- outputValue = CycleValues.ElementAt(_currentIndex).PinValue;
+ if (_currentIndex >= CycleValues.Count())
+ _currentIndex = 0;
}
+ object? outputValue = CycleValues.ElementAt(_currentIndex).PinValue;
if (Output.Type.IsInstanceOfType(outputValue))
Output.Value = outputValue;
else if (Output.Type.IsValueType)
Output.Value = Output.Type.GetDefault()!;
}
+ private bool EvaluateEvent(IDataModelEvent dataModelEvent)
+ {
+ if (dataModelEvent.LastTrigger <= _lastTrigger)
+ return false;
+
+ _lastTrigger = dataModelEvent.LastTrigger;
+ return true;
+ }
+
+ private bool EvaluateValue(object? pathValue)
+ {
+ if (Equals(pathValue, _lastPathValue))
+ return false;
+
+ _lastPathValue = pathValue;
+ return true;
+ }
+
private void CycleValuesOnPinAdded(object? sender, SingleValueEventArgs e)
{
e.Value.PinConnected += OnPinConnected;
@@ -109,7 +127,6 @@ public class DataModelEventNode : Node
@@ -11,6 +11,8 @@
DataModelPath="{CompiledBinding DataModelPath}"
Modules="{CompiledBinding Modules}"
ShowDataModelValues="{CompiledBinding ShowDataModelValues.Value}"
- FilterTypes="{CompiledBinding FilterTypes}"
- VerticalAlignment="Top"/>
+ ShowFullPath="{CompiledBinding ShowFullPaths.Value}"
+ IsEventPicker="True"
+ VerticalAlignment="Top"
+ MaxWidth="300"/>
\ No newline at end of file
diff --git a/src/Artemis.VisualScripting/Nodes/DataModel/Screens/DataModelEventNodeCustomViewModel.cs b/src/Artemis.VisualScripting/Nodes/DataModel/Screens/DataModelEventNodeCustomViewModel.cs
index 59149f30c..dd1334f4d 100644
--- a/src/Artemis.VisualScripting/Nodes/DataModel/Screens/DataModelEventNodeCustomViewModel.cs
+++ b/src/Artemis.VisualScripting/Nodes/DataModel/Screens/DataModelEventNodeCustomViewModel.cs
@@ -50,7 +50,6 @@ public class DataModelEventNodeCustomViewModel : CustomNodeViewModel
public PluginSetting ShowFullPaths { get; }
public PluginSetting ShowDataModelValues { get; }
- public ObservableCollection FilterTypes { get; } = new() {typeof(IDataModelEvent)};
public ObservableCollection? Modules
{
diff --git a/src/Artemis.VisualScripting/Nodes/DataModel/Screens/DataModelNodeCustomView.axaml b/src/Artemis.VisualScripting/Nodes/DataModel/Screens/DataModelNodeCustomView.axaml
index 1d7ae7c0d..376e79962 100644
--- a/src/Artemis.VisualScripting/Nodes/DataModel/Screens/DataModelNodeCustomView.axaml
+++ b/src/Artemis.VisualScripting/Nodes/DataModel/Screens/DataModelNodeCustomView.axaml
@@ -2,8 +2,8 @@
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:screens="clr-namespace:Artemis.VisualScripting.Nodes.DataModel.Screens"
+ xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.DataModelPicker;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.VisualScripting.Nodes.DataModel.Screens.DataModelNodeCustomView"
x:DataType="screens:DataModelNodeCustomViewModel">
@@ -11,5 +11,6 @@
DataModelPath="{CompiledBinding DataModelPath}"
Modules="{CompiledBinding Modules}"
ShowDataModelValues="{CompiledBinding ShowDataModelValues.Value}"
- ShowFullPath="{CompiledBinding ShowFullPaths.Value}"/>
+ ShowFullPath="{CompiledBinding ShowFullPaths.Value}"
+ MaxWidth="300"/>
\ No newline at end of file
diff --git a/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomView.axaml b/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomView.axaml
index 55bc99fd7..cb4208523 100644
--- a/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomView.axaml
+++ b/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomView.axaml
@@ -9,7 +9,7 @@
\ No newline at end of file