mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Nodes - Support dynamic children in data model node
This commit is contained in:
parent
bd3bf20d92
commit
4fbd229846
@ -60,7 +60,7 @@ namespace Artemis.Core
|
||||
if (!IsEnabled)
|
||||
return;
|
||||
|
||||
// TODO: Update the base node
|
||||
// TODO: Update the 'base value' node
|
||||
|
||||
Script.Run();
|
||||
}
|
||||
@ -77,7 +77,7 @@ namespace Artemis.Core
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBinding");
|
||||
if (Properties.Any(d => d.DisplayName == displayName))
|
||||
throw new ArtemisCoreException($"A databinding property named '{displayName}' is already registered.");
|
||||
throw new ArtemisCoreException($"A data binding property named '{displayName}' is already registered.");
|
||||
|
||||
DataBindingProperty<TProperty> property = new(getter, setter, displayName);
|
||||
_properties.Add(property);
|
||||
@ -110,6 +110,8 @@ namespace Artemis.Core
|
||||
if (disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
_isEnabled = false;
|
||||
|
||||
Script.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -365,6 +365,31 @@ namespace Artemis.Core
|
||||
Entity.DataModelId = DataModelId;
|
||||
}
|
||||
|
||||
#region Equality members
|
||||
|
||||
/// <inheritdoc cref="Equals(object)"/>>
|
||||
protected bool Equals(DataModelPath other)
|
||||
{
|
||||
return ReferenceEquals(Target, other.Target) && Path == other.Path;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((DataModelPath) obj);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Target, Path);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -295,7 +295,10 @@ namespace Artemis.Core
|
||||
private void DynamicChildOnDynamicChildAdded(object? sender, DynamicDataModelChildEventArgs e)
|
||||
{
|
||||
if (e.Key == Identifier)
|
||||
{
|
||||
DataModelPath.Invalidate();
|
||||
DataModelPath.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
private void DynamicChildOnDynamicChildRemoved(object? sender, DynamicDataModelChildEventArgs e)
|
||||
|
||||
@ -83,7 +83,7 @@ namespace Artemis.Core.Services
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
node.Initialize(script);
|
||||
return node;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
@ -16,6 +17,7 @@ namespace Artemis.Core
|
||||
|
||||
public IReadOnlyCollection<IPin> Pins { get; }
|
||||
public IReadOnlyCollection<IPinCollection> PinCollections { get; }
|
||||
|
||||
|
||||
event EventHandler Resetting;
|
||||
|
||||
|
||||
@ -10,6 +10,14 @@ namespace Artemis.Core
|
||||
{
|
||||
public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageModel
|
||||
{
|
||||
private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e)
|
||||
{
|
||||
Load();
|
||||
}
|
||||
|
||||
public event EventHandler<INode>? NodeAdded;
|
||||
public event EventHandler<INode>? NodeRemoved;
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
internal NodeScriptEntity Entity { get; }
|
||||
@ -28,21 +36,14 @@ namespace Artemis.Core
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<INode>? NodeAdded;
|
||||
public event EventHandler<INode>? NodeRemoved;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public NodeScript(string name, string description, object? context = null)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Description = description;
|
||||
this.Context = context;
|
||||
this.Entity = new NodeScriptEntity();
|
||||
Name = name;
|
||||
Description = description;
|
||||
Context = context;
|
||||
Entity = new NodeScriptEntity();
|
||||
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
|
||||
@ -50,10 +51,10 @@ namespace Artemis.Core
|
||||
|
||||
internal NodeScript(string name, string description, NodeScriptEntity entity, object? context = null)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Description = description;
|
||||
this.Entity = entity;
|
||||
this.Context = context;
|
||||
Name = name;
|
||||
Description = description;
|
||||
Entity = entity;
|
||||
Context = context;
|
||||
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
|
||||
@ -89,6 +90,12 @@ namespace Artemis.Core
|
||||
{
|
||||
NodeTypeStore.NodeTypeAdded -= NodeTypeStoreOnNodeTypeChanged;
|
||||
NodeTypeStore.NodeTypeRemoved -= NodeTypeStoreOnNodeTypeChanged;
|
||||
|
||||
foreach (INode node in _nodes)
|
||||
{
|
||||
if (node is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -98,20 +105,18 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
_nodes.Clear();
|
||||
|
||||
// Create nodes
|
||||
Dictionary<int, INode> nodes = new();
|
||||
foreach (NodeEntity entityNode in Entity.Nodes)
|
||||
{
|
||||
INode? node = LoadNode(entityNode, entityNode.IsExitNode ? ExitNode : null);
|
||||
if (node == null)
|
||||
continue;
|
||||
nodes.Add(entityNode.Id, node);
|
||||
_nodes.Add(node);
|
||||
}
|
||||
|
||||
LoadConnections(nodes);
|
||||
|
||||
_nodes.Clear();
|
||||
_nodes.AddRange(nodes.Values);
|
||||
LoadConnections();
|
||||
}
|
||||
|
||||
private INode? LoadNode(NodeEntity nodeEntity, INode? node)
|
||||
@ -147,12 +152,19 @@ namespace Artemis.Core
|
||||
return node;
|
||||
}
|
||||
|
||||
private void LoadConnections(Dictionary<int, INode> nodes)
|
||||
/// <summary>
|
||||
/// Loads missing connections between the nodes of this node script from the <see cref="Entity"/>
|
||||
/// </summary>
|
||||
public void LoadConnections()
|
||||
{
|
||||
List<INode> nodes = Nodes.ToList();
|
||||
foreach (NodeConnectionEntity nodeConnectionEntity in Entity.Connections)
|
||||
{
|
||||
// Find the source and target node
|
||||
if (!nodes.TryGetValue(nodeConnectionEntity.SourceNode, out INode? source) || !nodes.TryGetValue(nodeConnectionEntity.TargetNode, out INode? target))
|
||||
INode? source = nodes.ElementAtOrDefault(nodeConnectionEntity.SourceNode);
|
||||
if (source == null)
|
||||
continue;
|
||||
INode? target = nodes.ElementAtOrDefault(nodeConnectionEntity.TargetNode);
|
||||
if (target == null)
|
||||
continue;
|
||||
|
||||
IPin? sourcePin = nodeConnectionEntity.SourcePinCollectionId == -1
|
||||
@ -165,9 +177,15 @@ namespace Artemis.Core
|
||||
// Ensure both nodes have the required pins
|
||||
if (sourcePin == null || targetPin == null)
|
||||
continue;
|
||||
// Ensure the connection is valid
|
||||
if (sourcePin.Direction == targetPin.Direction)
|
||||
continue;
|
||||
|
||||
targetPin.ConnectTo(sourcePin);
|
||||
sourcePin.ConnectTo(targetPin);
|
||||
// Only connect the nodes if they aren't already connected (LoadConnections may be called twice or more)
|
||||
if (!targetPin.ConnectedTo.Contains(sourcePin))
|
||||
targetPin.ConnectTo(sourcePin);
|
||||
if (!sourcePin.ConnectedTo.Contains(targetPin))
|
||||
sourcePin.ConnectTo(targetPin);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,13 +196,12 @@ namespace Artemis.Core
|
||||
Entity.Description = Description;
|
||||
|
||||
Entity.Nodes.Clear();
|
||||
|
||||
|
||||
// No need to save the exit node if that's all there is
|
||||
if (Nodes.Count() == 1)
|
||||
return;
|
||||
|
||||
int id = 0;
|
||||
|
||||
foreach (INode node in Nodes)
|
||||
{
|
||||
NodeEntity nodeEntity = new()
|
||||
@ -248,9 +265,11 @@ namespace Artemis.Core
|
||||
targetPinId = targetCollection.ToList().IndexOf(targetPin);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetPinId = targetPin.Node.Pins.IndexOf(targetPin);
|
||||
}
|
||||
|
||||
Entity.Connections.Add(new NodeConnectionEntity()
|
||||
Entity.Connections.Add(new NodeConnectionEntity
|
||||
{
|
||||
SourceType = sourcePin.Type.Name,
|
||||
SourceNode = nodes.IndexOf(node),
|
||||
@ -259,7 +278,7 @@ namespace Artemis.Core
|
||||
TargetType = targetPin.Type.Name,
|
||||
TargetNode = nodes.IndexOf(targetPin.Node),
|
||||
TargetPinCollectionId = targetPinCollectionId,
|
||||
TargetPinId = targetPinId,
|
||||
TargetPinId = targetPinId
|
||||
});
|
||||
}
|
||||
|
||||
@ -268,15 +287,6 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e)
|
||||
{
|
||||
Load();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class NodeScript<T> : NodeScript, INodeScript<T>
|
||||
|
||||
@ -44,7 +44,10 @@ namespace Artemis.UI.Shared.Controls
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ShowFullPathProperty = DependencyProperty.Register(nameof(ShowFullPath), typeof(bool), typeof(DataModelPicker));
|
||||
public static readonly DependencyProperty ShowFullPathProperty = DependencyProperty.Register(
|
||||
nameof(ShowFullPath), typeof(bool), typeof(DataModelPicker),
|
||||
new FrameworkPropertyMetadata(ShowFullPathPropertyCallback)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when drawing the button
|
||||
@ -86,7 +89,7 @@ namespace Artemis.UI.Shared.Controls
|
||||
public DataModelPicker()
|
||||
{
|
||||
SelectPropertyCommand = new DelegateCommand(ExecuteSelectPropertyCommand);
|
||||
|
||||
Unloaded += (_, _) => DataModelViewModel?.Dispose();
|
||||
InitializeComponent();
|
||||
GetDataModel();
|
||||
UpdateValueDisplay();
|
||||
@ -253,7 +256,12 @@ namespace Artemis.UI.Shared.Controls
|
||||
if (context is not DataModelVisualizationViewModel selected)
|
||||
return;
|
||||
|
||||
DataModelPath = selected.DataModelPath;
|
||||
if (selected.DataModelPath == null)
|
||||
return;
|
||||
if (selected.DataModelPath.Equals(DataModelPath))
|
||||
return;
|
||||
|
||||
DataModelPath = new DataModelPath(selected.DataModelPath);
|
||||
OnDataModelPathSelected(new DataModelSelectedEventArgs(DataModelPath));
|
||||
}
|
||||
|
||||
@ -278,6 +286,14 @@ namespace Artemis.UI.Shared.Controls
|
||||
dataModelPicker.UpdateValueDisplay();
|
||||
}
|
||||
|
||||
private static void ShowFullPathPropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not DataModelPicker dataModelPicker)
|
||||
return;
|
||||
|
||||
dataModelPicker.UpdateValueDisplay();
|
||||
}
|
||||
|
||||
private static void ModulesPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not DataModelPicker dataModelPicker)
|
||||
@ -290,6 +306,9 @@ namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
if (d is not DataModelPicker dataModelPicker)
|
||||
return;
|
||||
|
||||
if (e.OldValue is DataModelPropertiesViewModel vm)
|
||||
vm.Dispose();
|
||||
}
|
||||
|
||||
private static void ExtraDataModelViewModelsPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
|
||||
@ -11,10 +11,12 @@ namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||
private readonly DataModelNode _node;
|
||||
private BindableCollection<Module> _modules;
|
||||
|
||||
public DataModelNodeCustomViewModel(DataModelNode node, ISettingsService settingsService) : base(node)
|
||||
public DataModelNodeCustomViewModel(DataModelNode node, ISettingsService settingsService, IPluginManagementService test) : base(node)
|
||||
{
|
||||
_node = node;
|
||||
|
||||
var tessst = test.GetFeaturesOfType<Module>();
|
||||
|
||||
ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
|
||||
ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
|
||||
}
|
||||
@ -33,19 +35,15 @@ namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||
get => _node.DataModelPath;
|
||||
set
|
||||
{
|
||||
if (ReferenceEquals(_node.DataModelPath, value))
|
||||
return;
|
||||
|
||||
_node.DataModelPath.Dispose();
|
||||
_node.DataModelPath = value;
|
||||
_node.DataModelPath.Save();
|
||||
|
||||
if (_node.DataModelPath != null)
|
||||
{
|
||||
_node.DataModelPath.Save();
|
||||
_node.Storage = _node.DataModelPath.Entity;
|
||||
}
|
||||
else
|
||||
{
|
||||
_node.Storage = null;
|
||||
}
|
||||
|
||||
_node.UpdateOutputPin();
|
||||
_node.Storage = _node.DataModelPath.Entity;
|
||||
_node.UpdateOutputPin(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,11 +3,12 @@ using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.VisualScripting.Nodes.CustomViewModels;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.VisualScripting.Nodes
|
||||
{
|
||||
[Node("Data Model-Value", "Outputs a selectable data model value.")]
|
||||
public class DataModelNode : Node<DataModelNodeCustomViewModel>
|
||||
public class DataModelNode : Node<DataModelNodeCustomViewModel>, IDisposable
|
||||
{
|
||||
private DataModelPath _dataModelPath;
|
||||
|
||||
@ -21,7 +22,7 @@ namespace Artemis.VisualScripting.Nodes
|
||||
public DataModelPath DataModelPath
|
||||
{
|
||||
get => _dataModelPath;
|
||||
set => SetAndNotify(ref _dataModelPath , value);
|
||||
set => SetAndNotify(ref _dataModelPath, value);
|
||||
}
|
||||
|
||||
public override void Initialize(INodeScript script)
|
||||
@ -32,29 +33,58 @@ namespace Artemis.VisualScripting.Nodes
|
||||
return;
|
||||
|
||||
DataModelPath = new DataModelPath(null, pathEntity);
|
||||
UpdateOutputPin();
|
||||
DataModelPath.PathValidated += DataModelPathOnPathValidated;
|
||||
|
||||
UpdateOutputPin(false);
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
if (DataModelPath.IsValid && Output != null)
|
||||
Output.Value = DataModelPath.GetValue()!;
|
||||
}
|
||||
|
||||
public void UpdateOutputPin()
|
||||
{
|
||||
if (Output != null && Output.Type == DataModelPath?.GetPropertyType())
|
||||
return;
|
||||
|
||||
if (Output != null && Pins.Contains(Output))
|
||||
if (DataModelPath.IsValid)
|
||||
{
|
||||
RemovePin(Output);
|
||||
Output = null;
|
||||
}
|
||||
if (Output == null)
|
||||
UpdateOutputPin(false);
|
||||
|
||||
Type type = DataModelPath?.GetPropertyType();
|
||||
if (type != null)
|
||||
Output = CreateOutputPin(type);
|
||||
Output.Value = DataModelPath.GetValue() ?? Output.Type.GetDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateOutputPin(bool loadConnections)
|
||||
{
|
||||
Execute.OnUIThread(() =>
|
||||
{
|
||||
if (Output != null && Output.Type == DataModelPath?.GetPropertyType())
|
||||
return;
|
||||
|
||||
if (Output != null)
|
||||
{
|
||||
RemovePin(Output);
|
||||
Output = null;
|
||||
}
|
||||
|
||||
Type type = DataModelPath?.GetPropertyType();
|
||||
if (type != null)
|
||||
Output = CreateOutputPin(type);
|
||||
|
||||
if (loadConnections && Script is NodeScript nodeScript)
|
||||
nodeScript.LoadConnections();
|
||||
});
|
||||
}
|
||||
|
||||
private void DataModelPathOnPathValidated(object sender, EventArgs e)
|
||||
{
|
||||
UpdateOutputPin(true);
|
||||
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
DataModelPath.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user