diff --git a/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs b/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs index e832df0b9..e5da142a7 100644 --- a/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs +++ b/src/Artemis.Core/Services/ProcessMonitor/ProcessMonitorService.cs @@ -11,17 +11,15 @@ namespace Artemis.Core.Services; internal class ProcessMonitorService : IProcessMonitorService { private readonly ProcessComparer _comparer; - private readonly ILogger _logger; private Process[] _lastScannedProcesses; - public ProcessMonitorService(ILogger logger) + public ProcessMonitorService() { - _logger = logger; + _comparer = new ProcessComparer(); _lastScannedProcesses = Process.GetProcesses(); Timer processScanTimer = new(1000); processScanTimer.Elapsed += OnTimerElapsed; processScanTimer.Start(); - _comparer = new ProcessComparer(); ProcessActivationRequirement.ProcessMonitorService = this; } @@ -30,16 +28,9 @@ internal class ProcessMonitorService : IProcessMonitorService { Process[] newProcesses = Process.GetProcesses(); foreach (Process startedProcess in newProcesses.Except(_lastScannedProcesses, _comparer)) - { ProcessStarted?.Invoke(this, new ProcessEventArgs(startedProcess)); - //_logger.Verbose("Started Process: {startedProcess}", startedProcess.ProcessName); - } - foreach (Process stoppedProcess in _lastScannedProcesses.Except(newProcesses, _comparer)) - { ProcessStopped?.Invoke(this, new ProcessEventArgs(stoppedProcess)); - //_logger.Verbose("Stopped Process: {stoppedProcess}", stoppedProcess.ProcessName); - } _lastScannedProcesses = newProcesses; } diff --git a/src/Artemis.Core/VisualScripting/Interfaces/INodeScript.cs b/src/Artemis.Core/VisualScripting/Interfaces/INodeScript.cs index c45fbdb96..456692a4d 100644 --- a/src/Artemis.Core/VisualScripting/Interfaces/INodeScript.cs +++ b/src/Artemis.Core/VisualScripting/Interfaces/INodeScript.cs @@ -8,7 +8,7 @@ namespace Artemis.Core; /// /// Represents a node script /// -public interface INodeScript : INotifyPropertyChanged, IDisposable +public interface INodeScript : INotifyPropertyChanged, IDisposable, IStorageModel { /// /// Gets the name of the node script. @@ -66,6 +66,11 @@ public interface INodeScript : INotifyPropertyChanged, IDisposable /// /// The node to remove void RemoveNode(INode node); + + /// + /// Loads missing connections between the nodes of this node script from storage + /// + void LoadConnections(); } /// diff --git a/src/Artemis.Core/VisualScripting/NodeScript.cs b/src/Artemis.Core/VisualScripting/NodeScript.cs index ce6a7af00..8abc29fbd 100644 --- a/src/Artemis.Core/VisualScripting/NodeScript.cs +++ b/src/Artemis.Core/VisualScripting/NodeScript.cs @@ -12,7 +12,7 @@ namespace Artemis.Core; /// /// Represents a node script /// -public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageModel +public abstract class NodeScript : CorePropertyChanged, INodeScript { private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e) { @@ -226,9 +226,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageMod return node; } - /// - /// Loads missing connections between the nodes of this node script from the - /// + /// public void LoadConnections() { List nodes = Nodes.ToList(); diff --git a/src/Artemis.UI.Linux/Providers/Input/LinuxInputProvider.cs b/src/Artemis.UI.Linux/Providers/Input/LinuxInputProvider.cs index c903a1d84..26c5c36ed 100644 --- a/src/Artemis.UI.Linux/Providers/Input/LinuxInputProvider.cs +++ b/src/Artemis.UI.Linux/Providers/Input/LinuxInputProvider.cs @@ -33,12 +33,14 @@ public class LinuxInputProvider : InputProvider protected override void Dispose(bool disposing) { if (disposing) + { for (int i = _readers.Count - 1; i >= 0; i--) { _readers[i].InputEvent -= OnInputEvent; _readers[i].Dispose(); _readers.RemoveAt(i); } + } base.Dispose(disposing); } @@ -49,6 +51,7 @@ public class LinuxInputProvider : InputProvider { if (sender is not LinuxInputDeviceReader reader) return; + switch (reader.InputDevice.DeviceType) { case LinuxDeviceType.Keyboard: @@ -64,40 +67,34 @@ public class LinuxInputProvider : InputProvider private void HandleKeyboardData(LinuxInputDevice keyboard, LinuxInputEventArgs args) { - switch (args.Type) + if (args.Type != LinuxInputEventType.KEY) + return; + + KeyboardKey key = InputUtilities.KeyFromKeyCode((LinuxKeyboardKeyCodes) args.Code); + bool isDown = args.Value != 0; + + //_logger.Verbose($"Keyboard Key: {(LinuxKeyboardKeyCodes)args.Code} | Down: {isDown}"); + + LinuxInputDevice.LinuxInputId identifier = keyboard.InputId; + OnIdentifierReceived(identifier, InputDeviceType.Keyboard); + ArtemisDevice? device = null; + + try { - case LinuxInputEventType.KEY: - KeyboardKey key = InputUtilities.KeyFromKeyCode((LinuxKeyboardKeyCodes) args.Code); - bool isDown = args.Value != 0; - - //_logger.Verbose($"Keyboard Key: {(LinuxKeyboardKeyCodes)args.Code} | Down: {isDown}"); - - LinuxInputDevice.LinuxInputId identifier = keyboard.InputId; - - OnIdentifierReceived(identifier, InputDeviceType.Keyboard); - - ArtemisDevice? device = null; - - try - { - device = _inputService.GetDeviceByIdentifier(this, identifier, InputDeviceType.Keyboard); - } - catch (Exception e) - { - _logger.Warning(e, "Failed to retrieve input device by its identifier"); - } - - OnKeyboardDataReceived(device, key, isDown); - break; + device = _inputService.GetDeviceByIdentifier(this, identifier, InputDeviceType.Keyboard); } + catch (Exception e) + { + _logger.Warning(e, "Failed to retrieve input device by its identifier"); + } + + OnKeyboardDataReceived(device, key, isDown); } private void HandleMouseData(LinuxInputDevice mouse, LinuxInputEventArgs args) { LinuxInputDevice.LinuxInputId identifier = mouse.InputId; - OnIdentifierReceived(identifier, InputDeviceType.Mouse); - ArtemisDevice? device = null; try @@ -112,13 +109,11 @@ public class LinuxInputProvider : InputProvider switch (args.Type) { case LinuxInputEventType.KEY: - var key = (LinuxKeyboardKeyCodes)args.Code; - if (key == LinuxKeyboardKeyCodes.BTN_TOUCH || + LinuxKeyboardKeyCodes key = (LinuxKeyboardKeyCodes) args.Code; + if (key == LinuxKeyboardKeyCodes.BTN_TOUCH || (key >= LinuxKeyboardKeyCodes.BTN_TOOL_PEN && key <= LinuxKeyboardKeyCodes.BTN_TOOL_QUADTAP)) - { //trackpad input, ignore. return; - } //0 - up //1 - down diff --git a/src/Artemis.UI.Shared/Services/NodeEditor/Commands/ConnectPins.cs b/src/Artemis.UI.Shared/Services/NodeEditor/Commands/ConnectPins.cs index c7b01be05..0b65a56e1 100644 --- a/src/Artemis.UI.Shared/Services/NodeEditor/Commands/ConnectPins.cs +++ b/src/Artemis.UI.Shared/Services/NodeEditor/Commands/ConnectPins.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Artemis.Core; namespace Artemis.UI.Shared.Services.NodeEditor.Commands; @@ -8,9 +9,9 @@ namespace Artemis.UI.Shared.Services.NodeEditor.Commands; /// public class ConnectPins : INodeEditorCommand { - private readonly List? _originalConnections; - private readonly IPin _source; - private readonly IPin _target; + private readonly IPin _output; + private readonly IPin _input; + private readonly IPin? _originalConnection; /// /// Creates a new instance of the class. @@ -19,10 +20,18 @@ public class ConnectPins : INodeEditorCommand /// The target of the connection. public ConnectPins(IPin source, IPin target) { - _source = source; - _target = target; + if (source.Direction == PinDirection.Output) + { + _output = source; + _input = target; + } + else + { + _output = target; + _input = source; + } - _originalConnections = _target.Direction == PinDirection.Input ? new List(_target.ConnectedTo) : null; + _originalConnection = _input.ConnectedTo.FirstOrDefault(); } #region Implementation of INodeEditorCommand @@ -33,20 +42,16 @@ public class ConnectPins : INodeEditorCommand /// public void Execute() { - if (_target.Direction == PinDirection.Input) - _target.DisconnectAll(); - _source.ConnectTo(_target); + _input.DisconnectAll(); + _output.ConnectTo(_input); } /// public void Undo() { - _target.DisconnectFrom(_source); - - if (_originalConnections == null) - return; - foreach (IPin pin in _originalConnections) - _target.ConnectTo(pin); + _input.DisconnectAll(); + if (_originalConnection != null) + _input.ConnectTo(_originalConnection); } #endregion diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs index 9000af7e3..eb1dd81e5 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs @@ -25,6 +25,7 @@ public class DataBindingViewModel : ActivatableViewModelBase private ObservableAsPropertyHelper? _layerProperty; private ObservableAsPropertyHelper? _nodeScriptViewModel; private bool _playing; + private bool _editorOpen; public DataBindingViewModel(IProfileEditorService profileEditorService, INodeVmFactory nodeVmFactory, IWindowService windowService, ISettingsService settingsService) { @@ -49,11 +50,18 @@ public class DataBindingViewModel : ActivatableViewModelBase .DisposeWith(d); _profileEditorService.Playing.CombineLatest(_profileEditorService.SuspendedEditing).Subscribe(tuple => _playing = tuple.First || tuple.Second).DisposeWith(d); - DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(60.0 / 1000), DispatcherPriority.Render, Update); + DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Render, Update); + // TODO: Remove in favor of saving each time a node editor command is executed + DispatcherTimer saveTimer = new(TimeSpan.FromMinutes(2), DispatcherPriority.Normal, Save); + updateTimer.Start(); + saveTimer.Start(); + Disposable.Create(() => { updateTimer.Stop(); + saveTimer.Stop(); + _profileEditorService.SaveProfile(); }).DisposeWith(d); }); @@ -74,11 +82,19 @@ public class DataBindingViewModel : ActivatableViewModelBase public async Task OpenEditor() { - if (LayerProperty != null && LayerProperty.BaseDataBinding.IsEnabled) + if (LayerProperty == null || !LayerProperty.BaseDataBinding.IsEnabled) + return; + + try { + _editorOpen = true; await _windowService.ShowDialogAsync(("nodeScript", LayerProperty.BaseDataBinding.Script)); await _profileEditorService.SaveProfileAsync(); } + finally + { + _editorOpen = false; + } } private void Update(object? sender, EventArgs e) @@ -89,4 +105,10 @@ public class DataBindingViewModel : ActivatableViewModelBase LayerProperty?.UpdateDataBinding(); } + + private void Save(object? sender, EventArgs e) + { + if (!_editorOpen) + _profileEditorService.SaveProfile(); + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs b/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs index c71578605..09dc7d049 100644 --- a/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs +++ b/src/Artemis.UI/Screens/VisualScripting/NodeScriptWindowViewModel.cs @@ -12,6 +12,7 @@ using Artemis.UI.Shared; using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services.NodeEditor; using Artemis.UI.Shared.Services.NodeEditor.Commands; +using Artemis.UI.Shared.Services.ProfileEditor; using Avalonia; using Avalonia.Threading; using DynamicData; @@ -25,6 +26,7 @@ public class NodeScriptWindowViewModel : DialogViewModelBase private readonly INodeEditorService _nodeEditorService; private readonly INodeService _nodeService; private readonly ISettingsService _settingsService; + private readonly IProfileService _profileService; private readonly IWindowService _windowService; public NodeScriptWindowViewModel(NodeScript nodeScript, @@ -32,6 +34,7 @@ public class NodeScriptWindowViewModel : DialogViewModelBase INodeEditorService nodeEditorService, INodeVmFactory vmFactory, ISettingsService settingsService, + IProfileService profileService, IWindowService windowService) { NodeScript = nodeScript; @@ -43,6 +46,7 @@ public class NodeScriptWindowViewModel : DialogViewModelBase _nodeService = nodeService; _nodeEditorService = nodeEditorService; _settingsService = settingsService; + _profileService = profileService; _windowService = windowService; SourceList nodeSourceList = new(); @@ -60,8 +64,17 @@ public class NodeScriptWindowViewModel : DialogViewModelBase this.WhenActivated(d => { DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Normal, Update); + // TODO: Remove in favor of saving each time a node editor command is executed + DispatcherTimer saveTimer = new(TimeSpan.FromMinutes(2), DispatcherPriority.Normal, Save); + updateTimer.Start(); - Disposable.Create(() => updateTimer.Stop()).DisposeWith(d); + saveTimer.Start(); + + Disposable.Create(() => + { + updateTimer.Stop(); + saveTimer.Stop(); + }).DisposeWith(d); }); } @@ -133,4 +146,10 @@ public class NodeScriptWindowViewModel : DialogViewModelBase { NodeScript.Run(); } + + private void Save(object? sender, EventArgs e) + { + if (NodeScript.Context is Profile profile) + _profileService.SaveProfile(profile, true); + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs b/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs index 5631a38be..bb3a1670d 100644 --- a/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs +++ b/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.ObjectModel; +using System.Linq; using System.Reactive; using System.Reactive.Linq; using Artemis.Core; @@ -41,44 +42,23 @@ public class NodeViewModel : ActivatableViewModelBase DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v)); - SourceList nodePins = new(); - SourceList nodePinCollections = new(); + SourceList nodePins = new(); + SourceList nodePinCollections = new(); // Create observable collections split up by direction - nodePins.Connect() - .Filter(n => n.Direction == PinDirection.Input) - .Transform(p => (PinViewModel) nodeVmFactory.InputPinViewModel(p, nodeScriptViewModel)) - .Bind(out ReadOnlyObservableCollection inputPins) - .Subscribe(); - nodePins.Connect() - .Filter(n => n.Direction == PinDirection.Output) - .Transform(p => (PinViewModel) nodeVmFactory.OutputPinViewModel(p, nodeScriptViewModel)) - .Bind(out ReadOnlyObservableCollection outputPins) - .Subscribe(); + nodePins.Connect().Filter(n => n.Pin.Direction == PinDirection.Input).Bind(out ReadOnlyObservableCollection inputPins).Subscribe(); + nodePins.Connect().Filter(n => n.Pin.Direction == PinDirection.Output).Bind(out ReadOnlyObservableCollection outputPins).Subscribe(); InputPinViewModels = inputPins; OutputPinViewModels = outputPins; // Same again but for pin collections - nodePinCollections.Connect() - .Filter(n => n.Direction == PinDirection.Input) - .Transform(c => (PinCollectionViewModel) nodeVmFactory.InputPinCollectionViewModel(c, nodeScriptViewModel)) - .Bind(out ReadOnlyObservableCollection inputPinCollections) - .Subscribe(); - nodePinCollections.Connect() - .Filter(n => n.Direction == PinDirection.Output) - .Transform(c => (PinCollectionViewModel) nodeVmFactory.OutputPinCollectionViewModel(c, nodeScriptViewModel)) - .Bind(out ReadOnlyObservableCollection outputPinCollections) - .Subscribe(); + nodePinCollections.Connect().Filter(n => n.PinCollection.Direction == PinDirection.Input).Bind(out ReadOnlyObservableCollection inputPinCollections).Subscribe(); + nodePinCollections.Connect().Filter(n => n.PinCollection.Direction == PinDirection.Output).Bind(out ReadOnlyObservableCollection outputPinCollections).Subscribe(); InputPinCollectionViewModels = inputPinCollections; OutputPinCollectionViewModels = outputPinCollections; // Create a single observable collection containing all pin view models - InputPinViewModels.ToObservableChangeSet() - .Merge(OutputPinViewModels.ToObservableChangeSet()) - .Merge(InputPinCollectionViewModels.ToObservableChangeSet().TransformMany(c => c.PinViewModels)) - .Merge(OutputPinCollectionViewModels.ToObservableChangeSet().TransformMany(c => c.PinViewModels)) - .Bind(out ReadOnlyObservableCollection pins) - .Subscribe(); + nodePins.Connect().Merge(nodePinCollections.Connect().TransformMany(c => c.PinViewModels)).Bind(out ReadOnlyObservableCollection pins).Subscribe(); PinViewModels = pins; @@ -101,30 +81,54 @@ public class NodeViewModel : ActivatableViewModelBase // Subscribe to pin changes Observable.FromEventPattern>(x => Node.PinAdded += x, x => Node.PinAdded -= x) - .Subscribe(p => nodePins.Add(p.EventArgs.Value)) + .Subscribe(p => + { + if (p.EventArgs.Value.Direction == PinDirection.Input) + nodePins.Add(nodeVmFactory.InputPinViewModel(p.EventArgs.Value, nodeScriptViewModel)); + else + nodePins.Add(nodeVmFactory.OutputPinViewModel(p.EventArgs.Value, nodeScriptViewModel)); + }) .DisposeWith(d); Observable.FromEventPattern>(x => Node.PinRemoved += x, x => Node.PinRemoved -= x) - .Subscribe(p => nodePins.Remove(p.EventArgs.Value)) + .Subscribe(p => nodePins!.Remove(nodePins.Items.FirstOrDefault(vm => vm.Pin == p.EventArgs.Value))) .DisposeWith(d); nodePins.Edit(l => { l.Clear(); - l.AddRange(Node.Pins); + foreach (IPin nodePin in Node.Pins) + { + if (nodePin.Direction == PinDirection.Input) + l.Add(nodeVmFactory.InputPinViewModel(nodePin, nodeScriptViewModel)); + else + l.Add(nodeVmFactory.OutputPinViewModel(nodePin, nodeScriptViewModel)); + } }); - + // Subscribe to pin collection changes Observable.FromEventPattern>(x => Node.PinCollectionAdded += x, x => Node.PinCollectionAdded -= x) - .Subscribe(p => nodePinCollections.Add(p.EventArgs.Value)) + .Subscribe(p => + { + if (p.EventArgs.Value.Direction == PinDirection.Input) + nodeVmFactory.InputPinCollectionViewModel(p.EventArgs.Value, nodeScriptViewModel); + else + nodeVmFactory.OutputPinCollectionViewModel(p.EventArgs.Value, nodeScriptViewModel); + }) .DisposeWith(d); Observable.FromEventPattern>(x => Node.PinCollectionRemoved += x, x => Node.PinCollectionRemoved -= x) - .Subscribe(p => nodePinCollections.Remove(p.EventArgs.Value)) + .Subscribe(p => nodePinCollections!.Remove(nodePinCollections.Items.FirstOrDefault(vm => vm.PinCollection == p.EventArgs.Value))) .DisposeWith(d); nodePinCollections.Edit(l => { l.Clear(); - l.AddRange(Node.PinCollections); + foreach (IPinCollection nodePinCollection in Node.PinCollections) + { + if (nodePinCollection.Direction == PinDirection.Input) + l.Add(nodeVmFactory.InputPinCollectionViewModel(nodePinCollection, nodeScriptViewModel)); + else + l.Add(nodeVmFactory.OutputPinCollectionViewModel(nodePinCollection, nodeScriptViewModel)); + } }); - + if (Node is Node coreNode) CustomNodeViewModel = coreNode.GetCustomViewModel(nodeScriptViewModel.NodeScript); }); diff --git a/src/Artemis.VisualScripting/Nodes/Branching/EnumSwitchNode.cs b/src/Artemis.VisualScripting/Nodes/Branching/EnumSwitchNode.cs new file mode 100644 index 000000000..f94911661 --- /dev/null +++ b/src/Artemis.VisualScripting/Nodes/Branching/EnumSwitchNode.cs @@ -0,0 +1,97 @@ +using Artemis.Core; +using Artemis.Core.Events; +using Humanizer; + +namespace Artemis.VisualScripting.Nodes.Branching; + +[Node("Switch (Enum)", "Outputs the input that corresponds to the switch value", "Operators", InputType = typeof(Enum), OutputType = typeof(object))] +public class EnumSwitchNode : Node +{ + private readonly Dictionary _inputPins; + + public EnumSwitchNode() : base("Enum Branch", "desc") + { + _inputPins = new Dictionary(); + + Output = CreateOutputPin(typeof(object), "Result"); + SwitchValue = CreateInputPin("Switch"); + + SwitchValue.PinConnected += OnSwitchPinConnected; + SwitchValue.PinDisconnected += OnSwitchPinDisconnected; + } + + public OutputPin Output { get; } + public InputPin SwitchValue { get; } + + public override void Evaluate() + { + if (SwitchValue.Value is null) + { + Output.Value = null; + return; + } + + if (!_inputPins.TryGetValue(SwitchValue.Value, out InputPin? pin)) + { + Output.Value = null; + return; + } + + if (pin.ConnectedTo.Count == 0) + { + Output.Value = null; + return; + } + + Output.Value = pin.Value; + } + + private void OnInputPinDisconnected(object? sender, SingleValueEventArgs e) + { + // if this is the last pin to disconnect, reset the type. + if (_inputPins.Values.All(i => i.ConnectedTo.Count == 0)) + ChangeType(typeof(object)); + } + + private void OnInputPinConnected(object? sender, SingleValueEventArgs e) + { + // change the type of our inputs and output + // depending on the first node the user connects to + ChangeType(e.Value.Type); + } + + private void OnSwitchPinConnected(object? sender, SingleValueEventArgs e) + { + if (SwitchValue.ConnectedTo.Count == 0) + return; + + Type enumType = SwitchValue.ConnectedTo[0].Type; + foreach (Enum enumValue in Enum.GetValues(enumType).Cast()) + { + InputPin pin = CreateOrAddInputPin(typeof(object), enumValue.ToString().Humanize(LetterCasing.Sentence)); + pin.PinConnected += OnInputPinConnected; + pin.PinDisconnected += OnInputPinDisconnected; + _inputPins[enumValue] = pin; + } + } + + private void OnSwitchPinDisconnected(object? sender, SingleValueEventArgs e) + { + foreach (InputPin input in _inputPins.Values) + { + input.PinConnected -= OnInputPinConnected; + input.PinDisconnected -= OnInputPinDisconnected; + RemovePin(input); + } + + _inputPins.Clear(); + ChangeType(typeof(object)); + } + + private void ChangeType(Type type) + { + foreach (InputPin input in _inputPins.Values) + input.ChangeType(type); + Output.ChangeType(type); + } +} \ No newline at end of file diff --git a/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventCycleNode.cs b/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventCycleNode.cs index cab064242..a5de87de0 100644 --- a/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventCycleNode.cs +++ b/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventCycleNode.cs @@ -48,9 +48,7 @@ public class DataModelEventCycleNode : Node propertyAccessor, OutputPin outputPin) in _propertyPins) + { + if (!outputPin.ConnectedTo.Any()) + continue; + object value = dataModelEvent.LastEventArgumentsUntyped != null ? propertyAccessor(dataModelEvent.LastEventArgumentsUntyped) : outputPin.Type.GetDefault()!; + outputPin.Value = outputPin.IsNumeric ? new Numeric(value) : value; + } + } + + private void UpdateDataModelPath() + { + DataModelPath? old = _dataModelPath; + _dataModelPath = Storage != null ? new DataModelPath(Storage) : null; + if (_dataModelPath != null) + _dataModelPath.PathValidated += DataModelPathOnPathValidated; + + if (old != null) + { + old.PathValidated -= DataModelPathOnPathValidated; + old.Dispose(); + } + + UpdateOutputPins(); + } + + private void UpdateOutputPins() + { + object? pathValue = _dataModelPath?.GetValue(); + if (pathValue is IDataModelEvent dataModelEvent) + CreatePins(dataModelEvent); + } + private void CreatePins(IDataModelEvent? dataModelEvent) { if (_dataModelEvent == dataModelEvent) @@ -72,34 +112,12 @@ public class DataModelEventNode : Node propertyAccessor, OutputPin outputPin) in _propertyPins) - { - if (!outputPin.ConnectedTo.Any()) - continue; - object value = dataModelEvent.LastEventArgumentsUntyped != null ? propertyAccessor(dataModelEvent.LastEventArgumentsUntyped) : outputPin.Type.GetDefault()!; - outputPin.Value = outputPin.IsNumeric ? new Numeric(value) : value; - } - } - - private void UpdateDataModelPath() - { - DataModelPath? old = _dataModelPath; - _dataModelPath = Storage != null ? new DataModelPath(Storage) : null; - old?.Dispose(); - - object? pathValue = _dataModelPath?.GetValue(); - if (pathValue is IDataModelEvent dataModelEvent) - CreatePins(dataModelEvent); + // Update the output pin now that the type is known and attempt to restore the connection that was likely missing + UpdateOutputPins(); + Script?.LoadConnections(); } /// diff --git a/src/Artemis.VisualScripting/Nodes/DataModel/DataModelNode.cs b/src/Artemis.VisualScripting/Nodes/DataModel/DataModelNode.cs index 411f7c1f1..3c3054e1e 100644 --- a/src/Artemis.VisualScripting/Nodes/DataModel/DataModelNode.cs +++ b/src/Artemis.VisualScripting/Nodes/DataModel/DataModelNode.cs @@ -1,7 +1,6 @@ using Artemis.Core; using Artemis.Storage.Entities.Profile; using Artemis.VisualScripting.Nodes.DataModel.Screens; -using Avalonia.Threading; namespace Artemis.VisualScripting.Nodes.DataModel; @@ -61,17 +60,24 @@ public class DataModelNode : Node