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:
parent
f824f16658
commit
06ab2c5bb6
@ -4,8 +4,8 @@ using System.Collections.ObjectModel;
|
|||||||
using Ninject;
|
using Ninject;
|
||||||
using Ninject.Parameters;
|
using Ninject.Parameters;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a kind of node inside a <see cref="NodeScript" />
|
/// Represents a kind of node inside a <see cref="NodeScript" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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()
|
||||||
{
|
{
|
||||||
@ -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);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
@ -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
|
||||||
{
|
{
|
||||||
protected CustomNodeViewModel(INode node)
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="CustomNodeViewModel" /> class.
|
||||||
|
/// </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,12 +28,15 @@ 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
|
||||||
@ -34,16 +46,12 @@ namespace Artemis.UI.Shared.VisualScripting
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)));
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user