1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2026-03-24 10:18:47 +00:00

Merge branch 'development'

This commit is contained in:
Robert Beekman 2022-10-06 16:43:11 +02:00
commit 5a571f09d7
85 changed files with 453 additions and 199 deletions

View File

@ -10,7 +10,7 @@ jobs:
version: version:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
version-suffix: ${{ steps.get-version.outputs.version-suffix }} version-number: ${{ steps.get-version.outputs.version-number }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -22,9 +22,12 @@ jobs:
run: | run: |
$MidnightUtc = [DateTime]::UtcNow.Date $MidnightUtc = [DateTime]::UtcNow.Date
$BranchName = "${{ github.ref_name }}".replace('/','-').replace('.','-') $BranchName = "${{ github.ref_name }}".replace('/','-').replace('.','-')
$ApiVersion = (Select-Xml -Path 'src/Artemis.Core/Artemis.Core.csproj' -XPath '//PluginApiVersion').Node.InnerText
$NumberOfCommitsToday = (git log --after=$($MidnightUtc.ToString("o")) --oneline | Measure-Object -Line).Lines $NumberOfCommitsToday = (git log --after=$($MidnightUtc.ToString("o")) --oneline | Measure-Object -Line).Lines
$VersionSuffix = "$BranchName.$($MidnightUtc.ToString("yyyyMMdd")).$NumberOfCommitsToday" $VersionNumber = "$ApiVersion.$($MidnightUtc.ToString("yyyy.MMdd")).$NumberOfCommitsToday"
Write-Output "::set-output name=version-suffix::$VersionSuffix" # If we're not in master, add the branch name to the version so it counts as prerelease
if ($BranchName -ne "master") { $VersionNumber += "-$BranchName" }
Write-Output "::set-output name=version-number::$VersionNumber"
nuget: nuget:
name: Publish Nuget Packages name: Publish Nuget Packages
@ -38,8 +41,8 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Pack Artemis.Core - name: Pack Artemis.Core
run: dotnet pack -c Release -p:VersionSuffix=${{ needs.version.outputs.version-suffix }} -p:BuildingNuget=True src/Artemis.Core/Artemis.Core.csproj run: dotnet pack -c Release -p:Version=${{ needs.version.outputs.version-number }} -p:BuildingNuget=True src/Artemis.Core/Artemis.Core.csproj
- name: Pack Artemis.UI.Shared - name: Pack Artemis.UI.Shared
run: dotnet pack -c Release -p:VersionSuffix=${{ needs.version.outputs.version-suffix }} src/Artemis.UI.Shared/Artemis.UI.Shared.csproj run: dotnet pack -c Release -p:Version=${{ needs.version.outputs.version-number }} src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
- name: Push Nugets - name: Push Nugets
run: dotnet nuget push **/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate run: dotnet nuget push **/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate

View File

@ -10,10 +10,15 @@
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageId>ArtemisRGB.Core</PackageId> <PackageId>ArtemisRGB.Core</PackageId>
<!-- API Version --> <PluginApiVersion>1</PluginApiVersion>
<VersionPrefix>1.0.0.0</VersionPrefix> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<!--Used to embed the above PluginApiVersion property into the assembly as metadata-->
<AssemblyMetadata Include="PluginApiVersion" Value="$(PluginApiVersion)" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Artemis.Storage\Artemis.Storage.csproj" /> <ProjectReference Include="..\Artemis.Storage\Artemis.Storage.csproj" />
<ProjectReference Condition="'$(BuildingNuget)' == 'True'" Update="..\Artemis.Storage\Artemis.Storage.csproj" PrivateAssets="All" /> <ProjectReference Condition="'$(BuildingNuget)' == 'True'" Update="..\Artemis.Storage\Artemis.Storage.csproj" PrivateAssets="All" />

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using Artemis.Core.JsonConverters; using Artemis.Core.JsonConverters;
using Artemis.Core.Services; using Artemis.Core.Services;
@ -61,7 +62,8 @@ public static class Constants
/// <summary> /// <summary>
/// The current API version for plugins /// The current API version for plugins
/// </summary> /// </summary>
public static readonly Version PluginApi = CoreAssembly.GetName().Version!; public static readonly int PluginApiVersion = int.Parse(CoreAssembly.GetCustomAttributes<AssemblyMetadataAttribute>()
.First(a => a.Key == "PluginApiVersion").Value);
/// <summary> /// <summary>
/// The plugin info used by core components of Artemis /// The plugin info used by core components of Artemis

View File

@ -16,12 +16,13 @@ namespace Artemis.Core;
/// </summary> /// </summary>
public sealed class Layer : RenderProfileElement public sealed class Layer : RenderProfileElement
{ {
private readonly List<Layer> _renderCopies; private readonly List<Layer> _renderCopies = new();
private LayerGeneralProperties _general; private LayerGeneralProperties _general = new();
private LayerTransformProperties _transform = new();
private BaseLayerBrush? _layerBrush; private BaseLayerBrush? _layerBrush;
private LayerShape? _layerShape; private LayerShape? _layerShape;
private List<ArtemisLed> _leds; private List<ArtemisLed> _leds = new();
private LayerTransformProperties _transform; private List<LedEntity> _missingLeds = new();
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="Layer" /> class and adds itself to the child collection of the provided /// Creates a new instance of the <see cref="Layer" /> class and adds itself to the child collection of the provided
@ -37,16 +38,9 @@ public sealed class Layer : RenderProfileElement
Profile = Parent.Profile; Profile = Parent.Profile;
Name = name; Name = name;
Suspended = false; Suspended = false;
// TODO: move to top
_renderCopies = new List<Layer>();
_general = new LayerGeneralProperties();
_transform = new LayerTransformProperties();
_leds = new List<ArtemisLed>();
Leds = new ReadOnlyCollection<ArtemisLed>(_leds); Leds = new ReadOnlyCollection<ArtemisLed>(_leds);
Adapter = new LayerAdapter(this); Adapter = new LayerAdapter(this);
Initialize(); Initialize();
} }
@ -63,16 +57,9 @@ public sealed class Layer : RenderProfileElement
Profile = profile; Profile = profile;
Parent = parent; Parent = parent;
// TODO: move to top
_renderCopies = new List<Layer>();
_general = new LayerGeneralProperties();
_transform = new LayerTransformProperties();
_leds = new List<ArtemisLed>();
Leds = new ReadOnlyCollection<ArtemisLed>(_leds); Leds = new ReadOnlyCollection<ArtemisLed>(_leds);
Adapter = new LayerAdapter(this); Adapter = new LayerAdapter(this);
Load(); Load();
Initialize(); Initialize();
} }
@ -327,6 +314,7 @@ public sealed class Layer : RenderProfileElement
// LEDs // LEDs
LayerEntity.Leds.Clear(); LayerEntity.Leds.Clear();
SaveMissingLeds();
foreach (ArtemisLed artemisLed in Leds) foreach (ArtemisLed artemisLed in Leds)
{ {
LedEntity ledEntity = new() LedEntity ledEntity = new()
@ -786,6 +774,8 @@ public sealed class Layer : RenderProfileElement
a.RgbLed.Id.ToString() == ledEntity.LedName); a.RgbLed.Id.ToString() == ledEntity.LedName);
if (match != null) if (match != null)
leds.Add(match); leds.Add(match);
else
_missingLeds.Add(ledEntity);
} }
_leds = leds; _leds = leds;
@ -793,6 +783,11 @@ public sealed class Layer : RenderProfileElement
CalculateRenderProperties(); CalculateRenderProperties();
} }
private void SaveMissingLeds()
{
LayerEntity.Leds.AddRange(_missingLeds.Except(LayerEntity.Leds, LedEntity.LedEntityComparer));
}
#endregion #endregion
#region Brush management #region Brush management

View File

@ -191,7 +191,7 @@ public class PluginInfo : CorePropertyChanged, IPrerequisitesSubject
/// <summary> /// <summary>
/// Gets a boolean indicating whether this plugin is compatible with the current operating system and API version /// Gets a boolean indicating whether this plugin is compatible with the current operating system and API version
/// </summary> /// </summary>
public bool IsCompatible => Platforms.MatchesCurrentOperatingSystem() && Api != null && Api >= Constants.PluginApi; public bool IsCompatible => Platforms.MatchesCurrentOperatingSystem() && Api != null && Api.Major >= Constants.PluginApiVersion;
internal string PreferredPluginDirectory => $"{Main.Split(".dll")[0].Replace("/", "").Replace("\\", "")}-{Guid.ToString().Substring(0, 8)}"; internal string PreferredPluginDirectory => $"{Main.Split(".dll")[0].Replace("/", "").Replace("\\", "")}-{Guid.ToString().Substring(0, 8)}";

View File

@ -75,8 +75,9 @@ internal class NodeService : INodeService
string name = nodeAttribute?.Name ?? nodeType.Name; string name = nodeAttribute?.Name ?? nodeType.Name;
string description = nodeAttribute?.Description ?? string.Empty; string description = nodeAttribute?.Description ?? string.Empty;
string category = nodeAttribute?.Category ?? string.Empty; string category = nodeAttribute?.Category ?? string.Empty;
string helpUrl = nodeAttribute?.HelpUrl ?? string.Empty;
NodeData nodeData = new(plugin, nodeType, name, description, category, nodeAttribute?.InputType, nodeAttribute?.OutputType, (s, e) => CreateNode(s, e, nodeType)); NodeData nodeData = new(plugin, nodeType, name, description, category, helpUrl, nodeAttribute?.InputType, nodeAttribute?.OutputType, (s, e) => CreateNode(s, e, nodeType));
return NodeTypeStore.Add(nodeData); return NodeTypeStore.Add(nodeData);
} }

View File

@ -18,12 +18,12 @@ public interface INode : INotifyPropertyChanged, IBreakableModel
/// <summary> /// <summary>
/// Gets the name of the node /// Gets the name of the node
/// </summary> /// </summary>
string Name { get; } string Name { get; set; }
/// <summary> /// <summary>
/// Gets the description of the node /// Gets the description of the node
/// </summary> /// </summary>
string Description { get; } string Description { get; set; }
/// <summary> /// <summary>
/// Gets a boolean indicating whether the node is the exit node of the script /// Gets a boolean indicating whether the node is the exit node of the script
@ -45,6 +45,11 @@ public interface INode : INotifyPropertyChanged, IBreakableModel
/// </summary> /// </summary>
public double Y { get; set; } public double Y { get; set; }
/// <summary>
/// Gets or sets the help URL of the node
/// </summary>
string HelpUrl { get; set; }
/// <summary> /// <summary>
/// Gets a read-only collection of the pins on this node /// Gets a read-only collection of the pins on this node
/// </summary> /// </summary>

View File

@ -24,6 +24,11 @@ public class NodeAttribute : Attribute
/// </summary> /// </summary>
public string Category { get; } = string.Empty; public string Category { get; } = string.Empty;
/// <summary>
/// Gets the help URL of the node
/// </summary>
public string HelpUrl { get; init; } = string.Empty;
/// <summary> /// <summary>
/// Gets the primary input type of the node /// Gets the primary input type of the node
/// </summary> /// </summary>
@ -65,5 +70,16 @@ public class NodeAttribute : Attribute
Category = category; Category = category;
} }
/// <summary>
/// Creates a new instance of the <see cref="NodeAttribute" /> class
/// </summary>
public NodeAttribute(string name, string description, string category, string helpUrl)
{
Name = name;
Description = description;
Category = category;
HelpUrl = helpUrl;
}
#endregion #endregion
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using Artemis.Storage.Entities.Profile.Nodes; using Artemis.Storage.Entities.Profile.Nodes;
using Castle.Core.Internal;
namespace Artemis.Core; namespace Artemis.Core;
@ -10,13 +11,14 @@ public class NodeData
{ {
#region Constructors #region Constructors
internal NodeData(Plugin plugin, Type type, string name, string description, string category, Type? inputType, Type? outputType, Func<INodeScript, NodeEntity?, INode> create) internal NodeData(Plugin plugin, Type type, string name, string description, string category, string helpUrl, Type? inputType, Type? outputType, Func<INodeScript, NodeEntity?, INode> create)
{ {
Plugin = plugin; Plugin = plugin;
Type = type; Type = type;
Name = name; Name = name;
Description = description; Description = description;
Category = category; Category = category;
HelpUrl = helpUrl;
InputType = inputType; InputType = inputType;
OutputType = outputType; OutputType = outputType;
_create = create; _create = create;
@ -34,7 +36,15 @@ 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)
{ {
return _create(script, entity); INode node = _create(script, entity);
if (string.IsNullOrWhiteSpace(node.Name))
node.Name = Name;
if (string.IsNullOrWhiteSpace(node.Description))
node.Description = Description;
if (string.IsNullOrWhiteSpace(node.HelpUrl))
node.HelpUrl = HelpUrl;
return node;
} }
#endregion #endregion
@ -101,6 +111,11 @@ public class NodeData
/// </summary> /// </summary>
public string Category { get; } public string Category { get; }
/// <summary>
/// Gets the help URL of the node this data represents
/// </summary>
public string HelpUrl { get; }
/// <summary> /// <summary>
/// Gets the primary input type of the node this data represents /// Gets the primary input type of the node this data represents
/// </summary> /// </summary>

View File

@ -46,7 +46,7 @@ public abstract class Node : BreakableModel, INode
public string Name public string Name
{ {
get => _name; get => _name;
protected set => SetAndNotify(ref _name, value); set => SetAndNotify(ref _name, value);
} }
private string _description; private string _description;
@ -55,7 +55,7 @@ public abstract class Node : BreakableModel, INode
public string Description public string Description
{ {
get => _description; get => _description;
protected set => SetAndNotify(ref _description, value); set => SetAndNotify(ref _description, value);
} }
private double _x; private double _x;
@ -76,6 +76,13 @@ public abstract class Node : BreakableModel, INode
set => SetAndNotify(ref _y, value); set => SetAndNotify(ref _y, value);
} }
/// <inheritdoc />
public string HelpUrl
{
get => _helpUrl;
set => SetAndNotify(ref _helpUrl, value);
}
/// <inheritdoc /> /// <inheritdoc />
public virtual bool IsExitNode => false; public virtual bool IsExitNode => false;
@ -88,6 +95,7 @@ public abstract class Node : BreakableModel, INode
public IReadOnlyCollection<IPin> Pins => new ReadOnlyCollection<IPin>(_pins); public IReadOnlyCollection<IPin> Pins => new ReadOnlyCollection<IPin>(_pins);
private readonly List<IPinCollection> _pinCollections = new(); private readonly List<IPinCollection> _pinCollections = new();
private string _helpUrl;
/// <inheritdoc /> /// <inheritdoc />
public IReadOnlyCollection<IPinCollection> PinCollections => new ReadOnlyCollection<IPinCollection>(_pinCollections); public IReadOnlyCollection<IPinCollection> PinCollections => new ReadOnlyCollection<IPinCollection>(_pinCollections);
@ -106,6 +114,7 @@ public abstract class Node : BreakableModel, INode
{ {
_name = string.Empty; _name = string.Empty;
_description = string.Empty; _description = string.Empty;
_helpUrl = string.Empty;
_id = Guid.NewGuid(); _id = Guid.NewGuid();
} }
@ -116,6 +125,7 @@ public abstract class Node : BreakableModel, INode
{ {
_name = name; _name = name;
_description = description; _description = description;
_helpUrl = string.Empty;
_id = Guid.NewGuid(); _id = Guid.NewGuid();
} }

View File

@ -1,7 +1,37 @@
namespace Artemis.Storage.Entities.Profile; using System;
using System.Collections.Generic;
namespace Artemis.Storage.Entities.Profile;
public class LedEntity public class LedEntity
{ {
#region LedEntityEqualityComparer
private sealed class LedEntityEqualityComparer : IEqualityComparer<LedEntity>
{
public bool Equals(LedEntity x, LedEntity y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null))
return false;
if (ReferenceEquals(y, null))
return false;
if (x.GetType() != y.GetType())
return false;
return x.LedName == y.LedName && x.DeviceIdentifier == y.DeviceIdentifier && x.PhysicalLayout == y.PhysicalLayout;
}
public int GetHashCode(LedEntity obj)
{
return HashCode.Combine(obj.LedName, obj.DeviceIdentifier, obj.PhysicalLayout);
}
}
public static IEqualityComparer<LedEntity> LedEntityComparer { get; } = new LedEntityEqualityComparer();
#endregion
public string LedName { get; set; } public string LedName { get; set; }
public string DeviceIdentifier { get; set; } public string DeviceIdentifier { get; set; }

View File

@ -4,6 +4,22 @@ namespace Artemis.Storage.Entities.Profile.Nodes;
public class NodeConnectionEntity public class NodeConnectionEntity
{ {
public NodeConnectionEntity()
{
}
public NodeConnectionEntity(NodeConnectionEntity nodeConnectionEntity)
{
SourceType = nodeConnectionEntity.SourceType;
SourceNode = nodeConnectionEntity.SourceNode;
TargetNode = nodeConnectionEntity.TargetNode;
SourcePinCollectionId = nodeConnectionEntity.SourcePinCollectionId;
SourcePinId = nodeConnectionEntity.SourcePinId;
TargetType = nodeConnectionEntity.TargetType;
TargetPinCollectionId = nodeConnectionEntity.TargetPinCollectionId;
TargetPinId = nodeConnectionEntity.TargetPinId;
}
public string SourceType { get; set; } public string SourceType { get; set; }
public Guid SourceNode { get; set; } public Guid SourceNode { get; set; }
public Guid TargetNode { get; set; } public Guid TargetNode { get; set; }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Artemis.Storage.Entities.Profile.Nodes; namespace Artemis.Storage.Entities.Profile.Nodes;
@ -10,6 +11,22 @@ public class NodeEntity
PinCollections = new List<NodePinCollectionEntity>(); PinCollections = new List<NodePinCollectionEntity>();
} }
public NodeEntity(NodeEntity nodeEntity)
{
Id = nodeEntity.Id;
Type = nodeEntity.Type;
PluginId = nodeEntity.PluginId;
Name = nodeEntity.Name;
Description = nodeEntity.Description;
IsExitNode = nodeEntity.IsExitNode;
X = nodeEntity.X;
Y = nodeEntity.Y;
Storage = nodeEntity.Storage;
PinCollections = nodeEntity.PinCollections.Select(p => new NodePinCollectionEntity(p)).ToList();
}
public Guid Id { get; set; } public Guid Id { get; set; }
public string Type { get; set; } public string Type { get; set; }
public Guid PluginId { get; set; } public Guid PluginId { get; set; }

View File

@ -2,6 +2,17 @@
public class NodePinCollectionEntity public class NodePinCollectionEntity
{ {
public NodePinCollectionEntity()
{
}
public NodePinCollectionEntity(NodePinCollectionEntity nodePinCollectionEntity)
{
Id = nodePinCollectionEntity.Id;
Direction = nodePinCollectionEntity.Direction;
Amount = nodePinCollectionEntity.Amount;
}
public int Id { get; set; } public int Id { get; set; }
public int Direction { set; get; } public int Direction { set; get; }
public int Amount { get; set; } public int Amount { get; set; }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using Artemis.Core; using Artemis.Core;
namespace Artemis.UI.Shared.Services.NodeEditor.Commands; namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
@ -36,6 +37,7 @@ public class AddNode : INodeEditorCommand, IDisposable
/// <inheritdoc /> /// <inheritdoc />
public void Execute() public void Execute()
{ {
if (!_nodeScript.Nodes.Contains(_node))
_nodeScript.AddNode(_node); _nodeScript.AddNode(_node);
_isRemoved = false; _isRemoved = false;
} }

View File

@ -45,7 +45,7 @@
<!-- Add Styles Here --> <!-- Add Styles Here -->
<Style Selector="TextBox.condensed"> <Style Selector="TextBox.condensed">
<Setter Property="Padding" Value="2" /> <Setter Property="Padding" Value="6 1" />
<Setter Property="FontSize" Value="13" /> <Setter Property="FontSize" Value="13" />
<Setter Property="MinHeight" Value="24" /> <Setter Property="MinHeight" Value="24" />
</Style> </Style>

View File

@ -10,7 +10,8 @@
<!-- Add Controls for Previewer Here --> <!-- Add Controls for Previewer Here -->
<TextBox Text="99999999" <TextBox Text="99999999"
attached:TextBoxAssist.PrefixText="%" attached:TextBoxAssist.PrefixText="%"
attached:TextBoxAssist.SuffixText="%"></TextBox> attached:TextBoxAssist.SuffixText="%">
</TextBox>
<controls:NumberBox Value="99999999" <controls:NumberBox Value="99999999"
attached:NumberBoxAssist.PrefixText="%" attached:NumberBoxAssist.PrefixText="%"
attached:NumberBoxAssist.SuffixText="%" /> attached:NumberBoxAssist.SuffixText="%" />
@ -50,17 +51,30 @@
Margin="{TemplateBinding BorderThickness}"> Margin="{TemplateBinding BorderThickness}">
<Grid ColumnDefinitions="Auto,*,Auto"> <Grid ColumnDefinitions="Auto,*,Auto">
<ContentPresenter Grid.Column="0" Content="{TemplateBinding InnerLeftContent}" /> <ContentPresenter Grid.Column="0" Content="{TemplateBinding InnerLeftContent}" />
<DockPanel x:Name="PART_InnerDockPanel" Grid.Column="1" Margin="{TemplateBinding Padding}"> <Grid x:Name="PART_InnerGrid"
<TextBlock Name="PART_Prefix" Grid.Column="1"
Text="{TemplateBinding attached:TextBoxAssist.PrefixText}" RowDefinitions="Auto,Auto"
IsHitTestVisible="False" ColumnDefinitions="Auto,*,Auto"
DockPanel.Dock="Left"/> Cursor="IBeam"
<TextBlock Name="PART_FloatingWatermark" Margin="{TemplateBinding Padding}">
<TextBlock Grid.Row="0"
Grid.ColumnSpan="3"
Name="PART_FloatingWatermark"
Foreground="{DynamicResource SystemAccentColor}" Foreground="{DynamicResource SystemAccentColor}"
FontSize="{TemplateBinding FontSize}" FontSize="{TemplateBinding FontSize}"
Text="{TemplateBinding Watermark}" Text="{TemplateBinding Watermark}" />
DockPanel.Dock="Top" />
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}" <TextBlock Grid.Row="1"
Grid.Column="0"
Name="PART_Prefix"
Text="{TemplateBinding attached:TextBoxAssist.PrefixText}"
IsVisible="{TemplateBinding attached:TextBoxAssist.PrefixText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
IsHitTestVisible="False"
DockPanel.Dock="Left" />
<ScrollViewer Grid.Row="1"
Grid.Column="1"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"> VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<Panel> <Panel>
<TextBlock Name="PART_Watermark" <TextBlock Name="PART_Watermark"
@ -88,12 +102,14 @@
</Panel> </Panel>
</ScrollViewer> </ScrollViewer>
<TextBlock Name="PART_Suffix" <TextBlock Grid.Row="1"
Grid.Column="2"
Name="PART_Suffix"
Text="{TemplateBinding attached:TextBoxAssist.SuffixText}" Text="{TemplateBinding attached:TextBoxAssist.SuffixText}"
IsVisible="{TemplateBinding attached:TextBoxAssist.SuffixText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
IsHitTestVisible="False" IsHitTestVisible="False"
HorizontalAlignment="Right" HorizontalAlignment="Right" />
DockPanel.Dock="Right"/> </Grid>
</DockPanel>
<ContentPresenter Grid.Column="2" Content="{TemplateBinding InnerRightContent}" /> <ContentPresenter Grid.Column="2" Content="{TemplateBinding InnerRightContent}" />
</Grid> </Grid>
</Border> </Border>

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.Storage.Entities.Profile.Nodes;
using FluentAvalonia.Core;
namespace Artemis.UI.Models;
public class NodesClipboardModel
{
public NodesClipboardModel(NodeScript nodeScript, List<INode> nodes)
{
nodeScript.Save();
// Grab all entities belonging to provided nodes
Nodes = nodeScript.Entity.Nodes.Where(e => nodes.Any(n => n.Id == e.Id)).ToList();
// Grab all connections between provided nodes
Connections = nodeScript.Entity.Connections.Where(e => nodes.Any(n => n.Id == e.SourceNode) && nodes.Any(n => n.Id == e.TargetNode)).ToList();
}
public NodesClipboardModel()
{
Nodes = new List<NodeEntity>();
Connections = new List<NodeConnectionEntity>();
}
public List<NodeEntity> Nodes { get; set; }
public List<NodeConnectionEntity> Connections { get; set; }
public List<INode> Paste(NodeScript nodeScript, double x, double y)
{
if (!Nodes.Any())
return new List<INode>();
nodeScript.Save();
// Copy the entities, not messing with the originals
List<NodeEntity> nodes = Nodes.Select(n => new NodeEntity(n)).ToList();
List<NodeConnectionEntity> connections = Connections.Select(c => new NodeConnectionEntity(c)).ToList();
double xOffset = x - nodes.Min(n => n.X);
double yOffset = y - nodes.Min(n => n.Y);
foreach (NodeEntity node in nodes)
{
// Give each node a new GUID, updating any connections to it
Guid newGuid = Guid.NewGuid();
foreach (NodeConnectionEntity connection in connections)
{
if (connection.SourceNode == node.Id)
connection.SourceNode = newGuid;
else if (connection.TargetNode == node.Id)
connection.TargetNode = newGuid;
// Only add the connection if this is the first time we hit it
if (!nodeScript.Entity.Connections.Contains(connection))
nodeScript.Entity.Connections.Add(connection);
}
node.Id = newGuid;
node.X += xOffset;
node.Y += yOffset;
nodeScript.Entity.Nodes.Add(node);
}
nodeScript.Load();
// Return the newly created nodes
return nodeScript.Nodes.Where(n => nodes.Any(e => e.Id == n.Id)).ToList();
}
}

View File

@ -46,6 +46,8 @@
Width="44" Width="44"
Height="44" Height="44"
ToolTip.Tip="View website" ToolTip.Tip="View website"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://artemis-rgb.com"> NavigateUri="https://artemis-rgb.com">
<avalonia:MaterialIcon Kind="Web" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="Web" Width="20" Height="20" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
@ -53,6 +55,8 @@
Width="44" Width="44"
Height="44" Height="44"
ToolTip.Tip="View GitHub repository" ToolTip.Tip="View GitHub repository"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://github.com/Artemis-RGB/Artemis"> NavigateUri="https://github.com/Artemis-RGB/Artemis">
<avalonia:MaterialIcon Kind="Github" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="Github" Width="20" Height="20" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
@ -60,6 +64,8 @@
Width="44" Width="44"
Height="44" Height="44"
ToolTip.Tip="View Wiki" ToolTip.Tip="View Wiki"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://wiki.artemis-rgb.com"> NavigateUri="https://wiki.artemis-rgb.com">
<avalonia:MaterialIcon Kind="BookOpenOutline" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="BookOpenOutline" Width="20" Height="20" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
@ -67,6 +73,8 @@
Width="44" Width="44"
Height="44" Height="44"
ToolTip.Tip="Join our Discord" ToolTip.Tip="Join our Discord"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://discord.gg/S3MVaC9"> NavigateUri="https://discord.gg/S3MVaC9">
<avalonia:MaterialIcon Kind="Chat" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="Chat" Width="20" Height="20" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
@ -74,6 +82,8 @@
Width="44" Width="44"
Height="44" Height="44"
ToolTip.Tip="View donation options" ToolTip.Tip="View donation options"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://wiki.artemis-rgb.com/en/donating"> NavigateUri="https://wiki.artemis-rgb.com/en/donating">
<avalonia:MaterialIcon Kind="Gift" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="Gift" Width="20" Height="20" />
</controls:HyperlinkButton> </controls:HyperlinkButton>

View File

@ -24,11 +24,6 @@
Stroke="{CompiledBinding CableColor, Converter={StaticResource ColorToSolidColorBrushConverter}}" Stroke="{CompiledBinding CableColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
StrokeThickness="4" StrokeThickness="4"
StrokeLineCap="Round"> StrokeLineCap="Round">
<Path.Transitions>
<Transitions>
<ThicknessTransition Property="Margin" Duration="200"></ThicknessTransition>
</Transitions>
</Path.Transitions>
<Path.Data> <Path.Data>
<PathGeometry> <PathGeometry>
<PathGeometry.Figures> <PathGeometry.Figures>
@ -47,8 +42,6 @@
BorderThickness="2" BorderThickness="2"
CornerRadius="3" CornerRadius="3"
Padding="4" Padding="4"
Canvas.Left="{CompiledBinding ValuePoint.X}"
Canvas.Top="{CompiledBinding ValuePoint.Y}"
IsVisible="{CompiledBinding DisplayValue}"> IsVisible="{CompiledBinding DisplayValue}">
<ContentControl Content="{CompiledBinding FromViewModel.Pin.PinValue}"> <ContentControl Content="{CompiledBinding FromViewModel.Pin.PinValue}">
<ContentControl.DataTemplates> <ContentControl.DataTemplates>

View File

@ -8,6 +8,7 @@ using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Rendering;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Screens.VisualScripting; namespace Artemis.UI.Screens.VisualScripting;
@ -29,9 +30,9 @@ public class CableView : ReactiveUserControl<CableViewModel>
{ {
_valueBorder.GetObservable(BoundsProperty).Subscribe(rect => _valueBorder.RenderTransform = new TranslateTransform(rect.Width / 2 * -1, rect.Height / 2 * -1)).DisposeWith(d); _valueBorder.GetObservable(BoundsProperty).Subscribe(rect => _valueBorder.RenderTransform = new TranslateTransform(rect.Width / 2 * -1, rect.Height / 2 * -1)).DisposeWith(d);
ViewModel.WhenAnyValue(vm => vm.FromPoint).Subscribe(_ => Update()).DisposeWith(d); ViewModel.WhenAnyValue(vm => vm.FromPoint).Subscribe(_ => Update(true)).DisposeWith(d);
ViewModel.WhenAnyValue(vm => vm.ToPoint).Subscribe(_ => Update()).DisposeWith(d); ViewModel.WhenAnyValue(vm => vm.ToPoint).Subscribe(_ => Update(false)).DisposeWith(d);
Update(); Update(true);
}); });
} }
@ -40,10 +41,12 @@ public class CableView : ReactiveUserControl<CableViewModel>
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
private void Update() private void Update(bool from)
{ {
// Workaround for https://github.com/AvaloniaUI/Avalonia/issues/4748 // Workaround for https://github.com/AvaloniaUI/Avalonia/issues/4748
_cablePath.Margin = _cablePath.Margin != new Thickness(0, 0, 0, 0) ? new Thickness(0, 0, 0, 0) : new Thickness(1, 1, 0, 0); _cablePath.Margin = new Thickness(_cablePath.Margin.Left + 1, _cablePath.Margin.Top + 1, 0, 0);
if (_cablePath.Margin.Left > 2)
_cablePath.Margin = new Thickness(0, 0, 0, 0);
PathFigure pathFigure = ((PathGeometry) _cablePath.Data).Figures.First(); PathFigure pathFigure = ((PathGeometry) _cablePath.Data).Figures.First();
BezierSegment segment = (BezierSegment) pathFigure.Segments!.First(); BezierSegment segment = (BezierSegment) pathFigure.Segments!.First();
@ -51,6 +54,11 @@ public class CableView : ReactiveUserControl<CableViewModel>
segment.Point1 = new Point(ViewModel.FromPoint.X + CABLE_OFFSET, ViewModel.FromPoint.Y); segment.Point1 = new Point(ViewModel.FromPoint.X + CABLE_OFFSET, ViewModel.FromPoint.Y);
segment.Point2 = new Point(ViewModel.ToPoint.X - CABLE_OFFSET, ViewModel.ToPoint.Y); segment.Point2 = new Point(ViewModel.ToPoint.X - CABLE_OFFSET, ViewModel.ToPoint.Y);
segment.Point3 = new Point(ViewModel.ToPoint.X, ViewModel.ToPoint.Y); segment.Point3 = new Point(ViewModel.ToPoint.X, ViewModel.ToPoint.Y);
Canvas.SetLeft(_valueBorder, ViewModel.FromPoint.X + (ViewModel.ToPoint.X - ViewModel.FromPoint.X) / 2);
Canvas.SetTop(_valueBorder, ViewModel.FromPoint.Y + (ViewModel.ToPoint.Y - ViewModel.FromPoint.Y) / 2);
_cablePath.InvalidateVisual();
} }
private void OnPointerEnter(object? sender, PointerEventArgs e) private void OnPointerEnter(object? sender, PointerEventArgs e)

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using Artemis.Core; using Artemis.Core;
@ -27,7 +28,6 @@ public class CableViewModel : ActivatableViewModelBase
private PinViewModel? _fromViewModel; private PinViewModel? _fromViewModel;
private ObservableAsPropertyHelper<Point>? _toPoint; private ObservableAsPropertyHelper<Point>? _toPoint;
private PinViewModel? _toViewModel; private PinViewModel? _toViewModel;
private ObservableAsPropertyHelper<Point>? _valuePoint;
public CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to, ISettingsService settingsService) public CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to, ISettingsService settingsService)
{ {
@ -63,11 +63,6 @@ public class CableViewModel : ActivatableViewModelBase
.Switch() .Switch()
.ToProperty(this, vm => vm.ToPoint) .ToProperty(this, vm => vm.ToPoint)
.DisposeWith(d); .DisposeWith(d);
_valuePoint = this.WhenAnyValue(vm => vm.FromPoint, vm => vm.ToPoint).Select(tuple => new Point(
tuple.Item1.X + (tuple.Item2.X - tuple.Item1.X) / 2,
tuple.Item1.Y + (tuple.Item2.Y - tuple.Item1.Y) / 2
)).ToProperty(this, vm => vm.ValuePoint)
.DisposeWith(d);
// Not a perfect solution but this makes sure the cable never renders at 0,0 (can happen when the cable spawns before the pin ever rendered) // Not a perfect solution but this makes sure the cable never renders at 0,0 (can happen when the cable spawns before the pin ever rendered)
_connected = this.WhenAnyValue(vm => vm.FromPoint, vm => vm.ToPoint) _connected = this.WhenAnyValue(vm => vm.FromPoint, vm => vm.ToPoint)
@ -104,11 +99,10 @@ public class CableViewModel : ActivatableViewModelBase
} }
public bool Connected => _connected?.Value ?? false; public bool Connected => _connected?.Value ?? false;
public bool IsFirst => _from.ConnectedTo[0] == _to; public bool IsFirst => _from.ConnectedTo.FirstOrDefault() == _to;
public Point FromPoint => _fromPoint?.Value ?? new Point(); public Point FromPoint => _fromPoint?.Value ?? new Point();
public Point ToPoint => _toPoint?.Value ?? new Point(); public Point ToPoint => _toPoint?.Value ?? new Point();
public Point ValuePoint => _valuePoint?.Value ?? new Point();
public Color CableColor => _cableColor?.Value ?? new Color(255, 255, 255, 255); public Color CableColor => _cableColor?.Value ?? new Color(255, 255, 255, 255);
public void UpdateDisplayValue(bool hoveringOver) public void UpdateDisplayValue(bool hoveringOver)

View File

@ -30,6 +30,8 @@
<KeyBinding Command="{CompiledBinding ClearSelection}" Gesture="Escape" /> <KeyBinding Command="{CompiledBinding ClearSelection}" Gesture="Escape" />
<KeyBinding Command="{CompiledBinding DeleteSelected}" Gesture="Delete" /> <KeyBinding Command="{CompiledBinding DeleteSelected}" Gesture="Delete" />
<KeyBinding Command="{CompiledBinding DuplicateSelected}" Gesture="Ctrl+D" /> <KeyBinding Command="{CompiledBinding DuplicateSelected}" Gesture="Ctrl+D" />
<KeyBinding Command="{CompiledBinding CopySelected}" Gesture="Ctrl+C" />
<KeyBinding Command="{CompiledBinding PasteSelected}" Gesture="Ctrl+V" />
<KeyBinding Command="{CompiledBinding History.Undo}" Gesture="Ctrl+Z" /> <KeyBinding Command="{CompiledBinding History.Undo}" Gesture="Ctrl+Z" />
<KeyBinding Command="{CompiledBinding History.Redo}" Gesture="Ctrl+Y" /> <KeyBinding Command="{CompiledBinding History.Redo}" Gesture="Ctrl+Y" />
</UserControl.KeyBindings> </UserControl.KeyBindings>

View File

@ -14,6 +14,7 @@ using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.VisualTree;
using DynamicData.Binding; using DynamicData.Binding;
using ReactiveUI; using ReactiveUI;
@ -39,6 +40,8 @@ public class NodeScriptView : ReactiveUserControl<NodeScriptViewModel>
_zoomBorder.AddHandler(PointerReleasedEvent, CanvasOnPointerReleased, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true); _zoomBorder.AddHandler(PointerReleasedEvent, CanvasOnPointerReleased, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true);
_zoomBorder.AddHandler(PointerWheelChangedEvent, ZoomOnPointerWheelChanged, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true); _zoomBorder.AddHandler(PointerWheelChangedEvent, ZoomOnPointerWheelChanged, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true);
_zoomBorder.AddHandler(PointerMovedEvent, ZoomOnPointerMoved, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble, true);
this.WhenActivated(d => this.WhenActivated(d =>
{ {
ViewModel!.AutoFitRequested += ViewModelOnAutoFitRequested; ViewModel!.AutoFitRequested += ViewModelOnAutoFitRequested;
@ -67,6 +70,12 @@ public class NodeScriptView : ReactiveUserControl<NodeScriptViewModel>
e.Handled = true; e.Handled = true;
} }
private void ZoomOnPointerMoved(object? sender, PointerEventArgs e)
{
if (ViewModel != null)
ViewModel.PastePosition = e.GetPosition(_grid);
}
private void ShowPickerAt(Point point) private void ShowPickerAt(Point point)
{ {
if (ViewModel == null) if (ViewModel == null)

View File

@ -6,9 +6,12 @@ using System.Linq;
using System.Reactive; using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Events; using Artemis.Core.Events;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Models;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.VisualScripting.Pins; using Artemis.UI.Screens.VisualScripting.Pins;
using Artemis.UI.Shared; using Artemis.UI.Shared;
@ -16,6 +19,7 @@ using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands; using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Avalonia; using Avalonia;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Input;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using ReactiveUI; using ReactiveUI;
@ -24,6 +28,8 @@ namespace Artemis.UI.Screens.VisualScripting;
public class NodeScriptViewModel : ActivatableViewModelBase public class NodeScriptViewModel : ActivatableViewModelBase
{ {
public const string CLIPBOARD_DATA_FORMAT = "Artemis.Nodes";
private readonly INodeEditorService _nodeEditorService; private readonly INodeEditorService _nodeEditorService;
private readonly INodeService _nodeService; private readonly INodeService _nodeService;
private readonly SourceList<NodeViewModel> _nodeViewModels; private readonly SourceList<NodeViewModel> _nodeViewModels;
@ -33,6 +39,7 @@ public class NodeScriptViewModel : ActivatableViewModelBase
private DragCableViewModel? _dragViewModel; private DragCableViewModel? _dragViewModel;
private List<NodeViewModel>? _initialNodeSelection; private List<NodeViewModel>? _initialNodeSelection;
private Matrix _panMatrix; private Matrix _panMatrix;
private Point _pastePosition;
public NodeScriptViewModel(NodeScript nodeScript, bool isPreview, INodeVmFactory nodeVmFactory, INodeService nodeService, INodeEditorService nodeEditorService) public NodeScriptViewModel(NodeScript nodeScript, bool isPreview, INodeVmFactory nodeVmFactory, INodeService nodeService, INodeEditorService nodeEditorService)
{ {
@ -86,8 +93,8 @@ public class NodeScriptViewModel : ActivatableViewModelBase
ClearSelection = ReactiveCommand.Create(ExecuteClearSelection); ClearSelection = ReactiveCommand.Create(ExecuteClearSelection);
DeleteSelected = ReactiveCommand.Create(ExecuteDeleteSelected); DeleteSelected = ReactiveCommand.Create(ExecuteDeleteSelected);
DuplicateSelected = ReactiveCommand.Create(ExecuteDuplicateSelected); DuplicateSelected = ReactiveCommand.Create(ExecuteDuplicateSelected);
CopySelected = ReactiveCommand.Create(ExecuteCopySelected); CopySelected = ReactiveCommand.CreateFromTask(ExecuteCopySelected);
PasteSelected = ReactiveCommand.Create(ExecutePasteSelected); PasteSelected = ReactiveCommand.CreateFromTask(ExecutePasteSelected);
} }
public NodeScript NodeScript { get; } public NodeScript NodeScript { get; }
@ -118,6 +125,12 @@ public class NodeScriptViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _panMatrix, value); set => RaiseAndSetIfChanged(ref _panMatrix, value);
} }
public Point PastePosition
{
get => _pastePosition;
set => RaiseAndSetIfChanged(ref _pastePosition, value);
}
public void DeleteSelectedNodes() public void DeleteSelectedNodes()
{ {
List<NodeViewModel> toRemove = NodeViewModels.Where(vm => vm.IsSelected && !vm.Node.IsDefaultNode && !vm.Node.IsExitNode).ToList(); List<NodeViewModel> toRemove = NodeViewModels.Where(vm => vm.IsSelected && !vm.Node.IsDefaultNode && !vm.Node.IsExitNode).ToList();
@ -279,11 +292,39 @@ public class NodeScriptViewModel : ActivatableViewModelBase
} }
} }
private void ExecuteCopySelected() private async Task ExecuteCopySelected()
{ {
if (Application.Current?.Clipboard == null)
return;
List<INode> nodes = NodeViewModels.Where(vm => vm.IsSelected).Select(vm => vm.Node).Where(n => !n.IsDefaultNode && !n.IsExitNode).ToList();
DataObject dataObject = new();
string copy = CoreJson.SerializeObject(new NodesClipboardModel(NodeScript, nodes), true);
dataObject.Set(CLIPBOARD_DATA_FORMAT, copy);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
} }
private void ExecutePasteSelected() private async Task ExecutePasteSelected()
{ {
if (Application.Current?.Clipboard == null)
return;
byte[]? bytes = (byte[]?) await Application.Current.Clipboard.GetDataAsync(CLIPBOARD_DATA_FORMAT);
if (bytes == null!)
return;
NodesClipboardModel? nodesClipboardModel = CoreJson.DeserializeObject<NodesClipboardModel>(Encoding.Unicode.GetString(bytes), true);
if (nodesClipboardModel == null)
return;
List<INode> nodes = nodesClipboardModel.Paste(NodeScript, PastePosition.X, PastePosition.Y);
using NodeEditorCommandScope scope = _nodeEditorService.CreateCommandScope(NodeScript, "Paste nodes");
foreach (INode node in nodes)
_nodeEditorService.ExecuteCommand(NodeScript, new AddNode(NodeScript, node));
// Select only the new nodes
foreach (NodeViewModel nodeViewModel in NodeViewModels)
nodeViewModel.IsSelected = nodes.Contains(nodeViewModel.Node);
} }
} }

View File

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting" xmlns:visualScripting="clr-namespace:Artemis.UI.Screens.VisualScripting"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="250" d:DesignHeight="150" mc:Ignorable="d" d:DesignWidth="250" d:DesignHeight="150"
x:Class="Artemis.UI.Screens.VisualScripting.NodeView" x:Class="Artemis.UI.Screens.VisualScripting.NodeView"
x:DataType="visualScripting:NodeViewModel"> x:DataType="visualScripting:NodeViewModel">
@ -37,7 +38,7 @@
ClipToBounds="True" ClipToBounds="True"
Background="{DynamicResource ContentDialogBackground}"> Background="{DynamicResource ContentDialogBackground}">
<Border Background="{DynamicResource TaskDialogHeaderBackground}"> <Border Background="{DynamicResource TaskDialogHeaderBackground}">
<Grid Classes="node-header" VerticalAlignment="Top" ColumnDefinitions="Auto,*,Auto"> <Grid Classes="node-header" VerticalAlignment="Top" ColumnDefinitions="Auto,*,Auto,Auto">
<Button Grid.Column="0" <Button Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
IsVisible="{CompiledBinding Node.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}" IsVisible="{CompiledBinding Node.BrokenState, Converter={x:Static ObjectConverters.IsNotNull}}"
@ -53,7 +54,17 @@
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="10 0 0 0" Text="{CompiledBinding Node.Name}" ToolTip.Tip="{CompiledBinding Node.Description}" /> <TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="10 0 0 0" Text="{CompiledBinding Node.Name}" ToolTip.Tip="{CompiledBinding Node.Description}" />
<Button Grid.Column="2" VerticalAlignment="Center" Classes="icon-button icon-button-small" Margin="5" Command="{CompiledBinding DeleteNode}">
<controls:HyperlinkButton Grid.Column="2"
IsVisible="{CompiledBinding Node.HelpUrl, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
VerticalAlignment="Center"
Classes="icon-button icon-button-small"
Margin="5 5 -3 5"
ToolTip.Tip="View node help"
NavigateUri="{CompiledBinding Node.HelpUrl}">
<avalonia:MaterialIcon Kind="Help" />
</controls:HyperlinkButton>
<Button Grid.Column="3" VerticalAlignment="Center" Classes="icon-button icon-button-small" Margin="5" Command="{CompiledBinding DeleteNode}">
<avalonia:MaterialIcon Kind="Close"></avalonia:MaterialIcon> <avalonia:MaterialIcon Kind="Close"></avalonia:MaterialIcon>
</Button> </Button>
</Grid> </Grid>

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Branching;
[Node("Branch", "Forwards one of two values depending on an input boolean", "Branching", InputType = typeof(object), OutputType = typeof(object))] [Node("Branch", "Forwards one of two values depending on an input boolean", "Branching", InputType = typeof(object), OutputType = typeof(object))]
public class BooleanBranchNode : Node public class BooleanBranchNode : Node
{ {
public BooleanBranchNode() : base("Branch", "Forwards one of two values depending on an input boolean") public BooleanBranchNode()
{ {
BooleanInput = CreateInputPin<bool>(); BooleanInput = CreateInputPin<bool>();
TrueInput = CreateInputPin(typeof(object), "True"); TrueInput = CreateInputPin(typeof(object), "True");

View File

@ -9,7 +9,7 @@ public class EnumSwitchNode : Node
{ {
private readonly Dictionary<Enum, InputPin> _inputPins; private readonly Dictionary<Enum, InputPin> _inputPins;
public EnumSwitchNode() : base("Enum Branch", "desc") public EnumSwitchNode()
{ {
_inputPins = new Dictionary<Enum, InputPin>(); _inputPins = new Dictionary<Enum, InputPin>();

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Color;
[Node("Brighten Color", "Brightens a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))] [Node("Brighten Color", "Brightens a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
public class BrightenSKColorNode : Node public class BrightenSKColorNode : Node
{ {
public BrightenSKColorNode() : base("Brighten Color", "Brightens a color by a specified amount in percent") public BrightenSKColorNode()
{ {
Input = CreateInputPin<SKColor>("Color"); Input = CreateInputPin<SKColor>("Color");
Percentage = CreateInputPin<Numeric>("%"); Percentage = CreateInputPin<Numeric>("%");

View File

@ -11,7 +11,7 @@ public class ColorGradientFromPinsNode : Node
public InputPinCollection<SKColor> Colors { get; set; } public InputPinCollection<SKColor> Colors { get; set; }
public InputPinCollection<Numeric> Positions { get; set; } public InputPinCollection<Numeric> Positions { get; set; }
public ColorGradientFromPinsNode() : base("Color Gradient", "Outputs a Color Gradient from colors and positions") public ColorGradientFromPinsNode()
{ {
Colors = CreateInputPinCollection<SKColor>("Colors", 0); Colors = CreateInputPinCollection<SKColor>("Colors", 0);
Positions = CreateInputPinCollection<Numeric>("Positions", 0); Positions = CreateInputPinCollection<Numeric>("Positions", 0);

View File

@ -10,7 +10,7 @@ public class ColorGradientNode : Node<ColorGradient, ColorGradientNodeCustomView
{ {
private readonly List<InputPin> _inputPins; private readonly List<InputPin> _inputPins;
public ColorGradientNode() : base("Color Gradient", "Outputs a color gradient with the given colors") public ColorGradientNode()
{ {
_inputPins = new List<InputPin>(); _inputPins = new List<InputPin>();

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Color;
[Node("Darken Color", "Darkens a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))] [Node("Darken Color", "Darkens a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
public class DarkenSKColorNode : Node public class DarkenSKColorNode : Node
{ {
public DarkenSKColorNode() : base("Darken Color", "Darkens a color by a specified amount in percent") public DarkenSKColorNode()
{ {
Input = CreateInputPin<SKColor>("Color"); Input = CreateInputPin<SKColor>("Color");
Percentage = CreateInputPin<Numeric>("%"); Percentage = CreateInputPin<Numeric>("%");

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Color;
[Node("Desaturate Color", "Desaturates a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))] [Node("Desaturate Color", "Desaturates a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
public class DesaturateSKColorNode : Node public class DesaturateSKColorNode : Node
{ {
public DesaturateSKColorNode() : base("Desaturate Color", "Desaturates a color by a specified amount in percent") public DesaturateSKColorNode()
{ {
Input = CreateInputPin<SKColor>("Color"); Input = CreateInputPin<SKColor>("Color");
Percentage = CreateInputPin<Numeric>("%"); Percentage = CreateInputPin<Numeric>("%");

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Color;
[Node("HSL Color", "Creates a color from hue, saturation and lightness values", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))] [Node("HSL Color", "Creates a color from hue, saturation and lightness values", "Color", InputType = typeof(Numeric), OutputType = typeof(SKColor))]
public class HslSKColorNode : Node public class HslSKColorNode : Node
{ {
public HslSKColorNode() : base("HSL Color", "Creates a color from hue, saturation and lightness values") public HslSKColorNode()
{ {
H = CreateInputPin<Numeric>("H"); H = CreateInputPin<Numeric>("H");
S = CreateInputPin<Numeric>("S"); S = CreateInputPin<Numeric>("S");

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Color;
[Node("Invert Color", "Inverts a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))] [Node("Invert Color", "Inverts a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
public class InvertSKColorNode : Node public class InvertSKColorNode : Node
{ {
public InvertSKColorNode() : base("Invert Color", "Inverts a color") public InvertSKColorNode()
{ {
Input = CreateInputPin<SKColor>(); Input = CreateInputPin<SKColor>();
Output = CreateOutputPin<SKColor>(); Output = CreateOutputPin<SKColor>();

View File

@ -20,8 +20,8 @@ public class LerpSKColorNode : Node
#region Constructors #region Constructors
public LerpSKColorNode() public LerpSKColorNode()
: base("Lerp", "Interpolates linear between the two values A and B")
{ {
Name = "Lerp";
A = CreateInputPin<SKColor>("A"); A = CreateInputPin<SKColor>("A");
B = CreateInputPin<SKColor>("B"); B = CreateInputPin<SKColor>("B");
T = CreateInputPin<Numeric>("T"); T = CreateInputPin<Numeric>("T");

View File

@ -10,7 +10,6 @@ public class RampSKColorNode : Node<ColorGradient, RampSKColorNodeCustomViewMode
#region Constructors #region Constructors
public RampSKColorNode() public RampSKColorNode()
: base("Color Ramp", "Maps values to colors with the use of a gradient.")
{ {
Input = CreateInputPin<Numeric>(); Input = CreateInputPin<Numeric>();
Output = CreateOutputPin<SKColor>(); Output = CreateOutputPin<SKColor>();

View File

@ -18,7 +18,6 @@ public class RgbSKColorNode : Node
#region Constructors #region Constructors
public RgbSKColorNode() public RgbSKColorNode()
: base("RGB Color", "Creates a color from red, green and blue values")
{ {
R = CreateInputPin<Numeric>("R"); R = CreateInputPin<Numeric>("R");
G = CreateInputPin<Numeric>("G"); G = CreateInputPin<Numeric>("G");

View File

@ -6,7 +6,7 @@ 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))] [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 class RotateHueSKColorNode : Node
{ {
public RotateHueSKColorNode() : base("Rotate Color Hue", "Rotates the hue of a color by a specified amount in degrees") public RotateHueSKColorNode()
{ {
Input = CreateInputPin<SKColor>("Color"); Input = CreateInputPin<SKColor>("Color");
Amount = CreateInputPin<Numeric>("Amount"); Amount = CreateInputPin<Numeric>("Amount");

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Color;
[Node("Saturate Color", "Saturates a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))] [Node("Saturate Color", "Saturates a color by a specified amount in percent", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
public class SaturateSKColorNode : Node public class SaturateSKColorNode : Node
{ {
public SaturateSKColorNode() : base("Saturate Color", "Saturates a color by a specified amount in percent") public SaturateSKColorNode()
{ {
Input = CreateInputPin<SKColor>("Color"); Input = CreateInputPin<SKColor>("Color");
Percentage = CreateInputPin<Numeric>("%"); Percentage = CreateInputPin<Numeric>("%");

View File

@ -9,8 +9,8 @@ public class SumSKColorsNode : Node
#region Constructors #region Constructors
public SumSKColorsNode() public SumSKColorsNode()
: base("Sum", "Sums the connected color values.")
{ {
Name = "Sum";
Values = CreateInputPinCollection<SKColor>("Values", 2); Values = CreateInputPinCollection<SKColor>("Values", 2);
Sum = CreateOutputPin<SKColor>("Sum"); Sum = CreateOutputPin<SKColor>("Sum");
} }

View File

@ -8,7 +8,6 @@ public class ConvertToNumericNode : Node
#region Constructors #region Constructors
public ConvertToNumericNode() public ConvertToNumericNode()
: base("To Numeric", "Converts the input to a numeric.")
{ {
Input = CreateInputPin<object>(); Input = CreateInputPin<object>();
Output = CreateOutputPin<Numeric>(); Output = CreateOutputPin<Numeric>();

View File

@ -2,13 +2,12 @@
namespace Artemis.VisualScripting.Nodes.Conversion; namespace Artemis.VisualScripting.Nodes.Conversion;
[Node("To String", "Converts the input to a string.", "Conversion", InputType = typeof(object), OutputType = typeof(string))] [Node("To Text", "Converts the input to text.", "Conversion", InputType = typeof(object), OutputType = typeof(string))]
public class ConvertToStringNode : Node public class ConvertToStringNode : Node
{ {
#region Constructors #region Constructors
public ConvertToStringNode() public ConvertToStringNode()
: base("To String", "Converts the input to a string.")
{ {
Input = CreateInputPin<object>(); Input = CreateInputPin<object>();
String = CreateOutputPin<string>(); String = CreateOutputPin<string>();

View File

@ -15,7 +15,7 @@ public class DataModelEventCycleNode : Node<DataModelPathEntity, DataModelEventC
private DateTime _lastTrigger; private DateTime _lastTrigger;
private bool _updating; private bool _updating;
public DataModelEventCycleNode() : base("Data Model-Event Value Cycle", "Cycles through provided values each time the select event fires.") public DataModelEventCycleNode()
{ {
_currentType = typeof(object); _currentType = typeof(object);

View File

@ -16,7 +16,7 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
private OutputPin? _oldValuePin; private OutputPin? _oldValuePin;
private int _valueChangeCount; private int _valueChangeCount;
public DataModelEventNode() : base("Data Model-Event", "Outputs the latest values of a data model event.") public DataModelEventNode()
{ {
_objectOutputPins = new ObjectOutputPins(this); _objectOutputPins = new ObjectOutputPins(this);

View File

@ -9,7 +9,7 @@ public class DataModelNode : Node<DataModelPathEntity, DataModelNodeCustomViewMo
{ {
private DataModelPath? _dataModelPath; private DataModelPath? _dataModelPath;
public DataModelNode() : base("Data Model", "Outputs a selectable data model value") public DataModelNode()
{ {
Output = CreateOutputPin(typeof(object)); Output = CreateOutputPin(typeof(object));
StorageModified += (_, _) => UpdateDataModelPath(); StorageModified += (_, _) => UpdateDataModelPath();

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Easing;
[Node("Easing Type", "Outputs a selectable easing type.", "Easing", OutputType = typeof(Easings.Functions))] [Node("Easing Type", "Outputs a selectable easing type.", "Easing", OutputType = typeof(Easings.Functions))]
public class EasingTypeNode : Node<Easings.Functions, EasingTypeNodeCustomViewModel> public class EasingTypeNode : Node<Easings.Functions, EasingTypeNodeCustomViewModel>
{ {
public EasingTypeNode() : base("Easing Type", "Outputs a selectable easing type.") public EasingTypeNode()
{ {
Output = CreateOutputPin<Easings.Functions>(); Output = CreateOutputPin<Easings.Functions>();
} }

View File

@ -11,7 +11,7 @@ public class NumericEasingNode : Node
private float _sourceValue; private float _sourceValue;
private float _targetValue; private float _targetValue;
public NumericEasingNode() : base("Numeric Easing", "Outputs an eased numeric value") public NumericEasingNode()
{ {
Input = CreateInputPin<Numeric>(); Input = CreateInputPin<Numeric>();
EasingTime = CreateInputPin<Numeric>("delay"); EasingTime = CreateInputPin<Numeric>("delay");

View File

@ -12,7 +12,7 @@ public class SKColorEasingNode : Node
private SKColor _sourceValue; private SKColor _sourceValue;
private SKColor _targetValue; private SKColor _targetValue;
public SKColorEasingNode() : base("Color Easing", "Outputs an eased color value") public SKColorEasingNode()
{ {
Input = CreateInputPin<SKColor>(); Input = CreateInputPin<SKColor>();
EasingTime = CreateInputPin<Numeric>("delay"); EasingTime = CreateInputPin<Numeric>("delay");

View File

@ -7,7 +7,7 @@ namespace Artemis.VisualScripting.Nodes.List;
[Node("List Operator (Simple)", "Checks if any/all/no values in the input list match the input value", "List", InputType = typeof(IEnumerable), OutputType = typeof(bool))] [Node("List Operator (Simple)", "Checks if any/all/no values in the input list match the input value", "List", InputType = typeof(IEnumerable), OutputType = typeof(bool))]
public class ListOperatorNode : Node<ListOperator, ListOperatorNodeCustomViewModel> public class ListOperatorNode : Node<ListOperator, ListOperatorNodeCustomViewModel>
{ {
public ListOperatorNode() : base("List Operator (Simple)", "Checks if any/all/no values in the input list match the input value") public ListOperatorNode()
{ {
InputList = CreateInputPin<IList>(); InputList = CreateInputPin<IList>();
InputValue = CreateInputPin<object>(); InputValue = CreateInputPin<object>();

View File

@ -11,7 +11,7 @@ public class ListOperatorPredicateNode : Node<ListOperatorEntity, ListOperatorPr
private readonly object _scriptLock = new(); private readonly object _scriptLock = new();
private ListOperatorPredicateStartNode _startNode; private ListOperatorPredicateStartNode _startNode;
public ListOperatorPredicateNode() : base("List Operator (Advanced)", "Checks if any/all/no values in the input list match a condition") public ListOperatorPredicateNode()
{ {
_startNode = new ListOperatorPredicateStartNode {X = -200}; _startNode = new ListOperatorPredicateStartNode {X = -200};

View File

@ -19,7 +19,6 @@ public class ClampNode : Node
#region Constructors #region Constructors
public ClampNode() public ClampNode()
: base("Clamp", "Clamps the value to be in between min and max")
{ {
Value = CreateInputPin<Numeric>("Value"); Value = CreateInputPin<Numeric>("Value");
Min = CreateInputPin<Numeric>("Min"); Min = CreateInputPin<Numeric>("Min");

View File

@ -9,7 +9,6 @@ public class CounterNode : Node
private float _progress; private float _progress;
public CounterNode() public CounterNode()
: base("Counter", "Counts from 0.0 to 1.0 at a configurable rate.")
{ {
Time = CreateInputPin<Numeric>("Time (ms)"); Time = CreateInputPin<Numeric>("Time (ms)");
Output = CreateOutputPin<Numeric>(); Output = CreateOutputPin<Numeric>();

View File

@ -19,7 +19,6 @@ public class LerpNode : Node
#region Constructors #region Constructors
public LerpNode() public LerpNode()
: base("Lerp", "Interpolates linear between the two values A and B")
{ {
A = CreateInputPin<Numeric>("A"); A = CreateInputPin<Numeric>("A");
B = CreateInputPin<Numeric>("B"); B = CreateInputPin<Numeric>("B");

View File

@ -5,7 +5,7 @@ using NoStringEvaluating.Models.FormulaChecker;
namespace Artemis.VisualScripting.Nodes.Mathematics; namespace Artemis.VisualScripting.Nodes.Mathematics;
[Node("Math Expression", "Outputs the result of a math expression.", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))] [Node("Math Expression", "Outputs the result of a math expression.", "Mathematics", "https://wiki.artemis-rgb.com/en/guides/user/profiles/nodes/mathematics/math-expression", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel> public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel>
{ {
private readonly IFormulaChecker _checker; private readonly IFormulaChecker _checker;
@ -15,7 +15,6 @@ public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel
#region Constructors #region Constructors
public MathExpressionNode(INoStringEvaluator evaluator, IFormulaChecker checker) public MathExpressionNode(INoStringEvaluator evaluator, IFormulaChecker checker)
: base("Math Expression", "Outputs the result of a math expression.")
{ {
_evaluator = evaluator; _evaluator = evaluator;
_checker = checker; _checker = checker;

View File

@ -8,7 +8,6 @@ public class MaxNumericsNode : Node
#region Constructors #region Constructors
public MaxNumericsNode() public MaxNumericsNode()
: base("Max", "Outputs the largest of the connected numeric values.")
{ {
Values = CreateInputPinCollection<Numeric>("Values", 2); Values = CreateInputPinCollection<Numeric>("Values", 2);
Max = CreateOutputPin<Numeric>("Max"); Max = CreateOutputPin<Numeric>("Max");

View File

@ -8,7 +8,6 @@ public class MinNumericsNode : Node
#region Constructors #region Constructors
public MinNumericsNode() public MinNumericsNode()
: base("Min", "Outputs the smallest of the connected numeric values.")
{ {
Values = CreateInputPinCollection<Numeric>("Values", 2); Values = CreateInputPinCollection<Numeric>("Values", 2);
Min = CreateOutputPin<Numeric>("Min"); Min = CreateOutputPin<Numeric>("Min");

View File

@ -19,7 +19,6 @@ public class RangeNode : Node
#region Constructors #region Constructors
public RangeNode() public RangeNode()
: base("Range", "Selects the best integer value in the given range by the given percentage")
{ {
Min = CreateInputPin<Numeric>("Min"); Min = CreateInputPin<Numeric>("Min");
Max = CreateInputPin<Numeric>("Max"); Max = CreateInputPin<Numeric>("Max");

View File

@ -5,7 +5,7 @@ namespace Artemis.VisualScripting.Nodes.Mathematics;
[Node("Round", "Outputs a rounded numeric value.", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))] [Node("Round", "Outputs a rounded numeric value.", "Mathematics", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
public class RoundNode : Node public class RoundNode : Node
{ {
public RoundNode() : base("Round", "Outputs a rounded numeric value.") public RoundNode()
{ {
Input = CreateInputPin<Numeric>(); Input = CreateInputPin<Numeric>();
Output = CreateOutputPin<Numeric>(); Output = CreateOutputPin<Numeric>();

View File

@ -17,7 +17,6 @@ public class SaturateNode : Node
#region Constructors #region Constructors
public SaturateNode() public SaturateNode()
: base("Saturate", "Clamps the value to be in between 0 and 1")
{ {
Value = CreateInputPin<Numeric>(); Value = CreateInputPin<Numeric>();

View File

@ -8,7 +8,6 @@ public class SumNumericsNode : Node
#region Constructors #region Constructors
public SumNumericsNode() public SumNumericsNode()
: base("Sum", "Sums the connected numeric values.")
{ {
Values = CreateInputPinCollection<Numeric>("Values", 2); Values = CreateInputPinCollection<Numeric>("Values", 2);
Sum = CreateOutputPin<Numeric>("Sum"); Sum = CreateOutputPin<Numeric>("Sum");

View File

@ -8,7 +8,6 @@ public class AndNode : Node
#region Constructors #region Constructors
public AndNode() public AndNode()
: base("And", "Checks if all inputs are true.")
{ {
Input = CreateInputPinCollection<bool>(); Input = CreateInputPinCollection<bool>();
Result = CreateOutputPin<bool>(); Result = CreateOutputPin<bool>();

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Operators;
[Node("Enum Equals", "Determines the equality between an input and a selected enum value", "Operators", InputType = typeof(Enum), OutputType = typeof(bool))] [Node("Enum Equals", "Determines the equality between an input and a selected enum value", "Operators", InputType = typeof(Enum), OutputType = typeof(bool))]
public class EnumEqualsNode : Node<long, EnumEqualsNodeCustomViewModel> public class EnumEqualsNode : Node<long, EnumEqualsNodeCustomViewModel>
{ {
public EnumEqualsNode() : base("Enum Equals", "Determines the equality between an input and a selected enum value") public EnumEqualsNode()
{ {
InputPin = CreateInputPin<Enum>(); InputPin = CreateInputPin<Enum>();
OutputPin = CreateOutputPin<bool>(); OutputPin = CreateOutputPin<bool>();

View File

@ -8,7 +8,6 @@ public class EqualsNode : Node
#region Constructors #region Constructors
public EqualsNode() public EqualsNode()
: base("Equals", "Checks if the two inputs are equals.")
{ {
Input1 = CreateInputPin<object>(); Input1 = CreateInputPin<object>();
Input2 = CreateInputPin<object>(); Input2 = CreateInputPin<object>();

View File

@ -9,7 +9,6 @@ public class GreaterThanNode : Node
#region Constructors #region Constructors
public GreaterThanNode() public GreaterThanNode()
: base("Greater than", "Checks if the first input is greater than the second.")
{ {
Input1 = CreateInputPin<object>(); Input1 = CreateInputPin<object>();
Input2 = CreateInputPin<object>(); Input2 = CreateInputPin<object>();

View File

@ -9,7 +9,6 @@ public class LessThanNode : Node
#region Constructors #region Constructors
public LessThanNode() public LessThanNode()
: base("Less than", "Checks if the first input is less than the second.")
{ {
Input1 = CreateInputPin<object>(); Input1 = CreateInputPin<object>();
Input2 = CreateInputPin<object>(); Input2 = CreateInputPin<object>();

View File

@ -8,7 +8,6 @@ public class NegateNode : Node
#region Constructors #region Constructors
public NegateNode() public NegateNode()
: base("Negate", "Negates the boolean.")
{ {
Input = CreateInputPin<bool>(); Input = CreateInputPin<bool>();
Output = CreateOutputPin<bool>(); Output = CreateOutputPin<bool>();

View File

@ -8,7 +8,6 @@ public class OrNode : Node
#region Constructors #region Constructors
public OrNode() public OrNode()
: base("Or", "Checks if any inputs are true.")
{ {
Input = CreateInputPinCollection<bool>(); Input = CreateInputPinCollection<bool>();
Result = CreateOutputPin<bool>(); Result = CreateOutputPin<bool>();

View File

@ -8,7 +8,6 @@ public class XorNode : Node
#region Constructors #region Constructors
public XorNode() public XorNode()
: base("Exclusive Or", "Checks if one of the inputs is true.")
{ {
Input = CreateInputPinCollection<bool>(); Input = CreateInputPinCollection<bool>();
Result = CreateOutputPin<bool>(); Result = CreateOutputPin<bool>();

View File

@ -6,7 +6,7 @@ namespace Artemis.VisualScripting.Nodes.Static;
[Node("Display Value", "Displays an input value for testing purposes.", "Static", InputType = typeof(object))] [Node("Display Value", "Displays an input value for testing purposes.", "Static", InputType = typeof(object))]
public class DisplayValueNode : Node<string, DisplayValueNodeCustomViewModel> public class DisplayValueNode : Node<string, DisplayValueNodeCustomViewModel>
{ {
public DisplayValueNode() : base("Display Value", "Displays an input value for testing purposes.") public DisplayValueNode()
{ {
Input = CreateInputPin<object>(); Input = CreateInputPin<object>();
} }

View File

@ -16,7 +16,6 @@ public class RandomNumericValueNode : Node
#region Constructors #region Constructors
public RandomNumericValueNode() public RandomNumericValueNode()
: base("Random", "Generates a random value between 0 and 1")
{ {
Output = CreateOutputPin<Numeric>(); Output = CreateOutputPin<Numeric>();
} }

View File

@ -9,8 +9,8 @@ public class StaticBooleanValueNode : Node<bool, StaticBooleanValueNodeCustomVie
#region Constructors #region Constructors
public StaticBooleanValueNode() public StaticBooleanValueNode()
: base("Boolean", "Outputs a configurable static boolean value.")
{ {
Name = "Boolean";
Output = CreateOutputPin<bool>(); Output = CreateOutputPin<bool>();
} }

View File

@ -9,8 +9,8 @@ public class StaticNumericValueNode : Node<Numeric, StaticNumericValueNodeCustom
#region Constructors #region Constructors
public StaticNumericValueNode() public StaticNumericValueNode()
: base("Numeric", "Outputs a configurable numeric value.")
{ {
Name = "Numeric";
Output = CreateOutputPin<Numeric>(); Output = CreateOutputPin<Numeric>();
} }

View File

@ -10,8 +10,8 @@ public class StaticSKColorValueNode : Node<SKColor, StaticSKColorValueNodeCustom
#region Constructors #region Constructors
public StaticSKColorValueNode() public StaticSKColorValueNode()
: base("Color", "Outputs a configurable color value.")
{ {
Name = "Color";
Output = CreateOutputPin<SKColor>(); Output = CreateOutputPin<SKColor>();
Storage = new SKColor(255, 0, 0); Storage = new SKColor(255, 0, 0);
} }

View File

@ -9,8 +9,8 @@ public class StaticStringValueNode : Node<string, StaticStringValueNodeCustomVie
#region Constructors #region Constructors
public StaticStringValueNode() public StaticStringValueNode()
: base("Text", "Outputs a configurable text value.")
{ {
Name = "Text";
Output = CreateOutputPin<string>(); Output = CreateOutputPin<string>();
} }

View File

@ -6,7 +6,6 @@ namespace Artemis.VisualScripting.Nodes.Text;
public class StringContainsNode : Node public class StringContainsNode : Node
{ {
public StringContainsNode() public StringContainsNode()
: base("Contains", "Checks whether the first input is contained in the second input.")
{ {
Input1 = CreateInputPin<string>(); Input1 = CreateInputPin<string>();
Input2 = CreateInputPin<string>(); Input2 = CreateInputPin<string>();

View File

@ -8,7 +8,6 @@ public class StringFormatNode : Node
#region Constructors #region Constructors
public StringFormatNode() public StringFormatNode()
: base("Format", "Formats the input string.")
{ {
Format = CreateInputPin<string>("Format"); Format = CreateInputPin<string>("Format");
Values = CreateInputPinCollection<object>("Values"); Values = CreateInputPinCollection<object>("Values");

View File

@ -7,7 +7,6 @@ namespace Artemis.VisualScripting.Nodes.Text;
public class StringLengthNode : Node public class StringLengthNode : Node
{ {
public StringLengthNode() public StringLengthNode()
: base("Text Length", "Outputs text length.")
{ {
Input1 = CreateInputPin<string>(); Input1 = CreateInputPin<string>();
Result = CreateOutputPin<Numeric>(); Result = CreateOutputPin<Numeric>();

View File

@ -7,7 +7,6 @@ namespace Artemis.VisualScripting.Nodes.Text;
public class StringNullOrEmptyNode : Node public class StringNullOrEmptyNode : Node
{ {
public StringNullOrEmptyNode() public StringNullOrEmptyNode()
: base("Text is empty", "Outputs true if empty")
{ {
Input1 = CreateInputPin<string>(); Input1 = CreateInputPin<string>();
Output1 = CreateOutputPin<bool>(); Output1 = CreateOutputPin<bool>();

View File

@ -10,7 +10,7 @@ public class StringRegexMatchNode : Node
private Regex? _regex; private Regex? _regex;
private Exception? _exception; private Exception? _exception;
public StringRegexMatchNode() : base("Regex Match", "Checks provided regex pattern matches the input.") public StringRegexMatchNode()
{ {
Pattern = CreateInputPin<string>("Pattern"); Pattern = CreateInputPin<string>("Pattern");
Input = CreateInputPin<string>("Input"); Input = CreateInputPin<string>("Input");

View File

@ -25,7 +25,6 @@ public class DelayNode : Node
#region Constructors #region Constructors
public DelayNode() public DelayNode()
: base("Delay", "Delays the resolution of the input pin(s) for the given time after each update")
{ {
Delay = CreateInputPin<Numeric>("Delay"); Delay = CreateInputPin<Numeric>("Delay");
Input = CreateInputPinCollection(typeof(object), initialCount: 0); Input = CreateInputPinCollection(typeof(object), initialCount: 0);

View File

@ -17,7 +17,6 @@ public class EdgeNode : Node
#region Constructors #region Constructors
public EdgeNode() public EdgeNode()
: base("Edge", "Outputs true on each edge when the input changes")
{ {
Input = CreateInputPin<bool>(); Input = CreateInputPin<bool>();
Output = CreateOutputPin<bool>(); Output = CreateOutputPin<bool>();

View File

@ -18,7 +18,6 @@ public class FlipFlopNode : Node
#region Constructors #region Constructors
public FlipFlopNode() public FlipFlopNode()
: base("FlipFlop", "Inverts the output when the input changes from false to true")
{ {
Input = CreateInputPin<bool>(); Input = CreateInputPin<bool>();
Output = CreateOutputPin<bool>(); Output = CreateOutputPin<bool>();

View File

@ -25,7 +25,6 @@ public class LatchNode : Node
#region Constructors #region Constructors
public LatchNode() public LatchNode()
: base("Latch", "Only passes the input to the output as long as the control-pin is true. If the control pin is false the last passed value is provided.")
{ {
Control = CreateInputPin<bool>("Control"); Control = CreateInputPin<bool>("Control");
Input = CreateInputPinCollection(typeof(object), initialCount: 0); Input = CreateInputPinCollection(typeof(object), initialCount: 0);

View File

@ -25,7 +25,6 @@ public class SequencerNode : Node
#region Constructors #region Constructors
public SequencerNode() public SequencerNode()
: base("Sequencer", "Advances on input every time the control has a rising edge (change to true)")
{ {
_currentType = typeof(object); _currentType = typeof(object);