diff --git a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs index cc342e12c..f198b5397 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs @@ -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 _script; + private DefaultNode _startNode; private DataModelPath? _eventPath; private DateTime _lastProcessedTrigger; private object? _lastProcessedValue; private EventOverlapMode _overlapMode; - private NodeScript _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($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile); + _script = new NodeScript($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile, new List {_startNode}); } internal EventCondition(EventConditionEntity entity, RenderProfileElement profileElement) @@ -95,109 +96,6 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition set => SetAndNotify(ref _toggleOffMode, value); } - /// - /// Updates the event node, applying the selected event - /// - 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(); - } - - /// - /// Gets the start node of the event script, if any - /// - /// The start node of the event script, if any. - 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; - } - /// public IConditionEntity Entity => _entity; @@ -263,6 +161,116 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition Script?.Dispose(); EventPath?.Dispose(); } + + /// + /// Updates the event node, applying the selected event + /// + 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(); + } + + /// + /// Gets the start node of the event script, if any + /// + /// The start node of the event script, if any. + 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($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile) - : new NodeScript($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile); - UpdateEventNode(); + ? new NodeScript(name, description, _entity.Script, ProfileElement.Profile, new List {_startNode}) + : new NodeScript(name, description, ProfileElement.Profile, new List {_startNode}); } /// @@ -310,71 +320,9 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition /// 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 -} - -/// -/// Represents a mode for render elements to start their timeline when display conditions events are fired. -/// -public enum EventTriggerMode -{ - /// - /// Play the timeline once. - /// - Play, - - /// - /// Toggle repeating the timeline. - /// - Toggle -} - -/// -/// Represents a mode for render elements to configure the behaviour of events that overlap i.e. trigger again before -/// the timeline finishes. -/// -public enum EventOverlapMode -{ - /// - /// Stop the current run and restart the timeline - /// - Restart, - - /// - /// Play another copy of the timeline on top of the current run - /// - Copy, - - /// - /// 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/Conditions/EventOverlapMode.cs b/src/Artemis.Core/Models/Profile/Conditions/EventOverlapMode.cs new file mode 100644 index 000000000..56068a797 --- /dev/null +++ b/src/Artemis.Core/Models/Profile/Conditions/EventOverlapMode.cs @@ -0,0 +1,23 @@ +namespace Artemis.Core; + +/// +/// Represents a mode for render elements to configure the behaviour of events that overlap i.e. trigger again before +/// the timeline finishes. +/// +public enum EventOverlapMode +{ + /// + /// Stop the current run and restart the timeline + /// + Restart, + + /// + /// Play another copy of the timeline on top of the current run + /// + Copy, + + /// + /// Ignore subsequent event fires until the timeline finishes + /// + Ignore +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/EventToggleOffMode.cs b/src/Artemis.Core/Models/Profile/Conditions/EventToggleOffMode.cs new file mode 100644 index 000000000..c8db3aa6a --- /dev/null +++ b/src/Artemis.Core/Models/Profile/Conditions/EventToggleOffMode.cs @@ -0,0 +1,18 @@ +namespace Artemis.Core; + +/// +/// 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/Conditions/EventTriggerMode.cs b/src/Artemis.Core/Models/Profile/Conditions/EventTriggerMode.cs new file mode 100644 index 000000000..cfe31816d --- /dev/null +++ b/src/Artemis.Core/Models/Profile/Conditions/EventTriggerMode.cs @@ -0,0 +1,17 @@ +namespace Artemis.Core; + +/// +/// Represents a mode for render elements to start their timeline when display conditions events are fired. +/// +public enum EventTriggerMode +{ + /// + /// Play the timeline once. + /// + Play, + + /// + /// Toggle repeating the timeline. + /// + Toggle +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Input/InputProvider.cs b/src/Artemis.Core/Services/Input/InputProvider.cs index f04b1a1f9..c51a25d63 100644 --- a/src/Artemis.Core/Services/Input/InputProvider.cs +++ b/src/Artemis.Core/Services/Input/InputProvider.cs @@ -8,6 +8,13 @@ namespace Artemis.Core.Services; /// 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; } + /// /// Called when the input service requests a event /// diff --git a/src/Artemis.Core/Services/Input/InputService.cs b/src/Artemis.Core/Services/Input/InputService.cs index 187f0db4b..48ae607f6 100644 --- a/src/Artemis.Core/Services/Input/InputService.cs +++ b/src/Artemis.Core/Services/Input/InputService.cs @@ -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, ArtemisDevice> _deviceCache = new(); private List _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(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 _pressedButtons = new(); + private void InputProviderOnMouseButtonDataReceived(object? sender, InputProviderMouseButtonEventArgs e) { bool foundLedId = InputKeyUtilities.MouseButtonLedIdMap.TryGetValue(e.Button, out LedId ledId); diff --git a/src/Artemis.Core/VisualScripting/Internal/EventConditionEventStartNode.cs b/src/Artemis.Core/VisualScripting/Internal/EventConditionEventStartNode.cs index 7ceadc837..f417e8871 100644 --- a/src/Artemis.Core/VisualScripting/Internal/EventConditionEventStartNode.cs +++ b/src/Artemis.Core/VisualScripting/Internal/EventConditionEventStartNode.cs @@ -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) diff --git a/src/Artemis.Core/VisualScripting/Internal/EventConditionValueChangedStartNode.cs b/src/Artemis.Core/VisualScripting/Internal/EventConditionValueChangedStartNode.cs index 2b98acbf6..c334681bc 100644 --- a/src/Artemis.Core/VisualScripting/Internal/EventConditionValueChangedStartNode.cs +++ b/src/Artemis.Core/VisualScripting/Internal/EventConditionValueChangedStartNode.cs @@ -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; diff --git a/src/Artemis.Core/VisualScripting/Internal/IEventConditionNode.cs b/src/Artemis.Core/VisualScripting/Internal/IEventConditionNode.cs deleted file mode 100644 index 87d45f50a..000000000 --- a/src/Artemis.Core/VisualScripting/Internal/IEventConditionNode.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Artemis.Core.Internal; - -internal interface IEventConditionNode : INode -{ -} \ No newline at end of file diff --git a/src/Artemis.Core/VisualScripting/Node.cs b/src/Artemis.Core/VisualScripting/Node.cs index 70e02fa12..f212524de 100644 --- a/src/Artemis.Core/VisualScripting/Node.cs +++ b/src/Artemis.Core/VisualScripting/Node.cs @@ -339,7 +339,7 @@ public abstract class Node : BreakableModel, INode } /// - /// 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. /// /// The script the node is contained in public virtual void Initialize(INodeScript script) diff --git a/src/Artemis.Core/VisualScripting/NodeScript.cs b/src/Artemis.Core/VisualScripting/NodeScript.cs index e98dafcf3..9bfb08011 100644 --- a/src/Artemis.Core/VisualScripting/NodeScript.cs +++ b/src/Artemis.Core/VisualScripting/NodeScript.cs @@ -80,7 +80,8 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript /// The context of the node script, usually a or /// /// - protected NodeScript(string name, string description, object? context = null) + /// A list of default nodes to add to the node script. + protected NodeScript(string name, string description, object? context = null, List? 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? 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 : NodeScript, INodeScript #region Constructors /// - 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? defaultNodes = null) + : base(name, description, entity, context, defaultNodes) { ExitNode = new ExitNode(name, description); AddNode(ExitNode); @@ -424,8 +437,8 @@ public class NodeScript : NodeScript, INodeScript } /// - public NodeScript(string name, string description, object? context = null) - : base(name, description, context) + public NodeScript(string name, string description, object? context = null, List? defaultNodes = null) + : base(name, description, context, defaultNodes) { ExitNode = new ExitNode(name, description); AddNode(ExitNode); diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventConditionPath.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventConditionPath.cs index d424de4ae..d103772cd 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventConditionPath.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateEventConditionPath.cs @@ -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(); diff --git a/src/Artemis.UI.Windows/Providers/Input/WindowsInputProvider.cs b/src/Artemis.UI.Windows/Providers/Input/WindowsInputProvider.cs index d834c3002..1091fca54 100644 --- a/src/Artemis.UI.Windows/Providers/Input/WindowsInputProvider.cs +++ b/src/Artemis.UI.Windows/Providers/Input/WindowsInputProvider.cs @@ -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; } diff --git a/src/Artemis.VisualScripting/Nodes/List/ListOperatorPredicateNode.cs b/src/Artemis.VisualScripting/Nodes/List/ListOperatorPredicateNode.cs index 571eedc83..e6d811e88 100644 --- a/src/Artemis.VisualScripting/Nodes/List/ListOperatorPredicateNode.cs +++ b/src/Artemis.VisualScripting/Nodes/List/ListOperatorPredicateNode.cs @@ -9,10 +9,11 @@ namespace Artemis.VisualScripting.Nodes.List; public class ListOperatorPredicateNode : Node, 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(); Output = CreateOutputPin(); @@ -31,21 +32,8 @@ public class ListOperatorPredicateNode : Node("Is match", "Determines whether the current list item is a match", Storage.Script, script.Context) - : new NodeScript("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("Is match", "Determines whether the current list item is a match", Storage.Script, script.Context, new List {_startNode}) + : new NodeScript("Is match", "Determines whether the current list item is a match", script.Context, new List {_startNode}); } } @@ -101,6 +89,7 @@ public class ListOperatorPredicateNode : Node 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 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 e) + { + CanOpenEditor = false; + } + + private void InputListOnPinConnected(object? sender, SingleValueEventArgs e) + { + CanOpenEditor = true; + } } \ No newline at end of file