mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 11:26:27 +00:00
Nodes - Added gradient nodes
Nodes - Added color gradient pin type Data bindings - Changed color gradient data bindings to now take a color gradient
This commit is contained in:
1 parent
32a444fbeb
commit
9c117d2773
48 files changed
+830
-344
No files matched your search
@@ -93,4 +93,6 @@
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=stores_005Cregistrations/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=utilities/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=visualscripting/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=visualscripting_005Cinterfaces/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=visualscripting_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=visualscripting_005Cnodes/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=visualscripting_005Cpins/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -1,13 +1,8 @@
|
||||
using System.Collections.Specialized;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core;
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class ColorGradientLayerProperty : LayerProperty<ColorGradient>
|
||||
{
|
||||
private ColorGradient? _subscribedGradient;
|
||||
|
||||
internal ColorGradientLayerProperty()
|
||||
{
|
||||
KeyframesSupported = false;
|
||||
@@ -22,29 +17,6 @@ public class ColorGradientLayerProperty : LayerProperty<ColorGradient>
|
||||
return p.CurrentValue;
|
||||
}
|
||||
|
||||
#region Overrides of LayerProperty<ColorGradient>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnCurrentValueSet()
|
||||
{
|
||||
// Don't allow color gradients to be null
|
||||
if (BaseValue == null!)
|
||||
BaseValue = new ColorGradient(DefaultValue);
|
||||
|
||||
if (!ReferenceEquals(_subscribedGradient, BaseValue))
|
||||
{
|
||||
if (_subscribedGradient != null)
|
||||
_subscribedGradient.CollectionChanged -= SubscribedGradientOnPropertyChanged;
|
||||
_subscribedGradient = BaseValue;
|
||||
_subscribedGradient.CollectionChanged += SubscribedGradientOnPropertyChanged;
|
||||
}
|
||||
|
||||
CreateDataBindingRegistrations();
|
||||
base.OnCurrentValueSet();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||
{
|
||||
@@ -60,33 +32,12 @@ public class ColorGradientLayerProperty : LayerProperty<ColorGradient>
|
||||
if (BaseValue == null!)
|
||||
BaseValue = new ColorGradient(DefaultValue);
|
||||
|
||||
base.OnInitialize();
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue, value =>
|
||||
{
|
||||
if (value != null)
|
||||
CurrentValue = value;
|
||||
}, "Value");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void CreateDataBindingRegistrations()
|
||||
{
|
||||
DataBinding.ClearDataBindingProperties();
|
||||
if (CurrentValue == null!)
|
||||
return;
|
||||
|
||||
for (int index = 0; index < CurrentValue.Count; index++)
|
||||
{
|
||||
int stopIndex = index;
|
||||
|
||||
void Setter(SKColor value)
|
||||
{
|
||||
CurrentValue[stopIndex].Color = value;
|
||||
}
|
||||
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue[stopIndex].Color, Setter, $"Color #{stopIndex + 1}");
|
||||
}
|
||||
}
|
||||
|
||||
private void SubscribedGradientOnPropertyChanged(object? sender, NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
if (CurrentValue.Count != DataBinding.Properties.Count)
|
||||
CreateDataBindingRegistrations();
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ public class DataBindingProperty<TProperty> : IDataBindingProperty
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetValue(object? value)
|
||||
public void SetValue(object value)
|
||||
{
|
||||
// Numeric has a bunch of conversion, this seems the cheapest way to use them :)
|
||||
switch (value)
|
||||
|
||||
@@ -27,5 +27,5 @@ public interface IDataBindingProperty
|
||||
/// Sets the value of the property this registration points to
|
||||
/// </summary>
|
||||
/// <param name="value">A value matching the type of <see cref="ValueType" /></param>
|
||||
void SetValue(object? value);
|
||||
void SetValue(object value);
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public class PluginFeatureAttribute : Attribute
|
||||
/// The plugins display icon that's shown in the settings see <see href="https://materialdesignicons.com" /> for
|
||||
/// available icons
|
||||
/// </summary>
|
||||
[Obsolete("Feature icons are no longer shown in the UI.")]
|
||||
public string? Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -31,20 +31,7 @@ public class PluginFeatureInfo : CorePropertyChanged, IPrerequisitesSubject
|
||||
|
||||
Name = attribute?.Name ?? featureType.Name.Humanize(LetterCasing.Title);
|
||||
Description = attribute?.Description;
|
||||
Icon = attribute?.Icon;
|
||||
AlwaysEnabled = attribute?.AlwaysEnabled ?? false;
|
||||
|
||||
if (Icon != null) return;
|
||||
if (typeof(DeviceProvider).IsAssignableFrom(featureType))
|
||||
Icon = "Devices";
|
||||
else if (typeof(Module).IsAssignableFrom(featureType))
|
||||
Icon = "VectorRectangle";
|
||||
else if (typeof(LayerBrushProvider).IsAssignableFrom(featureType))
|
||||
Icon = "Brush";
|
||||
else if (typeof(LayerEffectProvider).IsAssignableFrom(featureType))
|
||||
Icon = "AutoAwesome";
|
||||
else
|
||||
Icon = "Plugin";
|
||||
}
|
||||
|
||||
internal PluginFeatureInfo(Plugin plugin, PluginFeatureAttribute? attribute, PluginFeature instance)
|
||||
@@ -56,19 +43,8 @@ public class PluginFeatureInfo : CorePropertyChanged, IPrerequisitesSubject
|
||||
|
||||
Name = attribute?.Name ?? instance.GetType().Name.Humanize(LetterCasing.Title);
|
||||
Description = attribute?.Description;
|
||||
Icon = attribute?.Icon;
|
||||
AlwaysEnabled = attribute?.AlwaysEnabled ?? false;
|
||||
Instance = instance;
|
||||
|
||||
if (Icon != null) return;
|
||||
Icon = Instance switch
|
||||
{
|
||||
DeviceProvider => "Devices",
|
||||
Module => "VectorRectangle",
|
||||
LayerBrushProvider => "Brush",
|
||||
LayerEffectProvider => "AutoAwesome",
|
||||
_ => "Plugin"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -110,17 +86,6 @@ public class PluginFeatureInfo : CorePropertyChanged, IPrerequisitesSubject
|
||||
set => SetAndNotify(ref _description, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The plugins display icon that's shown in the settings see <see href="https://materialdesignicons.com" /> for
|
||||
/// available icons
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string? Icon
|
||||
{
|
||||
get => _icon;
|
||||
set => SetAndNotify(ref _icon, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the feature to always be enabled as long as the plugin is enabled and cannot be disabled.
|
||||
/// <para>Note: always <see langword="true" /> if this is the plugin's only feature</para>
|
||||
@@ -142,20 +107,7 @@ public class PluginFeatureInfo : CorePropertyChanged, IPrerequisitesSubject
|
||||
get => _instance;
|
||||
internal set => SetAndNotify(ref _instance, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representing either a full path pointing to an svg or the markdown icon
|
||||
/// </summary>
|
||||
public string? ResolvedIcon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Icon == null)
|
||||
return null;
|
||||
return Icon.Contains('.') ? Plugin.ResolveRelativePath(Icon) : Icon;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal PluginFeatureEntity Entity { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -23,7 +23,10 @@ internal class DataBindingExitNode<TLayerProperty> : Node, IExitNode
|
||||
public void ApplyToDataBinding()
|
||||
{
|
||||
foreach ((IDataBindingProperty? property, object? pendingValue) in _propertyValues)
|
||||
property.SetValue(pendingValue);
|
||||
{
|
||||
if (pendingValue != null)
|
||||
property.SetValue(pendingValue);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
|
||||
@@ -15,8 +15,7 @@ internal class EventConditionEventStartNode : DefaultNode
|
||||
|
||||
public void SetDataModelEvent(IDataModelEvent? dataModelEvent)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void CreatePins(IDataModelEvent? dataModelEvent)
|
||||
{
|
||||
|
||||
@@ -93,10 +93,8 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeRemoved;
|
||||
|
||||
if (defaultNodes != null)
|
||||
{
|
||||
foreach (DefaultNode defaultNode in defaultNodes)
|
||||
AddNode(defaultNode);
|
||||
}
|
||||
}
|
||||
|
||||
internal NodeScript(string name, string description, NodeScriptEntity entity, object? context = null, List<DefaultNode>? defaultNodes = null)
|
||||
@@ -109,12 +107,10 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
|
||||
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeAdded;
|
||||
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeRemoved;
|
||||
|
||||
|
||||
if (defaultNodes != null)
|
||||
{
|
||||
foreach (DefaultNode defaultNode in defaultNodes)
|
||||
AddNode(defaultNode);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,32 @@
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the position of a node's custom view model.
|
||||
/// </summary>
|
||||
public enum CustomNodeViewModelPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// Puts the view model above the pins.
|
||||
/// </summary>
|
||||
AbovePins,
|
||||
|
||||
/// <summary>
|
||||
/// Puts the view model between the pins, vertically aligned to the top.
|
||||
/// </summary>
|
||||
BetweenPinsTop,
|
||||
|
||||
/// <summary>
|
||||
/// Puts the view model between the pins, vertically aligned to the center.
|
||||
/// </summary>
|
||||
BetweenPinsCenter,
|
||||
|
||||
/// <summary>
|
||||
/// Puts the view model between the pins, vertically aligned to the bottom.
|
||||
/// </summary>
|
||||
BetweenPinsBottom,
|
||||
|
||||
/// <summary>
|
||||
/// Puts the view model below the pins.
|
||||
/// </summary>
|
||||
BelowPins
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,18 @@
|
||||
namespace Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a node that has a custom view model.
|
||||
/// </summary>
|
||||
public interface ICustomViewModelNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the node's custom view model.
|
||||
/// </summary>
|
||||
CustomNodeViewModelPosition ViewModelPosition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the node must show it's custom view model, if <see langword="null" />, no custom view model is used
|
||||
/// </summary>
|
||||
/// <returns>The custom view model, if <see langword="null" />, no custom view model is used</returns>
|
||||
ICustomNodeViewModel? GetCustomViewModel(NodeScript nodeScript);
|
||||
}
|
||||
+2
-112
@@ -2,10 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Artemis.Core.Events;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
|
||||
namespace Artemis.Core;
|
||||
|
||||
@@ -339,7 +336,8 @@ public abstract class Node : BreakableModel, INode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the node was loaded from storage or newly created, at this point pin connections aren't reestablished yet.
|
||||
/// Called when the node was loaded from storage or newly created, at this point pin connections aren't reestablished
|
||||
/// yet.
|
||||
/// </summary>
|
||||
/// <param name="script">The script the node is contained in</param>
|
||||
public virtual void Initialize(INodeScript script)
|
||||
@@ -374,16 +372,6 @@ public abstract class Node : BreakableModel, INode
|
||||
TryOrBreak(Evaluate, "Failed to evaluate");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the node must show it's custom view model, if <see langword="null" />, no custom view model is used
|
||||
/// </summary>
|
||||
/// <param name="nodeScript"></param>
|
||||
/// <returns>The custom view model, if <see langword="null" />, no custom view model is used</returns>
|
||||
public virtual ICustomNodeViewModel? GetCustomViewModel(NodeScript nodeScript)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the <see cref="Storage" /> object into a string
|
||||
/// </summary>
|
||||
@@ -402,102 +390,4 @@ public abstract class Node : BreakableModel, INode
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type
|
||||
/// <typeparamref name="TStorage" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="TStorage">The type of value the node stores</typeparam>
|
||||
public abstract class Node<TStorage> : Node
|
||||
{
|
||||
private TStorage? _storage;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected Node()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected Node(string name, string description) : base(name, description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the storage object of this node, this is saved across sessions
|
||||
/// </summary>
|
||||
public TStorage? Storage
|
||||
{
|
||||
get => _storage;
|
||||
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;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SerializeStorage()
|
||||
{
|
||||
return CoreJson.SerializeObject(Storage, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DeserializeStorage(string serialized)
|
||||
{
|
||||
Storage = CoreJson.DeserializeObject<TStorage>(serialized) ?? default(TStorage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="TStorage">The type of value the node stores</typeparam>
|
||||
/// <typeparam name="TViewModel">The type of view model the node uses</typeparam>
|
||||
public abstract class Node<TStorage, TViewModel> : Node<TStorage> where TViewModel : ICustomNodeViewModel
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected Node()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected Node(string name, string description) : base(name, description)
|
||||
{
|
||||
}
|
||||
|
||||
[Inject]
|
||||
internal IKernel Kernel { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a view model is required
|
||||
/// </summary>
|
||||
/// <param name="nodeScript"></param>
|
||||
public virtual TViewModel GetViewModel(NodeScript nodeScript)
|
||||
{
|
||||
// Limit to one constructor, there's no need to have more and it complicates things anyway
|
||||
ConstructorInfo[] constructors = typeof(TViewModel).GetConstructors();
|
||||
if (constructors.Length != 1)
|
||||
throw new ArtemisCoreException("Node VMs must have exactly one constructor");
|
||||
|
||||
// Find the ScriptConfiguration parameter, it is required by the base constructor so its there for sure
|
||||
ParameterInfo? configurationParameter = constructors.First().GetParameters().FirstOrDefault(p => GetType().IsAssignableFrom(p.ParameterType));
|
||||
|
||||
if (configurationParameter?.Name == null)
|
||||
throw new ArtemisCoreException($"Couldn't find a valid constructor argument on {typeof(TViewModel).Name} with type {GetType().Name}");
|
||||
return Kernel.Get<TViewModel>(new ConstructorArgument(configurationParameter.Name, this), new ConstructorArgument("script", nodeScript));
|
||||
}
|
||||
|
||||
/// <param name="nodeScript"></param>
|
||||
/// <inheritdoc />
|
||||
public override ICustomNodeViewModel? GetCustomViewModel(NodeScript nodeScript)
|
||||
{
|
||||
return GetViewModel(nodeScript);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a kind of node inside a <see cref="NodeScript" /> containing storage value of type
|
||||
/// <typeparamref name="TStorage" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="TStorage">The type of value the node stores</typeparam>
|
||||
public abstract class Node<TStorage> : Node
|
||||
{
|
||||
private TStorage? _storage;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected Node()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected Node(string name, string description) : base(name, description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the storage object of this node, this is saved across sessions
|
||||
/// </summary>
|
||||
public TStorage? Storage
|
||||
{
|
||||
get => _storage;
|
||||
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;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SerializeStorage()
|
||||
{
|
||||
return CoreJson.SerializeObject(Storage, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DeserializeStorage(string serialized)
|
||||
{
|
||||
Storage = CoreJson.DeserializeObject<TStorage>(serialized) ?? default(TStorage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Artemis.Core;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// 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" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="TStorage">The type of value the node stores</typeparam>
|
||||
/// <typeparam name="TViewModel">The type of view model the node uses</typeparam>
|
||||
public abstract class Node<TStorage, TViewModel> : Node<TStorage>, ICustomViewModelNode where TViewModel : ICustomNodeViewModel
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected Node()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected Node(string name, string description) : base(name, description)
|
||||
{
|
||||
}
|
||||
|
||||
[Inject]
|
||||
internal IKernel Kernel { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a view model is required
|
||||
/// </summary>
|
||||
/// <param name="nodeScript"></param>
|
||||
public virtual TViewModel GetViewModel(NodeScript nodeScript)
|
||||
{
|
||||
// Limit to one constructor, there's no need to have more and it complicates things anyway
|
||||
ConstructorInfo[] constructors = typeof(TViewModel).GetConstructors();
|
||||
if (constructors.Length != 1)
|
||||
throw new ArtemisCoreException("Node VMs must have exactly one constructor");
|
||||
|
||||
// Find the ScriptConfiguration parameter, it is required by the base constructor so its there for sure
|
||||
ParameterInfo? configurationParameter = constructors.First().GetParameters().FirstOrDefault(p => GetType().IsAssignableFrom(p.ParameterType));
|
||||
|
||||
if (configurationParameter?.Name == null)
|
||||
throw new ArtemisCoreException($"Couldn't find a valid constructor argument on {typeof(TViewModel).Name} with type {GetType().Name}");
|
||||
return Kernel.Get<TViewModel>(new ConstructorArgument(configurationParameter.Name, this), new ConstructorArgument("script", nodeScript));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the node's custom view model.
|
||||
/// </summary>
|
||||
public CustomNodeViewModelPosition ViewModelPosition { get; protected set; } = CustomNodeViewModelPosition.BetweenPinsTop;
|
||||
|
||||
/// <param name="nodeScript"></param>
|
||||
/// <inheritdoc />
|
||||
public ICustomNodeViewModel GetCustomViewModel(NodeScript nodeScript)
|
||||
{
|
||||
return GetViewModel(nodeScript);
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user