mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Event conditions - Fix start-node disconnecting
Input - Performance optimizations
This commit is contained in:
parent
b19854ee47
commit
b779a86d13
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
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.Abstract;
|
||||||
@ -13,12 +14,12 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
|||||||
{
|
{
|
||||||
private readonly string _displayName;
|
private readonly string _displayName;
|
||||||
private readonly EventConditionEntity _entity;
|
private readonly EventConditionEntity _entity;
|
||||||
|
private NodeScript<bool> _script;
|
||||||
|
private DefaultNode _startNode;
|
||||||
private DataModelPath? _eventPath;
|
private DataModelPath? _eventPath;
|
||||||
private DateTime _lastProcessedTrigger;
|
private DateTime _lastProcessedTrigger;
|
||||||
private object? _lastProcessedValue;
|
private object? _lastProcessedValue;
|
||||||
private EventOverlapMode _overlapMode;
|
private EventOverlapMode _overlapMode;
|
||||||
private NodeScript<bool> _script;
|
|
||||||
private IEventConditionNode _startNode;
|
|
||||||
private EventToggleOffMode _toggleOffMode;
|
private EventToggleOffMode _toggleOffMode;
|
||||||
private EventTriggerMode _triggerMode;
|
private EventTriggerMode _triggerMode;
|
||||||
private bool _wasMet;
|
private bool _wasMet;
|
||||||
@ -33,7 +34,7 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
|||||||
_entity = new EventConditionEntity();
|
_entity = new EventConditionEntity();
|
||||||
_displayName = profileElement.GetType().Name;
|
_displayName = profileElement.GetType().Name;
|
||||||
_startNode = new EventConditionEventStartNode {X = -300};
|
_startNode = new EventConditionEventStartNode {X = -300};
|
||||||
_script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
_script = new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile, new List<DefaultNode> {_startNode});
|
||||||
}
|
}
|
||||||
|
|
||||||
internal EventCondition(EventConditionEntity entity, RenderProfileElement profileElement)
|
internal EventCondition(EventConditionEntity entity, RenderProfileElement profileElement)
|
||||||
@ -95,109 +96,6 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
|||||||
set => SetAndNotify(ref _toggleOffMode, value);
|
set => SetAndNotify(ref _toggleOffMode, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the event node, applying the selected event
|
|
||||||
/// </summary>
|
|
||||||
public void UpdateEventNode()
|
|
||||||
{
|
|
||||||
if (EventPath == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Script.Nodes.Contains(_startNode))
|
|
||||||
Script.AddNode(_startNode);
|
|
||||||
Script.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the start node of the event script, if any
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The start node of the event script, if any.</returns>
|
|
||||||
public INode GetStartNode()
|
|
||||||
{
|
|
||||||
return _startNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReplaceStartNode(IEventConditionNode newStartNode)
|
|
||||||
{
|
|
||||||
if (Script.Nodes.Contains(_startNode))
|
|
||||||
Script.RemoveNode(_startNode);
|
|
||||||
|
|
||||||
_startNode = newStartNode;
|
|
||||||
if (!Script.Nodes.Contains(_startNode))
|
|
||||||
Script.AddNode(_startNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool Evaluate()
|
|
||||||
{
|
|
||||||
if (EventPath == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
Script.Run();
|
|
||||||
return Script.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IConditionEntity Entity => _entity;
|
public IConditionEntity Entity => _entity;
|
||||||
|
|
||||||
@ -263,6 +161,116 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
|||||||
Script?.Dispose();
|
Script?.Dispose();
|
||||||
EventPath?.Dispose();
|
EventPath?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the event node, applying the selected event
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateEventNode(bool updateScript)
|
||||||
|
{
|
||||||
|
if (EventPath == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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();
|
||||||
|
if (updateScript)
|
||||||
|
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();
|
||||||
|
if (updateScript)
|
||||||
|
ReplaceStartNode(valueChangedNode);
|
||||||
|
_startNode = valueChangedNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valueChangedNode = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
valueChangedNode.UpdateOutputPins(EventPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Script can be null if called before load
|
||||||
|
if (!updateScript)
|
||||||
|
return;
|
||||||
|
if (!Script.Nodes.Contains(_startNode))
|
||||||
|
{
|
||||||
|
Script.AddNode(_startNode);
|
||||||
|
Script.LoadConnections();
|
||||||
|
}
|
||||||
|
Script.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the start node of the event script, if any
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The start node of the event script, if any.</returns>
|
||||||
|
public INode GetStartNode()
|
||||||
|
{
|
||||||
|
return _startNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReplaceStartNode(DefaultNode newStartNode)
|
||||||
|
{
|
||||||
|
if (Script.Nodes.Contains(_startNode))
|
||||||
|
Script.RemoveNode(_startNode);
|
||||||
|
if (!Script.Nodes.Contains(newStartNode))
|
||||||
|
Script.AddNode(newStartNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Evaluate()
|
||||||
|
{
|
||||||
|
if (EventPath == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
Script.Run();
|
||||||
|
return Script.Result;
|
||||||
|
}
|
||||||
|
|
||||||
#region Storage
|
#region Storage
|
||||||
|
|
||||||
@ -275,11 +283,13 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
|||||||
|
|
||||||
if (_entity.EventPath != null)
|
if (_entity.EventPath != null)
|
||||||
EventPath = new DataModelPath(_entity.EventPath);
|
EventPath = new DataModelPath(_entity.EventPath);
|
||||||
|
UpdateEventNode(false);
|
||||||
|
|
||||||
|
string name = $"Activate {_displayName}";
|
||||||
|
string description = $"Whether or not the event should activate the {_displayName}";
|
||||||
Script = _entity.Script != null
|
Script = _entity.Script != null
|
||||||
? new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile)
|
? new NodeScript<bool>(name, description, _entity.Script, ProfileElement.Profile, new List<DefaultNode> {_startNode})
|
||||||
: new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
: new NodeScript<bool>(name, description, ProfileElement.Profile, new List<DefaultNode> {_startNode});
|
||||||
UpdateEventNode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -310,71 +320,9 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void LoadNodeScript()
|
public void LoadNodeScript()
|
||||||
{
|
{
|
||||||
|
UpdateEventNode(true);
|
||||||
Script.Load();
|
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 == EventConditionEventStartNode.NodeId || n.Id == EventConditionValueChangedStartNode.NodeId);
|
|
||||||
if (existingEventNode != null)
|
|
||||||
_startNode = (IEventConditionNode) existingEventNode;
|
|
||||||
|
|
||||||
UpdateEventNode();
|
|
||||||
Script.LoadConnections();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a mode for render elements to start their timeline when display conditions events are fired.
|
|
||||||
/// </summary>
|
|
||||||
public enum EventTriggerMode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Play the timeline once.
|
|
||||||
/// </summary>
|
|
||||||
Play,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Toggle repeating the timeline.
|
|
||||||
/// </summary>
|
|
||||||
Toggle
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a mode for render elements to configure the behaviour of events that overlap i.e. trigger again before
|
|
||||||
/// the timeline finishes.
|
|
||||||
/// </summary>
|
|
||||||
public enum EventOverlapMode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stop the current run and restart the timeline
|
|
||||||
/// </summary>
|
|
||||||
Restart,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Play another copy of the timeline on top of the current run
|
|
||||||
/// </summary>
|
|
||||||
Copy,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ignore subsequent event fires until the timeline finishes
|
|
||||||
/// </summary>
|
|
||||||
Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a mode for render elements when toggling off the event when using <see cref="EventTriggerMode.Toggle" />
|
|
||||||
/// .
|
|
||||||
/// </summary>
|
|
||||||
public enum EventToggleOffMode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// When the event toggles the condition off, finish the the current run of the main timeline
|
|
||||||
/// </summary>
|
|
||||||
Finish,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When the event toggles the condition off, skip to the end segment of the timeline
|
|
||||||
/// </summary>
|
|
||||||
SkipToEnd
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
namespace Artemis.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a mode for render elements to configure the behaviour of events that overlap i.e. trigger again before
|
||||||
|
/// the timeline finishes.
|
||||||
|
/// </summary>
|
||||||
|
public enum EventOverlapMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stop the current run and restart the timeline
|
||||||
|
/// </summary>
|
||||||
|
Restart,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Play another copy of the timeline on top of the current run
|
||||||
|
/// </summary>
|
||||||
|
Copy,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ignore subsequent event fires until the timeline finishes
|
||||||
|
/// </summary>
|
||||||
|
Ignore
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
namespace Artemis.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a mode for render elements when toggling off the event when using <see cref="EventTriggerMode.Toggle" />
|
||||||
|
/// .
|
||||||
|
/// </summary>
|
||||||
|
public enum EventToggleOffMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// When the event toggles the condition off, finish the the current run of the main timeline
|
||||||
|
/// </summary>
|
||||||
|
Finish,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the event toggles the condition off, skip to the end segment of the timeline
|
||||||
|
/// </summary>
|
||||||
|
SkipToEnd
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
namespace Artemis.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a mode for render elements to start their timeline when display conditions events are fired.
|
||||||
|
/// </summary>
|
||||||
|
public enum EventTriggerMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Play the timeline once.
|
||||||
|
/// </summary>
|
||||||
|
Play,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggle repeating the timeline.
|
||||||
|
/// </summary>
|
||||||
|
Toggle
|
||||||
|
}
|
||||||
@ -8,6 +8,13 @@ namespace Artemis.Core.Services;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class InputProvider : IDisposable
|
public abstract class InputProvider : IDisposable
|
||||||
{
|
{
|
||||||
|
public InputProvider()
|
||||||
|
{
|
||||||
|
ProviderName = GetType().FullName ?? throw new InvalidOperationException("Input provider must have a type with a name");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string ProviderName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the input service requests a <see cref="KeyboardToggleStatusReceived" /> event
|
/// Called when the input service requests a <see cref="KeyboardToggleStatusReceived" /> event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -10,12 +10,18 @@ internal class InputService : IInputService
|
|||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IRgbService _rgbService;
|
||||||
|
private ArtemisDevice? _firstKeyboard;
|
||||||
|
private ArtemisDevice? _firstMouse;
|
||||||
|
private int _keyboardCount;
|
||||||
|
private int _mouseCount;
|
||||||
|
|
||||||
public InputService(ILogger logger, IRgbService rgbService)
|
public InputService(ILogger logger, IRgbService rgbService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_rgbService = rgbService;
|
_rgbService = rgbService;
|
||||||
|
|
||||||
|
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
|
||||||
|
_rgbService.DeviceRemoved += RgbServiceOnDevicesModified;
|
||||||
BustIdentifierCache();
|
BustIdentifierCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,8 +138,6 @@ internal class InputService : IInputService
|
|||||||
|
|
||||||
private readonly Dictionary<Tuple<InputProvider, object>, ArtemisDevice> _deviceCache = new();
|
private readonly Dictionary<Tuple<InputProvider, object>, ArtemisDevice> _deviceCache = new();
|
||||||
private List<ArtemisDevice> _devices = new();
|
private List<ArtemisDevice> _devices = new();
|
||||||
private ArtemisDevice? _cachedFallbackKeyboard;
|
|
||||||
private ArtemisDevice? _cachedFallbackMouse;
|
|
||||||
private ArtemisDevice? _identifyingDevice;
|
private ArtemisDevice? _identifyingDevice;
|
||||||
|
|
||||||
public void IdentifyDevice(ArtemisDevice device)
|
public void IdentifyDevice(ArtemisDevice device)
|
||||||
@ -164,13 +168,29 @@ internal class InputService : IInputService
|
|||||||
if (provider == null) throw new ArgumentNullException(nameof(provider));
|
if (provider == null) throw new ArgumentNullException(nameof(provider));
|
||||||
if (identifier == null) throw new ArgumentNullException(nameof(identifier));
|
if (identifier == null) throw new ArgumentNullException(nameof(identifier));
|
||||||
|
|
||||||
|
// We will almost always only have zero or one of each
|
||||||
|
if (type == InputDeviceType.Keyboard)
|
||||||
|
{
|
||||||
|
if (_keyboardCount == 0)
|
||||||
|
return null;
|
||||||
|
if (_keyboardCount == 1)
|
||||||
|
return _firstKeyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == InputDeviceType.Mouse)
|
||||||
|
{
|
||||||
|
if (_mouseCount == 0)
|
||||||
|
return null;
|
||||||
|
if (_mouseCount == 1)
|
||||||
|
return _firstMouse;
|
||||||
|
}
|
||||||
|
|
||||||
// Try cache first
|
// Try cache first
|
||||||
ArtemisDevice? cacheMatch = GetDeviceFromCache(provider, identifier);
|
ArtemisDevice? cacheMatch = GetDeviceFromCache(provider, identifier);
|
||||||
if (cacheMatch != null)
|
if (cacheMatch != null)
|
||||||
return cacheMatch;
|
return cacheMatch;
|
||||||
|
|
||||||
string providerName = provider.GetType().FullName!;
|
ArtemisDevice? match = _devices.FirstOrDefault(m => m.InputIdentifiers.Any(i => Equals(i.InputProvider, provider.ProviderName) && Equals(i.Identifier, identifier)));
|
||||||
ArtemisDevice? match = _devices.FirstOrDefault(m => m.InputIdentifiers.Any(i => Equals(i.InputProvider, providerName) && Equals(i.Identifier, identifier)));
|
|
||||||
|
|
||||||
// If a match was found cache it to speed up the next event and return the match
|
// If a match was found cache it to speed up the next event and return the match
|
||||||
if (match != null)
|
if (match != null)
|
||||||
@ -179,34 +199,16 @@ internal class InputService : IInputService
|
|||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no match, apply our fallback type
|
|
||||||
if (type == InputDeviceType.None)
|
|
||||||
return null;
|
|
||||||
if (type == InputDeviceType.Keyboard)
|
if (type == InputDeviceType.Keyboard)
|
||||||
{
|
return _firstKeyboard;
|
||||||
if (_cachedFallbackKeyboard != null)
|
|
||||||
return _cachedFallbackKeyboard;
|
|
||||||
_cachedFallbackKeyboard = _rgbService.EnabledDevices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard);
|
|
||||||
return _cachedFallbackKeyboard;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == InputDeviceType.Mouse)
|
if (type == InputDeviceType.Mouse)
|
||||||
{
|
return _firstMouse;
|
||||||
if (_cachedFallbackMouse != null)
|
|
||||||
return _cachedFallbackMouse;
|
|
||||||
_cachedFallbackMouse = _rgbService.EnabledDevices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Mouse);
|
|
||||||
return _cachedFallbackMouse;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BustIdentifierCache()
|
public void BustIdentifierCache()
|
||||||
{
|
{
|
||||||
_deviceCache.Clear();
|
_deviceCache.Clear();
|
||||||
_cachedFallbackKeyboard = null;
|
|
||||||
_cachedFallbackMouse = null;
|
|
||||||
|
|
||||||
_devices = _rgbService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
|
_devices = _rgbService.EnabledDevices.Where(d => d.InputIdentifiers.Any()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,12 +222,7 @@ internal class InputService : IInputService
|
|||||||
_deviceCache.TryGetValue(new Tuple<InputProvider, object>(provider, identifier), out ArtemisDevice? device);
|
_deviceCache.TryGetValue(new Tuple<InputProvider, object>(provider, identifier), out ArtemisDevice? device);
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SurfaceConfigurationChanged(object? sender, SurfaceConfigurationEventArgs e)
|
|
||||||
{
|
|
||||||
BustIdentifierCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InputProviderOnIdentifierReceived(object? sender, InputProviderIdentifierEventArgs e)
|
private void InputProviderOnIdentifierReceived(object? sender, InputProviderIdentifierEventArgs e)
|
||||||
{
|
{
|
||||||
// Don't match if there is no device or if the device type differs from the event device type
|
// Don't match if there is no device or if the device type differs from the event device type
|
||||||
@ -236,16 +233,24 @@ internal class InputService : IInputService
|
|||||||
if (!(sender is InputProvider inputProvider))
|
if (!(sender is InputProvider inputProvider))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string providerName = inputProvider.GetType().FullName!;
|
|
||||||
|
|
||||||
// Remove existing identification
|
// Remove existing identification
|
||||||
_identifyingDevice.InputIdentifiers.RemoveAll(i => i.InputProvider == providerName);
|
_identifyingDevice.InputIdentifiers.RemoveAll(i => i.InputProvider == inputProvider.ProviderName);
|
||||||
_identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(providerName, e.Identifier));
|
_identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(inputProvider.ProviderName, e.Identifier));
|
||||||
|
|
||||||
StopIdentify();
|
StopIdentify();
|
||||||
OnDeviceIdentified();
|
OnDeviceIdentified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RgbServiceOnDevicesModified(object? sender, DeviceEventArgs args)
|
||||||
|
{
|
||||||
|
_firstKeyboard = _rgbService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||||
|
_firstMouse = _rgbService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||||
|
_keyboardCount = _rgbService.Devices.Count(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||||
|
_mouseCount = _rgbService.Devices.Count(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||||
|
|
||||||
|
BustIdentifierCache();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Keyboard
|
#region Keyboard
|
||||||
@ -379,6 +384,7 @@ internal class InputService : IInputService
|
|||||||
|
|
||||||
private readonly HashSet<MouseButton> _pressedButtons = new();
|
private readonly HashSet<MouseButton> _pressedButtons = new();
|
||||||
|
|
||||||
|
|
||||||
private void InputProviderOnMouseButtonDataReceived(object? sender, InputProviderMouseButtonEventArgs e)
|
private void InputProviderOnMouseButtonDataReceived(object? sender, InputProviderMouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
bool foundLedId = InputKeyUtilities.MouseButtonLedIdMap.TryGetValue(e.Button, out LedId ledId);
|
bool foundLedId = InputKeyUtilities.MouseButtonLedIdMap.TryGetValue(e.Button, out LedId ledId);
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Artemis.Core.Internal;
|
namespace Artemis.Core.Internal;
|
||||||
|
|
||||||
internal class EventConditionEventStartNode : DefaultNode, IEventConditionNode
|
internal class EventConditionEventStartNode : DefaultNode
|
||||||
{
|
{
|
||||||
internal static readonly Guid NodeId = new("278735FE-69E9-4A73-A6B8-59E83EE19305");
|
internal static readonly Guid NodeId = new("278735FE-69E9-4A73-A6B8-59E83EE19305");
|
||||||
private readonly ObjectOutputPins _objectOutputPins;
|
private readonly ObjectOutputPins _objectOutputPins;
|
||||||
@ -13,6 +13,11 @@ internal class EventConditionEventStartNode : DefaultNode, IEventConditionNode
|
|||||||
_objectOutputPins = new ObjectOutputPins(this);
|
_objectOutputPins = new ObjectOutputPins(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetDataModelEvent(IDataModelEvent? dataModelEvent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void CreatePins(IDataModelEvent? dataModelEvent)
|
public void CreatePins(IDataModelEvent? dataModelEvent)
|
||||||
{
|
{
|
||||||
if (_dataModelEvent == dataModelEvent)
|
if (_dataModelEvent == dataModelEvent)
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Artemis.Core.Internal;
|
namespace Artemis.Core.Internal;
|
||||||
|
|
||||||
internal class EventConditionValueChangedStartNode : DefaultNode, IEventConditionNode
|
internal class EventConditionValueChangedStartNode : DefaultNode
|
||||||
{
|
{
|
||||||
internal static readonly Guid NodeId = new("F9A270DB-A231-4800-BAB3-DC1F96856756");
|
internal static readonly Guid NodeId = new("F9A270DB-A231-4800-BAB3-DC1F96856756");
|
||||||
private object? _newValue;
|
private object? _newValue;
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
namespace Artemis.Core.Internal;
|
|
||||||
|
|
||||||
internal interface IEventConditionNode : INode
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@ -339,7 +339,7 @@ public abstract class Node : BreakableModel, INode
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the node was loaded from storage or newly created
|
/// Called when the node was loaded from storage or newly created, at this point pin connections aren't reestablished yet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="script">The script the node is contained in</param>
|
/// <param name="script">The script the node is contained in</param>
|
||||||
public virtual void Initialize(INodeScript script)
|
public virtual void Initialize(INodeScript script)
|
||||||
|
|||||||
@ -80,7 +80,8 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
|||||||
/// The context of the node script, usually a <see cref="Profile" /> or
|
/// The context of the node script, usually a <see cref="Profile" /> or
|
||||||
/// <see cref="ProfileConfiguration" />
|
/// <see cref="ProfileConfiguration" />
|
||||||
/// </param>
|
/// </param>
|
||||||
protected NodeScript(string name, string description, object? context = null)
|
/// <param name="defaultNodes">A list of default nodes to add to the node script.</param>
|
||||||
|
protected NodeScript(string name, string description, object? context = null, List<DefaultNode>? defaultNodes = null)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Description = description;
|
Description = description;
|
||||||
@ -90,9 +91,15 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
|||||||
|
|
||||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeAdded;
|
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeAdded;
|
||||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeRemoved;
|
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeRemoved;
|
||||||
|
|
||||||
|
if (defaultNodes != null)
|
||||||
|
{
|
||||||
|
foreach (DefaultNode defaultNode in defaultNodes)
|
||||||
|
AddNode(defaultNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal NodeScript(string name, string description, NodeScriptEntity entity, object? context = null)
|
internal NodeScript(string name, string description, NodeScriptEntity entity, object? context = null, List<DefaultNode>? defaultNodes = null)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Description = description;
|
Description = description;
|
||||||
@ -102,6 +109,12 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
|||||||
|
|
||||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeAdded;
|
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeAdded;
|
||||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeRemoved;
|
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeRemoved;
|
||||||
|
|
||||||
|
if (defaultNodes != null)
|
||||||
|
{
|
||||||
|
foreach (DefaultNode defaultNode in defaultNodes)
|
||||||
|
AddNode(defaultNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -414,8 +427,8 @@ public class NodeScript<T> : NodeScript, INodeScript<T>
|
|||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public NodeScript(string name, string description, NodeScriptEntity entity, object? context = null)
|
public NodeScript(string name, string description, NodeScriptEntity entity, object? context = null, List<DefaultNode>? defaultNodes = null)
|
||||||
: base(name, description, entity, context)
|
: base(name, description, entity, context, defaultNodes)
|
||||||
{
|
{
|
||||||
ExitNode = new ExitNode<T>(name, description);
|
ExitNode = new ExitNode<T>(name, description);
|
||||||
AddNode(ExitNode);
|
AddNode(ExitNode);
|
||||||
@ -424,8 +437,8 @@ public class NodeScript<T> : NodeScript, INodeScript<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public NodeScript(string name, string description, object? context = null)
|
public NodeScript(string name, string description, object? context = null, List<DefaultNode>? defaultNodes = null)
|
||||||
: base(name, description, context)
|
: base(name, description, context, defaultNodes)
|
||||||
{
|
{
|
||||||
ExitNode = new ExitNode<T>(name, description);
|
ExitNode = new ExitNode<T>(name, description);
|
||||||
AddNode(ExitNode);
|
AddNode(ExitNode);
|
||||||
|
|||||||
@ -49,7 +49,7 @@ public class UpdateEventConditionPath : IProfileEditorCommand, IDisposable
|
|||||||
|
|
||||||
// Change the end node
|
// Change the end node
|
||||||
_eventCondition.EventPath = _value;
|
_eventCondition.EventPath = _value;
|
||||||
_eventCondition.UpdateEventNode();
|
_eventCondition.UpdateEventNode(true);
|
||||||
|
|
||||||
_executed = true;
|
_executed = true;
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ public class UpdateEventConditionPath : IProfileEditorCommand, IDisposable
|
|||||||
{
|
{
|
||||||
// Change the end node
|
// Change the end node
|
||||||
_eventCondition.EventPath = _oldValue;
|
_eventCondition.EventPath = _oldValue;
|
||||||
_eventCondition.UpdateEventNode();
|
_eventCondition.UpdateEventNode(true);
|
||||||
|
|
||||||
// Restore old connections
|
// Restore old connections
|
||||||
_store?.Restore();
|
_store?.Restore();
|
||||||
|
|||||||
@ -19,7 +19,6 @@ public class WindowsInputProvider : InputProvider
|
|||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly SpongeWindow _sponge;
|
private readonly SpongeWindow _sponge;
|
||||||
private readonly Timer _taskManagerTimer;
|
private readonly Timer _taskManagerTimer;
|
||||||
private DateTime _lastMouseUpdate;
|
|
||||||
private int _lastProcessId;
|
private int _lastProcessId;
|
||||||
|
|
||||||
public WindowsInputProvider(ILogger logger, IInputService inputService)
|
public WindowsInputProvider(ILogger logger, IInputService inputService)
|
||||||
@ -160,8 +159,8 @@ public class WindowsInputProvider : InputProvider
|
|||||||
|
|
||||||
#region Mouse
|
#region Mouse
|
||||||
|
|
||||||
private int _mouseDeltaX;
|
private int _previousMouseX;
|
||||||
private int _mouseDeltaY;
|
private int _previousMouseY;
|
||||||
|
|
||||||
private void HandleMouseData(RawInputData data, RawInputMouseData mouseData)
|
private void HandleMouseData(RawInputData data, RawInputMouseData mouseData)
|
||||||
{
|
{
|
||||||
@ -169,10 +168,8 @@ public class WindowsInputProvider : InputProvider
|
|||||||
// This can create a small inaccuracy of course, but Artemis is not a shooter :')
|
// This can create a small inaccuracy of course, but Artemis is not a shooter :')
|
||||||
if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None)
|
if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None)
|
||||||
{
|
{
|
||||||
_mouseDeltaX += mouseData.Mouse.LastX;
|
_previousMouseX += mouseData.Mouse.LastX;
|
||||||
_mouseDeltaY += mouseData.Mouse.LastY;
|
_previousMouseY += mouseData.Mouse.LastY;
|
||||||
if (DateTime.Now - _lastMouseUpdate < TimeSpan.FromMilliseconds(40))
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArtemisDevice? device = null;
|
ArtemisDevice? device = null;
|
||||||
@ -193,10 +190,7 @@ public class WindowsInputProvider : InputProvider
|
|||||||
if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None)
|
if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None)
|
||||||
{
|
{
|
||||||
Win32Point cursorPosition = GetCursorPosition();
|
Win32Point cursorPosition = GetCursorPosition();
|
||||||
OnMouseMoveDataReceived(device, cursorPosition.X, cursorPosition.Y, _mouseDeltaX, _mouseDeltaY);
|
OnMouseMoveDataReceived(device, cursorPosition.X, cursorPosition.Y, cursorPosition.X - _previousMouseX, cursorPosition.Y - _previousMouseY);
|
||||||
_mouseDeltaX = 0;
|
|
||||||
_mouseDeltaY = 0;
|
|
||||||
_lastMouseUpdate = DateTime.Now;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,10 +9,11 @@ namespace Artemis.VisualScripting.Nodes.List;
|
|||||||
public class ListOperatorPredicateNode : Node<ListOperatorEntity, ListOperatorPredicateNodeCustomViewModel>, IDisposable
|
public class ListOperatorPredicateNode : Node<ListOperatorEntity, ListOperatorPredicateNodeCustomViewModel>, IDisposable
|
||||||
{
|
{
|
||||||
private readonly object _scriptLock = new();
|
private readonly object _scriptLock = new();
|
||||||
private ListOperatorPredicateStartNode? _startNode;
|
private ListOperatorPredicateStartNode _startNode;
|
||||||
|
|
||||||
public ListOperatorPredicateNode() : base("List Operator (Advanced)", "Checks if any/all/no values in the input list match a condition")
|
public ListOperatorPredicateNode() : base("List Operator (Advanced)", "Checks if any/all/no values in the input list match a condition")
|
||||||
{
|
{
|
||||||
|
_startNode = new ListOperatorPredicateStartNode {X = -200};
|
||||||
|
|
||||||
InputList = CreateInputPin<IList>();
|
InputList = CreateInputPin<IList>();
|
||||||
Output = CreateOutputPin<bool>();
|
Output = CreateOutputPin<bool>();
|
||||||
@ -31,21 +32,8 @@ public class ListOperatorPredicateNode : Node<ListOperatorEntity, ListOperatorPr
|
|||||||
lock (_scriptLock)
|
lock (_scriptLock)
|
||||||
{
|
{
|
||||||
Script = Storage?.Script != null
|
Script = Storage?.Script != null
|
||||||
? new NodeScript<bool>("Is match", "Determines whether the current list item is a match", Storage.Script, script.Context)
|
? new NodeScript<bool>("Is match", "Determines whether the current list item is a match", Storage.Script, script.Context, new List<DefaultNode> {_startNode})
|
||||||
: new NodeScript<bool>("Is match", "Determines whether the current list item is a match", script.Context);
|
: new NodeScript<bool>("Is match", "Determines whether the current list item is a match", script.Context, new List<DefaultNode> {_startNode});
|
||||||
|
|
||||||
// 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 == ListOperatorPredicateStartNode.NodeId);
|
|
||||||
if (existingEventNode != null)
|
|
||||||
_startNode = (ListOperatorPredicateStartNode) existingEventNode;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_startNode = new ListOperatorPredicateStartNode {X = -200};
|
|
||||||
Script.AddNode(_startNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateStartNode();
|
|
||||||
Script.LoadConnections();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +89,7 @@ public class ListOperatorPredicateNode : Node<ListOperatorEntity, ListOperatorPr
|
|||||||
lock (_scriptLock)
|
lock (_scriptLock)
|
||||||
{
|
{
|
||||||
UpdateStartNode();
|
UpdateStartNode();
|
||||||
|
Script?.LoadConnections();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Events;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
using Artemis.UI.Shared.Services.NodeEditor;
|
||||||
using Artemis.UI.Shared.VisualScripting;
|
using Artemis.UI.Shared.VisualScripting;
|
||||||
@ -12,17 +14,37 @@ public class ListOperatorPredicateNodeCustomViewModel : CustomNodeViewModel
|
|||||||
private readonly ListOperatorPredicateNode _node;
|
private readonly ListOperatorPredicateNode _node;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private ListOperator _operator;
|
private ListOperator _operator;
|
||||||
|
private bool _canOpenEditor;
|
||||||
|
|
||||||
public ListOperatorPredicateNodeCustomViewModel(ListOperatorPredicateNode node, INodeScript script, IWindowService windowService) : base(node, script)
|
public ListOperatorPredicateNodeCustomViewModel(ListOperatorPredicateNode node, INodeScript script, IWindowService windowService) : base(node, script)
|
||||||
{
|
{
|
||||||
_node = node;
|
_node = node;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
|
|
||||||
OpenEditor = ReactiveCommand.CreateFromTask(ExecuteOpenEditor);
|
OpenEditor = ReactiveCommand.CreateFromTask(ExecuteOpenEditor, this.WhenAnyValue(vm => vm.CanOpenEditor));
|
||||||
|
CanOpenEditor = node.InputList.ConnectedTo.Any();
|
||||||
|
|
||||||
|
this.WhenActivated(d =>
|
||||||
|
{
|
||||||
|
node.InputList.PinConnected += InputListOnPinConnected;
|
||||||
|
node.InputList.PinDisconnected += InputListOnPinDisconnected;
|
||||||
|
|
||||||
|
Disposable.Create(() =>
|
||||||
|
{
|
||||||
|
node.InputList.PinConnected -= InputListOnPinConnected;
|
||||||
|
node.InputList.PinDisconnected -= InputListOnPinDisconnected;
|
||||||
|
}).DisposeWith(d);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> OpenEditor { get; }
|
public ReactiveCommand<Unit, Unit> OpenEditor { get; }
|
||||||
|
|
||||||
|
private bool CanOpenEditor
|
||||||
|
{
|
||||||
|
get => _canOpenEditor;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _canOpenEditor, value);
|
||||||
|
}
|
||||||
|
|
||||||
public ListOperator Operator
|
public ListOperator Operator
|
||||||
{
|
{
|
||||||
get => _operator;
|
get => _operator;
|
||||||
@ -40,4 +62,14 @@ public class ListOperatorPredicateNodeCustomViewModel : CustomNodeViewModel
|
|||||||
_node.Storage ??= new ListOperatorEntity();
|
_node.Storage ??= new ListOperatorEntity();
|
||||||
_node.Storage.Script = _node.Script.Entity;
|
_node.Storage.Script = _node.Script.Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InputListOnPinDisconnected(object? sender, SingleValueEventArgs<IPin> e)
|
||||||
|
{
|
||||||
|
CanOpenEditor = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InputListOnPinConnected(object? sender, SingleValueEventArgs<IPin> e)
|
||||||
|
{
|
||||||
|
CanOpenEditor = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user