mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Core - Implemented node providers and removed nodes from the main project
This commit is contained in:
commit
93e74ef8d3
@ -5,7 +5,7 @@ using Artemis.Core.DryIoc.Factories;
|
|||||||
using Artemis.Core.Providers;
|
using Artemis.Core.Providers;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.Storage;
|
using Artemis.Storage;
|
||||||
using Artemis.Storage.Migrations.Interfaces;
|
using Artemis.Storage.Migrations;
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using DryIoc;
|
using DryIoc;
|
||||||
|
|
||||||
@ -36,6 +36,7 @@ public static class ContainerExtensions
|
|||||||
|
|
||||||
// Bind migrations
|
// Bind migrations
|
||||||
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IStorageMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
|
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IStorageMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
|
||||||
|
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IProfileMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
|
||||||
|
|
||||||
container.RegisterMany(coreAssembly, type => type.IsAssignableTo<ILayoutProvider>(), Reuse.Singleton);
|
container.RegisterMany(coreAssembly, type => type.IsAssignableTo<ILayoutProvider>(), Reuse.Singleton);
|
||||||
container.Register<IPluginSettingsFactory, PluginSettingsFactory>(Reuse.Singleton);
|
container.Register<IPluginSettingsFactory, PluginSettingsFactory>(Reuse.Singleton);
|
||||||
|
|||||||
81
src/Artemis.Core/Plugins/Nodes/NodeProvider.cs
Normal file
81
src/Artemis.Core/Plugins/Nodes/NodeProvider.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Reflection;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Nodes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows you to register one or more <see cref="INode" />s usable by node scripts.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class NodeProvider : PluginFeature
|
||||||
|
{
|
||||||
|
private readonly List<NodeData> _nodeDescriptors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="NodeProvider"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public NodeProvider()
|
||||||
|
{
|
||||||
|
_nodeDescriptors = new List<NodeData>();
|
||||||
|
NodeDescriptors = new ReadOnlyCollection<NodeData>(_nodeDescriptors);
|
||||||
|
Disabled += OnDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A read-only collection of all nodes added with <see cref="RegisterNodeType{T}" />
|
||||||
|
/// </summary>
|
||||||
|
public ReadOnlyCollection<NodeData> NodeDescriptors { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a node descriptor for a given node, so that it appears in the UI.
|
||||||
|
/// <para>Note: You do not need to manually remove these on disable</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the node you wish to register</typeparam>
|
||||||
|
protected void RegisterNodeType<T>() where T : INode
|
||||||
|
{
|
||||||
|
RegisterNodeType(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a node descriptor for a given node, so that it appears in the UI.
|
||||||
|
/// <para>Note: You do not need to manually remove these on disable</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeType">The type of the node you wish to register</param>
|
||||||
|
protected void RegisterNodeType(Type nodeType)
|
||||||
|
{
|
||||||
|
if (!IsEnabled)
|
||||||
|
throw new ArtemisPluginFeatureException(this, "Can only add a node descriptor when the plugin is enabled");
|
||||||
|
if (nodeType == null)
|
||||||
|
throw new ArgumentNullException(nameof(nodeType));
|
||||||
|
if (!nodeType.IsAssignableTo(typeof(INode)))
|
||||||
|
throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
|
||||||
|
|
||||||
|
NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute<NodeAttribute>();
|
||||||
|
string name = nodeAttribute?.Name ?? nodeType.Name;
|
||||||
|
string description = nodeAttribute?.Description ?? string.Empty;
|
||||||
|
string category = nodeAttribute?.Category ?? string.Empty;
|
||||||
|
string helpUrl = nodeAttribute?.HelpUrl ?? string.Empty;
|
||||||
|
|
||||||
|
NodeData nodeData = new(this, nodeType, name, description, category, helpUrl, nodeAttribute?.InputType, nodeAttribute?.OutputType);
|
||||||
|
_nodeDescriptors.Add(nodeData);
|
||||||
|
NodeTypeStore.Add(nodeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a color for lines of the provided type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The color to add.</param>
|
||||||
|
/// <typeparam name="T">The type to use the color for.</typeparam>
|
||||||
|
protected TypeColorRegistration RegisterTypeColor<T>(SKColor color)
|
||||||
|
{
|
||||||
|
return NodeTypeStore.AddColor(typeof(T), color, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisabled(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// The store will clean up the registrations by itself, the plugin feature just needs to clear its own list
|
||||||
|
_nodeDescriptors.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Artemis.Storage.Entities.Profile.Nodes;
|
using Artemis.Storage.Entities.Profile.Nodes;
|
||||||
using DryIoc;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
@ -13,31 +11,8 @@ namespace Artemis.Core.Services;
|
|||||||
|
|
||||||
internal class NodeService : INodeService
|
internal class NodeService : INodeService
|
||||||
{
|
{
|
||||||
#region Constants
|
|
||||||
|
|
||||||
private static readonly Type TypeNode = typeof(INode);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private readonly IContainer _container;
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public NodeService(IContainer container)
|
|
||||||
{
|
|
||||||
_container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
public IEnumerable<NodeData> AvailableNodes => NodeTypeStore.GetAll();
|
public IEnumerable<NodeData> AvailableNodes => NodeTypeStore.GetAll();
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public List<Type> GetRegisteredTypes()
|
public List<Type> GetRegisteredTypes()
|
||||||
{
|
{
|
||||||
@ -53,7 +28,7 @@ internal class NodeService : INodeService
|
|||||||
|
|
||||||
// Objects represent an input that can take any type, these are hardcoded white
|
// Objects represent an input that can take any type, these are hardcoded white
|
||||||
if (type == typeof(object))
|
if (type == typeof(object))
|
||||||
return new TypeColorRegistration(type, new SKColor(255, 255, 255, 255), Constants.CorePlugin);
|
return new TypeColorRegistration(type, new SKColor(255, 255, 255, 255), Constants.CorePluginFeature);
|
||||||
|
|
||||||
// Come up with a random color based on the type name that should be the same each time
|
// Come up with a random color based on the type name that should be the same each time
|
||||||
MD5 md5Hasher = MD5.Create();
|
MD5 md5Hasher = MD5.Create();
|
||||||
@ -61,32 +36,7 @@ internal class NodeService : INodeService
|
|||||||
int hash = BitConverter.ToInt32(hashed, 0);
|
int hash = BitConverter.ToInt32(hashed, 0);
|
||||||
|
|
||||||
SKColor baseColor = SKColor.FromHsl(hash % 255, 50 + hash % 50, 50);
|
SKColor baseColor = SKColor.FromHsl(hash % 255, 50 + hash % 50, 50);
|
||||||
return new TypeColorRegistration(type, baseColor, Constants.CorePlugin);
|
return new TypeColorRegistration(type, baseColor, Constants.CorePluginFeature);
|
||||||
}
|
|
||||||
|
|
||||||
public NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType)
|
|
||||||
{
|
|
||||||
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
|
|
||||||
if (nodeType == null) throw new ArgumentNullException(nameof(nodeType));
|
|
||||||
|
|
||||||
if (!TypeNode.IsAssignableFrom(nodeType)) throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
|
|
||||||
|
|
||||||
NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute<NodeAttribute>();
|
|
||||||
string name = nodeAttribute?.Name ?? nodeType.Name;
|
|
||||||
string description = nodeAttribute?.Description ?? string.Empty;
|
|
||||||
string category = nodeAttribute?.Category ?? string.Empty;
|
|
||||||
string helpUrl = nodeAttribute?.HelpUrl ?? string.Empty;
|
|
||||||
|
|
||||||
NodeData nodeData = new(plugin, nodeType, name, description, category, helpUrl, nodeAttribute?.InputType, nodeAttribute?.OutputType, (s, e) => CreateNode(s, e, nodeType));
|
|
||||||
return NodeTypeStore.Add(nodeData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color)
|
|
||||||
{
|
|
||||||
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
|
|
||||||
if (type == null) throw new ArgumentNullException(nameof(type));
|
|
||||||
|
|
||||||
return NodeTypeStore.AddColor(type, color, plugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ExportScript(NodeScript nodeScript)
|
public string ExportScript(NodeScript nodeScript)
|
||||||
@ -103,33 +53,6 @@ internal class NodeService : INodeService
|
|||||||
|
|
||||||
target.LoadFromEntity(entity);
|
target.LoadFromEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private INode CreateNode(INodeScript script, NodeEntity? entity, Type nodeType)
|
|
||||||
{
|
|
||||||
INode node = _container.Resolve(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
|
|
||||||
if (node is Node concreteNode)
|
|
||||||
concreteNode.Container = _container;
|
|
||||||
|
|
||||||
if (entity != null)
|
|
||||||
{
|
|
||||||
node.X = entity.X;
|
|
||||||
node.Y = entity.Y;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (node is Node nodeImplementation)
|
|
||||||
nodeImplementation.DeserializeStorage(entity.Storage);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node.TryInitialize(script);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -153,21 +76,6 @@ public interface INodeService : IArtemisService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
TypeColorRegistration GetTypeColorRegistration(Type type);
|
TypeColorRegistration GetTypeColorRegistration(Type type);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a node of the provided <paramref name="nodeType" />
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin">The plugin the node belongs to</param>
|
|
||||||
/// <param name="nodeType">The type of node to initialize</param>
|
|
||||||
NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a type with a provided color for use in the node editor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin">The plugin making the registration</param>
|
|
||||||
/// <param name="type">The type to associate the color with</param>
|
|
||||||
/// <param name="color">The color to display</param>
|
|
||||||
TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exports the provided node script to JSON.
|
/// Exports the provided node script to JSON.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -8,8 +8,10 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
using Artemis.Storage.Migrations;
|
||||||
using Artemis.Storage.Repositories.Interfaces;
|
using Artemis.Storage.Repositories.Interfaces;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ internal class ProfileService : IProfileService
|
|||||||
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
|
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
|
||||||
private readonly List<ProfileCategory> _profileCategories;
|
private readonly List<ProfileCategory> _profileCategories;
|
||||||
private readonly IProfileRepository _profileRepository;
|
private readonly IProfileRepository _profileRepository;
|
||||||
|
private readonly List<IProfileMigration> _profileMigrators;
|
||||||
private readonly List<Exception> _renderExceptions = new();
|
private readonly List<Exception> _renderExceptions = new();
|
||||||
private readonly List<Exception> _updateExceptions = new();
|
private readonly List<Exception> _updateExceptions = new();
|
||||||
|
|
||||||
@ -35,13 +38,15 @@ internal class ProfileService : IProfileService
|
|||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
IInputService inputService,
|
IInputService inputService,
|
||||||
IDeviceService deviceService,
|
IDeviceService deviceService,
|
||||||
IProfileRepository profileRepository)
|
IProfileRepository profileRepository,
|
||||||
|
List<IProfileMigration> profileMigrators)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_profileCategoryRepository = profileCategoryRepository;
|
_profileCategoryRepository = profileCategoryRepository;
|
||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
_deviceService = deviceService;
|
_deviceService = deviceService;
|
||||||
_profileRepository = profileRepository;
|
_profileRepository = profileRepository;
|
||||||
|
_profileMigrators = profileMigrators;
|
||||||
_profileCategories = new List<ProfileCategory>(_profileCategoryRepository.GetAll().Select(c => new ProfileCategory(c)).OrderBy(c => c.Order));
|
_profileCategories = new List<ProfileCategory>(_profileCategoryRepository.GetAll().Select(c => new ProfileCategory(c)).OrderBy(c => c.Order));
|
||||||
|
|
||||||
_deviceService.LedsChanged += DeviceServiceOnLedsChanged;
|
_deviceService.LedsChanged += DeviceServiceOnLedsChanged;
|
||||||
@ -461,7 +466,12 @@ internal class ProfileService : IProfileService
|
|||||||
|
|
||||||
await using Stream profileStream = profileEntry.Open();
|
await using Stream profileStream = profileEntry.Open();
|
||||||
using StreamReader profileReader = new(profileStream);
|
using StreamReader profileReader = new(profileStream);
|
||||||
ProfileEntity? profileEntity = JsonConvert.DeserializeObject<ProfileEntity>(await profileReader.ReadToEndAsync(), IProfileService.ExportSettings);
|
JObject? profileJson = JsonConvert.DeserializeObject<JObject>(await profileReader.ReadToEndAsync(), IProfileService.ExportSettings);
|
||||||
|
|
||||||
|
// Before deserializing, apply any pending migrations
|
||||||
|
MigrateProfile(configurationEntity, profileJson);
|
||||||
|
|
||||||
|
ProfileEntity? profileEntity = profileJson?.ToObject<ProfileEntity>(JsonSerializer.Create(IProfileService.ExportSettings));
|
||||||
if (profileEntity == null)
|
if (profileEntity == null)
|
||||||
throw new ArtemisCoreException("Could not import profile, failed to deserialize profile.json");
|
throw new ArtemisCoreException("Could not import profile, failed to deserialize profile.json");
|
||||||
|
|
||||||
@ -545,6 +555,21 @@ internal class ProfileService : IProfileService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MigrateProfile(ProfileConfigurationEntity configurationEntity, JObject? profileJson)
|
||||||
|
{
|
||||||
|
if (profileJson == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (IProfileMigration profileMigrator in _profileMigrators.OrderBy(m => m.Version))
|
||||||
|
{
|
||||||
|
if (profileMigrator.Version <= configurationEntity.Version)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
profileMigrator.Migrate(profileJson);
|
||||||
|
configurationEntity.Version = profileMigrator.Version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Populates all missing LEDs on all currently active profiles
|
/// Populates all missing LEDs on all currently active profiles
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -13,16 +13,13 @@ internal class NodeTypeStore
|
|||||||
|
|
||||||
public static NodeTypeRegistration Add(NodeData nodeData)
|
public static NodeTypeRegistration Add(NodeData nodeData)
|
||||||
{
|
{
|
||||||
if (nodeData.Plugin == null)
|
|
||||||
throw new ArtemisCoreException("Cannot add a data binding modifier type that is not associated with a plugin");
|
|
||||||
|
|
||||||
NodeTypeRegistration typeRegistration;
|
NodeTypeRegistration typeRegistration;
|
||||||
lock (Registrations)
|
lock (Registrations)
|
||||||
{
|
{
|
||||||
if (Registrations.Any(r => r.NodeData == nodeData))
|
if (Registrations.Any(r => r.NodeData == nodeData))
|
||||||
throw new ArtemisCoreException($"Data binding modifier type store already contains modifier '{nodeData.Name}'");
|
throw new ArtemisCoreException($"Data binding modifier type store already contains modifier '{nodeData.Name}'");
|
||||||
|
|
||||||
typeRegistration = new NodeTypeRegistration(nodeData, nodeData.Plugin) {IsInStore = true};
|
typeRegistration = new NodeTypeRegistration(nodeData, nodeData.Provider) {IsInStore = true};
|
||||||
Registrations.Add(typeRegistration);
|
Registrations.Add(typeRegistration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,24 +57,12 @@ internal class NodeTypeStore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Plugin? GetPlugin(INode node)
|
public static TypeColorRegistration AddColor(Type type, SKColor color, PluginFeature pluginFeature)
|
||||||
{
|
|
||||||
Type nodeType = node.GetType();
|
|
||||||
lock (Registrations)
|
|
||||||
{
|
|
||||||
return Registrations.FirstOrDefault(r => r.NodeData.Type == nodeType)?.Plugin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TypeColorRegistration AddColor(Type type, SKColor color, Plugin plugin)
|
|
||||||
{
|
{
|
||||||
TypeColorRegistration typeColorRegistration;
|
TypeColorRegistration typeColorRegistration;
|
||||||
lock (ColorRegistrations)
|
lock (ColorRegistrations)
|
||||||
{
|
{
|
||||||
if (ColorRegistrations.Any(r => r.Type == type))
|
typeColorRegistration = new TypeColorRegistration(type, color, pluginFeature) {IsInStore = true};
|
||||||
throw new ArtemisCoreException($"Node color store already contains a color for '{type.Name}'");
|
|
||||||
|
|
||||||
typeColorRegistration = new TypeColorRegistration(type, color, plugin) {IsInStore = true};
|
|
||||||
ColorRegistrations.Add(typeColorRegistration);
|
ColorRegistrations.Add(typeColorRegistration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,12 +9,12 @@ namespace Artemis.Core;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NodeTypeRegistration
|
public class NodeTypeRegistration
|
||||||
{
|
{
|
||||||
internal NodeTypeRegistration(NodeData nodeData, Plugin plugin)
|
internal NodeTypeRegistration(NodeData nodeData, PluginFeature pluginFeature)
|
||||||
{
|
{
|
||||||
NodeData = nodeData;
|
NodeData = nodeData;
|
||||||
Plugin = plugin;
|
PluginFeature = pluginFeature;
|
||||||
|
|
||||||
Plugin.Disabled += OnDisabled;
|
PluginFeature.Disabled += OnDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -23,9 +23,9 @@ public class NodeTypeRegistration
|
|||||||
public NodeData NodeData { get; }
|
public NodeData NodeData { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin the node is associated with
|
/// Gets the plugin feature the node is associated with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Plugin Plugin { get; }
|
public PluginFeature PluginFeature { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether the registration is in the internal Core store
|
/// Gets a boolean indicating whether the registration is in the internal Core store
|
||||||
@ -39,12 +39,12 @@ public class NodeTypeRegistration
|
|||||||
/// <returns><see langword="true" /> if the entity matches this registration; otherwise <see langword="false" />.</returns>
|
/// <returns><see langword="true" /> if the entity matches this registration; otherwise <see langword="false" />.</returns>
|
||||||
public bool MatchesEntity(NodeEntity entity)
|
public bool MatchesEntity(NodeEntity entity)
|
||||||
{
|
{
|
||||||
return Plugin.Guid == entity.PluginId && NodeData.Type.Name == entity.Type;
|
return PluginFeature.Id == entity.ProviderId && NodeData.Type.Name == entity.Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisabled(object? sender, EventArgs e)
|
private void OnDisabled(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Plugin.Disabled -= OnDisabled;
|
PluginFeature.Disabled -= OnDisabled;
|
||||||
if (IsInStore)
|
if (IsInStore)
|
||||||
NodeTypeStore.Remove(this);
|
NodeTypeStore.Remove(this);
|
||||||
}
|
}
|
||||||
@ -55,13 +55,13 @@ public class NodeTypeRegistration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class TypeColorRegistration
|
public class TypeColorRegistration
|
||||||
{
|
{
|
||||||
internal TypeColorRegistration(Type type, SKColor color, Plugin plugin)
|
internal TypeColorRegistration(Type type, SKColor color, PluginFeature pluginFeature)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
Color = color;
|
Color = color;
|
||||||
Plugin = plugin;
|
PluginFeature = pluginFeature;
|
||||||
|
|
||||||
Plugin.Disabled += OnDisabled;
|
PluginFeature.Disabled += OnDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -80,9 +80,9 @@ public class TypeColorRegistration
|
|||||||
public SKColor DarkenedColor => Color.Darken(0.35f);
|
public SKColor DarkenedColor => Color.Darken(0.35f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin type color is associated with
|
/// Gets the plugin feature this type color is associated with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Plugin Plugin { get; }
|
public PluginFeature PluginFeature { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether the registration is in the internal Core store
|
/// Gets a boolean indicating whether the registration is in the internal Core store
|
||||||
@ -91,7 +91,7 @@ public class TypeColorRegistration
|
|||||||
|
|
||||||
private void OnDisabled(object? sender, EventArgs e)
|
private void OnDisabled(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Plugin.Disabled -= OnDisabled;
|
PluginFeature.Disabled -= OnDisabled;
|
||||||
if (IsInStore)
|
if (IsInStore)
|
||||||
NodeTypeStore.RemoveColor(this);
|
NodeTypeStore.RemoveColor(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,11 @@ public interface INode : INotifyPropertyChanged, IBreakableModel
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Guid Id { get; set; }
|
Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the node data with information about this node
|
||||||
|
/// </summary>
|
||||||
|
NodeData? NodeData { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the node
|
/// Gets the name of the node
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Artemis.Core.Nodes;
|
||||||
using Artemis.Storage.Entities.Profile.Nodes;
|
using Artemis.Storage.Entities.Profile.Nodes;
|
||||||
|
|
||||||
namespace Artemis.Core;
|
namespace Artemis.Core;
|
||||||
@ -10,9 +11,9 @@ public class NodeData
|
|||||||
{
|
{
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
internal NodeData(Plugin plugin, Type type, string name, string description, string category, string helpUrl, Type? inputType, Type? outputType, Func<INodeScript, NodeEntity?, INode> create)
|
internal NodeData(NodeProvider provider, Type type, string name, string description, string category, string helpUrl, Type? inputType, Type? outputType)
|
||||||
{
|
{
|
||||||
Plugin = plugin;
|
Provider = provider;
|
||||||
Type = type;
|
Type = type;
|
||||||
Name = name;
|
Name = name;
|
||||||
Description = description;
|
Description = description;
|
||||||
@ -20,7 +21,6 @@ public class NodeData
|
|||||||
HelpUrl = helpUrl;
|
HelpUrl = helpUrl;
|
||||||
InputType = inputType;
|
InputType = inputType;
|
||||||
OutputType = outputType;
|
OutputType = outputType;
|
||||||
_create = create;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -35,7 +35,8 @@ public class NodeData
|
|||||||
/// <returns>The returning node of type <see cref="Type" /></returns>
|
/// <returns>The returning node of type <see cref="Type" /></returns>
|
||||||
public INode CreateNode(INodeScript script, NodeEntity? entity)
|
public INode CreateNode(INodeScript script, NodeEntity? entity)
|
||||||
{
|
{
|
||||||
INode node = _create(script, entity);
|
INode node = (INode) Provider.Plugin.Resolve(Type);
|
||||||
|
node.NodeData = this;
|
||||||
if (string.IsNullOrWhiteSpace(node.Name))
|
if (string.IsNullOrWhiteSpace(node.Name))
|
||||||
node.Name = Name;
|
node.Name = Name;
|
||||||
if (string.IsNullOrWhiteSpace(node.Description))
|
if (string.IsNullOrWhiteSpace(node.Description))
|
||||||
@ -43,6 +44,22 @@ public class NodeData
|
|||||||
if (string.IsNullOrWhiteSpace(node.HelpUrl))
|
if (string.IsNullOrWhiteSpace(node.HelpUrl))
|
||||||
node.HelpUrl = HelpUrl;
|
node.HelpUrl = HelpUrl;
|
||||||
|
|
||||||
|
if (entity != null)
|
||||||
|
{
|
||||||
|
node.X = entity.X;
|
||||||
|
node.Y = entity.Y;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (node is Node nodeImplementation)
|
||||||
|
nodeImplementation.DeserializeStorage(entity.Storage);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.TryInitialize(script);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,9 +110,9 @@ public class NodeData
|
|||||||
#region Properties & Fields
|
#region Properties & Fields
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin that provided this node data
|
/// Gets the node provider that provided this node data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Plugin Plugin { get; }
|
public NodeProvider Provider { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of <see cref="INode" /> this data represents
|
/// Gets the type of <see cref="INode" /> this data represents
|
||||||
@ -132,7 +149,5 @@ public class NodeData
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Type? OutputType { get; }
|
public Type? OutputType { get; }
|
||||||
|
|
||||||
private readonly Func<INodeScript, NodeEntity?, INode> _create;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@ -161,6 +161,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
|||||||
{
|
{
|
||||||
foreach (INode node in _nodes)
|
foreach (INode node in _nodes)
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once SuspiciousTypeConversion.Global - Provided by plugins
|
||||||
if (node is IDisposable disposable)
|
if (node is IDisposable disposable)
|
||||||
disposable.Dispose();
|
disposable.Dispose();
|
||||||
}
|
}
|
||||||
@ -181,6 +182,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
|||||||
foreach (INode removeNode in removeNodes)
|
foreach (INode removeNode in removeNodes)
|
||||||
{
|
{
|
||||||
RemoveNode(removeNode);
|
RemoveNode(removeNode);
|
||||||
|
// ReSharper disable once SuspiciousTypeConversion.Global - Provided by plugins
|
||||||
if (removeNode is IDisposable disposable)
|
if (removeNode is IDisposable disposable)
|
||||||
disposable.Dispose();
|
disposable.Dispose();
|
||||||
}
|
}
|
||||||
@ -312,7 +314,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
|||||||
NodeEntity nodeEntity = new()
|
NodeEntity nodeEntity = new()
|
||||||
{
|
{
|
||||||
Id = node.Id,
|
Id = node.Id,
|
||||||
PluginId = NodeTypeStore.GetPlugin(node)?.Guid ?? Constants.CorePlugin.Guid,
|
ProviderId = node.NodeData?.Provider.Id ?? Constants.CorePluginFeature.Id,
|
||||||
Type = node.GetType().Name,
|
Type = node.GetType().Name,
|
||||||
X = node.X,
|
X = node.X,
|
||||||
Y = node.Y,
|
Y = node.Y,
|
||||||
|
|||||||
@ -41,6 +41,9 @@ public abstract class Node : BreakableModel, INode
|
|||||||
set => SetAndNotify(ref _id, value);
|
set => SetAndNotify(ref _id, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public NodeData? NodeData { get; set; }
|
||||||
|
|
||||||
private string _name;
|
private string _name;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -104,8 +107,6 @@ public abstract class Node : BreakableModel, INode
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string BrokenDisplayName => Name;
|
public override string BrokenDisplayName => Name;
|
||||||
|
|
||||||
internal IContainer Container { get; set; } = null!;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Construtors
|
#region Construtors
|
||||||
|
|||||||
@ -25,7 +25,9 @@ public abstract class Node<TStorage, TViewModel> : Node<TStorage>, ICustomViewMo
|
|||||||
/// <param name="nodeScript"></param>
|
/// <param name="nodeScript"></param>
|
||||||
public virtual TViewModel GetViewModel(NodeScript nodeScript)
|
public virtual TViewModel GetViewModel(NodeScript nodeScript)
|
||||||
{
|
{
|
||||||
return Container.Resolve<TViewModel>(args: new object[] {this, nodeScript});
|
if (NodeData == null)
|
||||||
|
throw new ArtemisCoreException("Nodes without node data (default nodes or exit nodes) cannot have custom view models");
|
||||||
|
return NodeData.Provider.Plugin.Container.Resolve<TViewModel>(args: new object[] {this, nodeScript});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -15,7 +15,7 @@ public class NodeEntity
|
|||||||
{
|
{
|
||||||
Id = nodeEntity.Id;
|
Id = nodeEntity.Id;
|
||||||
Type = nodeEntity.Type;
|
Type = nodeEntity.Type;
|
||||||
PluginId = nodeEntity.PluginId;
|
ProviderId = nodeEntity.ProviderId;
|
||||||
|
|
||||||
Name = nodeEntity.Name;
|
Name = nodeEntity.Name;
|
||||||
Description = nodeEntity.Description;
|
Description = nodeEntity.Description;
|
||||||
@ -29,7 +29,7 @@ public class NodeEntity
|
|||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public string Type { get; set; } = string.Empty;
|
public string Type { get; set; } = string.Empty;
|
||||||
public Guid PluginId { get; set; }
|
public string ProviderId { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string Description { get; set; } = string.Empty;
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|||||||
@ -26,4 +26,5 @@ public class ProfileConfigurationEntity
|
|||||||
public Guid ProfileId { get; set; }
|
public Guid ProfileId { get; set; }
|
||||||
|
|
||||||
public bool FadeInAndOut { get; set; }
|
public bool FadeInAndOut { get; set; }
|
||||||
|
public int Version { get; set; }
|
||||||
}
|
}
|
||||||
@ -1,11 +1,9 @@
|
|||||||
using LiteDB;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Artemis.Storage.Migrations;
|
namespace Artemis.Storage.Migrations;
|
||||||
|
|
||||||
internal interface IProfileMigration
|
public interface IProfileMigration
|
||||||
{
|
{
|
||||||
int Version { get; }
|
int Version { get; }
|
||||||
void Migrate(JObject profileJson);
|
void Migrate(JObject profileJson);
|
||||||
void Migrate(BsonDocument profileBson);
|
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
namespace Artemis.Storage.Migrations.Interfaces;
|
namespace Artemis.Storage.Migrations;
|
||||||
|
|
||||||
public interface IStorageMigration
|
public interface IStorageMigration
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
using LiteDB;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Artemis.Storage.Migrations.Profile;
|
namespace Artemis.Storage.Migrations.Profile;
|
||||||
@ -21,10 +20,8 @@ internal class M0001NodeProviders : IProfileMigration
|
|||||||
if (folders != null)
|
if (folders != null)
|
||||||
{
|
{
|
||||||
foreach (JToken folder in folders)
|
foreach (JToken folder in folders)
|
||||||
{
|
|
||||||
MigrateProfileElement(folder);
|
MigrateProfileElement(folder);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (layers != null)
|
if (layers != null)
|
||||||
{
|
{
|
||||||
@ -38,12 +35,6 @@ internal class M0001NodeProviders : IProfileMigration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Migrate(BsonDocument profileBson)
|
|
||||||
{
|
|
||||||
throw new System.NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MigrateProfileElement(JToken profileElement)
|
private void MigrateProfileElement(JToken profileElement)
|
||||||
{
|
{
|
||||||
JArray? layerEffects = (JArray?) profileElement["LayerEffects"]?["$values"];
|
JArray? layerEffects = (JArray?) profileElement["LayerEffects"]?["$values"];
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Storage.Migrations.Interfaces;
|
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
namespace Artemis.Storage.Migrations;
|
namespace Artemis.Storage.Migrations.Storage;
|
||||||
|
|
||||||
public class M0020AvaloniaReset : IStorageMigration
|
public class M0020AvaloniaReset : IStorageMigration
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,10 +3,9 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Artemis.Storage.Entities.Profile.Nodes;
|
using Artemis.Storage.Entities.Profile.Nodes;
|
||||||
using Artemis.Storage.Migrations.Interfaces;
|
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
namespace Artemis.Storage.Migrations;
|
namespace Artemis.Storage.Migrations.Storage;
|
||||||
|
|
||||||
public class M0021GradientNodes : IStorageMigration
|
public class M0021GradientNodes : IStorageMigration
|
||||||
{
|
{
|
||||||
@ -22,7 +21,7 @@ public class M0021GradientNodes : IStorageMigration
|
|||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Type = "ColorGradientNode",
|
Type = "ColorGradientNode",
|
||||||
PluginId = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"),
|
ProviderId = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78",
|
||||||
Name = "Color Gradient",
|
Name = "Color Gradient",
|
||||||
Description = "Outputs a color gradient with the given colors",
|
Description = "Outputs a color gradient with the given colors",
|
||||||
X = exitNode.X,
|
X = exitNode.X,
|
||||||
|
|||||||
@ -3,10 +3,9 @@ using Artemis.Storage.Entities.Profile;
|
|||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
using Artemis.Storage.Entities.Profile.Conditions;
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
using Artemis.Storage.Entities.Profile.Nodes;
|
using Artemis.Storage.Entities.Profile.Nodes;
|
||||||
using Artemis.Storage.Migrations.Interfaces;
|
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
namespace Artemis.Storage.Migrations;
|
namespace Artemis.Storage.Migrations.Storage;
|
||||||
|
|
||||||
public class M0022TransitionNodes : IStorageMigration
|
public class M0022TransitionNodes : IStorageMigration
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Artemis.Storage.Migrations.Interfaces;
|
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
namespace Artemis.Storage.Migrations;
|
namespace Artemis.Storage.Migrations.Storage;
|
||||||
|
|
||||||
public class M0023LayoutProviders : IStorageMigration
|
public class M0023LayoutProviders : IStorageMigration
|
||||||
{
|
{
|
||||||
|
|||||||
100
src/Artemis.Storage/Migrations/Storage/M0024NodeProviders.cs
Normal file
100
src/Artemis.Storage/Migrations/Storage/M0024NodeProviders.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
using LiteDB;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Migrations.Storage;
|
||||||
|
|
||||||
|
public class M0024NodeProviders : IStorageMigration
|
||||||
|
{
|
||||||
|
public int UserVersion => 24;
|
||||||
|
|
||||||
|
public void Apply(LiteRepository repository)
|
||||||
|
{
|
||||||
|
List<ProfileCategoryEntity> profileCategories = repository.Query<ProfileCategoryEntity>().ToList();
|
||||||
|
foreach (ProfileCategoryEntity profileCategory in profileCategories)
|
||||||
|
{
|
||||||
|
foreach (ProfileConfigurationEntity profileConfigurationEntity in profileCategory.ProfileConfigurations)
|
||||||
|
{
|
||||||
|
profileConfigurationEntity.Version = 1;
|
||||||
|
}
|
||||||
|
repository.Update(profileCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
ILiteCollection<BsonDocument> collection = repository.Database.GetCollection("ProfileEntity");
|
||||||
|
foreach (BsonDocument profileBson in collection.FindAll())
|
||||||
|
{
|
||||||
|
BsonArray? folders = profileBson["Folders"]?.AsArray;
|
||||||
|
BsonArray? layers = profileBson["Layers"]?.AsArray;
|
||||||
|
|
||||||
|
if (folders != null)
|
||||||
|
{
|
||||||
|
foreach (BsonValue folder in folders)
|
||||||
|
MigrateProfileElement(folder.AsDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layers != null)
|
||||||
|
{
|
||||||
|
foreach (BsonValue layer in layers)
|
||||||
|
{
|
||||||
|
MigrateProfileElement(layer.AsDocument);
|
||||||
|
MigratePropertyGroup(layer.AsDocument["GeneralPropertyGroup"].AsDocument);
|
||||||
|
MigratePropertyGroup(layer.AsDocument["TransformPropertyGroup"].AsDocument);
|
||||||
|
MigratePropertyGroup(layer.AsDocument["LayerBrush"]?["PropertyGroup"].AsDocument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.Update(profileBson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MigrateProfileElement(BsonDocument profileElement)
|
||||||
|
{
|
||||||
|
BsonArray? layerEffects = profileElement["LayerEffects"]?.AsArray;
|
||||||
|
if (layerEffects != null)
|
||||||
|
{
|
||||||
|
foreach (BsonValue layerEffect in layerEffects)
|
||||||
|
MigratePropertyGroup(layerEffect.AsDocument["PropertyGroup"].AsDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
BsonValue? displayCondition = profileElement["DisplayCondition"];
|
||||||
|
if (displayCondition != null)
|
||||||
|
MigrateNodeScript(displayCondition.AsDocument["Script"].AsDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MigratePropertyGroup(BsonDocument? propertyGroup)
|
||||||
|
{
|
||||||
|
if (propertyGroup == null || propertyGroup.Keys.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BsonArray? properties = propertyGroup["Properties"]?.AsArray;
|
||||||
|
BsonArray? propertyGroups = propertyGroup["PropertyGroups"]?.AsArray;
|
||||||
|
|
||||||
|
if (properties != null)
|
||||||
|
{
|
||||||
|
foreach (BsonValue property in properties)
|
||||||
|
MigrateNodeScript(property.AsDocument["DataBinding"]?["NodeScript"]?.AsDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyGroups != null)
|
||||||
|
{
|
||||||
|
foreach (BsonValue childPropertyGroup in propertyGroups)
|
||||||
|
MigratePropertyGroup(childPropertyGroup.AsDocument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MigrateNodeScript(BsonDocument? nodeScript)
|
||||||
|
{
|
||||||
|
if (nodeScript == null || nodeScript.Keys.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BsonArray? nodes = nodeScript["Nodes"]?.AsArray;
|
||||||
|
if (nodes == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (BsonValue node in nodes)
|
||||||
|
{
|
||||||
|
node.AsDocument["Type"] = node.AsDocument["Type"]?.AsString?.Replace("Artemis.VisualScripting.Nodes", "Artemis.Plugins.Nodes.General.Nodes");
|
||||||
|
node.AsDocument["ProviderId"] = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -29,7 +29,6 @@ internal class PluginRepository : IPluginRepository
|
|||||||
public void SavePlugin(PluginEntity pluginEntity)
|
public void SavePlugin(PluginEntity pluginEntity)
|
||||||
{
|
{
|
||||||
_repository.Upsert(pluginEntity);
|
_repository.Upsert(pluginEntity);
|
||||||
_repository.Database.Checkpoint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddSetting(PluginSettingEntity pluginSettingEntity)
|
public void AddSetting(PluginSettingEntity pluginSettingEntity)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Storage.Migrations.Interfaces;
|
using Artemis.Storage.Migrations;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|||||||
@ -18,8 +18,10 @@ internal class DataModelUIService : IDataModelUIService
|
|||||||
private readonly IContainer _container;
|
private readonly IContainer _container;
|
||||||
private readonly List<DataModelVisualizationRegistration> _registeredDataModelDisplays;
|
private readonly List<DataModelVisualizationRegistration> _registeredDataModelDisplays;
|
||||||
private readonly List<DataModelVisualizationRegistration> _registeredDataModelEditors;
|
private readonly List<DataModelVisualizationRegistration> _registeredDataModelEditors;
|
||||||
|
private readonly PluginSetting<bool> _showFullPaths;
|
||||||
|
private readonly PluginSetting<bool> _showDataModelValues;
|
||||||
|
|
||||||
public DataModelUIService(IDataModelService dataModelService, IContainer container)
|
public DataModelUIService(IDataModelService dataModelService, IContainer container, ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_dataModelService = dataModelService;
|
_dataModelService = dataModelService;
|
||||||
_container = container;
|
_container = container;
|
||||||
@ -28,6 +30,8 @@ internal class DataModelUIService : IDataModelUIService
|
|||||||
|
|
||||||
RegisteredDataModelEditors = new ReadOnlyCollection<DataModelVisualizationRegistration>(_registeredDataModelEditors);
|
RegisteredDataModelEditors = new ReadOnlyCollection<DataModelVisualizationRegistration>(_registeredDataModelEditors);
|
||||||
RegisteredDataModelDisplays = new ReadOnlyCollection<DataModelVisualizationRegistration>(_registeredDataModelDisplays);
|
RegisteredDataModelDisplays = new ReadOnlyCollection<DataModelVisualizationRegistration>(_registeredDataModelDisplays);
|
||||||
|
ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
|
||||||
|
ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute? description, object? initialValue)
|
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute? description, object? initialValue)
|
||||||
@ -43,6 +47,8 @@ internal class DataModelUIService : IDataModelUIService
|
|||||||
|
|
||||||
public IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelEditors { get; }
|
public IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelEditors { get; }
|
||||||
public IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelDisplays { get; }
|
public IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelDisplays { get; }
|
||||||
|
public PluginSetting<bool> ShowFullPaths { get; }
|
||||||
|
public PluginSetting<bool> ShowDataModelValues { get; }
|
||||||
|
|
||||||
public DataModelPropertiesViewModel GetMainDataModelVisualization()
|
public DataModelPropertiesViewModel GetMainDataModelVisualization()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -104,4 +104,14 @@ public interface IDataModelUIService : IArtemisSharedUIService
|
|||||||
/// <param name="updateCallback">A function to call whenever the input was updated (submitted or not)</param>
|
/// <param name="updateCallback">A function to call whenever the input was updated (submitted or not)</param>
|
||||||
/// <returns>The most appropriate input view model for the provided <paramref name="propertyType" /></returns>
|
/// <returns>The most appropriate input view model for the provided <paramref name="propertyType" /></returns>
|
||||||
DataModelInputViewModel? GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute? description, object? initialValue, Action<object?, bool> updateCallback);
|
DataModelInputViewModel? GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute? description, object? initialValue, Action<object?, bool> updateCallback);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean indicating whether or not to show full paths when displaying data model paths.
|
||||||
|
/// </summary>
|
||||||
|
PluginSetting<bool> ShowFullPaths { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a boolean indicating whether or not to show values when displaying data model paths.
|
||||||
|
/// </summary>
|
||||||
|
PluginSetting<bool> ShowDataModelValues { get; }
|
||||||
}
|
}
|
||||||
@ -27,6 +27,7 @@ public class AddNode : INodeEditorCommand, IDisposable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once SuspiciousTypeConversion.Global - Provided by plugins
|
||||||
if (_isRemoved && _node is IDisposable disposableNode)
|
if (_isRemoved && _node is IDisposable disposableNode)
|
||||||
disposableNode.Dispose();
|
disposableNode.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,7 @@ public class DeleteNode : INodeEditorCommand, IDisposable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once SuspiciousTypeConversion.Global - Provided by plugins
|
||||||
if (_isRemoved && _node is IDisposable disposableNode)
|
if (_isRemoved && _node is IDisposable disposableNode)
|
||||||
disposableNode.Dispose();
|
disposableNode.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,6 +87,7 @@ public class DuplicateNode : INodeEditorCommand, IDisposable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once SuspiciousTypeConversion.Global - Provided by plugins
|
||||||
if (!_executed && _copy is IDisposable disposableNode)
|
if (!_executed && _copy is IDisposable disposableNode)
|
||||||
disposableNode.Dispose();
|
disposableNode.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||||
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj" />
|
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj" />
|
||||||
<ProjectReference Include="..\Artemis.VisualScripting\Artemis.VisualScripting.csproj" />
|
|
||||||
<ProjectReference Include="..\Artemis.WebClient.Updating\Artemis.WebClient.Updating.csproj" />
|
<ProjectReference Include="..\Artemis.WebClient.Updating\Artemis.WebClient.Updating.csproj" />
|
||||||
<ProjectReference Include="..\Artemis.WebClient.Workshop\Artemis.WebClient.Workshop.csproj" />
|
<ProjectReference Include="..\Artemis.WebClient.Workshop\Artemis.WebClient.Workshop.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -10,10 +10,8 @@ using Artemis.UI.Screens.Root;
|
|||||||
using Artemis.UI.Shared.DataModelPicker;
|
using Artemis.UI.Shared.DataModelPicker;
|
||||||
using Artemis.UI.Shared.DryIoc;
|
using Artemis.UI.Shared.DryIoc;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Artemis.VisualScripting.DryIoc;
|
|
||||||
using Artemis.WebClient.Updating.DryIoc;
|
using Artemis.WebClient.Updating.DryIoc;
|
||||||
using Artemis.WebClient.Workshop.DryIoc;
|
using Artemis.WebClient.Workshop.DryIoc;
|
||||||
using Artemis.WebClient.Workshop.Services;
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
@ -49,7 +47,6 @@ public static class ArtemisBootstrapper
|
|||||||
_container.RegisterSharedUI();
|
_container.RegisterSharedUI();
|
||||||
_container.RegisterUpdatingClient();
|
_container.RegisterUpdatingClient();
|
||||||
_container.RegisterWorkshopClient();
|
_container.RegisterWorkshopClient();
|
||||||
_container.RegisterNoStringEvaluating();
|
|
||||||
configureServices?.Invoke(_container);
|
configureServices?.Invoke(_container);
|
||||||
|
|
||||||
_container.UseDryIocDependencyResolver();
|
_container.UseDryIocDependencyResolver();
|
||||||
|
|||||||
@ -18,11 +18,13 @@ using DynamicData;
|
|||||||
using DynamicData.Binding;
|
using DynamicData.Binding;
|
||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.VisualScripting;
|
namespace Artemis.UI.Screens.VisualScripting;
|
||||||
|
|
||||||
public partial class NodeViewModel : ActivatableViewModelBase
|
public partial class NodeViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
private readonly INodeEditorService _nodeEditorService;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private ObservableAsPropertyHelper<bool>? _hasInputPins;
|
private ObservableAsPropertyHelper<bool>? _hasInputPins;
|
||||||
@ -39,8 +41,9 @@ public partial class NodeViewModel : ActivatableViewModelBase
|
|||||||
[Notify] private bool _displayCustomViewModelBelow;
|
[Notify] private bool _displayCustomViewModelBelow;
|
||||||
[Notify] private VerticalAlignment _customViewModelVerticalAlignment;
|
[Notify] private VerticalAlignment _customViewModelVerticalAlignment;
|
||||||
|
|
||||||
public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService, IWindowService windowService)
|
public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, ILogger logger, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService, IWindowService windowService)
|
||||||
{
|
{
|
||||||
|
_logger = logger;
|
||||||
_nodeEditorService = nodeEditorService;
|
_nodeEditorService = nodeEditorService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
NodeScriptViewModel = nodeScriptViewModel;
|
NodeScriptViewModel = nodeScriptViewModel;
|
||||||
@ -137,25 +140,7 @@ public partial class NodeViewModel : ActivatableViewModelBase
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Set up the custom node VM if needed
|
// Set up the custom node VM if needed
|
||||||
if (Node is ICustomViewModelNode customViewModelNode)
|
SetupCustomNodeViewModel();
|
||||||
{
|
|
||||||
CustomNodeViewModel = customViewModelNode.GetCustomViewModel(nodeScriptViewModel.NodeScript);
|
|
||||||
if (customViewModelNode.ViewModelPosition == CustomNodeViewModelPosition.AbovePins)
|
|
||||||
DisplayCustomViewModelAbove = true;
|
|
||||||
else if (customViewModelNode.ViewModelPosition == CustomNodeViewModelPosition.BelowPins)
|
|
||||||
DisplayCustomViewModelBelow = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DisplayCustomViewModelBetween = true;
|
|
||||||
|
|
||||||
if (customViewModelNode.ViewModelPosition == CustomNodeViewModelPosition.BetweenPinsTop)
|
|
||||||
CustomViewModelVerticalAlignment = VerticalAlignment.Top;
|
|
||||||
else if (customViewModelNode.ViewModelPosition == CustomNodeViewModelPosition.BetweenPinsTop)
|
|
||||||
CustomViewModelVerticalAlignment = VerticalAlignment.Center;
|
|
||||||
else
|
|
||||||
CustomViewModelVerticalAlignment = VerticalAlignment.Bottom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,4 +194,35 @@ public partial class NodeViewModel : ActivatableViewModelBase
|
|||||||
if (Node.BrokenState != null && Node.BrokenStateException != null)
|
if (Node.BrokenState != null && Node.BrokenStateException != null)
|
||||||
_windowService.ShowExceptionDialog(Node.BrokenState, Node.BrokenStateException);
|
_windowService.ShowExceptionDialog(Node.BrokenState, Node.BrokenStateException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetupCustomNodeViewModel()
|
||||||
|
{
|
||||||
|
if (Node is not ICustomViewModelNode customViewModelNode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CustomNodeViewModel = customViewModelNode.GetCustomViewModel(NodeScriptViewModel.NodeScript);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Error(e, "Failed to instantiate custom node view model");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customViewModelNode.ViewModelPosition == CustomNodeViewModelPosition.AbovePins)
|
||||||
|
DisplayCustomViewModelAbove = true;
|
||||||
|
else if (customViewModelNode.ViewModelPosition == CustomNodeViewModelPosition.BelowPins)
|
||||||
|
DisplayCustomViewModelBelow = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DisplayCustomViewModelBetween = true;
|
||||||
|
|
||||||
|
if (customViewModelNode.ViewModelPosition == CustomNodeViewModelPosition.BetweenPinsTop)
|
||||||
|
CustomViewModelVerticalAlignment = VerticalAlignment.Top;
|
||||||
|
else if (customViewModelNode.ViewModelPosition == CustomNodeViewModelPosition.BetweenPinsTop)
|
||||||
|
CustomViewModelVerticalAlignment = VerticalAlignment.Center;
|
||||||
|
else
|
||||||
|
CustomViewModelVerticalAlignment = VerticalAlignment.Bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -6,5 +6,4 @@ public interface IRegistrationService : IArtemisUIService
|
|||||||
void RegisterBuiltInDataModelInputs();
|
void RegisterBuiltInDataModelInputs();
|
||||||
void RegisterBuiltInPropertyEditors();
|
void RegisterBuiltInPropertyEditors();
|
||||||
void RegisterControllers();
|
void RegisterControllers();
|
||||||
void RegisterBuiltInNodeTypes();
|
|
||||||
}
|
}
|
||||||
@ -1,7 +1,4 @@
|
|||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Controllers;
|
using Artemis.UI.Controllers;
|
||||||
@ -13,10 +10,8 @@ using Artemis.UI.Shared.Routing;
|
|||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||||
using Artemis.UI.Shared.Services.PropertyInput;
|
using Artemis.UI.Shared.Services.PropertyInput;
|
||||||
using Artemis.VisualScripting.Nodes.Mathematics;
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using DryIoc;
|
using DryIoc;
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.UI.Services;
|
namespace Artemis.UI.Services;
|
||||||
|
|
||||||
@ -26,7 +21,6 @@ public class RegistrationService : IRegistrationService
|
|||||||
private readonly IInputService _inputService;
|
private readonly IInputService _inputService;
|
||||||
private readonly IContainer _container;
|
private readonly IContainer _container;
|
||||||
private readonly IRouter _router;
|
private readonly IRouter _router;
|
||||||
private readonly INodeService _nodeService;
|
|
||||||
private readonly IPropertyInputService _propertyInputService;
|
private readonly IPropertyInputService _propertyInputService;
|
||||||
private readonly IWebServerService _webServerService;
|
private readonly IWebServerService _webServerService;
|
||||||
private bool _registeredBuiltInPropertyEditors;
|
private bool _registeredBuiltInPropertyEditors;
|
||||||
@ -36,7 +30,6 @@ public class RegistrationService : IRegistrationService
|
|||||||
IInputService inputService,
|
IInputService inputService,
|
||||||
IPropertyInputService propertyInputService,
|
IPropertyInputService propertyInputService,
|
||||||
IProfileEditorService profileEditorService,
|
IProfileEditorService profileEditorService,
|
||||||
INodeService nodeService,
|
|
||||||
IDataModelUIService dataModelUIService,
|
IDataModelUIService dataModelUIService,
|
||||||
IWebServerService webServerService,
|
IWebServerService webServerService,
|
||||||
IDeviceLayoutService deviceLayoutService // here to make sure it is instantiated
|
IDeviceLayoutService deviceLayoutService // here to make sure it is instantiated
|
||||||
@ -46,13 +39,11 @@ public class RegistrationService : IRegistrationService
|
|||||||
_router = router;
|
_router = router;
|
||||||
_inputService = inputService;
|
_inputService = inputService;
|
||||||
_propertyInputService = propertyInputService;
|
_propertyInputService = propertyInputService;
|
||||||
_nodeService = nodeService;
|
|
||||||
_dataModelUIService = dataModelUIService;
|
_dataModelUIService = dataModelUIService;
|
||||||
_webServerService = webServerService;
|
_webServerService = webServerService;
|
||||||
|
|
||||||
CreateCursorResources();
|
CreateCursorResources();
|
||||||
RegisterRoutes();
|
RegisterRoutes();
|
||||||
RegisterBuiltInNodeTypes();
|
|
||||||
RegisterControllers();
|
RegisterControllers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,22 +96,4 @@ public class RegistrationService : IRegistrationService
|
|||||||
{
|
{
|
||||||
_webServerService.AddController<RemoteController>(Constants.CorePlugin.Features.First().Instance!);
|
_webServerService.AddController<RemoteController>(Constants.CorePlugin.Features.First().Instance!);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterBuiltInNodeTypes()
|
|
||||||
{
|
|
||||||
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(bool), new SKColor(0xFFCD3232));
|
|
||||||
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(string), new SKColor(0xFFFFD700));
|
|
||||||
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(Numeric), new SKColor(0xFF32CD32));
|
|
||||||
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(float), new SKColor(0xFFFF7C00));
|
|
||||||
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(SKColor), new SKColor(0xFFAD3EED));
|
|
||||||
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(IList), new SKColor(0xFFED3E61));
|
|
||||||
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(Enum), new SKColor(0xFF1E90FF));
|
|
||||||
_nodeService.RegisterTypeColor(Constants.CorePlugin, typeof(ColorGradient), new SKColor(0xFF00B2A9));
|
|
||||||
|
|
||||||
foreach (Type nodeType in typeof(SumNumericsNode).Assembly.GetTypes().Where(t => typeof(INode).IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract && !t.IsInterface))
|
|
||||||
{
|
|
||||||
if (nodeType.GetCustomAttribute(typeof(NodeAttribute)) != null)
|
|
||||||
_nodeService.RegisterNodeType(Constants.CorePlugin, nodeType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,17 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<Platforms>x64</Platforms>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="NoStringEvaluating" Version="2.5.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
|
||||||
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Avalonia.Data.Converters;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Converters;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts input into <see cref="Numeric" />.
|
|
||||||
/// </summary>
|
|
||||||
public class JsonConverter : IValueConverter
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
return JsonConvert.SerializeObject(value, Formatting.Indented);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
string? json = value?.ToString();
|
|
||||||
return json == null ? null : JsonConvert.DeserializeObject(json, targetType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Avalonia.Data.Converters;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Converters;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts input into <see cref="Numeric" />.
|
|
||||||
/// </summary>
|
|
||||||
public class NumericConverter : IValueConverter
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
if (value is not Numeric numeric)
|
|
||||||
return value;
|
|
||||||
|
|
||||||
object result = Numeric.IsTypeCompatible(targetType) ? numeric.ToType(targetType, NumberFormatInfo.InvariantInfo) : value;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
return new Numeric(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
using DryIoc;
|
|
||||||
using Microsoft.Extensions.ObjectPool;
|
|
||||||
using NoStringEvaluating;
|
|
||||||
using NoStringEvaluating.Contract;
|
|
||||||
using NoStringEvaluating.Models.Values;
|
|
||||||
using NoStringEvaluating.Services.Cache;
|
|
||||||
using NoStringEvaluating.Services.Checking;
|
|
||||||
using NoStringEvaluating.Services.Parsing;
|
|
||||||
using NoStringEvaluating.Services.Parsing.NodeReaders;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.DryIoc;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides an extension method to register services onto a DryIoc <see cref="IContainer"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static class ContainerExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Registers NoStringEvaluating services into the container.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The builder building the current container</param>
|
|
||||||
public static void RegisterNoStringEvaluating(this IContainer container)
|
|
||||||
{
|
|
||||||
// Pooling
|
|
||||||
container.RegisterInstance(ObjectPool.Create<Stack<InternalEvaluatorValue>>());
|
|
||||||
container.RegisterInstance(ObjectPool.Create<List<InternalEvaluatorValue>>());
|
|
||||||
container.RegisterInstance(ObjectPool.Create<ValueKeeperContainer>());
|
|
||||||
|
|
||||||
// Parser
|
|
||||||
container.Register<IFormulaCache, FormulaCache>(Reuse.Singleton);
|
|
||||||
container.Register<IFunctionReader, FunctionReader>(Reuse.Singleton);
|
|
||||||
container.Register<IFormulaParser, FormulaParser>(Reuse.Singleton);
|
|
||||||
|
|
||||||
// Checker
|
|
||||||
container.Register<IFormulaChecker, FormulaChecker>(Reuse.Singleton);
|
|
||||||
|
|
||||||
// Evaluator
|
|
||||||
container.Register<INoStringEvaluator, NoStringEvaluator>(Reuse.Singleton);
|
|
||||||
container.Register<INoStringEvaluatorNullable, NoStringEvaluatorNullable>(Reuse.Singleton);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Events;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Branching;
|
|
||||||
|
|
||||||
[Node("Branch", "Forwards one of two values depending on an input boolean", "Branching", InputType = typeof(object), OutputType = typeof(object))]
|
|
||||||
public class BooleanBranchNode : Node
|
|
||||||
{
|
|
||||||
public BooleanBranchNode()
|
|
||||||
{
|
|
||||||
BooleanInput = CreateInputPin<bool>();
|
|
||||||
TrueInput = CreateInputPin(typeof(object), "True");
|
|
||||||
FalseInput = CreateInputPin(typeof(object), "False");
|
|
||||||
|
|
||||||
Output = CreateOutputPin(typeof(object));
|
|
||||||
|
|
||||||
TrueInput.PinConnected += InputPinConnected;
|
|
||||||
FalseInput.PinConnected += InputPinConnected;
|
|
||||||
TrueInput.PinDisconnected += InputPinDisconnected;
|
|
||||||
FalseInput.PinDisconnected += InputPinDisconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<bool> BooleanInput { get; set; }
|
|
||||||
public InputPin TrueInput { get; set; }
|
|
||||||
public InputPin FalseInput { get; set; }
|
|
||||||
|
|
||||||
public OutputPin Output { get; set; }
|
|
||||||
|
|
||||||
#region Overrides of Node
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Output.Value = BooleanInput.Value ? TrueInput.Value : FalseInput.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private void InputPinConnected(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
if (TrueInput.ConnectedTo.Any())
|
|
||||||
ChangeType(TrueInput.ConnectedTo.First().Type);
|
|
||||||
else if (FalseInput.ConnectedTo.Any())
|
|
||||||
ChangeType(FalseInput.ConnectedTo.First().Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InputPinDisconnected(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
if (!TrueInput.ConnectedTo.Any() && !FalseInput.ConnectedTo.Any())
|
|
||||||
ChangeType(typeof(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ChangeType(Type type)
|
|
||||||
{
|
|
||||||
TrueInput.ChangeType(type);
|
|
||||||
FalseInput.ChangeType(type);
|
|
||||||
Output.ChangeType(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Events;
|
|
||||||
using Humanizer;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Branching;
|
|
||||||
|
|
||||||
[Node("Switch (Enum)", "Outputs the input that corresponds to the switch value", "Operators", InputType = typeof(Enum), OutputType = typeof(object))]
|
|
||||||
public class EnumSwitchNode : Node
|
|
||||||
{
|
|
||||||
private readonly Dictionary<Enum, InputPin> _inputPins;
|
|
||||||
|
|
||||||
public EnumSwitchNode()
|
|
||||||
{
|
|
||||||
_inputPins = new Dictionary<Enum, InputPin>();
|
|
||||||
|
|
||||||
Output = CreateOutputPin(typeof(object), "Result");
|
|
||||||
SwitchValue = CreateInputPin<Enum>("Switch");
|
|
||||||
|
|
||||||
SwitchValue.PinConnected += OnSwitchPinConnected;
|
|
||||||
SwitchValue.PinDisconnected += OnSwitchPinDisconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OutputPin Output { get; }
|
|
||||||
public InputPin<Enum> SwitchValue { get; }
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
if (SwitchValue.Value is null)
|
|
||||||
{
|
|
||||||
Output.Value = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_inputPins.TryGetValue(SwitchValue.Value, out InputPin? pin))
|
|
||||||
{
|
|
||||||
Output.Value = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pin.ConnectedTo.Count == 0)
|
|
||||||
{
|
|
||||||
Output.Value = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Output.Value = pin.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnInputPinDisconnected(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
// if this is the last pin to disconnect, reset the type.
|
|
||||||
if (_inputPins.Values.All(i => i.ConnectedTo.Count == 0))
|
|
||||||
ChangeType(typeof(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnInputPinConnected(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
// change the type of our inputs and output
|
|
||||||
// depending on the first node the user connects to
|
|
||||||
ChangeType(e.Value.Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSwitchPinConnected(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
if (SwitchValue.ConnectedTo.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Type enumType = SwitchValue.ConnectedTo[0].Type;
|
|
||||||
if (!enumType.IsEnum)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (Enum enumValue in Enum.GetValues(enumType).Cast<Enum>())
|
|
||||||
{
|
|
||||||
InputPin pin = CreateOrAddInputPin(typeof(object), enumValue.Humanize(LetterCasing.Sentence));
|
|
||||||
pin.PinConnected += OnInputPinConnected;
|
|
||||||
pin.PinDisconnected += OnInputPinDisconnected;
|
|
||||||
_inputPins[enumValue] = pin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSwitchPinDisconnected(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
foreach (InputPin input in _inputPins.Values)
|
|
||||||
{
|
|
||||||
input.PinConnected -= OnInputPinConnected;
|
|
||||||
input.PinDisconnected -= OnInputPinDisconnected;
|
|
||||||
RemovePin(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
_inputPins.Clear();
|
|
||||||
ChangeType(typeof(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ChangeType(Type type)
|
|
||||||
{
|
|
||||||
foreach (InputPin input in _inputPins.Values)
|
|
||||||
input.ChangeType(type);
|
|
||||||
Output.ChangeType(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Brighten Color", "Brightens a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
|
||||||
public class BrightenSKColorNode : Node
|
|
||||||
{
|
|
||||||
public BrightenSKColorNode()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<SKColor>("Color");
|
|
||||||
Percentage = CreateInputPin<Numeric>("%");
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<SKColor> Input { get; }
|
|
||||||
public InputPin<Numeric> Percentage { get; }
|
|
||||||
public OutputPin<SKColor> Output { get; set; }
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Input.Value.ToHsl(out float h, out float s, out float l);
|
|
||||||
l += l * (Percentage.Value / 100f);
|
|
||||||
Output.Value = SKColor.FromHsl(h, s, l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Events;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Color Gradient (Advanced)", "Outputs a Color Gradient from colors and positions", "Color", OutputType = typeof(ColorGradient))]
|
|
||||||
public class ColorGradientFromPinsNode : Node
|
|
||||||
{
|
|
||||||
public OutputPin<ColorGradient> Gradient { get; set; }
|
|
||||||
public InputPinCollection<SKColor> Colors { get; set; }
|
|
||||||
public InputPinCollection<Numeric> Positions { get; set; }
|
|
||||||
|
|
||||||
public ColorGradientFromPinsNode()
|
|
||||||
{
|
|
||||||
Colors = CreateInputPinCollection<SKColor>("Colors", 0);
|
|
||||||
Positions = CreateInputPinCollection<Numeric>("Positions", 0);
|
|
||||||
Gradient = CreateOutputPin<ColorGradient>("Gradient");
|
|
||||||
|
|
||||||
Colors.PinAdded += OnPinAdded;
|
|
||||||
Colors.PinRemoved += OnPinRemoved;
|
|
||||||
Positions.PinAdded += OnPinAdded;
|
|
||||||
Positions.PinRemoved += OnPinRemoved;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPinRemoved(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
int colorsCount = Colors.Count();
|
|
||||||
int positionsCount = Positions.Count();
|
|
||||||
if (colorsCount == positionsCount)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (colorsCount > positionsCount)
|
|
||||||
{
|
|
||||||
IPin pinToRemove = Colors.Last();
|
|
||||||
Colors.Remove(pinToRemove);
|
|
||||||
|
|
||||||
--colorsCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (positionsCount > colorsCount)
|
|
||||||
{
|
|
||||||
IPin pinToRemove = Positions.Last();
|
|
||||||
Positions.Remove(pinToRemove);
|
|
||||||
--positionsCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenamePins();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPinAdded(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
int colorsCount = Colors.Count();
|
|
||||||
int positionsCount = Positions.Count();
|
|
||||||
if (colorsCount == positionsCount)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (colorsCount < positionsCount)
|
|
||||||
{
|
|
||||||
Colors.Add(Colors.CreatePin());
|
|
||||||
|
|
||||||
++colorsCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (positionsCount < colorsCount)
|
|
||||||
{
|
|
||||||
Positions.Add(Positions.CreatePin());
|
|
||||||
|
|
||||||
++positionsCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenamePins();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RenamePins()
|
|
||||||
{
|
|
||||||
int colors = 0;
|
|
||||||
foreach (IPin item in Colors)
|
|
||||||
{
|
|
||||||
item.Name = $"Color #{++colors}";
|
|
||||||
}
|
|
||||||
|
|
||||||
int positions = 0;
|
|
||||||
foreach (IPin item in Positions)
|
|
||||||
{
|
|
||||||
item.Name = $"Position #{++positions}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
List<ColorGradientStop> stops = new List<ColorGradientStop>();
|
|
||||||
InputPin<SKColor>[] colors = Colors.Pins.ToArray();
|
|
||||||
InputPin<Numeric>[] positions = Positions.Pins.ToArray();
|
|
||||||
for (int i = 0; i < colors.Length; i++)
|
|
||||||
{
|
|
||||||
stops.Add(new ColorGradientStop(colors[i].Value, positions[i].Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
Gradient.Value = new ColorGradient(stops);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
using System.Collections.Specialized;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.VisualScripting.Nodes.Color.Screens;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Color Gradient (Simple)", "Outputs a color gradient with the given colors", "Color", OutputType = typeof(ColorGradient))]
|
|
||||||
public class ColorGradientNode : Node<ColorGradient, ColorGradientNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
private readonly List<InputPin> _inputPins;
|
|
||||||
|
|
||||||
public ColorGradientNode()
|
|
||||||
{
|
|
||||||
_inputPins = new List<InputPin>();
|
|
||||||
|
|
||||||
Gradient = ColorGradient.GetUnicornBarf();
|
|
||||||
Output = CreateOutputPin<ColorGradient>();
|
|
||||||
ViewModelPosition = CustomNodeViewModelPosition.AbovePins;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ColorGradient Gradient { get; private set; }
|
|
||||||
public OutputPin<ColorGradient> Output { get; }
|
|
||||||
|
|
||||||
public override void Initialize(INodeScript script)
|
|
||||||
{
|
|
||||||
UpdateGradient();
|
|
||||||
ComputeInputPins();
|
|
||||||
|
|
||||||
// Not expecting storage to get modified, but lets just make sure
|
|
||||||
StorageModified += OnStorageModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
ColorGradientStop[] stops = Gradient.ToArray();
|
|
||||||
|
|
||||||
if (_inputPins.Count != stops.Length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 0; i < _inputPins.Count; i++)
|
|
||||||
{
|
|
||||||
// if nothing is connected, leave the stop alone.
|
|
||||||
if (_inputPins[i].ConnectedTo.Count == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// if the pin has a connection, update the stop.
|
|
||||||
if (_inputPins[i].PinValue is SKColor color)
|
|
||||||
stops[i].Color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
Output.Value = Gradient;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisconnectAllInputPins()
|
|
||||||
{
|
|
||||||
foreach (InputPin item in _inputPins)
|
|
||||||
item.DisconnectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateGradient()
|
|
||||||
{
|
|
||||||
Gradient.CollectionChanged -= OnGradientCollectionChanged;
|
|
||||||
if (Storage != null)
|
|
||||||
Gradient = Storage;
|
|
||||||
else
|
|
||||||
Storage = Gradient;
|
|
||||||
Gradient.CollectionChanged += OnGradientCollectionChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ComputeInputPins()
|
|
||||||
{
|
|
||||||
int newAmount = Gradient.Count;
|
|
||||||
if (newAmount == _inputPins.Count)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (newAmount > _inputPins.Count)
|
|
||||||
_inputPins.Add(CreateOrAddInputPin(typeof(SKColor), string.Empty));
|
|
||||||
|
|
||||||
while (newAmount < _inputPins.Count)
|
|
||||||
{
|
|
||||||
InputPin pin = _inputPins.Last();
|
|
||||||
RemovePin(pin);
|
|
||||||
_inputPins.Remove(pin);
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
foreach (InputPin item in _inputPins)
|
|
||||||
item.Name = $"Color #{++index}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStorageModified(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
UpdateGradient();
|
|
||||||
ComputeInputPins();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGradientCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
// if the user reorders the gradient, let it slide and do nothing.
|
|
||||||
// of course, the user might want to change the input pins since they will no longer line up.
|
|
||||||
if (e.Action == NotifyCollectionChangedAction.Move)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// DisconnectAllInputPins();
|
|
||||||
ComputeInputPins();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Darken Color", "Darkens a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
|
||||||
public class DarkenSKColorNode : Node
|
|
||||||
{
|
|
||||||
public DarkenSKColorNode()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<SKColor>("Color");
|
|
||||||
Percentage = CreateInputPin<Numeric>("%");
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<SKColor> Input { get; }
|
|
||||||
public InputPin<Numeric> Percentage { get; }
|
|
||||||
public OutputPin<SKColor> Output { get; set; }
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Input.Value.ToHsl(out float h, out float s, out float l);
|
|
||||||
l -= l * (Percentage.Value / 100f);
|
|
||||||
Output.Value = SKColor.FromHsl(h, s, l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Desaturate Color", "Desaturates a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
|
||||||
public class DesaturateSKColorNode : Node
|
|
||||||
{
|
|
||||||
public DesaturateSKColorNode()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<SKColor>("Color");
|
|
||||||
Percentage = CreateInputPin<Numeric>("%");
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<SKColor> Input { get; }
|
|
||||||
public InputPin<Numeric> Percentage { get; }
|
|
||||||
public OutputPin<SKColor> Output { get; set; }
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Input.Value.ToHsl(out float h, out float s, out float l);
|
|
||||||
s -= s * (Percentage.Value / 100f);
|
|
||||||
Output.Value = SKColor.FromHsl(h, Math.Clamp(s, 0f, 100f), l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color
|
|
||||||
{
|
|
||||||
[Node("Gradient Builder", "Generates a gradient based on some values", "Color", OutputType = typeof(ColorGradient), HelpUrl = "https://krazydad.com/tutorials/makecolors.php")]
|
|
||||||
public class GradientBuilderNode : Node
|
|
||||||
{
|
|
||||||
public OutputPin<ColorGradient> Output { get; }
|
|
||||||
|
|
||||||
public InputPin<Numeric> Frequency1 { get; }
|
|
||||||
public InputPin<Numeric> Frequency2 { get; }
|
|
||||||
public InputPin<Numeric> Frequency3 { get; }
|
|
||||||
public InputPin<Numeric> Phase1 { get; }
|
|
||||||
public InputPin<Numeric> Phase2 { get; }
|
|
||||||
public InputPin<Numeric> Phase3 { get; }
|
|
||||||
public InputPin<Numeric> Center { get; }
|
|
||||||
public InputPin<Numeric> Width { get; }
|
|
||||||
public InputPin<Numeric> Length { get; }
|
|
||||||
|
|
||||||
public GradientBuilderNode()
|
|
||||||
{
|
|
||||||
Output = CreateOutputPin<ColorGradient>();
|
|
||||||
Frequency1 = CreateInputPin<Numeric>("Frequency 1");
|
|
||||||
Frequency2 = CreateInputPin<Numeric>("Frequency 2");
|
|
||||||
Frequency3 = CreateInputPin<Numeric>("Frequency 3");
|
|
||||||
Phase1 = CreateInputPin<Numeric>("Phase 1");
|
|
||||||
Phase2 = CreateInputPin<Numeric>("Phase 2");
|
|
||||||
Phase3 = CreateInputPin<Numeric>("Phase 3");
|
|
||||||
Center = CreateInputPin<Numeric>("Center");
|
|
||||||
Width = CreateInputPin<Numeric>("Width");
|
|
||||||
Length = CreateInputPin<Numeric>("Length");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
ColorGradient gradient = new ColorGradient();
|
|
||||||
|
|
||||||
for (int i = 0; i < Length.Value; i++)
|
|
||||||
{
|
|
||||||
Numeric r = Math.Sin(Frequency1.Value * i + Phase1.Value) * Width.Value + Center.Value;
|
|
||||||
Numeric g = Math.Sin(Frequency2.Value * i + Phase2.Value) * Width.Value + Center.Value;
|
|
||||||
Numeric b = Math.Sin(Frequency3.Value * i + Phase3.Value) * Width.Value + Center.Value;
|
|
||||||
gradient.Add(new ColorGradientStop(new SKColor((byte)r, (byte)g, (byte)b), i / Length.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
Output.Value = gradient;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("HSL Color", "Creates a color from hue, saturation and lightness numbers", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))]
|
|
||||||
public class HslSKColorNode : Node
|
|
||||||
{
|
|
||||||
public HslSKColorNode()
|
|
||||||
{
|
|
||||||
H = CreateInputPin<Numeric>("H");
|
|
||||||
S = CreateInputPin<Numeric>("S");
|
|
||||||
L = CreateInputPin<Numeric>("L");
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<Numeric> H { get; set; }
|
|
||||||
public InputPin<Numeric> S { get; set; }
|
|
||||||
public InputPin<Numeric> L { get; set; }
|
|
||||||
public OutputPin<SKColor> Output { get; }
|
|
||||||
|
|
||||||
#region Overrides of Node
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Output.Value = SKColor.FromHsl(H.Value, S.Value, L.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("HSV Color", "Creates a color from hue, saturation and value numbers", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))]
|
|
||||||
public class HsvSKColorNode : Node
|
|
||||||
{
|
|
||||||
public HsvSKColorNode()
|
|
||||||
{
|
|
||||||
H = CreateInputPin<Numeric>("H");
|
|
||||||
S = CreateInputPin<Numeric>("S");
|
|
||||||
V = CreateInputPin<Numeric>("V");
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<Numeric> H { get; set; }
|
|
||||||
public InputPin<Numeric> S { get; set; }
|
|
||||||
public InputPin<Numeric> V { get; set; }
|
|
||||||
public OutputPin<SKColor> Output { get; }
|
|
||||||
|
|
||||||
#region Overrides of Node
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Output.Value = SKColor.FromHsv(H.Value, S.Value, V.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Invert Color", "Inverts a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
|
||||||
public class InvertSKColorNode : Node
|
|
||||||
{
|
|
||||||
public InvertSKColorNode()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<SKColor>();
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<SKColor> Input { get; }
|
|
||||||
public OutputPin<SKColor> Output { get; set; }
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Output.Value = new SKColor(
|
|
||||||
(byte) (255 - Input.Value.Red),
|
|
||||||
(byte) (255 - Input.Value.Green),
|
|
||||||
(byte) (255 - Input.Value.Blue),
|
|
||||||
Input.Value.Alpha
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using RGB.NET.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Lerp (Color)", "Interpolates linear between the two colors A and B", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
|
||||||
public class LerpSKColorNode : Node
|
|
||||||
{
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
public InputPin<SKColor> A { get; }
|
|
||||||
public InputPin<SKColor> B { get; }
|
|
||||||
public InputPin<Numeric> T { get; }
|
|
||||||
|
|
||||||
public OutputPin<SKColor> Result { get; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public LerpSKColorNode()
|
|
||||||
{
|
|
||||||
Name = "Lerp";
|
|
||||||
A = CreateInputPin<SKColor>("A");
|
|
||||||
B = CreateInputPin<SKColor>("B");
|
|
||||||
T = CreateInputPin<Numeric>("T");
|
|
||||||
|
|
||||||
Result = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
SKColor a = A.Value;
|
|
||||||
SKColor b = B.Value;
|
|
||||||
float t = ((float)T.Value).Clamp(0f, 1f);
|
|
||||||
|
|
||||||
float aAlpha = a.Alpha.GetPercentageFromByteValue();
|
|
||||||
float aRed = a.Red.GetPercentageFromByteValue();
|
|
||||||
float aGreen = a.Green.GetPercentageFromByteValue();
|
|
||||||
float aBlue = a.Blue.GetPercentageFromByteValue();
|
|
||||||
|
|
||||||
float alpha = ((b.Alpha.GetPercentageFromByteValue() - aAlpha) * t) + aAlpha;
|
|
||||||
float red = ((b.Red.GetPercentageFromByteValue() - aRed) * t) + aRed;
|
|
||||||
float green = ((b.Green.GetPercentageFromByteValue() - aGreen) * t) + aGreen;
|
|
||||||
float blue = ((b.Blue.GetPercentageFromByteValue() - aBlue) * t) + aBlue;
|
|
||||||
|
|
||||||
Result.Value = new SKColor(red.GetByteValueFromPercentage(), green.GetByteValueFromPercentage(), blue.GetByteValueFromPercentage(), alpha.GetByteValueFromPercentage());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.VisualScripting.Nodes.Color.Screens;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Color Ramp", "Maps values to colors with the use of a gradient.", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))]
|
|
||||||
public class RampSKColorNode : Node<ColorGradient, RampSKColorNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public RampSKColorNode()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<Numeric>();
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
Storage = ColorGradient.GetUnicornBarf();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
// Wrap the input between 0 and 1
|
|
||||||
// 1 % 1 = 0, 2 % 1 = 0 etc. but we want that to be 1 but 0 should stay 0, call me stupid but this works and makes sense
|
|
||||||
float value = Input.Value % 1;
|
|
||||||
if (value == 0 && Input.Value != 0)
|
|
||||||
value = 1;
|
|
||||||
|
|
||||||
Output.Value = Storage?.GetColor(value) ?? SKColor.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
public InputPin<Numeric> Input { get; }
|
|
||||||
public OutputPin<SKColor> Output { get; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("RGB Color", "Creates a color from red, green and blue values", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))]
|
|
||||||
public class RgbSKColorNode : Node
|
|
||||||
{
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
public InputPin<Numeric> R { get; set; }
|
|
||||||
public InputPin<Numeric> G { get; set; }
|
|
||||||
public InputPin<Numeric> B { get; set; }
|
|
||||||
public OutputPin<SKColor> Output { get; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public RgbSKColorNode()
|
|
||||||
{
|
|
||||||
R = CreateInputPin<Numeric>("R");
|
|
||||||
G = CreateInputPin<Numeric>("G");
|
|
||||||
B = CreateInputPin<Numeric>("B");
|
|
||||||
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Evaluate() => Output.Value = new SKColor(R.Value, G.Value, B.Value);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Rotate Color Hue", "Rotates the hue of a color by a specified amount in degrees", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
|
||||||
public class RotateHueSKColorNode : Node
|
|
||||||
{
|
|
||||||
public RotateHueSKColorNode()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<SKColor>("Color");
|
|
||||||
Amount = CreateInputPin<Numeric>("Amount");
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<SKColor> Input { get; }
|
|
||||||
public InputPin<Numeric> Amount { get; }
|
|
||||||
public OutputPin<SKColor> Output { get; set; }
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Input.Value.ToHsl(out float h, out float s, out float l);
|
|
||||||
h += Amount.Value;
|
|
||||||
Output.Value = SKColor.FromHsl(h % 360, s, l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Saturate Color", "Saturates a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
|
||||||
public class SaturateSKColorNode : Node
|
|
||||||
{
|
|
||||||
public SaturateSKColorNode()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<SKColor>("Color");
|
|
||||||
Percentage = CreateInputPin<Numeric>("%");
|
|
||||||
Output = CreateOutputPin<SKColor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<SKColor> Input { get; }
|
|
||||||
public InputPin<Numeric> Percentage { get; }
|
|
||||||
public OutputPin<SKColor> Output { get; set; }
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Input.Value.ToHsl(out float h, out float s, out float l);
|
|
||||||
s += s * (Percentage.Value / 100f);
|
|
||||||
Output.Value = SKColor.FromHsl(h, Math.Clamp(s, 0f, 100f), l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.Color.Screens"
|
|
||||||
xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker;assembly=Artemis.UI.Shared"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.Color.Screens.ColorGradientNodeCustomView"
|
|
||||||
x:DataType="screens:ColorGradientNodeCustomViewModel">
|
|
||||||
<gradientPicker:GradientPickerButton Classes="condensed"
|
|
||||||
ColorGradient="{CompiledBinding Gradient}"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
FlyoutOpened="GradientPickerButton_OnFlyoutOpened"
|
|
||||||
FlyoutClosed="GradientPickerButton_OnFlyoutClosed" />
|
|
||||||
</UserControl>
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
using Artemis.UI.Shared.Controls.GradientPicker;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color.Screens;
|
|
||||||
|
|
||||||
public partial class ColorGradientNodeCustomView : ReactiveUserControl<ColorGradientNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public ColorGradientNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void GradientPickerButton_OnFlyoutOpened(GradientPickerButton sender, EventArgs args)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GradientPickerButton_OnFlyoutClosed(GradientPickerButton sender, EventArgs args)
|
|
||||||
{
|
|
||||||
ViewModel?.StoreGradient();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
|
||||||
using Artemis.UI.Shared.VisualScripting;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color.Screens;
|
|
||||||
|
|
||||||
public class ColorGradientNodeCustomViewModel : CustomNodeViewModel
|
|
||||||
{
|
|
||||||
private readonly ColorGradientNode _node;
|
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ColorGradientNodeCustomViewModel(ColorGradientNode node, INodeScript script, INodeEditorService nodeEditorService) : base(node, script)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_nodeEditorService = nodeEditorService;
|
|
||||||
|
|
||||||
Gradient = _node.Gradient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ColorGradient Gradient { get; }
|
|
||||||
|
|
||||||
public void StoreGradient()
|
|
||||||
{
|
|
||||||
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<ColorGradient>(_node, Gradient));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.Color.Screens"
|
|
||||||
xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker;assembly=Artemis.UI.Shared"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.Color.Screens.RampSKColorNodeCustomView"
|
|
||||||
x:DataType="screens:RampSKColorNodeCustomViewModel">
|
|
||||||
<gradientPicker:GradientPickerButton Width="110"
|
|
||||||
Classes="condensed"
|
|
||||||
ColorGradient="{CompiledBinding Gradient}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
FlyoutOpened="GradientPickerButton_OnFlyoutOpened"
|
|
||||||
FlyoutClosed="GradientPickerButton_OnFlyoutClosed"/>
|
|
||||||
</UserControl>
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
using Artemis.UI.Shared.Controls.GradientPicker;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color.Screens;
|
|
||||||
|
|
||||||
public partial class RampSKColorNodeCustomView : ReactiveUserControl<RampSKColorNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public RampSKColorNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void GradientPickerButton_OnFlyoutOpened(GradientPickerButton sender, EventArgs args)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GradientPickerButton_OnFlyoutClosed(GradientPickerButton sender, EventArgs args)
|
|
||||||
{
|
|
||||||
ViewModel?.StoreGradient();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
|
||||||
using Artemis.UI.Shared.VisualScripting;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color.Screens;
|
|
||||||
|
|
||||||
public class RampSKColorNodeCustomViewModel : CustomNodeViewModel
|
|
||||||
{
|
|
||||||
private readonly RampSKColorNode _node;
|
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public RampSKColorNodeCustomViewModel(RampSKColorNode node, INodeScript script, INodeEditorService nodeEditorService) : base(node, script)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_nodeEditorService = nodeEditorService;
|
|
||||||
|
|
||||||
Gradient = _node.Storage ?? new ColorGradient();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ColorGradient Gradient { get; }
|
|
||||||
|
|
||||||
public void StoreGradient()
|
|
||||||
{
|
|
||||||
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<ColorGradient>(_node, Gradient));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Color to HSL", "Outputs H, S and L values from a color", "Color", InputType = typeof(SKColor), OutputType = typeof(Numeric))]
|
|
||||||
public class SkColorHsl : Node
|
|
||||||
{
|
|
||||||
|
|
||||||
public SkColorHsl()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<SKColor>();
|
|
||||||
H = CreateOutputPin<Numeric>("H");
|
|
||||||
S = CreateOutputPin<Numeric>("S");
|
|
||||||
L = CreateOutputPin<Numeric>("L");
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<SKColor> Input { get; }
|
|
||||||
public OutputPin<Numeric> H { get; }
|
|
||||||
public OutputPin<Numeric> S { get; }
|
|
||||||
public OutputPin<Numeric> L { get; }
|
|
||||||
|
|
||||||
#region Overrides of Node
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Input.Value.ToHsl(out float h, out float s, out float l);
|
|
||||||
|
|
||||||
H.Value = h;
|
|
||||||
S.Value = s;
|
|
||||||
L.Value = l;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Color to HSV", "Outputs H, S and L values from a color", "Color", InputType = typeof(SKColor), OutputType = typeof(Numeric))]
|
|
||||||
public class SkColorHsv : Node
|
|
||||||
{
|
|
||||||
|
|
||||||
public SkColorHsv()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<SKColor>();
|
|
||||||
H = CreateOutputPin<Numeric>("H");
|
|
||||||
S = CreateOutputPin<Numeric>("S");
|
|
||||||
V = CreateOutputPin<Numeric>("V");
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<SKColor> Input { get; }
|
|
||||||
public OutputPin<Numeric> H { get; }
|
|
||||||
public OutputPin<Numeric> S { get; }
|
|
||||||
public OutputPin<Numeric> V { get; }
|
|
||||||
|
|
||||||
#region Overrides of Node
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Input.Value.ToHsv(out float h, out float s, out float v);
|
|
||||||
|
|
||||||
H.Value = h;
|
|
||||||
S.Value = s;
|
|
||||||
V.Value = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.ColorScience;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color
|
|
||||||
{
|
|
||||||
[Node("Sorted Gradient", "Generates a sorted gradient from the given colors", "Color", InputType = typeof(SKColor), OutputType = typeof(ColorGradient))]
|
|
||||||
public class SortedGradientNode : Node
|
|
||||||
{
|
|
||||||
private int lastComputedColorGroup;
|
|
||||||
public InputPinCollection<SKColor> Inputs { get; }
|
|
||||||
public OutputPin<ColorGradient> Output { get; }
|
|
||||||
|
|
||||||
public SortedGradientNode()
|
|
||||||
{
|
|
||||||
Inputs = CreateInputPinCollection<SKColor>();
|
|
||||||
Output = CreateOutputPin<ColorGradient>();
|
|
||||||
lastComputedColorGroup = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
int newHash = GetInputColorHash();
|
|
||||||
if (newHash == lastComputedColorGroup)
|
|
||||||
return;
|
|
||||||
|
|
||||||
SKColor[] colors = Inputs.Values.ToArray();
|
|
||||||
|
|
||||||
if (colors.Length == 0)
|
|
||||||
{
|
|
||||||
Output.Value = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorSorter.Sort(colors, SKColors.Black);
|
|
||||||
|
|
||||||
ColorGradient gradient = new();
|
|
||||||
for (int i = 0; i < colors.Length; i++)
|
|
||||||
{
|
|
||||||
gradient.Add(new ColorGradientStop(colors[i], (float)i / (colors.Length - 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Output.Value = gradient;
|
|
||||||
lastComputedColorGroup = newHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetInputColorHash()
|
|
||||||
{
|
|
||||||
int hash = 0;
|
|
||||||
|
|
||||||
foreach (SKColor color in Inputs.Values)
|
|
||||||
hash = HashCode.Combine(hash, color.GetHashCode());
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Color;
|
|
||||||
|
|
||||||
[Node("Sum (Color)", "Sums the connected color values.", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
|
|
||||||
public class SumSKColorsNode : Node
|
|
||||||
{
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public SumSKColorsNode()
|
|
||||||
{
|
|
||||||
Name = "Sum";
|
|
||||||
Values = CreateInputPinCollection<SKColor>("Values", 2);
|
|
||||||
Sum = CreateOutputPin<SKColor>("Sum");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
SKColor result = SKColor.Empty;
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
foreach (SKColor current in Values.Values)
|
|
||||||
{
|
|
||||||
result = first ? current : result.Sum(current);
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sum.Value = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
public InputPinCollection<SKColor> Values { get; }
|
|
||||||
|
|
||||||
public OutputPin<SKColor> Sum { get; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Conversion;
|
|
||||||
|
|
||||||
[Node("To Numeric", "Converts the input to a numeric.", "Conversion", InputType = typeof(object), OutputType = typeof(Numeric))]
|
|
||||||
public class ConvertToNumericNode : Node
|
|
||||||
{
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public ConvertToNumericNode()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<object>();
|
|
||||||
Output = CreateOutputPin<Numeric>();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
public InputPin<object> Input { get; }
|
|
||||||
|
|
||||||
public OutputPin<Numeric> Output { get; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Output.Value = Input.Value switch
|
|
||||||
{
|
|
||||||
int input => new Numeric(input),
|
|
||||||
double input => new Numeric(input),
|
|
||||||
float input => new Numeric(input),
|
|
||||||
byte input => new Numeric(input),
|
|
||||||
bool input => new Numeric(input ? 1 : 0),
|
|
||||||
_ => TryParse(Input.Value)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Numeric TryParse(object? input)
|
|
||||||
{
|
|
||||||
Numeric.TryParse(input?.ToString(), out Numeric value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Conversion;
|
|
||||||
|
|
||||||
[Node("To Text", "Converts the input to text.", "Conversion", InputType = typeof(object), OutputType = typeof(string))]
|
|
||||||
public class ConvertToStringNode : Node
|
|
||||||
{
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public ConvertToStringNode()
|
|
||||||
{
|
|
||||||
Input = CreateInputPin<object>();
|
|
||||||
String = CreateOutputPin<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
String.Value = Input.Value?.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
public InputPin<object> Input { get; }
|
|
||||||
|
|
||||||
public OutputPin<string> String { get; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,171 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Events;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
|
||||||
using Artemis.VisualScripting.Nodes.DataModel.Screens;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.DataModel;
|
|
||||||
|
|
||||||
[Node("Data Model-Event Value Cycle", "Cycles through provided values each time the select event fires.", "Data Model", OutputType = typeof(object))]
|
|
||||||
public class DataModelEventCycleNode : Node<DataModelPathEntity, DataModelEventCycleNodeCustomViewModel>, IDisposable
|
|
||||||
{
|
|
||||||
private int _currentIndex;
|
|
||||||
private Type _currentType;
|
|
||||||
private DataModelPath? _dataModelPath;
|
|
||||||
private IDataModelEvent? _subscribedEvent;
|
|
||||||
private object? _lastPathValue;
|
|
||||||
private bool _updating;
|
|
||||||
|
|
||||||
public DataModelEventCycleNode()
|
|
||||||
{
|
|
||||||
_currentType = typeof(object);
|
|
||||||
|
|
||||||
CycleValues = CreateInputPinCollection(typeof(object), "", 0);
|
|
||||||
Output = CreateOutputPin(typeof(object));
|
|
||||||
|
|
||||||
CycleValues.PinAdded += OnCycleValuesOnPinAdded;
|
|
||||||
CycleValues.PinRemoved += OnCycleValuesOnPinRemoved;
|
|
||||||
CycleValues.Add(CycleValues.CreatePin());
|
|
||||||
|
|
||||||
// Monitor storage for changes
|
|
||||||
StorageModified += (_, _) => UpdateDataModelPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public INodeScript? Script { get; set; }
|
|
||||||
|
|
||||||
public InputPinCollection CycleValues { get; }
|
|
||||||
public OutputPin Output { get; }
|
|
||||||
|
|
||||||
public override void Initialize(INodeScript script)
|
|
||||||
{
|
|
||||||
Script = script;
|
|
||||||
|
|
||||||
if (Storage != null)
|
|
||||||
UpdateDataModelPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
object? pathValue = _dataModelPath?.GetValue();
|
|
||||||
if (pathValue is not IDataModelEvent && EvaluateValue(pathValue))
|
|
||||||
Cycle();
|
|
||||||
|
|
||||||
object? outputValue = CycleValues.ElementAt(_currentIndex).PinValue;
|
|
||||||
if (Output.Type.IsInstanceOfType(outputValue))
|
|
||||||
Output.Value = outputValue;
|
|
||||||
else if (Output.Type.IsValueType)
|
|
||||||
Output.Value = Output.Type.GetDefault()!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool EvaluateValue(object? pathValue)
|
|
||||||
{
|
|
||||||
if (Equals(pathValue, _lastPathValue))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_lastPathValue = pathValue;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Cycle()
|
|
||||||
{
|
|
||||||
_currentIndex++;
|
|
||||||
|
|
||||||
if (_currentIndex >= CycleValues.Count())
|
|
||||||
_currentIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateDataModelPath()
|
|
||||||
{
|
|
||||||
DataModelPath? old = _dataModelPath;
|
|
||||||
|
|
||||||
if (_subscribedEvent != null)
|
|
||||||
{
|
|
||||||
_subscribedEvent.EventTriggered -= OnEventTriggered;
|
|
||||||
_subscribedEvent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dataModelPath = Storage != null ? new DataModelPath(Storage) : null;
|
|
||||||
|
|
||||||
if (_dataModelPath?.GetValue() is IDataModelEvent newEvent)
|
|
||||||
{
|
|
||||||
_subscribedEvent = newEvent;
|
|
||||||
_subscribedEvent.EventTriggered += OnEventTriggered;
|
|
||||||
}
|
|
||||||
|
|
||||||
old?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ChangeCurrentType(Type type)
|
|
||||||
{
|
|
||||||
CycleValues.ChangeType(type);
|
|
||||||
Output.ChangeType(type);
|
|
||||||
|
|
||||||
_currentType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEventTriggered(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
Cycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCycleValuesOnPinAdded(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
e.Value.PinConnected += OnPinConnected;
|
|
||||||
e.Value.PinDisconnected += OnPinDisconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCycleValuesOnPinRemoved(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
e.Value.PinConnected -= OnPinConnected;
|
|
||||||
e.Value.PinDisconnected -= OnPinDisconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPinDisconnected(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
if (_updating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// If there's still a connected pin, stick to the current type
|
|
||||||
if (CycleValues.Any(v => v.ConnectedTo.Any()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
ChangeCurrentType(typeof(object));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPinConnected(object? sender, SingleValueEventArgs<IPin> e)
|
|
||||||
{
|
|
||||||
if (_updating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_updating = true;
|
|
||||||
|
|
||||||
// No need to change anything if the types haven't changed
|
|
||||||
if (_currentType != e.Value.Type)
|
|
||||||
ChangeCurrentType(e.Value.Type);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_subscribedEvent != null)
|
|
||||||
{
|
|
||||||
_subscribedEvent.EventTriggered -= OnEventTriggered;
|
|
||||||
_subscribedEvent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dataModelPath?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,164 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
|
||||||
using Artemis.VisualScripting.Nodes.DataModel.Screens;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.DataModel;
|
|
||||||
|
|
||||||
[Node("Data Model-Event", "Outputs the latest values of a data model event.", "Data Model", OutputType = typeof(object))]
|
|
||||||
public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCustomViewModel>, IDisposable
|
|
||||||
{
|
|
||||||
private readonly ObjectOutputPins _objectOutputPins;
|
|
||||||
private IDataModelEvent? _dataModelEvent;
|
|
||||||
private DataModelPath? _dataModelPath;
|
|
||||||
private DateTime _lastTrigger;
|
|
||||||
private object? _lastValue;
|
|
||||||
private OutputPin? _newValuePin;
|
|
||||||
private OutputPin? _oldValuePin;
|
|
||||||
private int _valueChangeCount;
|
|
||||||
|
|
||||||
public DataModelEventNode()
|
|
||||||
{
|
|
||||||
_objectOutputPins = new ObjectOutputPins(this);
|
|
||||||
|
|
||||||
TimeSinceLastTrigger = CreateOutputPin<Numeric>("Time since trigger");
|
|
||||||
TriggerCount = CreateOutputPin<Numeric>("Trigger count");
|
|
||||||
|
|
||||||
// Monitor storage for changes
|
|
||||||
StorageModified += (_, _) => UpdateDataModelPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public INodeScript? Script { get; set; }
|
|
||||||
public OutputPin<Numeric> TimeSinceLastTrigger { get; }
|
|
||||||
public OutputPin<Numeric> TriggerCount { get; }
|
|
||||||
|
|
||||||
public override void Initialize(INodeScript script)
|
|
||||||
{
|
|
||||||
Script = script;
|
|
||||||
|
|
||||||
if (Storage == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
UpdateDataModelPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
object? pathValue = _dataModelPath?.GetValue();
|
|
||||||
|
|
||||||
// If the path is a data model event, evaluate the event
|
|
||||||
if (pathValue is IDataModelEvent dataModelEvent)
|
|
||||||
{
|
|
||||||
TimeSinceLastTrigger.Value = dataModelEvent.TimeSinceLastTrigger.TotalMilliseconds;
|
|
||||||
TriggerCount.Value = dataModelEvent.TriggerCount;
|
|
||||||
|
|
||||||
_objectOutputPins.SetCurrentValue(dataModelEvent.LastEventArgumentsUntyped);
|
|
||||||
}
|
|
||||||
// If the path is a regular value, evaluate the current value
|
|
||||||
else if (_oldValuePin != null && _newValuePin != null)
|
|
||||||
{
|
|
||||||
if (_newValuePin.IsNumeric)
|
|
||||||
pathValue = new Numeric(pathValue);
|
|
||||||
|
|
||||||
if (Equals(_lastValue, pathValue))
|
|
||||||
{
|
|
||||||
TimeSinceLastTrigger.Value = (DateTime.Now - _lastTrigger).TotalMilliseconds;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_valueChangeCount++;
|
|
||||||
_lastTrigger = DateTime.Now;
|
|
||||||
|
|
||||||
_oldValuePin.Value = _lastValue;
|
|
||||||
_newValuePin.Value = pathValue;
|
|
||||||
|
|
||||||
_lastValue = pathValue;
|
|
||||||
|
|
||||||
TimeSinceLastTrigger.Value = 0;
|
|
||||||
TriggerCount.Value = _valueChangeCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateDataModelPath()
|
|
||||||
{
|
|
||||||
DataModelPath? old = _dataModelPath;
|
|
||||||
_dataModelPath = Storage != null ? new DataModelPath(Storage) : null;
|
|
||||||
if (_dataModelPath != null)
|
|
||||||
_dataModelPath.PathValidated += DataModelPathOnPathValidated;
|
|
||||||
|
|
||||||
if (old != null)
|
|
||||||
{
|
|
||||||
old.PathValidated -= DataModelPathOnPathValidated;
|
|
||||||
old.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateOutputPins();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateOutputPins()
|
|
||||||
{
|
|
||||||
object? pathValue = _dataModelPath?.GetValue();
|
|
||||||
|
|
||||||
if (pathValue is IDataModelEvent dataModelEvent)
|
|
||||||
CreateEventPins(dataModelEvent);
|
|
||||||
else
|
|
||||||
CreateValuePins();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateEventPins(IDataModelEvent dataModelEvent)
|
|
||||||
{
|
|
||||||
if (_dataModelEvent == dataModelEvent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ClearPins();
|
|
||||||
_dataModelEvent = dataModelEvent;
|
|
||||||
_objectOutputPins.ChangeType(dataModelEvent.ArgumentsType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateValuePins()
|
|
||||||
{
|
|
||||||
ClearPins();
|
|
||||||
|
|
||||||
Type? propertyType = _dataModelPath?.GetPropertyType();
|
|
||||||
if (propertyType == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Numeric.IsTypeCompatible(propertyType))
|
|
||||||
propertyType = typeof(Numeric);
|
|
||||||
|
|
||||||
_oldValuePin = CreateOrAddOutputPin(propertyType, "Old value");
|
|
||||||
_newValuePin = CreateOrAddOutputPin(propertyType, "New value");
|
|
||||||
_lastValue = null;
|
|
||||||
_valueChangeCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearPins()
|
|
||||||
{
|
|
||||||
// Clear the output pins by changing the type to null
|
|
||||||
_objectOutputPins.ChangeType(null);
|
|
||||||
|
|
||||||
if (_oldValuePin != null)
|
|
||||||
{
|
|
||||||
RemovePin(_oldValuePin);
|
|
||||||
_oldValuePin = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_newValuePin != null)
|
|
||||||
{
|
|
||||||
RemovePin(_newValuePin);
|
|
||||||
_newValuePin = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataModelPathOnPathValidated(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
// Update the output pin now that the type is known and attempt to restore the connection that was likely missing
|
|
||||||
UpdateOutputPins();
|
|
||||||
Script?.LoadConnections();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_dataModelPath?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
|
||||||
using Artemis.VisualScripting.Nodes.DataModel.Screens;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.DataModel;
|
|
||||||
|
|
||||||
[Node("Data Model-Value", "Outputs a selectable data model value.", "Data Model", OutputType = typeof(object))]
|
|
||||||
public class DataModelNode : Node<DataModelPathEntity, DataModelNodeCustomViewModel>, IDisposable
|
|
||||||
{
|
|
||||||
private DataModelPath? _dataModelPath;
|
|
||||||
|
|
||||||
public DataModelNode()
|
|
||||||
{
|
|
||||||
Output = CreateOutputPin(typeof(object));
|
|
||||||
StorageModified += (_, _) => UpdateDataModelPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public INodeScript? Script { get; private set; }
|
|
||||||
public OutputPin Output { get; }
|
|
||||||
|
|
||||||
public override void Initialize(INodeScript script)
|
|
||||||
{
|
|
||||||
Script = script;
|
|
||||||
|
|
||||||
if (Storage == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
UpdateDataModelPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
if (_dataModelPath == null || !_dataModelPath.IsValid)
|
|
||||||
return;
|
|
||||||
|
|
||||||
object? pathValue = _dataModelPath.GetValue();
|
|
||||||
if (pathValue == null)
|
|
||||||
{
|
|
||||||
if (!Output.Type.IsValueType)
|
|
||||||
Output.Value = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Output.Value = Output.IsNumeric ? new Numeric(pathValue) : pathValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateOutputPin()
|
|
||||||
{
|
|
||||||
Type? type = _dataModelPath?.GetPropertyType();
|
|
||||||
if (type == null)
|
|
||||||
type = typeof(object);
|
|
||||||
else if (Numeric.IsTypeCompatible(type))
|
|
||||||
type = typeof(Numeric);
|
|
||||||
|
|
||||||
if (Output.Type != type)
|
|
||||||
Output.ChangeType(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateDataModelPath()
|
|
||||||
{
|
|
||||||
DataModelPath? old = _dataModelPath;
|
|
||||||
_dataModelPath = Storage != null ? new DataModelPath(Storage) : null;
|
|
||||||
if (_dataModelPath != null)
|
|
||||||
_dataModelPath.PathValidated += DataModelPathOnPathValidated;
|
|
||||||
|
|
||||||
if (old != null)
|
|
||||||
{
|
|
||||||
old.PathValidated -= DataModelPathOnPathValidated;
|
|
||||||
old.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateOutputPin();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataModelPathOnPathValidated(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
// Update the output pin now that the type is known and attempt to restore the connection that was likely missing
|
|
||||||
UpdateOutputPin();
|
|
||||||
Script?.LoadConnections();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_dataModelPath?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.DataModel.Screens"
|
|
||||||
xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.DataModelPicker;assembly=Artemis.UI.Shared"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.DataModel.Screens.DataModelEventCycleNodeCustomView"
|
|
||||||
x:DataType="screens:DataModelEventCycleNodeCustomViewModel">
|
|
||||||
<dataModelPicker:DataModelPickerButton Classes="condensed"
|
|
||||||
DataModelPath="{CompiledBinding DataModelPath}"
|
|
||||||
Modules="{CompiledBinding Modules}"
|
|
||||||
ShowDataModelValues="{CompiledBinding ShowDataModelValues.Value}"
|
|
||||||
ShowFullPath="{CompiledBinding ShowFullPaths.Value}"
|
|
||||||
IsEventPicker="True"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
MaxWidth="300"/>
|
|
||||||
</UserControl>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.DataModel.Screens;
|
|
||||||
|
|
||||||
public partial class DataModelEventCycleNodeCustomView : ReactiveUserControl<DataModelEventCycleNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public DataModelEventCycleNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Reactive.Disposables;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Modules;
|
|
||||||
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 ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.DataModel.Screens;
|
|
||||||
|
|
||||||
public class DataModelEventCycleNodeCustomViewModel : CustomNodeViewModel
|
|
||||||
{
|
|
||||||
private readonly DataModelEventCycleNode _cycleNode;
|
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
|
||||||
private DataModelPath? _dataModelPath;
|
|
||||||
private ObservableCollection<Module>? _modules;
|
|
||||||
private bool _updating;
|
|
||||||
|
|
||||||
public DataModelEventCycleNodeCustomViewModel(DataModelEventCycleNode cycleNode, INodeScript script, ISettingsService settingsService, INodeEditorService nodeEditorService) : base(cycleNode, script)
|
|
||||||
{
|
|
||||||
_cycleNode = cycleNode;
|
|
||||||
_nodeEditorService = nodeEditorService;
|
|
||||||
|
|
||||||
ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
|
|
||||||
ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
|
|
||||||
Modules = new ObservableCollection<Module>();
|
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
|
||||||
// Set up extra modules
|
|
||||||
if (_cycleNode.Script?.Context is Profile scriptProfile && scriptProfile.Configuration.Module != null)
|
|
||||||
Modules = new ObservableCollection<Module> {scriptProfile.Configuration.Module};
|
|
||||||
else if (_cycleNode.Script?.Context is ProfileConfiguration profileConfiguration && profileConfiguration.Module != null)
|
|
||||||
Modules = new ObservableCollection<Module> {profileConfiguration.Module};
|
|
||||||
|
|
||||||
// Subscribe to node changes
|
|
||||||
_cycleNode.WhenAnyValue(n => n.Storage).Subscribe(UpdateDataModelPath).DisposeWith(d);
|
|
||||||
this.WhenAnyValue(vm => vm.DataModelPath).WhereNotNull().Subscribe(ApplyDataModelPath).DisposeWith(d);
|
|
||||||
|
|
||||||
Disposable.Create(() =>
|
|
||||||
{
|
|
||||||
_dataModelPath?.Dispose();
|
|
||||||
_dataModelPath = null;
|
|
||||||
}).DisposeWith(d);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public PluginSetting<bool> ShowFullPaths { get; }
|
|
||||||
public PluginSetting<bool> ShowDataModelValues { get; }
|
|
||||||
|
|
||||||
public ObservableCollection<Module>? Modules
|
|
||||||
{
|
|
||||||
get => _modules;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _modules, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataModelPath? DataModelPath
|
|
||||||
{
|
|
||||||
get => _dataModelPath;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _dataModelPath, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateDataModelPath(DataModelPathEntity? entity)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_updating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_updating = true;
|
|
||||||
|
|
||||||
DataModelPath? old = DataModelPath;
|
|
||||||
DataModelPath = entity != null ? new DataModelPath(entity) : null;
|
|
||||||
old?.Dispose();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyDataModelPath(DataModelPath path)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_updating)
|
|
||||||
return;
|
|
||||||
if (path.Path == _cycleNode.Storage?.Path)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_updating = true;
|
|
||||||
|
|
||||||
path.Save();
|
|
||||||
_nodeEditorService.ExecuteCommand(Script, new UpdateStorage<DataModelPathEntity>(_cycleNode, path?.Entity, "event"));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.DataModel.Screens"
|
|
||||||
xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.DataModelPicker;assembly=Artemis.UI.Shared"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.DataModel.Screens.DataModelEventNodeCustomView"
|
|
||||||
x:DataType="screens:DataModelEventNodeCustomViewModel">
|
|
||||||
<dataModelPicker:DataModelPickerButton Classes="condensed"
|
|
||||||
DataModelPath="{CompiledBinding DataModelPath}"
|
|
||||||
Modules="{CompiledBinding Modules}"
|
|
||||||
ShowDataModelValues="{CompiledBinding ShowDataModelValues.Value}"
|
|
||||||
ShowFullPath="{CompiledBinding ShowFullPaths.Value}"
|
|
||||||
FilterTypes="{CompiledBinding NodePinTypes}"
|
|
||||||
IsEventPicker="True"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
MaxWidth="300"/>
|
|
||||||
</UserControl>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.DataModel.Screens;
|
|
||||||
|
|
||||||
public partial class DataModelEventNodeCustomView : ReactiveUserControl<DataModelEventNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public DataModelEventNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Reactive.Disposables;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Modules;
|
|
||||||
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 ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.DataModel.Screens;
|
|
||||||
|
|
||||||
public class DataModelEventNodeCustomViewModel : CustomNodeViewModel
|
|
||||||
{
|
|
||||||
private readonly DataModelEventNode _node;
|
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
|
||||||
private DataModelPath? _dataModelPath;
|
|
||||||
private ObservableCollection<Module>? _modules;
|
|
||||||
private bool _updating;
|
|
||||||
|
|
||||||
public DataModelEventNodeCustomViewModel(DataModelEventNode node, INodeScript script, ISettingsService settingsService, INodeEditorService nodeEditorService, INodeService nodeService)
|
|
||||||
: base(node, script)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_nodeEditorService = nodeEditorService;
|
|
||||||
|
|
||||||
ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
|
|
||||||
ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
|
|
||||||
Modules = new ObservableCollection<Module>();
|
|
||||||
|
|
||||||
List<Type> nodePinTypes = nodeService.GetRegisteredTypes();
|
|
||||||
nodePinTypes.AddRange(Constants.NumberTypes);
|
|
||||||
nodePinTypes.Add(typeof(IDataModelEvent));
|
|
||||||
NodePinTypes = new ObservableCollection<Type>(nodePinTypes);
|
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
|
||||||
// Set up extra modules
|
|
||||||
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};
|
|
||||||
|
|
||||||
// Subscribe to node changes
|
|
||||||
_node.WhenAnyValue(n => n.Storage).Subscribe(UpdateDataModelPath).DisposeWith(d);
|
|
||||||
this.WhenAnyValue(vm => vm.DataModelPath).WhereNotNull().Subscribe(ApplyDataModelPath).DisposeWith(d);
|
|
||||||
|
|
||||||
Disposable.Create(() =>
|
|
||||||
{
|
|
||||||
_dataModelPath?.Dispose();
|
|
||||||
_dataModelPath = null;
|
|
||||||
}).DisposeWith(d);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public PluginSetting<bool> ShowFullPaths { get; }
|
|
||||||
public PluginSetting<bool> ShowDataModelValues { get; }
|
|
||||||
public ObservableCollection<Type> NodePinTypes { get; }
|
|
||||||
|
|
||||||
public ObservableCollection<Module>? Modules
|
|
||||||
{
|
|
||||||
get => _modules;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _modules, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataModelPath? DataModelPath
|
|
||||||
{
|
|
||||||
get => _dataModelPath;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _dataModelPath, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateDataModelPath(DataModelPathEntity? entity)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_updating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_updating = true;
|
|
||||||
|
|
||||||
DataModelPath? old = DataModelPath;
|
|
||||||
DataModelPath = entity != null ? new DataModelPath(entity) : null;
|
|
||||||
old?.Dispose();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.DataModel.Screens"
|
|
||||||
xmlns:dataModelPicker="clr-namespace:Artemis.UI.Shared.DataModelPicker;assembly=Artemis.UI.Shared"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.DataModel.Screens.DataModelNodeCustomView"
|
|
||||||
x:DataType="screens:DataModelNodeCustomViewModel">
|
|
||||||
<dataModelPicker:DataModelPickerButton Classes="condensed"
|
|
||||||
DataModelPath="{CompiledBinding DataModelPath}"
|
|
||||||
Modules="{CompiledBinding Modules}"
|
|
||||||
ShowDataModelValues="{CompiledBinding ShowDataModelValues.Value}"
|
|
||||||
ShowFullPath="{CompiledBinding ShowFullPaths.Value}"
|
|
||||||
FilterTypes="{CompiledBinding NodePinTypes}"
|
|
||||||
MaxWidth="300"/>
|
|
||||||
</UserControl>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.DataModel.Screens;
|
|
||||||
|
|
||||||
public partial class DataModelNodeCustomView : ReactiveUserControl<DataModelNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public DataModelNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Reactive.Disposables;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Modules;
|
|
||||||
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 ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.DataModel.Screens;
|
|
||||||
|
|
||||||
public class DataModelNodeCustomViewModel : CustomNodeViewModel
|
|
||||||
{
|
|
||||||
private readonly DataModelNode _node;
|
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
|
||||||
private DataModelPath? _dataModelPath;
|
|
||||||
private ObservableCollection<Module>? _modules;
|
|
||||||
private bool _updating;
|
|
||||||
|
|
||||||
public DataModelNodeCustomViewModel(DataModelNode node, INodeScript script, ISettingsService settingsService, INodeEditorService nodeEditorService, INodeService nodeService) : base(node, script)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_nodeEditorService = nodeEditorService;
|
|
||||||
|
|
||||||
ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
|
|
||||||
ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
|
|
||||||
|
|
||||||
List<Type> nodePinTypes = nodeService.GetRegisteredTypes();
|
|
||||||
nodePinTypes.AddRange(Constants.NumberTypes);
|
|
||||||
NodePinTypes = new ObservableCollection<Type>(nodePinTypes);
|
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
|
||||||
// Set up extra modules
|
|
||||||
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};
|
|
||||||
|
|
||||||
// Subscribe to node changes
|
|
||||||
_node.WhenAnyValue(n => n.Storage).Subscribe(UpdateDataModelPath).DisposeWith(d);
|
|
||||||
this.WhenAnyValue(vm => vm.DataModelPath).WhereNotNull().Subscribe(ApplyDataModelPath).DisposeWith(d);
|
|
||||||
|
|
||||||
Disposable.Create(() =>
|
|
||||||
{
|
|
||||||
_dataModelPath?.Dispose();
|
|
||||||
_dataModelPath = null;
|
|
||||||
}).DisposeWith(d);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public PluginSetting<bool> ShowFullPaths { get; }
|
|
||||||
public PluginSetting<bool> ShowDataModelValues { get; }
|
|
||||||
public ObservableCollection<Type> NodePinTypes { get; }
|
|
||||||
|
|
||||||
public ObservableCollection<Module>? Modules
|
|
||||||
{
|
|
||||||
get => _modules;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _modules, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataModelPath? DataModelPath
|
|
||||||
{
|
|
||||||
get => _dataModelPath;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _dataModelPath, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateDataModelPath(DataModelPathEntity? entity)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_updating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_updating = true;
|
|
||||||
|
|
||||||
DataModelPath? old = DataModelPath;
|
|
||||||
DataModelPath = entity != null ? new DataModelPath(entity) : null;
|
|
||||||
old?.Dispose();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,45 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.External.Commands;
|
|
||||||
|
|
||||||
public class UpdateLayerPropertyNodeSelectedLayerProperty : INodeEditorCommand
|
|
||||||
{
|
|
||||||
private readonly NodeConnectionStore _connections;
|
|
||||||
private readonly LayerPropertyNode _node;
|
|
||||||
private readonly ILayerProperty? _oldValue;
|
|
||||||
private readonly ILayerProperty? _value;
|
|
||||||
|
|
||||||
public UpdateLayerPropertyNodeSelectedLayerProperty(LayerPropertyNode node, ILayerProperty? value)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_connections = new NodeConnectionStore(_node);
|
|
||||||
|
|
||||||
_value = value;
|
|
||||||
_oldValue = _node.LayerProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string DisplayName => "Update node layer property";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Execute()
|
|
||||||
{
|
|
||||||
// Store connections as they currently are
|
|
||||||
_connections.Store();
|
|
||||||
|
|
||||||
// Update the selected profile element
|
|
||||||
_node.ChangeLayerProperty(_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Undo()
|
|
||||||
{
|
|
||||||
// Restore the previous layer property
|
|
||||||
_node.ChangeLayerProperty(_oldValue);
|
|
||||||
|
|
||||||
// Restore connections
|
|
||||||
_connections.Restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.External.Commands;
|
|
||||||
|
|
||||||
public class UpdateLayerPropertyNodeSelectedProfileElement : INodeEditorCommand
|
|
||||||
{
|
|
||||||
private readonly NodeConnectionStore _connections;
|
|
||||||
private readonly LayerPropertyNode _node;
|
|
||||||
private readonly ILayerProperty? _oldLayerProperty;
|
|
||||||
private readonly RenderProfileElement? _oldValue;
|
|
||||||
|
|
||||||
private readonly RenderProfileElement? _value;
|
|
||||||
|
|
||||||
public UpdateLayerPropertyNodeSelectedProfileElement(LayerPropertyNode node, RenderProfileElement? value)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_connections = new NodeConnectionStore(_node);
|
|
||||||
|
|
||||||
_value = value;
|
|
||||||
_oldValue = node.ProfileElement;
|
|
||||||
_oldLayerProperty = node.LayerProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string DisplayName => "Update node profile element";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Execute()
|
|
||||||
{
|
|
||||||
// Store connections as they currently are
|
|
||||||
_connections.Store();
|
|
||||||
|
|
||||||
// Update the selected profile element
|
|
||||||
_node.ChangeProfileElement(_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Undo()
|
|
||||||
{
|
|
||||||
// Can't undo it if that profile element is now gone :\
|
|
||||||
if (_oldValue != null && _oldValue.Disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Restore the previous profile element
|
|
||||||
_node.ChangeProfileElement(_oldValue);
|
|
||||||
|
|
||||||
// Restore the previous layer property
|
|
||||||
_node.ChangeLayerProperty(_oldLayerProperty);
|
|
||||||
|
|
||||||
// Restore connections
|
|
||||||
_connections.Restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,154 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.VisualScripting.Nodes.External.Screens;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.External;
|
|
||||||
|
|
||||||
// [Node("Layer/Folder Property", "Outputs the property of a selected layer or folder", "External")]
|
|
||||||
public class LayerPropertyNode : Node<LayerPropertyNodeEntity, LayerPropertyNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
private readonly object _layerPropertyLock = new();
|
|
||||||
private readonly List<OutputPin> _pinBucket = new();
|
|
||||||
|
|
||||||
public LayerPropertyNode() : base("Layer/Folder Property", "Outputs the property of a selected layer or folder")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public INodeScript? Script { get; private set; }
|
|
||||||
public RenderProfileElement? ProfileElement { get; private set; }
|
|
||||||
public ILayerProperty? LayerProperty { get; private set; }
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
lock (_layerPropertyLock)
|
|
||||||
{
|
|
||||||
// In this case remove the pins so no further evaluations occur
|
|
||||||
if (LayerProperty == null)
|
|
||||||
{
|
|
||||||
CreatePins();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IDataBindingProperty> list = LayerProperty.BaseDataBinding.Properties.ToList();
|
|
||||||
int index = 0;
|
|
||||||
foreach (IPin pin in Pins)
|
|
||||||
{
|
|
||||||
OutputPin outputPin = (OutputPin) pin;
|
|
||||||
IDataBindingProperty dataBindingProperty = list[index];
|
|
||||||
index++;
|
|
||||||
|
|
||||||
// TODO: Is this really non-nullable?
|
|
||||||
outputPin.Value = dataBindingProperty.GetValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Initialize(INodeScript script)
|
|
||||||
{
|
|
||||||
Script = script;
|
|
||||||
|
|
||||||
if (script.Context is Profile profile)
|
|
||||||
profile.ChildRemoved += ProfileOnChildRemoved;
|
|
||||||
|
|
||||||
LoadLayerProperty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadLayerProperty()
|
|
||||||
{
|
|
||||||
lock (_layerPropertyLock)
|
|
||||||
{
|
|
||||||
if (Script?.Context is not Profile profile || Storage == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
RenderProfileElement? element = profile.GetAllRenderElements().FirstOrDefault(l => l.EntityId == Storage.ElementId);
|
|
||||||
|
|
||||||
ProfileElement = element;
|
|
||||||
LayerProperty = element?.GetAllLayerProperties().FirstOrDefault(p => p.Path == Storage.PropertyPath);
|
|
||||||
CreatePins();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ChangeProfileElement(RenderProfileElement? profileElement)
|
|
||||||
{
|
|
||||||
lock (_layerPropertyLock)
|
|
||||||
{
|
|
||||||
ProfileElement = profileElement;
|
|
||||||
LayerProperty = profileElement?.GetAllLayerProperties().FirstOrDefault();
|
|
||||||
|
|
||||||
Storage = new LayerPropertyNodeEntity
|
|
||||||
{
|
|
||||||
ElementId = ProfileElement?.EntityId ?? Guid.Empty,
|
|
||||||
PropertyPath = null
|
|
||||||
};
|
|
||||||
|
|
||||||
CreatePins();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ChangeLayerProperty(ILayerProperty? layerProperty)
|
|
||||||
{
|
|
||||||
lock (_layerPropertyLock)
|
|
||||||
{
|
|
||||||
LayerProperty = layerProperty;
|
|
||||||
|
|
||||||
Storage = new LayerPropertyNodeEntity
|
|
||||||
{
|
|
||||||
ElementId = ProfileElement?.EntityId ?? Guid.Empty,
|
|
||||||
PropertyPath = LayerProperty?.Path
|
|
||||||
};
|
|
||||||
|
|
||||||
CreatePins();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreatePins()
|
|
||||||
{
|
|
||||||
while (Pins.Any())
|
|
||||||
RemovePin((Pin) Pins.First());
|
|
||||||
|
|
||||||
if (LayerProperty == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (IDataBindingProperty dataBindingRegistration in LayerProperty.BaseDataBinding.Properties)
|
|
||||||
CreateOrAddOutputPin(dataBindingRegistration.ValueType, dataBindingRegistration.DisplayName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates or adds an input pin to the node using a bucket.
|
|
||||||
/// The bucket might grow a bit over time as the user edits the node but pins won't get lost, enabling undo/redo in the
|
|
||||||
/// editor.
|
|
||||||
/// </summary>
|
|
||||||
private new void CreateOrAddOutputPin(Type valueType, string displayName)
|
|
||||||
{
|
|
||||||
// Grab the first pin from the bucket that isn't on the node yet
|
|
||||||
OutputPin? pin = _pinBucket.FirstOrDefault(p => !Pins.Contains(p));
|
|
||||||
|
|
||||||
// If there is none, create a new one and add it to the bucket
|
|
||||||
if (pin == null)
|
|
||||||
{
|
|
||||||
pin = CreateOutputPin(valueType, displayName);
|
|
||||||
_pinBucket.Add(pin);
|
|
||||||
}
|
|
||||||
// If there was a pin in the bucket, update it's type and display name and reuse it
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pin.ChangeType(valueType);
|
|
||||||
pin.Name = displayName;
|
|
||||||
AddPin(pin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProfileOnChildRemoved(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (Script?.Context is not Profile profile)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ProfileElement == null || !profile.GetAllRenderElements().Contains(ProfileElement))
|
|
||||||
ChangeProfileElement(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LayerPropertyNodeEntity
|
|
||||||
{
|
|
||||||
public Guid ElementId { get; set; }
|
|
||||||
public string? PropertyPath { get; set; }
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.External.Screens"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.External.Screens.LayerPropertyNodeCustomView"
|
|
||||||
x:DataType="screens:LayerPropertyNodeCustomViewModel">
|
|
||||||
<Grid>
|
|
||||||
<StackPanel Spacing="5" VerticalAlignment="Top" IsVisible="{CompiledBinding !OutsideProfileContext}">
|
|
||||||
<ComboBox Classes="condensed" ItemsSource="{CompiledBinding ProfileElements}" SelectedItem="{CompiledBinding SelectedProfileElement}" />
|
|
||||||
<ComboBox Classes="condensed" ItemsSource="{CompiledBinding LayerProperties}" SelectedItem="{CompiledBinding SelectedLayerProperty}" />
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel IsVisible="{CompiledBinding OutsideProfileContext}" VerticalAlignment="Center">
|
|
||||||
<TextBlock Classes="BodyStrongTextBlockStyle" TextAlignment="Center">
|
|
||||||
Node not available
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock Classes="BodyTextBlockStyle" Foreground="{DynamicResource TextFillColorSecondary}" TextAlignment="Center">
|
|
||||||
This node cannot be used outside profile scripts.
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</UserControl>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.External.Screens;
|
|
||||||
|
|
||||||
public partial class LayerPropertyNodeCustomView : ReactiveUserControl<LayerPropertyNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public LayerPropertyNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Reactive.Disposables;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
|
||||||
using Artemis.UI.Shared.VisualScripting;
|
|
||||||
using Artemis.VisualScripting.Nodes.External.Commands;
|
|
||||||
using DynamicData;
|
|
||||||
using ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.External.Screens;
|
|
||||||
|
|
||||||
public class LayerPropertyNodeCustomViewModel : CustomNodeViewModel
|
|
||||||
{
|
|
||||||
private readonly LayerPropertyNode _node;
|
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
|
||||||
private bool _outsideProfileContext;
|
|
||||||
|
|
||||||
public LayerPropertyNodeCustomViewModel(LayerPropertyNode node, INodeScript script, INodeEditorService nodeEditorService) : base(node, script)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_nodeEditorService = nodeEditorService;
|
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
|
||||||
if (_node.Script?.Context is not Profile profile)
|
|
||||||
{
|
|
||||||
OutsideProfileContext = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OutsideProfileContext = false;
|
|
||||||
Observable.FromEventPattern<ProfileElementEventArgs>(x => profile.DescendentAdded += x, x => profile.DescendentAdded -= x).Subscribe(_ => GetProfileElements()).DisposeWith(d);
|
|
||||||
Observable.FromEventPattern<ProfileElementEventArgs>(x => profile.DescendentRemoved += x, x => profile.DescendentRemoved -= x).Subscribe(_ => GetProfileElements()).DisposeWith(d);
|
|
||||||
GetProfileElements();
|
|
||||||
GetLayerProperties();
|
|
||||||
});
|
|
||||||
|
|
||||||
NodeModified += (_, _) => this.RaisePropertyChanged(nameof(SelectedProfileElement));
|
|
||||||
NodeModified += (_, _) => this.RaisePropertyChanged(nameof(SelectedLayerProperty));
|
|
||||||
this.WhenAnyValue(vm => vm.SelectedProfileElement).Subscribe(_ => GetLayerProperties());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableCollection<RenderProfileElement> ProfileElements { get; } = new();
|
|
||||||
public ObservableCollection<ILayerProperty> LayerProperties { get; } = new();
|
|
||||||
|
|
||||||
public bool OutsideProfileContext
|
|
||||||
{
|
|
||||||
get => _outsideProfileContext;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _outsideProfileContext, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RenderProfileElement? SelectedProfileElement
|
|
||||||
{
|
|
||||||
get => _node.ProfileElement;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != null && !Equals(_node.ProfileElement, value))
|
|
||||||
_nodeEditorService.ExecuteCommand(Script, new UpdateLayerPropertyNodeSelectedProfileElement(_node, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ILayerProperty? SelectedLayerProperty
|
|
||||||
{
|
|
||||||
get => _node.LayerProperty;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != null && !Equals(_node.LayerProperty, value))
|
|
||||||
_nodeEditorService.ExecuteCommand(Script, new UpdateLayerPropertyNodeSelectedLayerProperty(_node, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetProfileElements()
|
|
||||||
{
|
|
||||||
ProfileElements.Clear();
|
|
||||||
if (_node.Script?.Context is not Profile profile)
|
|
||||||
return;
|
|
||||||
|
|
||||||
List<RenderProfileElement> elements = new(profile.GetAllRenderElements());
|
|
||||||
|
|
||||||
ProfileElements.AddRange(elements.OrderBy(e => e.Order));
|
|
||||||
SelectedProfileElement = _node.ProfileElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetLayerProperties()
|
|
||||||
{
|
|
||||||
LayerProperties.Clear();
|
|
||||||
if (_node.ProfileElement == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
LayerProperties.AddRange(_node.ProfileElement.GetAllLayerProperties().Where(l => !l.IsHidden && l.DataBindingsSupported));
|
|
||||||
SelectedLayerProperty = _node.LayerProperty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input;
|
|
||||||
|
|
||||||
[Node("Hotkey enable/disable", "Outputs a boolean value enabled and disabled by a set of hotkeys", "Input", OutputType = typeof(bool))]
|
|
||||||
public class HotkeyEnableDisableNode : Node<HotkeyEnableDisableNodeEntity, HotkeyEnableDisableNodeCustomViewModel>, IDisposable
|
|
||||||
{
|
|
||||||
private readonly IInputService _inputService;
|
|
||||||
private Hotkey? _disableHotkey;
|
|
||||||
private Hotkey? _enableHotkey;
|
|
||||||
private bool _value;
|
|
||||||
private bool _retrievedInitialValue;
|
|
||||||
|
|
||||||
public HotkeyEnableDisableNode(IInputService inputService)
|
|
||||||
{
|
|
||||||
_inputService = inputService;
|
|
||||||
_inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
|
|
||||||
|
|
||||||
InitialValue = CreateInputPin<bool>();
|
|
||||||
Output = CreateOutputPin<bool>();
|
|
||||||
StorageModified += OnStorageModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<bool> InitialValue { get; }
|
|
||||||
public OutputPin<bool> Output { get; }
|
|
||||||
|
|
||||||
public override void Initialize(INodeScript script)
|
|
||||||
{
|
|
||||||
LoadHotkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
if (!_retrievedInitialValue)
|
|
||||||
{
|
|
||||||
_value = InitialValue.Value;
|
|
||||||
_retrievedInitialValue = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Output.Value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStorageModified(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
LoadHotkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadHotkeys()
|
|
||||||
{
|
|
||||||
if (Storage == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_enableHotkey = Storage.EnableHotkey != null ? new Hotkey(Storage.EnableHotkey) : null;
|
|
||||||
_disableHotkey = Storage.DisableHotkey != null ? new Hotkey(Storage.DisableHotkey) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (Storage == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_disableHotkey != null && _disableHotkey.MatchesEventArgs(e))
|
|
||||||
_value = false;
|
|
||||||
else if (_enableHotkey != null && _enableHotkey.MatchesEventArgs(e))
|
|
||||||
_value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_inputService.KeyboardKeyUp -= InputServiceOnKeyboardKeyUp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input;
|
|
||||||
|
|
||||||
public class HotkeyEnableDisableNodeEntity
|
|
||||||
{
|
|
||||||
public HotkeyEnableDisableNodeEntity(Hotkey? enableHotkey, Hotkey? disableHotkey)
|
|
||||||
{
|
|
||||||
enableHotkey?.Save();
|
|
||||||
EnableHotkey = enableHotkey?.Entity;
|
|
||||||
disableHotkey?.Save();
|
|
||||||
DisableHotkey = disableHotkey?.Entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProfileConfigurationHotkeyEntity? EnableHotkey { get; set; }
|
|
||||||
public ProfileConfigurationHotkeyEntity? DisableHotkey { get; set; }
|
|
||||||
}
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input;
|
|
||||||
|
|
||||||
[Node("Hotkey press", "Outputs a boolean value for as long as a hotkey is pressed", "Input", OutputType = typeof(bool))]
|
|
||||||
public class HotkeyPressNode : Node<HotkeyEnableDisableNodeEntity, HotkeyPressNodeCustomViewModel>, IDisposable
|
|
||||||
{
|
|
||||||
private readonly IInputService _inputService;
|
|
||||||
private Hotkey? _toggleHotkey;
|
|
||||||
private bool _value;
|
|
||||||
|
|
||||||
public HotkeyPressNode(IInputService inputService)
|
|
||||||
{
|
|
||||||
_inputService = inputService;
|
|
||||||
_inputService.KeyboardKeyDown += InputServiceOnKeyboardKeyDown;
|
|
||||||
_inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
|
|
||||||
|
|
||||||
Output = CreateOutputPin<bool>();
|
|
||||||
StorageModified += OnStorageModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OutputPin<bool> Output { get; }
|
|
||||||
|
|
||||||
public override void Initialize(INodeScript script)
|
|
||||||
{
|
|
||||||
LoadHotkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
Output.Value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStorageModified(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
LoadHotkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadHotkeys()
|
|
||||||
{
|
|
||||||
if (Storage == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_toggleHotkey = Storage.EnableHotkey != null ? new Hotkey(Storage.EnableHotkey) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InputServiceOnKeyboardKeyDown(object? sender, ArtemisKeyboardKeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (Storage == null)
|
|
||||||
return;
|
|
||||||
if (_toggleHotkey != null && _toggleHotkey.MatchesEventArgs(e))
|
|
||||||
_value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (Storage == null)
|
|
||||||
return;
|
|
||||||
if (_toggleHotkey != null && _toggleHotkey.MatchesEventArgs(e))
|
|
||||||
_value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_inputService.KeyboardKeyUp -= InputServiceOnKeyboardKeyUp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input;
|
|
||||||
|
|
||||||
[Node("Hotkey toggle", "Outputs a boolean value toggled by a hotkey", "Input", OutputType = typeof(bool))]
|
|
||||||
public class HotkeyToggleNode : Node<HotkeyEnableDisableNodeEntity, HotkeyToggleNodeCustomViewModel>, IDisposable
|
|
||||||
{
|
|
||||||
private readonly IInputService _inputService;
|
|
||||||
private Hotkey? _toggleHotkey;
|
|
||||||
private bool _value;
|
|
||||||
private bool _retrievedInitialValue;
|
|
||||||
|
|
||||||
public HotkeyToggleNode(IInputService inputService)
|
|
||||||
{
|
|
||||||
_inputService = inputService;
|
|
||||||
_inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
|
|
||||||
|
|
||||||
InitialValue = CreateInputPin<bool>();
|
|
||||||
Output = CreateOutputPin<bool>();
|
|
||||||
StorageModified += OnStorageModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputPin<bool> InitialValue { get; }
|
|
||||||
public OutputPin<bool> Output { get; }
|
|
||||||
|
|
||||||
public override void Initialize(INodeScript script)
|
|
||||||
{
|
|
||||||
LoadHotkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
if (!_retrievedInitialValue)
|
|
||||||
{
|
|
||||||
_value = InitialValue.Value;
|
|
||||||
_retrievedInitialValue = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Output.Value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStorageModified(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
LoadHotkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadHotkeys()
|
|
||||||
{
|
|
||||||
if (Storage == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_toggleHotkey = Storage.EnableHotkey != null ? new Hotkey(Storage.EnableHotkey) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (Storage == null)
|
|
||||||
return;
|
|
||||||
if (_toggleHotkey != null && _toggleHotkey.MatchesEventArgs(e))
|
|
||||||
_value = !_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_inputService.KeyboardKeyUp -= InputServiceOnKeyboardKeyUp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
using SkiaSharp;
|
|
||||||
using static Artemis.VisualScripting.Nodes.Input.PressedKeyPositionNodeEntity;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input;
|
|
||||||
|
|
||||||
[Node("Pressed Key Position", "Outputs the position of a pressed key relative to a layer", "Input", OutputType = typeof(Numeric))]
|
|
||||||
public class PressedKeyPositionNode : Node<PressedKeyPositionNodeEntity, PressedKeyPositionNodeCustomViewModel>, IDisposable
|
|
||||||
{
|
|
||||||
private readonly IInputService _inputService;
|
|
||||||
private Layer? _layer;
|
|
||||||
private SKPoint _ledPosition;
|
|
||||||
private Profile? _profile;
|
|
||||||
|
|
||||||
public PressedKeyPositionNode(IInputService inputService)
|
|
||||||
{
|
|
||||||
_inputService = inputService;
|
|
||||||
XPosition = CreateOutputPin<Numeric>("X");
|
|
||||||
YPosition = CreateOutputPin<Numeric>("Y");
|
|
||||||
|
|
||||||
StorageModified += OnStorageModified;
|
|
||||||
_inputService.KeyboardKeyDown += InputServiceOnKeyboardKeyDown;
|
|
||||||
_inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OutputPin<Numeric> XPosition { get; }
|
|
||||||
public OutputPin<Numeric> YPosition { get; }
|
|
||||||
|
|
||||||
public override void Initialize(INodeScript script)
|
|
||||||
{
|
|
||||||
Storage ??= new PressedKeyPositionNodeEntity();
|
|
||||||
|
|
||||||
_profile = script.Context as Profile;
|
|
||||||
_layer = _profile?.GetAllLayers().FirstOrDefault(l => l.EntityId == Storage.LayerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Evaluate()
|
|
||||||
{
|
|
||||||
XPosition.Value = _ledPosition.X;
|
|
||||||
YPosition.Value = _ledPosition.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InputServiceOnKeyboardKeyDown(object? sender, ArtemisKeyboardKeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (Storage?.RespondTo is KeyPressType.Down or KeyPressType.UpDown)
|
|
||||||
SetLedPosition(e.Led);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (Storage?.RespondTo is KeyPressType.Up or KeyPressType.UpDown)
|
|
||||||
SetLedPosition(e.Led);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetLedPosition(ArtemisLed? led)
|
|
||||||
{
|
|
||||||
if (_layer != null && led != null)
|
|
||||||
_ledPosition = new SKPoint((led.AbsoluteRectangle.MidX - _layer.Bounds.Left) / _layer.Bounds.Width, (led.AbsoluteRectangle.MidY - _layer.Bounds.Top) / _layer.Bounds.Height);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStorageModified(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
_layer = _profile?.GetAllLayers().FirstOrDefault(l => l.EntityId == Storage?.LayerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_inputService.KeyboardKeyDown -= InputServiceOnKeyboardKeyDown;
|
|
||||||
_inputService.KeyboardKeyUp -= InputServiceOnKeyboardKeyUp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input;
|
|
||||||
|
|
||||||
public class PressedKeyPositionNodeEntity
|
|
||||||
{
|
|
||||||
public PressedKeyPositionNodeEntity()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public PressedKeyPositionNodeEntity(Guid layerId, KeyPressType respondTo)
|
|
||||||
{
|
|
||||||
LayerId = layerId;
|
|
||||||
RespondTo = respondTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid LayerId { get; set; }
|
|
||||||
public KeyPressType RespondTo { get; set; }
|
|
||||||
|
|
||||||
public enum KeyPressType
|
|
||||||
{
|
|
||||||
[Description("Up")]
|
|
||||||
Up,
|
|
||||||
[Description("Down")]
|
|
||||||
Down,
|
|
||||||
[Description("Up/down")]
|
|
||||||
UpDown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.Input.Screens"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.Input.Screens.HotkeyEnableDisableNodeCustomView"
|
|
||||||
x:DataType="screens:HotkeyEnableDisableNodeCustomViewModel">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock FontSize="13" Margin="0 -2 0 5">Enable</TextBlock>
|
|
||||||
<shared:HotkeyBox Classes="condensed" Hotkey="{CompiledBinding EnableHotkey}" HotkeyChanged="HotkeyBox_OnHotkeyChanged" MinWidth="75" />
|
|
||||||
<TextBlock FontSize="13" Margin="0 5">Disable</TextBlock>
|
|
||||||
<shared:HotkeyBox Classes="condensed" Hotkey="{CompiledBinding DisableHotkey}" HotkeyChanged="HotkeyBox_OnHotkeyChanged" MinWidth="75" />
|
|
||||||
</StackPanel>
|
|
||||||
</UserControl>
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
using Artemis.UI.Shared;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
public partial class HotkeyEnableDisableNodeCustomView : ReactiveUserControl<HotkeyEnableDisableNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public HotkeyEnableDisableNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void HotkeyBox_OnHotkeyChanged(HotkeyBox sender, EventArgs args)
|
|
||||||
{
|
|
||||||
ViewModel?.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
using System.Reactive.Disposables;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
|
||||||
using Artemis.UI.Shared.VisualScripting;
|
|
||||||
using ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
public class HotkeyEnableDisableNodeCustomViewModel : CustomNodeViewModel
|
|
||||||
{
|
|
||||||
private readonly HotkeyEnableDisableNode _enableDisableNode;
|
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
|
||||||
private Hotkey? _enableHotkey;
|
|
||||||
private Hotkey? _disableHotkey;
|
|
||||||
private bool _updating;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public HotkeyEnableDisableNodeCustomViewModel(HotkeyEnableDisableNode enableDisableNode, INodeScript script, INodeEditorService nodeEditorService) : base(enableDisableNode, script)
|
|
||||||
{
|
|
||||||
_enableDisableNode = enableDisableNode;
|
|
||||||
_nodeEditorService = nodeEditorService;
|
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
|
||||||
Observable.FromEventPattern(x => _enableDisableNode.StorageModified += x, x => _enableDisableNode.StorageModified -= x).Subscribe(_ => Update()).DisposeWith(d);
|
|
||||||
Update();
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
_updating = true;
|
|
||||||
|
|
||||||
EnableHotkey = _enableDisableNode.Storage?.EnableHotkey != null ? new Hotkey(_enableDisableNode.Storage.EnableHotkey) : null;
|
|
||||||
DisableHotkey = _enableDisableNode.Storage?.DisableHotkey != null ? new Hotkey(_enableDisableNode.Storage.DisableHotkey) : null;
|
|
||||||
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Hotkey? EnableHotkey
|
|
||||||
{
|
|
||||||
get => _enableHotkey;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _enableHotkey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Hotkey? DisableHotkey
|
|
||||||
{
|
|
||||||
get => _disableHotkey;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _disableHotkey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
if (_updating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_nodeEditorService.ExecuteCommand(
|
|
||||||
Script,
|
|
||||||
new UpdateStorage<HotkeyEnableDisableNodeEntity>(_enableDisableNode, new HotkeyEnableDisableNodeEntity(EnableHotkey, DisableHotkey), "hotkey")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.Input.Screens"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.Input.Screens.HotkeyPressNodeCustomView"
|
|
||||||
x:DataType="screens:HotkeyPressNodeCustomViewModel">
|
|
||||||
<shared:HotkeyBox Classes="condensed" Hotkey="{CompiledBinding ToggleHotkey}" HotkeyChanged="HotkeyBox_OnHotkeyChanged" MinWidth="75" />
|
|
||||||
</UserControl>
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
using Artemis.UI.Shared;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
public partial class HotkeyPressNodeCustomView : ReactiveUserControl<HotkeyPressNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public HotkeyPressNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void HotkeyBox_OnHotkeyChanged(HotkeyBox sender, EventArgs args)
|
|
||||||
{
|
|
||||||
ViewModel?.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
using System.Reactive.Disposables;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
|
||||||
using Artemis.UI.Shared.VisualScripting;
|
|
||||||
using ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
public class HotkeyPressNodeCustomViewModel : CustomNodeViewModel
|
|
||||||
{
|
|
||||||
private readonly HotkeyPressNode _pressNode;
|
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
|
||||||
private Hotkey? _toggleHotkey;
|
|
||||||
private bool _updating;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public HotkeyPressNodeCustomViewModel(HotkeyPressNode pressNode, INodeScript script, INodeEditorService nodeEditorService) : base(pressNode, script)
|
|
||||||
{
|
|
||||||
_pressNode = pressNode;
|
|
||||||
_nodeEditorService = nodeEditorService;
|
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
|
||||||
Observable.FromEventPattern(x => _pressNode.StorageModified += x, x => _pressNode.StorageModified -= x).Subscribe(_ => Update()).DisposeWith(d);
|
|
||||||
Update();
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
_updating = true;
|
|
||||||
ToggleHotkey = _pressNode.Storage?.EnableHotkey != null ? new Hotkey(_pressNode.Storage.EnableHotkey) : null;
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Hotkey? ToggleHotkey
|
|
||||||
{
|
|
||||||
get => _toggleHotkey;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _toggleHotkey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
if (_updating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_nodeEditorService.ExecuteCommand(
|
|
||||||
Script,
|
|
||||||
new UpdateStorage<HotkeyEnableDisableNodeEntity>(_pressNode, new HotkeyEnableDisableNodeEntity(ToggleHotkey, null), "hotkey")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.Input.Screens"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.Input.Screens.HotkeyToggleNodeCustomView"
|
|
||||||
x:DataType="screens:HotkeyToggleNodeCustomViewModel">
|
|
||||||
<shared:HotkeyBox Classes="condensed" Hotkey="{CompiledBinding ToggleHotkey}" HotkeyChanged="HotkeyBox_OnHotkeyChanged" MinWidth="75" />
|
|
||||||
</UserControl>
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
using Artemis.UI.Shared;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
public partial class HotkeyToggleNodeCustomView : ReactiveUserControl<HotkeyToggleNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public HotkeyToggleNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void HotkeyBox_OnHotkeyChanged(HotkeyBox sender, EventArgs args)
|
|
||||||
{
|
|
||||||
ViewModel?.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
using System.Reactive.Disposables;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor;
|
|
||||||
using Artemis.UI.Shared.Services.NodeEditor.Commands;
|
|
||||||
using Artemis.UI.Shared.VisualScripting;
|
|
||||||
using ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
public class HotkeyToggleNodeCustomViewModel : CustomNodeViewModel
|
|
||||||
{
|
|
||||||
private readonly HotkeyToggleNode _toggleNode;
|
|
||||||
private readonly INodeEditorService _nodeEditorService;
|
|
||||||
private Hotkey? _toggleHotkey;
|
|
||||||
private bool _updating;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public HotkeyToggleNodeCustomViewModel(HotkeyToggleNode toggleNode, INodeScript script, INodeEditorService nodeEditorService) : base(toggleNode, script)
|
|
||||||
{
|
|
||||||
_toggleNode = toggleNode;
|
|
||||||
_nodeEditorService = nodeEditorService;
|
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
|
||||||
{
|
|
||||||
Observable.FromEventPattern(x => _toggleNode.StorageModified += x, x => _toggleNode.StorageModified -= x).Subscribe(_ => Update()).DisposeWith(d);
|
|
||||||
Update();
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
_updating = true;
|
|
||||||
ToggleHotkey = _toggleNode.Storage?.EnableHotkey != null ? new Hotkey(_toggleNode.Storage.EnableHotkey) : null;
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Hotkey? ToggleHotkey
|
|
||||||
{
|
|
||||||
get => _toggleHotkey;
|
|
||||||
set => this.RaiseAndSetIfChanged(ref _toggleHotkey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
if (_updating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_nodeEditorService.ExecuteCommand(
|
|
||||||
Script,
|
|
||||||
new UpdateStorage<HotkeyEnableDisableNodeEntity>(_toggleNode, new HotkeyEnableDisableNodeEntity(ToggleHotkey, null), "hotkey")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:screens="clr-namespace:Artemis.VisualScripting.Nodes.Input.Screens"
|
|
||||||
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
|
|
||||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.VisualScripting.Nodes.Input.Screens.PressedKeyPositionNodeCustomView"
|
|
||||||
x:DataType="screens:PressedKeyPositionNodeCustomViewModel">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock FontSize="13" Margin="0 -2 0 5">Layer</TextBlock>
|
|
||||||
<ComboBox Classes="condensed" ItemsSource="{CompiledBinding Layers}" SelectedItem="{CompiledBinding SelectedLayer}" PlaceholderText="Select a layer">
|
|
||||||
<ComboBox.ItemTemplate>
|
|
||||||
<DataTemplate DataType="core:Layer">
|
|
||||||
<StackPanel Spacing="5" Orientation="Horizontal">
|
|
||||||
<avalonia:MaterialIcon Kind="{CompiledBinding LayerBrush.Descriptor.Icon, FallbackValue=QuestionMark}" VerticalAlignment="Center" />
|
|
||||||
<TextBlock Text="{CompiledBinding Name}" />
|
|
||||||
</StackPanel>
|
|
||||||
</DataTemplate>
|
|
||||||
</ComboBox.ItemTemplate>
|
|
||||||
</ComboBox>
|
|
||||||
|
|
||||||
<TextBlock FontSize="13" Margin="0 5">Respond to</TextBlock>
|
|
||||||
<shared:EnumComboBox Classes="condensed" Value="{CompiledBinding RespondTo}"/>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
</UserControl>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.Input.Screens;
|
|
||||||
|
|
||||||
public partial class PressedKeyPositionNodeCustomView : ReactiveUserControl<PressedKeyPositionNodeCustomViewModel>
|
|
||||||
{
|
|
||||||
public PressedKeyPositionNodeCustomView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user