1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2026-01-02 10:43:31 +00:00

Node editor - Added undo/redo to event and datamodel nodes

This commit is contained in:
Robert 2022-03-25 20:21:45 +01:00
parent f824f16658
commit 06ab2c5bb6
17 changed files with 609 additions and 421 deletions

View File

@ -4,13 +4,13 @@ using System.Collections.ObjectModel;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;
namespace Artemis.Core namespace Artemis.Core;
/// <summary>
/// Represents a kind of node inside a <see cref="NodeScript" />
/// </summary>
public abstract class Node : CorePropertyChanged, INode
{ {
/// <summary>
/// Represents a kind of node inside a <see cref="NodeScript" />
/// </summary>
public abstract class Node : CorePropertyChanged, INode
{
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler? Resetting; public event EventHandler? Resetting;
@ -253,8 +253,9 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Called whenever the node must show it's custom view model, if <see langword="null" />, no custom view model is used /// Called whenever the node must show it's custom view model, if <see langword="null" />, no custom view model is used
/// </summary> /// </summary>
/// <param name="nodeScript"></param>
/// <returns>The custom view model, if <see langword="null" />, no custom view model is used</returns> /// <returns>The custom view model, if <see langword="null" />, no custom view model is used</returns>
public virtual ICustomNodeViewModel? GetCustomViewModel() public virtual ICustomNodeViewModel? GetCustomViewModel(NodeScript nodeScript)
{ {
return null; return null;
} }
@ -277,15 +278,15 @@ namespace Artemis.Core
} }
#endregion #endregion
} }
/// <summary> /// <summary>
/// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type /// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type
/// <typeparamref name="TStorage" />. /// <typeparamref name="TStorage" />.
/// </summary> /// </summary>
/// <typeparam name="TStorage">The type of value the node stores</typeparam> /// <typeparam name="TStorage">The type of value the node stores</typeparam>
public abstract class Node<TStorage> : Node public abstract class Node<TStorage> : Node
{ {
private TStorage? _storage; private TStorage? _storage;
/// <inheritdoc /> /// <inheritdoc />
@ -304,8 +305,17 @@ namespace Artemis.Core
public TStorage? Storage public TStorage? Storage
{ {
get => _storage; get => _storage;
set => SetAndNotify(ref _storage, value); set
{
if (SetAndNotify(ref _storage, value))
StorageModified?.Invoke(this, EventArgs.Empty);
} }
}
/// <summary>
/// Occurs whenever the storage of this node was modified.
/// </summary>
public event EventHandler? StorageModified;
internal override string SerializeStorage() internal override string SerializeStorage()
{ {
@ -316,16 +326,16 @@ namespace Artemis.Core
{ {
Storage = CoreJson.DeserializeObject<TStorage>(serialized) ?? default(TStorage); Storage = CoreJson.DeserializeObject<TStorage>(serialized) ?? default(TStorage);
} }
} }
/// <summary> /// <summary>
/// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type /// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type
/// <typeparamref name="TStorage" /> and a view model of type <typeparamref name="TViewModel" />. /// <typeparamref name="TStorage" /> and a view model of type <typeparamref name="TViewModel" />.
/// </summary> /// </summary>
/// <typeparam name="TStorage">The type of value the node stores</typeparam> /// <typeparam name="TStorage">The type of value the node stores</typeparam>
/// <typeparam name="TViewModel">The type of view model the node uses</typeparam> /// <typeparam name="TViewModel">The type of view model the node uses</typeparam>
public abstract class Node<TStorage, TViewModel> : Node<TStorage> where TViewModel : ICustomNodeViewModel public abstract class Node<TStorage, TViewModel> : Node<TStorage> where TViewModel : ICustomNodeViewModel
{ {
/// <inheritdoc /> /// <inheritdoc />
protected Node() protected Node()
{ {
@ -342,15 +352,16 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Called when a view model is required /// Called when a view model is required
/// </summary> /// </summary>
public virtual TViewModel GetViewModel() /// <param name="nodeScript"></param>
public virtual TViewModel GetViewModel(NodeScript nodeScript)
{ {
return Kernel.Get<TViewModel>(new ConstructorArgument("node", this)); return Kernel.Get<TViewModel>(new ConstructorArgument("node", this), new ConstructorArgument("script", nodeScript));
} }
/// <param name="nodeScript"></param>
/// <inheritdoc /> /// <inheritdoc />
public override ICustomNodeViewModel GetCustomViewModel() public override ICustomNodeViewModel? GetCustomViewModel(NodeScript nodeScript)
{ {
return GetViewModel(); return GetViewModel(nodeScript);
}
} }
} }

View File

@ -0,0 +1,72 @@
using System;
using Artemis.Core;
namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
/// <summary>
/// Represents a node editor command that can be used to update the storage value of a node.
/// </summary>
/// <typeparam name="TStorage">The type of value the node stores</typeparam>
public class UpdateStorage<TStorage> : INodeEditorCommand, IDisposable
{
private readonly Node<TStorage> _node;
private readonly TStorage? _originalValue;
private readonly TStorage? _value;
private readonly string _valueDescription;
private bool _updatedValue;
/// <summary>
/// Creates a new instance of the <see cref="UpdateStorage{T}" /> class.
/// </summary>
/// <param name="node">The node to update.</param>
/// <param name="value">The new value of the node.</param>
/// <param name="valueDescription">The description of the value that was updated.</param>
public UpdateStorage(Node<TStorage> node, TStorage? value, string valueDescription = "value")
{
_node = node;
_value = value;
_valueDescription = valueDescription;
_originalValue = _node.Storage;
}
#region Implementation of INodeEditorCommand
/// <inheritdoc />
public string DisplayName => $"Update node {_valueDescription}";
/// <inheritdoc />
public void Execute()
{
_updatedValue = true;
_node.Storage = _value;
}
/// <inheritdoc />
public void Undo()
{
_updatedValue = false;
_node.Storage = _originalValue;
}
#endregion
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
if (_updatedValue)
{
if (_originalValue is IDisposable disposable)
disposable.Dispose();
}
else
{
if (_value is IDisposable disposable)
disposable.Dispose();
}
}
#endregion
}

View File

@ -4,13 +4,22 @@ using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Shared.VisualScripting namespace Artemis.UI.Shared.VisualScripting;
/// <summary>
/// Represents a custom view model for a node
/// </summary>
public abstract class CustomNodeViewModel : ActivatableViewModelBase, ICustomNodeViewModel
{ {
public abstract class CustomNodeViewModel : ActivatableViewModelBase, ICustomNodeViewModel /// <summary>
{ /// Creates a new instance of the <see cref="CustomNodeViewModel" /> class.
protected CustomNodeViewModel(INode node) /// </summary>
/// <param name="node">The node the view model is for.</param>
/// <param name="script">The script the node is contained in.</param>
protected CustomNodeViewModel(INode node, INodeScript script)
{ {
Node = node; Node = node;
Script = script;
this.WhenActivated(d => this.WhenActivated(d =>
{ {
@ -19,31 +28,30 @@ namespace Artemis.UI.Shared.VisualScripting
}); });
} }
/// <summary>
/// Gets the node the view model is for.
/// </summary>
public INode Node { get; } public INode Node { get; }
#region Events /// <summary>
/// Gets script the node is contained in.
/// <inheritdoc /> /// </summary>
public event EventHandler NodeModified; public INodeScript Script { get; }
/// <summary> /// <summary>
/// Invokes the <see cref="NodeModified"/> event /// Invokes the <see cref="NodeModified" /> event
/// </summary> /// </summary>
protected virtual void OnNodeModified() protected virtual void OnNodeModified()
{ {
NodeModified?.Invoke(this, EventArgs.Empty); NodeModified?.Invoke(this, EventArgs.Empty);
} }
#endregion
#region Event handlers
private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e) private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == "Storage") if (e.PropertyName == "Storage")
OnNodeModified(); OnNodeModified();
} }
#endregion /// <inheritdoc />
} public event EventHandler? NodeModified;
} }

View File

@ -8,6 +8,7 @@ using Artemis.Core.Events;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.VisualScripting.Pins; using Artemis.UI.Screens.VisualScripting.Pins;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Shared.Services.NodeEditor; using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands; using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Avalonia; using Avalonia;
@ -21,15 +22,17 @@ namespace Artemis.UI.Screens.VisualScripting;
public class NodeScriptViewModel : ActivatableViewModelBase public class NodeScriptViewModel : ActivatableViewModelBase
{ {
private readonly INodeEditorService _nodeEditorService; private readonly INodeEditorService _nodeEditorService;
private readonly INotificationService _notificationService;
private readonly SourceList<NodeViewModel> _nodeViewModels; private readonly SourceList<NodeViewModel> _nodeViewModels;
private readonly INodeVmFactory _nodeVmFactory; private readonly INodeVmFactory _nodeVmFactory;
private DragCableViewModel? _dragViewModel; private DragCableViewModel? _dragViewModel;
private List<NodeViewModel>? _initialNodeSelection; private List<NodeViewModel>? _initialNodeSelection;
public NodeScriptViewModel(NodeScript nodeScript, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService) public NodeScriptViewModel(NodeScript nodeScript, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService, INotificationService notificationService)
{ {
_nodeVmFactory = nodeVmFactory; _nodeVmFactory = nodeVmFactory;
_nodeEditorService = nodeEditorService; _nodeEditorService = nodeEditorService;
_notificationService = notificationService;
_nodeViewModels = new SourceList<NodeViewModel>(); _nodeViewModels = new SourceList<NodeViewModel>();
NodeScript = nodeScript; NodeScript = nodeScript;
@ -44,6 +47,13 @@ public class NodeScriptViewModel : ActivatableViewModelBase
Observable.FromEventPattern<SingleValueEventArgs<INode>>(x => NodeScript.NodeRemoved += x, x => NodeScript.NodeRemoved -= x) Observable.FromEventPattern<SingleValueEventArgs<INode>>(x => NodeScript.NodeRemoved += x, x => NodeScript.NodeRemoved -= x)
.Subscribe(e => HandleNodeRemoved(e.EventArgs)) .Subscribe(e => HandleNodeRemoved(e.EventArgs))
.DisposeWith(d); .DisposeWith(d);
nodeEditorService.GetHistory(NodeScript).Undo
.Subscribe(c => _notificationService.CreateNotification().WithMessage(c != null ? $"Undid {c.DisplayName}" : "Nothing to undo").Show())
.DisposeWith(d);
nodeEditorService.GetHistory(NodeScript).Redo
.Subscribe(c => _notificationService.CreateNotification().WithMessage(c != null ? $"Redid {c.DisplayName}" : "Nothing to redo").Show())
.DisposeWith(d);
}); });
// Create VMs for all nodes // Create VMs for all nodes

View File

@ -113,7 +113,7 @@ public class NodeViewModel : ActivatableViewModelBase
})).DisposeWith(d); })).DisposeWith(d);
if (Node is Node coreNode) if (Node is Node coreNode)
CustomNodeViewModel = coreNode.GetCustomViewModel(); CustomNodeViewModel = coreNode.GetCustomViewModel(nodeScriptViewModel.NodeScript);
}); });
} }

View File

@ -1,10 +1,11 @@
using Artemis.UI.Shared.VisualScripting; using Artemis.Core;
using Artemis.UI.Shared.VisualScripting;
namespace Artemis.VisualScripting.Nodes.Color.CustomViewModels; namespace Artemis.VisualScripting.Nodes.Color.CustomViewModels;
public class StaticSKColorValueNodeCustomViewModel : CustomNodeViewModel public class StaticSKColorValueNodeCustomViewModel : CustomNodeViewModel
{ {
public StaticSKColorValueNodeCustomViewModel(StaticSKColorValueNode node) : base(node) public StaticSKColorValueNodeCustomViewModel(StaticSKColorValueNode node, INodeScript script) : base(node, script)
{ {
} }
} }

View File

@ -10,7 +10,7 @@ public class EnumEqualsNodeCustomViewModel : CustomNodeViewModel
{ {
private readonly EnumEqualsNode _node; private readonly EnumEqualsNode _node;
public EnumEqualsNodeCustomViewModel(EnumEqualsNode node) : base(node) public EnumEqualsNodeCustomViewModel(EnumEqualsNode node, INodeScript script) : base(node, script)
{ {
_node = node; _node = node;
} }

View File

@ -11,7 +11,7 @@ public class LayerPropertyNodeCustomViewModel : CustomNodeViewModel
private RenderProfileElement _selectedProfileElement; private RenderProfileElement _selectedProfileElement;
public LayerPropertyNodeCustomViewModel(LayerPropertyNode node) : base(node) public LayerPropertyNodeCustomViewModel(LayerPropertyNode node, INodeScript script) : base(node, script)
{ {
_node = node; _node = node;
} }

View File

@ -5,14 +5,14 @@ namespace Artemis.VisualScripting.Nodes.CustomViewModels;
public class StaticNumericValueNodeCustomViewModel : CustomNodeViewModel public class StaticNumericValueNodeCustomViewModel : CustomNodeViewModel
{ {
public StaticNumericValueNodeCustomViewModel(INode node) : base(node) public StaticNumericValueNodeCustomViewModel(INode node, INodeScript script) : base(node, script)
{ {
} }
} }
public class StaticStringValueNodeCustomViewModel : CustomNodeViewModel public class StaticStringValueNodeCustomViewModel : CustomNodeViewModel
{ {
public StaticStringValueNodeCustomViewModel(INode node) : base(node) public StaticStringValueNodeCustomViewModel(INode node, INodeScript script) : base(node, script)
{ {
} }
} }

View File

@ -1,9 +1,11 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Artemis.UI.Shared.VisualScripting; using Artemis.UI.Shared.VisualScripting;
using ReactiveUI; using ReactiveUI;
@ -12,29 +14,40 @@ namespace Artemis.VisualScripting.Nodes.DataModel.CustomViewModels;
public class DataModelEventNodeCustomViewModel : CustomNodeViewModel public class DataModelEventNodeCustomViewModel : CustomNodeViewModel
{ {
private readonly DataModelEventNode _node; private readonly DataModelEventNode _node;
private readonly INodeEditorService _nodeEditorService;
private bool _updating;
private DataModelPath? _dataModelPath;
private ObservableCollection<Module>? _modules; private ObservableCollection<Module>? _modules;
public DataModelEventNodeCustomViewModel(DataModelEventNode node, ISettingsService settingsService) : base(node) public DataModelEventNodeCustomViewModel(DataModelEventNode node, INodeScript script, ISettingsService settingsService, INodeEditorService nodeEditorService) : base(node, script)
{ {
_node = node; _node = node;
_nodeEditorService = nodeEditorService;
ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true); ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false); ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
Modules = new ObservableCollection<Module>();
this.WhenActivated(d => this.WhenActivated(d =>
{ {
if (Modules != null) // Set up extra modules
return; if (_node.Script?.Context is Profile scriptProfile && scriptProfile.Configuration.Module != null)
Modules = new ObservableCollection<Module> {scriptProfile.Configuration.Module};
else if (_node.Script?.Context is ProfileConfiguration profileConfiguration && profileConfiguration.Module != null)
Modules = new ObservableCollection<Module> {profileConfiguration.Module};
Modules = new ObservableCollection<Module>(); // Subscribe to node changes
if (_node.Script.Context is Profile scriptProfile && scriptProfile.Configuration.Module != null) _node.WhenAnyValue(n => n.Storage).Subscribe(UpdateDataModelPath).DisposeWith(d);
Modules.Add(scriptProfile.Configuration.Module); UpdateDataModelPath(_node.Storage);
else if (_node.Script.Context is ProfileConfiguration profileConfiguration && profileConfiguration.Module != null)
Modules.Add(profileConfiguration.Module);
_node.PropertyChanged += NodeOnPropertyChanged; Disposable.Create(() =>
Disposable.Create(() => _node.PropertyChanged -= NodeOnPropertyChanged).DisposeWith(d); {
_dataModelPath?.Dispose();
_dataModelPath = null;
}).DisposeWith(d);
}); });
this.WhenAnyValue(vm => vm.DataModelPath).Subscribe(ApplyDataModelPath);
} }
public PluginSetting<bool> ShowFullPaths { get; } public PluginSetting<bool> ShowFullPaths { get; }
@ -47,25 +60,48 @@ public class DataModelEventNodeCustomViewModel : CustomNodeViewModel
set => RaiseAndSetIfChanged(ref _modules, value); set => RaiseAndSetIfChanged(ref _modules, value);
} }
public DataModelPath DataModelPath public DataModelPath? DataModelPath
{ {
get => _node.DataModelPath; get => _dataModelPath;
set set => RaiseAndSetIfChanged(ref _dataModelPath, value);
}
private void UpdateDataModelPath(DataModelPathEntity? entity)
{ {
if (ReferenceEquals(_node.DataModelPath, value)) try
{
if (_updating)
return; return;
_node.DataModelPath?.Dispose(); _updating = true;
_node.DataModelPath = value;
_node.DataModelPath.Save();
_node.Storage = _node.DataModelPath.Entity; DataModelPath? old = DataModelPath;
DataModelPath = entity != null ? new DataModelPath(entity) : null;
old?.Dispose();
} }
} finally
private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == nameof(DataModelNode.DataModelPath)) _updating = false;
this.RaisePropertyChanged(nameof(DataModelPath)); }
}
private void ApplyDataModelPath(DataModelPath? path)
{
try
{
if (_updating)
return;
if (path?.Path == _node.Storage?.Path)
return;
_updating = true;
path?.Save();
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<DataModelPathEntity>(_node, path?.Entity, "event"));
}
finally
{
_updating = false;
}
} }
} }

View File

@ -4,6 +4,9 @@ using System.Reactive.Disposables;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Artemis.UI.Shared.VisualScripting; using Artemis.UI.Shared.VisualScripting;
using ReactiveUI; using ReactiveUI;
@ -12,29 +15,39 @@ namespace Artemis.VisualScripting.Nodes.DataModel.CustomViewModels;
public class DataModelNodeCustomViewModel : CustomNodeViewModel public class DataModelNodeCustomViewModel : CustomNodeViewModel
{ {
private readonly DataModelNode _node; private readonly DataModelNode _node;
private readonly INodeEditorService _nodeEditorService;
private ObservableCollection<Module>? _modules; private ObservableCollection<Module>? _modules;
private DataModelPath? _dataModelPath;
private bool _updating;
public DataModelNodeCustomViewModel(DataModelNode node, ISettingsService settingsService) : base(node) public DataModelNodeCustomViewModel(DataModelNode node, INodeScript script, ISettingsService settingsService, INodeEditorService nodeEditorService) : base(node, script)
{ {
_node = node; _node = node;
_nodeEditorService = nodeEditorService;
ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true); ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false); ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
this.WhenActivated(d => this.WhenActivated(d =>
{ {
if (Modules != null) // Set up extra modules
return;
Modules = new ObservableCollection<Module>();
if (_node.Script?.Context is Profile scriptProfile && scriptProfile.Configuration.Module != null) if (_node.Script?.Context is Profile scriptProfile && scriptProfile.Configuration.Module != null)
Modules.Add(scriptProfile.Configuration.Module); Modules = new ObservableCollection<Module> {scriptProfile.Configuration.Module};
else if (_node.Script?.Context is ProfileConfiguration profileConfiguration && profileConfiguration.Module != null) else if (_node.Script?.Context is ProfileConfiguration profileConfiguration && profileConfiguration.Module != null)
Modules.Add(profileConfiguration.Module); Modules = new ObservableCollection<Module> {profileConfiguration.Module};
_node.PropertyChanged += NodeOnPropertyChanged; // Subscribe to node changes
Disposable.Create(() => _node.PropertyChanged -= NodeOnPropertyChanged).DisposeWith(d); _node.WhenAnyValue(n => n.Storage).Subscribe(UpdateDataModelPath).DisposeWith(d);
UpdateDataModelPath(_node.Storage);
Disposable.Create(() =>
{
_dataModelPath?.Dispose();
_dataModelPath = null;
}).DisposeWith(d);
}); });
this.WhenAnyValue(vm => vm.DataModelPath).Subscribe(ApplyDataModelPath);
} }
public PluginSetting<bool> ShowFullPaths { get; } public PluginSetting<bool> ShowFullPaths { get; }
@ -48,24 +61,46 @@ public class DataModelNodeCustomViewModel : CustomNodeViewModel
public DataModelPath? DataModelPath public DataModelPath? DataModelPath
{ {
get => _node.DataModelPath; get => _dataModelPath;
set set => RaiseAndSetIfChanged(ref _dataModelPath, value);
}
private void UpdateDataModelPath(DataModelPathEntity? entity)
{ {
if (ReferenceEquals(_node.DataModelPath, value)) try
{
if (_updating)
return; return;
_node.DataModelPath?.Dispose(); _updating = true;
_node.DataModelPath = value;
_node.DataModelPath?.Save();
_node.Storage = _node.DataModelPath?.Entity; DataModelPath? old = DataModelPath;
_node.UpdateOutputPin(); DataModelPath = entity != null ? new DataModelPath(entity) : null;
old?.Dispose();
} }
} finally
private void NodeOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == nameof(DataModelNode.DataModelPath)) _updating = false;
this.RaisePropertyChanged(nameof(DataModelPath)); }
}
private void ApplyDataModelPath(DataModelPath? path)
{
try
{
if (_updating)
return;
if (path?.Path == _node.Storage?.Path)
return;
_updating = true;
path?.Save();
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<DataModelPathEntity>(_node, path?.Entity, "path"));
}
finally
{
_updating = false;
}
} }
} }

View File

@ -1,10 +1,12 @@
using Artemis.VisualScripting.Nodes.DataModel.CustomViewModels;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.VisualScripting.Nodes.DataModel.CustomViews namespace Artemis.VisualScripting.Nodes.DataModel.CustomViews
{ {
public partial class DataModelEventNodeCustomView : UserControl public partial class DataModelEventNodeCustomView : ReactiveUserControl<DataModelEventNodeCustomViewModel>
{ {
public DataModelEventNodeCustomView() public DataModelEventNodeCustomView()
{ {

View File

@ -1,10 +1,12 @@
using Artemis.VisualScripting.Nodes.DataModel.CustomViewModels;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.VisualScripting.Nodes.DataModel.CustomViews namespace Artemis.VisualScripting.Nodes.DataModel.CustomViews
{ {
public partial class DataModelNodeCustomView : UserControl public partial class DataModelNodeCustomView : ReactiveUserControl<DataModelNodeCustomViewModel>
{ {
public DataModelNodeCustomView() public DataModelNodeCustomView()
{ {

View File

@ -1,4 +1,5 @@
using Artemis.Core; using System.ComponentModel;
using Artemis.Core;
using Artemis.Core.Events; using Artemis.Core.Events;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Artemis.VisualScripting.Nodes.DataModel.CustomViewModels; using Artemis.VisualScripting.Nodes.DataModel.CustomViewModels;
@ -24,6 +25,9 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
CycleValues.PinAdded += CycleValuesOnPinAdded; CycleValues.PinAdded += CycleValuesOnPinAdded;
CycleValues.PinRemoved += CycleValuesOnPinRemoved; CycleValues.PinRemoved += CycleValuesOnPinRemoved;
CycleValues.Add(CycleValues.CreatePin()); CycleValues.Add(CycleValues.CreatePin());
// Monitor storage for changes
StorageModified += (_, _) => UpdateDataModelPath();
} }
public INodeScript? Script { get; set; } public INodeScript? Script { get; set; }
@ -31,12 +35,6 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
public InputPinCollection CycleValues { get; } public InputPinCollection CycleValues { get; }
public OutputPin Output { get; } public OutputPin Output { get; }
public DataModelPath? DataModelPath
{
get => _dataModelPath;
set => SetAndNotify(ref _dataModelPath, value);
}
public override void Initialize(INodeScript script) public override void Initialize(INodeScript script)
{ {
Script = script; Script = script;
@ -44,13 +42,13 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
if (Storage == null) if (Storage == null)
return; return;
DataModelPath = new DataModelPath(Storage); UpdateDataModelPath();
} }
public override void Evaluate() public override void Evaluate()
{ {
object? outputValue = null; object? outputValue = null;
if (DataModelPath?.GetValue() is IDataModelEvent dataModelEvent) if (_dataModelPath?.GetValue() is IDataModelEvent dataModelEvent)
{ {
if (dataModelEvent.LastTrigger > _lastTrigger) if (dataModelEvent.LastTrigger > _lastTrigger)
{ {
@ -111,6 +109,14 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
} }
} }
private void UpdateDataModelPath()
{
DataModelPath? old = _dataModelPath;
_dataModelPath = Storage != null ? new DataModelPath(Storage) : null;
old?.Dispose();
}
private void ChangeCurrentType(Type type) private void ChangeCurrentType(Type type)
{ {
CycleValues.ChangeType(type); CycleValues.ChangeType(type);
@ -140,5 +146,6 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_dataModelPath?.Dispose();
} }
} }

View File

@ -1,4 +1,5 @@
using Artemis.Core; using System.ComponentModel;
using Artemis.Core;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Artemis.VisualScripting.Nodes.DataModel.CustomViewModels; using Artemis.VisualScripting.Nodes.DataModel.CustomViewModels;
using Avalonia.Threading; using Avalonia.Threading;
@ -13,17 +14,12 @@ public class DataModelNode : Node<DataModelPathEntity, DataModelNodeCustomViewMo
public DataModelNode() : base("Data Model", "Outputs a selectable data model value") public DataModelNode() : base("Data Model", "Outputs a selectable data model value")
{ {
Output = CreateOutputPin(typeof(object)); Output = CreateOutputPin(typeof(object));
StorageModified += (_, _) => UpdateDataModelPath();
} }
public INodeScript? Script { get; private set; } public INodeScript? Script { get; private set; }
public OutputPin Output { get; } public OutputPin Output { get; }
public DataModelPath? DataModelPath
{
get => _dataModelPath;
set => SetAndNotify(ref _dataModelPath, value);
}
public override void Initialize(INodeScript script) public override void Initialize(INodeScript script)
{ {
Script = script; Script = script;
@ -31,18 +27,26 @@ public class DataModelNode : Node<DataModelPathEntity, DataModelNodeCustomViewMo
if (Storage == null) if (Storage == null)
return; return;
DataModelPath = new DataModelPath(Storage); UpdateDataModelPath();
DataModelPath.PathValidated += DataModelPathOnPathValidated; }
private void UpdateDataModelPath()
{
DataModelPath? old = _dataModelPath;
old?.Dispose();
_dataModelPath = Storage != null ? new DataModelPath(Storage) : null;
if (_dataModelPath != null)
_dataModelPath.PathValidated += DataModelPathOnPathValidated;
UpdateOutputPin(); UpdateOutputPin();
} }
public override void Evaluate() public override void Evaluate()
{ {
if (DataModelPath == null || !DataModelPath.IsValid) if (_dataModelPath == null || !_dataModelPath.IsValid)
return; return;
object? pathValue = DataModelPath.GetValue(); object? pathValue = _dataModelPath.GetValue();
if (pathValue == null) if (pathValue == null)
{ {
if (!Output.Type.IsValueType) if (!Output.Type.IsValueType)
@ -56,7 +60,7 @@ public class DataModelNode : Node<DataModelPathEntity, DataModelNodeCustomViewMo
public void UpdateOutputPin() public void UpdateOutputPin()
{ {
Type? type = DataModelPath?.GetPropertyType(); Type? type = _dataModelPath?.GetPropertyType();
if (Numeric.IsTypeCompatible(type)) if (Numeric.IsTypeCompatible(type))
type = typeof(Numeric); type = typeof(Numeric);
type ??= typeof(object); type ??= typeof(object);
@ -73,6 +77,6 @@ public class DataModelNode : Node<DataModelPathEntity, DataModelNodeCustomViewMo
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
DataModelPath?.Dispose(); _dataModelPath?.Dispose();
} }
} }

View File

@ -9,7 +9,7 @@ public class EasingTypeNodeCustomViewModel : CustomNodeViewModel
private readonly EasingTypeNode _node; private readonly EasingTypeNode _node;
private NodeEasingViewModel _selectedEasingViewModel; private NodeEasingViewModel _selectedEasingViewModel;
public EasingTypeNodeCustomViewModel(EasingTypeNode node) : base(node) public EasingTypeNodeCustomViewModel(EasingTypeNode node, INodeScript script) : base(node, script)
{ {
_node = node; _node = node;
EasingViewModels = new ObservableCollection<NodeEasingViewModel>(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(e => new NodeEasingViewModel(e))); EasingViewModels = new ObservableCollection<NodeEasingViewModel>(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(e => new NodeEasingViewModel(e)));

View File

@ -5,7 +5,7 @@ namespace Artemis.VisualScripting.Nodes.Maths.CustomViewModels;
public class MathExpressionNodeCustomViewModel : CustomNodeViewModel public class MathExpressionNodeCustomViewModel : CustomNodeViewModel
{ {
public MathExpressionNodeCustomViewModel(INode node) : base(node) public MathExpressionNodeCustomViewModel(INode node, INodeScript script) : base(node, script)
{ {
} }
} }