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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Internal;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
@ -13,12 +14,12 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
{
|
||||
private readonly string _displayName;
|
||||
private readonly EventConditionEntity _entity;
|
||||
private NodeScript<bool> _script;
|
||||
private DefaultNode _startNode;
|
||||
private DataModelPath? _eventPath;
|
||||
private DateTime _lastProcessedTrigger;
|
||||
private object? _lastProcessedValue;
|
||||
private EventOverlapMode _overlapMode;
|
||||
private NodeScript<bool> _script;
|
||||
private IEventConditionNode _startNode;
|
||||
private EventToggleOffMode _toggleOffMode;
|
||||
private EventTriggerMode _triggerMode;
|
||||
private bool _wasMet;
|
||||
@ -33,7 +34,7 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
_entity = new EventConditionEntity();
|
||||
_displayName = profileElement.GetType().Name;
|
||||
_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)
|
||||
@ -95,109 +96,6 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
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 />
|
||||
public IConditionEntity Entity => _entity;
|
||||
|
||||
@ -263,6 +161,116 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
Script?.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
|
||||
|
||||
@ -275,11 +283,13 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
|
||||
if (_entity.EventPath != null)
|
||||
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
|
||||
? new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile)
|
||||
: new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
||||
UpdateEventNode();
|
||||
? new NodeScript<bool>(name, description, _entity.Script, ProfileElement.Profile, new List<DefaultNode> {_startNode})
|
||||
: new NodeScript<bool>(name, description, ProfileElement.Profile, new List<DefaultNode> {_startNode});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -310,71 +320,9 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
/// <inheritdoc />
|
||||
public void LoadNodeScript()
|
||||
{
|
||||
UpdateEventNode(true);
|
||||
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
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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>
|
||||
/// Called when the input service requests a <see cref="KeyboardToggleStatusReceived" /> event
|
||||
/// </summary>
|
||||
|
||||
@ -10,12 +10,18 @@ internal class InputService : IInputService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IRgbService _rgbService;
|
||||
private ArtemisDevice? _firstKeyboard;
|
||||
private ArtemisDevice? _firstMouse;
|
||||
private int _keyboardCount;
|
||||
private int _mouseCount;
|
||||
|
||||
public InputService(ILogger logger, IRgbService rgbService)
|
||||
{
|
||||
_logger = logger;
|
||||
_rgbService = rgbService;
|
||||
|
||||
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
|
||||
_rgbService.DeviceRemoved += RgbServiceOnDevicesModified;
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
@ -132,8 +138,6 @@ internal class InputService : IInputService
|
||||
|
||||
private readonly Dictionary<Tuple<InputProvider, object>, ArtemisDevice> _deviceCache = new();
|
||||
private List<ArtemisDevice> _devices = new();
|
||||
private ArtemisDevice? _cachedFallbackKeyboard;
|
||||
private ArtemisDevice? _cachedFallbackMouse;
|
||||
private ArtemisDevice? _identifyingDevice;
|
||||
|
||||
public void IdentifyDevice(ArtemisDevice device)
|
||||
@ -164,13 +168,29 @@ internal class InputService : IInputService
|
||||
if (provider == null) throw new ArgumentNullException(nameof(provider));
|
||||
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
|
||||
ArtemisDevice? cacheMatch = GetDeviceFromCache(provider, identifier);
|
||||
if (cacheMatch != null)
|
||||
return cacheMatch;
|
||||
|
||||
string providerName = provider.GetType().FullName!;
|
||||
ArtemisDevice? match = _devices.FirstOrDefault(m => m.InputIdentifiers.Any(i => Equals(i.InputProvider, providerName) && Equals(i.Identifier, identifier)));
|
||||
ArtemisDevice? match = _devices.FirstOrDefault(m => m.InputIdentifiers.Any(i => Equals(i.InputProvider, provider.ProviderName) && Equals(i.Identifier, identifier)));
|
||||
|
||||
// If a match was found cache it to speed up the next event and return the match
|
||||
if (match != null)
|
||||
@ -179,34 +199,16 @@ internal class InputService : IInputService
|
||||
return match;
|
||||
}
|
||||
|
||||
// If there is no match, apply our fallback type
|
||||
if (type == InputDeviceType.None)
|
||||
return null;
|
||||
if (type == InputDeviceType.Keyboard)
|
||||
{
|
||||
if (_cachedFallbackKeyboard != null)
|
||||
return _cachedFallbackKeyboard;
|
||||
_cachedFallbackKeyboard = _rgbService.EnabledDevices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard);
|
||||
return _cachedFallbackKeyboard;
|
||||
}
|
||||
|
||||
return _firstKeyboard;
|
||||
if (type == InputDeviceType.Mouse)
|
||||
{
|
||||
if (_cachedFallbackMouse != null)
|
||||
return _cachedFallbackMouse;
|
||||
_cachedFallbackMouse = _rgbService.EnabledDevices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Mouse);
|
||||
return _cachedFallbackMouse;
|
||||
}
|
||||
|
||||
return _firstMouse;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void BustIdentifierCache()
|
||||
{
|
||||
_deviceCache.Clear();
|
||||
_cachedFallbackKeyboard = null;
|
||||
_cachedFallbackMouse = null;
|
||||
|
||||
_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);
|
||||
return device;
|
||||
}
|
||||
|
||||
private void SurfaceConfigurationChanged(object? sender, SurfaceConfigurationEventArgs e)
|
||||
{
|
||||
BustIdentifierCache();
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@ -236,16 +233,24 @@ internal class InputService : IInputService
|
||||
if (!(sender is InputProvider inputProvider))
|
||||
return;
|
||||
|
||||
string providerName = inputProvider.GetType().FullName!;
|
||||
|
||||
// Remove existing identification
|
||||
_identifyingDevice.InputIdentifiers.RemoveAll(i => i.InputProvider == providerName);
|
||||
_identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(providerName, e.Identifier));
|
||||
_identifyingDevice.InputIdentifiers.RemoveAll(i => i.InputProvider == inputProvider.ProviderName);
|
||||
_identifyingDevice.InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(inputProvider.ProviderName, e.Identifier));
|
||||
|
||||
StopIdentify();
|
||||
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
|
||||
|
||||
#region Keyboard
|
||||
@ -379,6 +384,7 @@ internal class InputService : IInputService
|
||||
|
||||
private readonly HashSet<MouseButton> _pressedButtons = new();
|
||||
|
||||
|
||||
private void InputProviderOnMouseButtonDataReceived(object? sender, InputProviderMouseButtonEventArgs e)
|
||||
{
|
||||
bool foundLedId = InputKeyUtilities.MouseButtonLedIdMap.TryGetValue(e.Button, out LedId ledId);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Artemis.Core.Internal;
|
||||
|
||||
internal class EventConditionEventStartNode : DefaultNode, IEventConditionNode
|
||||
internal class EventConditionEventStartNode : DefaultNode
|
||||
{
|
||||
internal static readonly Guid NodeId = new("278735FE-69E9-4A73-A6B8-59E83EE19305");
|
||||
private readonly ObjectOutputPins _objectOutputPins;
|
||||
@ -13,6 +13,11 @@ internal class EventConditionEventStartNode : DefaultNode, IEventConditionNode
|
||||
_objectOutputPins = new ObjectOutputPins(this);
|
||||
}
|
||||
|
||||
public void SetDataModelEvent(IDataModelEvent? dataModelEvent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void CreatePins(IDataModelEvent? dataModelEvent)
|
||||
{
|
||||
if (_dataModelEvent == dataModelEvent)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Artemis.Core.Internal;
|
||||
|
||||
internal class EventConditionValueChangedStartNode : DefaultNode, IEventConditionNode
|
||||
internal class EventConditionValueChangedStartNode : DefaultNode
|
||||
{
|
||||
internal static readonly Guid NodeId = new("F9A270DB-A231-4800-BAB3-DC1F96856756");
|
||||
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>
|
||||
/// 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>
|
||||
/// <param name="script">The script the node is contained in</param>
|
||||
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
|
||||
/// <see cref="ProfileConfiguration" />
|
||||
/// </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;
|
||||
Description = description;
|
||||
@ -90,9 +91,15 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeAdded;
|
||||
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;
|
||||
Description = description;
|
||||
@ -102,6 +109,12 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeAdded;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeRemoved;
|
||||
|
||||
if (defaultNodes != null)
|
||||
{
|
||||
foreach (DefaultNode defaultNode in defaultNodes)
|
||||
AddNode(defaultNode);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -414,8 +427,8 @@ public class NodeScript<T> : NodeScript, INodeScript<T>
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
public NodeScript(string name, string description, NodeScriptEntity entity, object? context = null)
|
||||
: base(name, description, entity, context)
|
||||
public NodeScript(string name, string description, NodeScriptEntity entity, object? context = null, List<DefaultNode>? defaultNodes = null)
|
||||
: base(name, description, entity, context, defaultNodes)
|
||||
{
|
||||
ExitNode = new ExitNode<T>(name, description);
|
||||
AddNode(ExitNode);
|
||||
@ -424,8 +437,8 @@ public class NodeScript<T> : NodeScript, INodeScript<T>
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NodeScript(string name, string description, object? context = null)
|
||||
: base(name, description, context)
|
||||
public NodeScript(string name, string description, object? context = null, List<DefaultNode>? defaultNodes = null)
|
||||
: base(name, description, context, defaultNodes)
|
||||
{
|
||||
ExitNode = new ExitNode<T>(name, description);
|
||||
AddNode(ExitNode);
|
||||
|
||||
@ -49,7 +49,7 @@ public class UpdateEventConditionPath : IProfileEditorCommand, IDisposable
|
||||
|
||||
// Change the end node
|
||||
_eventCondition.EventPath = _value;
|
||||
_eventCondition.UpdateEventNode();
|
||||
_eventCondition.UpdateEventNode(true);
|
||||
|
||||
_executed = true;
|
||||
}
|
||||
@ -59,7 +59,7 @@ public class UpdateEventConditionPath : IProfileEditorCommand, IDisposable
|
||||
{
|
||||
// Change the end node
|
||||
_eventCondition.EventPath = _oldValue;
|
||||
_eventCondition.UpdateEventNode();
|
||||
_eventCondition.UpdateEventNode(true);
|
||||
|
||||
// Restore old connections
|
||||
_store?.Restore();
|
||||
|
||||
@ -19,7 +19,6 @@ public class WindowsInputProvider : InputProvider
|
||||
private readonly ILogger _logger;
|
||||
private readonly SpongeWindow _sponge;
|
||||
private readonly Timer _taskManagerTimer;
|
||||
private DateTime _lastMouseUpdate;
|
||||
private int _lastProcessId;
|
||||
|
||||
public WindowsInputProvider(ILogger logger, IInputService inputService)
|
||||
@ -160,8 +159,8 @@ public class WindowsInputProvider : InputProvider
|
||||
|
||||
#region Mouse
|
||||
|
||||
private int _mouseDeltaX;
|
||||
private int _mouseDeltaY;
|
||||
private int _previousMouseX;
|
||||
private int _previousMouseY;
|
||||
|
||||
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 :')
|
||||
if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None)
|
||||
{
|
||||
_mouseDeltaX += mouseData.Mouse.LastX;
|
||||
_mouseDeltaY += mouseData.Mouse.LastY;
|
||||
if (DateTime.Now - _lastMouseUpdate < TimeSpan.FromMilliseconds(40))
|
||||
return;
|
||||
_previousMouseX += mouseData.Mouse.LastX;
|
||||
_previousMouseY += mouseData.Mouse.LastY;
|
||||
}
|
||||
|
||||
ArtemisDevice? device = null;
|
||||
@ -193,10 +190,7 @@ public class WindowsInputProvider : InputProvider
|
||||
if (mouseData.Mouse.Buttons == RawMouseButtonFlags.None)
|
||||
{
|
||||
Win32Point cursorPosition = GetCursorPosition();
|
||||
OnMouseMoveDataReceived(device, cursorPosition.X, cursorPosition.Y, _mouseDeltaX, _mouseDeltaY);
|
||||
_mouseDeltaX = 0;
|
||||
_mouseDeltaY = 0;
|
||||
_lastMouseUpdate = DateTime.Now;
|
||||
OnMouseMoveDataReceived(device, cursorPosition.X, cursorPosition.Y, cursorPosition.X - _previousMouseX, cursorPosition.Y - _previousMouseY);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -9,10 +9,11 @@ namespace Artemis.VisualScripting.Nodes.List;
|
||||
public class ListOperatorPredicateNode : Node<ListOperatorEntity, ListOperatorPredicateNodeCustomViewModel>, IDisposable
|
||||
{
|
||||
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")
|
||||
{
|
||||
_startNode = new ListOperatorPredicateStartNode {X = -200};
|
||||
|
||||
InputList = CreateInputPin<IList>();
|
||||
Output = CreateOutputPin<bool>();
|
||||
@ -31,21 +32,8 @@ public class ListOperatorPredicateNode : Node<ListOperatorEntity, ListOperatorPr
|
||||
lock (_scriptLock)
|
||||
{
|
||||
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", script.Context);
|
||||
|
||||
// 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();
|
||||
? 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 List<DefaultNode> {_startNode});
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,6 +89,7 @@ public class ListOperatorPredicateNode : Node<ListOperatorEntity, ListOperatorPr
|
||||
lock (_scriptLock)
|
||||
{
|
||||
UpdateStartNode();
|
||||
Script?.LoadConnections();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Events;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.NodeEditor;
|
||||
using Artemis.UI.Shared.VisualScripting;
|
||||
@ -12,17 +14,37 @@ public class ListOperatorPredicateNodeCustomViewModel : CustomNodeViewModel
|
||||
private readonly ListOperatorPredicateNode _node;
|
||||
private readonly IWindowService _windowService;
|
||||
private ListOperator _operator;
|
||||
private bool _canOpenEditor;
|
||||
|
||||
public ListOperatorPredicateNodeCustomViewModel(ListOperatorPredicateNode node, INodeScript script, IWindowService windowService) : base(node, script)
|
||||
{
|
||||
_node = node;
|
||||
_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; }
|
||||
|
||||
private bool CanOpenEditor
|
||||
{
|
||||
get => _canOpenEditor;
|
||||
set => this.RaiseAndSetIfChanged(ref _canOpenEditor, value);
|
||||
}
|
||||
|
||||
public ListOperator Operator
|
||||
{
|
||||
get => _operator;
|
||||
@ -40,4 +62,14 @@ public class ListOperatorPredicateNodeCustomViewModel : CustomNodeViewModel
|
||||
_node.Storage ??= new ListOperatorEntity();
|
||||
_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