mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge branch 'development'
This commit is contained in:
commit
9c72c0550e
@ -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;
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ namespace Artemis.Core;
|
||||
/// <summary>
|
||||
/// Represents a node script
|
||||
/// </summary>
|
||||
public interface INodeScript : INotifyPropertyChanged, IDisposable
|
||||
public interface INodeScript : INotifyPropertyChanged, IDisposable, IStorageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the node script.
|
||||
@ -66,6 +66,11 @@ public interface INodeScript : INotifyPropertyChanged, IDisposable
|
||||
/// </summary>
|
||||
/// <param name="node">The node to remove</param>
|
||||
void RemoveNode(INode node);
|
||||
|
||||
/// <summary>
|
||||
/// Loads missing connections between the nodes of this node script from storage
|
||||
/// </summary>
|
||||
void LoadConnections();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -12,7 +12,7 @@ namespace Artemis.Core;
|
||||
/// <summary>
|
||||
/// Represents a node script
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads missing connections between the nodes of this node script from the <see cref="Entity" />
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public void LoadConnections()
|
||||
{
|
||||
List<INode> nodes = Nodes.ToList();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
/// </summary>
|
||||
public class ConnectPins : INodeEditorCommand
|
||||
{
|
||||
private readonly List<IPin>? _originalConnections;
|
||||
private readonly IPin _source;
|
||||
private readonly IPin _target;
|
||||
private readonly IPin _output;
|
||||
private readonly IPin _input;
|
||||
private readonly IPin? _originalConnection;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ConnectPins" /> class.
|
||||
@ -19,10 +20,18 @@ public class ConnectPins : INodeEditorCommand
|
||||
/// <param name="target">The target of the connection.</param>
|
||||
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<IPin>(_target.ConnectedTo) : null;
|
||||
_originalConnection = _input.ConnectedTo.FirstOrDefault();
|
||||
}
|
||||
|
||||
#region Implementation of INodeEditorCommand
|
||||
@ -33,20 +42,16 @@ public class ConnectPins : INodeEditorCommand
|
||||
/// <inheritdoc />
|
||||
public void Execute()
|
||||
{
|
||||
if (_target.Direction == PinDirection.Input)
|
||||
_target.DisconnectAll();
|
||||
_source.ConnectTo(_target);
|
||||
_input.DisconnectAll();
|
||||
_output.ConnectTo(_input);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
|
||||
@ -25,6 +25,7 @@ public class DataBindingViewModel : ActivatableViewModelBase
|
||||
private ObservableAsPropertyHelper<ILayerProperty?>? _layerProperty;
|
||||
private ObservableAsPropertyHelper<NodeScriptViewModel?>? _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<NodeScriptWindowViewModel, bool>(("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();
|
||||
}
|
||||
}
|
||||
@ -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<bool>
|
||||
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<bool>
|
||||
INodeEditorService nodeEditorService,
|
||||
INodeVmFactory vmFactory,
|
||||
ISettingsService settingsService,
|
||||
IProfileService profileService,
|
||||
IWindowService windowService)
|
||||
{
|
||||
NodeScript = nodeScript;
|
||||
@ -43,6 +46,7 @@ public class NodeScriptWindowViewModel : DialogViewModelBase<bool>
|
||||
_nodeService = nodeService;
|
||||
_nodeEditorService = nodeEditorService;
|
||||
_settingsService = settingsService;
|
||||
_profileService = profileService;
|
||||
_windowService = windowService;
|
||||
|
||||
SourceList<NodeData> nodeSourceList = new();
|
||||
@ -60,8 +64,17 @@ public class NodeScriptWindowViewModel : DialogViewModelBase<bool>
|
||||
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<bool>
|
||||
{
|
||||
NodeScript.Run();
|
||||
}
|
||||
|
||||
private void Save(object? sender, EventArgs e)
|
||||
{
|
||||
if (NodeScript.Context is Profile profile)
|
||||
_profileService.SaveProfile(profile, true);
|
||||
}
|
||||
}
|
||||
@ -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<IPin> nodePins = new();
|
||||
SourceList<IPinCollection> nodePinCollections = new();
|
||||
SourceList<PinViewModel> nodePins = new();
|
||||
SourceList<PinCollectionViewModel> 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<PinViewModel> inputPins)
|
||||
.Subscribe();
|
||||
nodePins.Connect()
|
||||
.Filter(n => n.Direction == PinDirection.Output)
|
||||
.Transform(p => (PinViewModel) nodeVmFactory.OutputPinViewModel(p, nodeScriptViewModel))
|
||||
.Bind(out ReadOnlyObservableCollection<PinViewModel> outputPins)
|
||||
.Subscribe();
|
||||
nodePins.Connect().Filter(n => n.Pin.Direction == PinDirection.Input).Bind(out ReadOnlyObservableCollection<PinViewModel> inputPins).Subscribe();
|
||||
nodePins.Connect().Filter(n => n.Pin.Direction == PinDirection.Output).Bind(out ReadOnlyObservableCollection<PinViewModel> 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<PinCollectionViewModel> inputPinCollections)
|
||||
.Subscribe();
|
||||
nodePinCollections.Connect()
|
||||
.Filter(n => n.Direction == PinDirection.Output)
|
||||
.Transform(c => (PinCollectionViewModel) nodeVmFactory.OutputPinCollectionViewModel(c, nodeScriptViewModel))
|
||||
.Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> outputPinCollections)
|
||||
.Subscribe();
|
||||
nodePinCollections.Connect().Filter(n => n.PinCollection.Direction == PinDirection.Input).Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> inputPinCollections).Subscribe();
|
||||
nodePinCollections.Connect().Filter(n => n.PinCollection.Direction == PinDirection.Output).Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> 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<PinViewModel> pins)
|
||||
.Subscribe();
|
||||
nodePins.Connect().Merge(nodePinCollections.Connect().TransformMany(c => c.PinViewModels)).Bind(out ReadOnlyObservableCollection<PinViewModel> pins).Subscribe();
|
||||
|
||||
PinViewModels = pins;
|
||||
|
||||
@ -101,30 +81,54 @@ public class NodeViewModel : ActivatableViewModelBase
|
||||
|
||||
// Subscribe to pin changes
|
||||
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(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<SingleValueEventArgs<IPin>>(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<SingleValueEventArgs<IPinCollection>>(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<SingleValueEventArgs<IPinCollection>>(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);
|
||||
});
|
||||
|
||||
@ -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<Enum, InputPin> _inputPins;
|
||||
|
||||
public EnumSwitchNode() : base("Enum Branch", "desc")
|
||||
{
|
||||
_inputPins = new Dictionary<Enum, InputPin>();
|
||||
|
||||
Output = CreateOutputPin(typeof(object), "Result");
|
||||
SwitchValue = CreateInputPin<Enum>("Switch");
|
||||
|
||||
SwitchValue.PinConnected += OnSwitchPinConnected;
|
||||
SwitchValue.PinDisconnected += OnSwitchPinDisconnected;
|
||||
}
|
||||
|
||||
public OutputPin Output { get; }
|
||||
public InputPin<Enum> 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<IPin> 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<IPin> 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<IPin> e)
|
||||
{
|
||||
if (SwitchValue.ConnectedTo.Count == 0)
|
||||
return;
|
||||
|
||||
Type enumType = SwitchValue.ConnectedTo[0].Type;
|
||||
foreach (Enum enumValue in Enum.GetValues(enumType).Cast<Enum>())
|
||||
{
|
||||
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<IPin> 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);
|
||||
}
|
||||
}
|
||||
@ -48,9 +48,7 @@ public class DataModelEventCycleNode : Node<DataModelPathEntity, DataModelEventC
|
||||
public override void Evaluate()
|
||||
{
|
||||
object? pathValue = _dataModelPath?.GetValue();
|
||||
bool hasTriggered = pathValue is IDataModelEvent dataModelEvent
|
||||
? EvaluateEvent(dataModelEvent)
|
||||
: EvaluateValue(pathValue);
|
||||
bool hasTriggered = pathValue is IDataModelEvent dataModelEvent ? EvaluateEvent(dataModelEvent) : EvaluateValue(pathValue);
|
||||
|
||||
if (hasTriggered)
|
||||
{
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Events;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.VisualScripting.Nodes.DataModel.Screens;
|
||||
@ -41,6 +40,47 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
|
||||
UpdateDataModelPath();
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
object? pathValue = _dataModelPath?.GetValue();
|
||||
if (pathValue is not IDataModelEvent dataModelEvent)
|
||||
return;
|
||||
|
||||
TimeSinceLastTrigger.Value = new Numeric(dataModelEvent.TimeSinceLastTrigger.TotalMilliseconds);
|
||||
TriggerCount.Value = new Numeric(dataModelEvent.TriggerCount);
|
||||
|
||||
foreach ((Func<DataModelEventArgs, object> 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<DataModelPathEntity, DataModelEventNodeCu
|
||||
_propertyPins.Add(expression, CreateOrAddOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
|
||||
private void DataModelPathOnPathValidated(object? sender, EventArgs e)
|
||||
{
|
||||
object? pathValue = _dataModelPath?.GetValue();
|
||||
if (pathValue is not IDataModelEvent dataModelEvent)
|
||||
return;
|
||||
|
||||
TimeSinceLastTrigger.Value = new Numeric(dataModelEvent.TimeSinceLastTrigger.TotalMilliseconds);
|
||||
TriggerCount.Value = new Numeric(dataModelEvent.TriggerCount);
|
||||
|
||||
foreach ((Func<DataModelEventArgs, object> 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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@ -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<DataModelPathEntity, DataModelNodeCustomViewMo
|
||||
private void UpdateDataModelPath()
|
||||
{
|
||||
DataModelPath? old = _dataModelPath;
|
||||
old?.Dispose();
|
||||
|
||||
_dataModelPath = Storage != null ? new DataModelPath(Storage) : null;
|
||||
if (_dataModelPath != null)
|
||||
_dataModelPath.PathValidated += DataModelPathOnPathValidated;
|
||||
|
||||
if (old != null)
|
||||
{
|
||||
old.PathValidated -= DataModelPathOnPathValidated;
|
||||
old.Dispose();
|
||||
}
|
||||
|
||||
UpdateOutputPin();
|
||||
}
|
||||
|
||||
private void DataModelPathOnPathValidated(object? sender, EventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(UpdateOutputPin);
|
||||
// Update the output pin now that the type is known and attempt to restore the connection that was likely missing
|
||||
UpdateOutputPin();
|
||||
Script?.LoadConnections();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user