1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Nodes - Added categories

Nodes - Added quick node creation
Nodes - Added input and output types to NodeAttribute
This commit is contained in:
Robert 2021-08-26 22:09:04 +02:00
parent 73794591bc
commit c4340c3c97
29 changed files with 389 additions and 82 deletions

View File

@ -53,8 +53,8 @@ namespace Artemis.Core.Services
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;
NodeData nodeData = new(plugin, nodeType, name, description, category, (s, e) => CreateNode(s, e, nodeType)); NodeData nodeData = new(plugin, nodeType, name, description, category, nodeAttribute?.InputType, nodeAttribute?.OutputType, (s, e) => CreateNode(s, e, nodeType));
return NodeTypeStore.Add(nodeData); return NodeTypeStore.Add(nodeData);
} }

View File

@ -9,6 +9,8 @@ namespace Artemis.Core
public string Name { get; } public string Name { get; }
public string Description { get; set; } public string Description { get; set; }
public string Category { get; set; } public string Category { get; set; }
public Type InputType { get; set; }
public Type OutputType { get; set; }
#endregion #endregion

View File

@ -13,20 +13,24 @@ namespace Artemis.Core
public string Name { get; } public string Name { get; }
public string Description { get; } public string Description { get; }
public string Category { get; } public string Category { get; }
public Type? InputType { get; }
public Type? OutputType { get; }
private Func<INodeScript, NodeEntity?, INode> _create; private Func<INodeScript, NodeEntity?, INode> _create;
#endregion #endregion
#region Constructors #region Constructors
internal NodeData(Plugin plugin, Type type, string name, string description, string category, Func<INodeScript, NodeEntity?, INode>? create) internal NodeData(Plugin plugin, Type type, string name, string description, string category, Type? inputType, Type? outputType, Func<INodeScript, NodeEntity?, INode>? create)
{ {
this.Plugin = plugin; this.Plugin = plugin;
this.Type = type; this.Type = type;
this.Name = name; this.Name = name;
this.Description = description; this.Description = description;
this.Category = category; this.Category = category;
this.InputType = inputType;
this.OutputType = outputType;
this._create = create; this._create = create;
} }

View File

@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace Artemis.VisualScripting.Behaviors
{
// Source: https://stackoverflow.com/a/60474831/5015269
// Made some changes to add a foreground and background property
public static class HighlightTermBehavior
{
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(HighlightTermBehavior),
new FrameworkPropertyMetadata("", OnTextChanged));
public static readonly DependencyProperty TermToBeHighlightedProperty = DependencyProperty.RegisterAttached(
"TermToBeHighlighted",
typeof(string),
typeof(HighlightTermBehavior),
new FrameworkPropertyMetadata("", OnTextChanged));
public static readonly DependencyProperty HighlightForegroundProperty = DependencyProperty.RegisterAttached(
"HighlightForeground",
typeof(Color?),
typeof(HighlightTermBehavior),
new FrameworkPropertyMetadata(null, OnTextChanged));
public static readonly DependencyProperty HighlightBackgroundProperty = DependencyProperty.RegisterAttached(
"HighlightBackground",
typeof(Color?),
typeof(HighlightTermBehavior),
new FrameworkPropertyMetadata(null, OnTextChanged));
public static string GetText(FrameworkElement frameworkElement)
{
return (string) frameworkElement.GetValue(TextProperty);
}
public static void SetText(FrameworkElement frameworkElement, string value)
{
frameworkElement.SetValue(TextProperty, value);
}
public static string GetTermToBeHighlighted(FrameworkElement frameworkElement)
{
return (string) frameworkElement.GetValue(TermToBeHighlightedProperty);
}
public static void SetTermToBeHighlighted(FrameworkElement frameworkElement, string value)
{
frameworkElement.SetValue(TermToBeHighlightedProperty, value);
}
public static void SetHighlightForeground(FrameworkElement frameworkElement, Color? value)
{
frameworkElement.SetValue(HighlightForegroundProperty, value);
}
public static Color? GetHighlightForeground(FrameworkElement frameworkElement)
{
return (Color?) frameworkElement.GetValue(HighlightForegroundProperty);
}
public static void SetHighlightBackground(FrameworkElement frameworkElement, Color? value)
{
frameworkElement.SetValue(HighlightBackgroundProperty, value);
}
public static Color? GetHighlightBackground(FrameworkElement frameworkElement)
{
return (Color?) frameworkElement.GetValue(HighlightBackgroundProperty);
}
public static List<string> SplitTextIntoTermAndNotTermParts(string text, string term)
{
if (string.IsNullOrEmpty(text))
return new List<string> {string.Empty};
return Regex.Split(text, $@"({Regex.Escape(term)})", RegexOptions.IgnoreCase)
.Where(p => p != string.Empty)
.ToList();
}
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBlock textBlock)
SetTextBlockTextAndHighlightTerm(textBlock, GetText(textBlock), GetTermToBeHighlighted(textBlock));
}
private static void SetTextBlockTextAndHighlightTerm(TextBlock textBlock, string text, string termToBeHighlighted)
{
textBlock.Text = string.Empty;
if (TextIsEmpty(text))
return;
if (TextIsNotContainingTermToBeHighlighted(text, termToBeHighlighted))
{
AddPartToTextBlock(textBlock, text);
return;
}
List<string> textParts = SplitTextIntoTermAndNotTermParts(text, termToBeHighlighted);
foreach (string textPart in textParts)
AddPartToTextBlockAndHighlightIfNecessary(textBlock, termToBeHighlighted, textPart);
}
private static bool TextIsEmpty(string text)
{
return string.IsNullOrEmpty(text);
}
private static bool TextIsNotContainingTermToBeHighlighted(string text, string termToBeHighlighted)
{
if (text == null || termToBeHighlighted == null)
return true;
return text.Contains(termToBeHighlighted, StringComparison.OrdinalIgnoreCase) == false;
}
private static void AddPartToTextBlockAndHighlightIfNecessary(TextBlock textBlock, string termToBeHighlighted, string textPart)
{
if (textPart.Equals(termToBeHighlighted, StringComparison.OrdinalIgnoreCase))
AddHighlightedPartToTextBlock(textBlock, textPart);
else
AddPartToTextBlock(textBlock, textPart);
}
private static void AddPartToTextBlock(TextBlock textBlock, string part)
{
textBlock.Inlines.Add(new Run {Text = part});
}
private static void AddHighlightedPartToTextBlock(TextBlock textBlock, string part)
{
Color? foreground = GetHighlightForeground(textBlock);
Color? background = GetHighlightBackground(textBlock);
if (background == null)
{
Run run = new() {Text = part, FontWeight = FontWeights.ExtraBold};
if (foreground != null)
run.Foreground = new SolidColorBrush(foreground.Value);
textBlock.Inlines.Add(run);
return;
}
Border border = new()
{
Background = new SolidColorBrush(background.Value),
BorderThickness = new Thickness(0),
CornerRadius = new CornerRadius(2),
Child = new TextBlock {Text = part, FontWeight = FontWeights.Bold},
Padding = new Thickness(1),
Margin = new Thickness(-1, -5, -1, -5)
};
if (foreground != null)
((TextBlock) border.Child).Foreground = new SolidColorBrush(foreground.Value);
textBlock.Inlines.Add(border);
}
}
}

View File

@ -6,6 +6,7 @@ using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Input; using System.Windows.Input;
using Artemis.Core; using Artemis.Core;
using Artemis.VisualScripting.Editor.Controls.Wrapper;
namespace Artemis.VisualScripting.Editor.Controls namespace Artemis.VisualScripting.Editor.Controls
{ {
@ -37,16 +38,26 @@ namespace Artemis.VisualScripting.Editor.Controls
public IEnumerable AvailableNodes public IEnumerable AvailableNodes
{ {
get => (IEnumerable)GetValue(AvailableNodesProperty); get => (IEnumerable) GetValue(AvailableNodesProperty);
set => SetValue(AvailableNodesProperty, value); set => SetValue(AvailableNodesProperty, value);
} }
public static readonly DependencyProperty SourcePinProperty = DependencyProperty.Register(
"SourcePin", typeof(VisualScriptPin), typeof(VisualScriptNodeCreationBox), new PropertyMetadata(default(VisualScriptPin), OnSourcePinChanged));
public VisualScriptPin SourcePin
{
get => (VisualScriptPin) GetValue(SourcePinProperty);
set => SetValue(SourcePinProperty, value);
}
public static readonly DependencyProperty CreateNodeCommandProperty = DependencyProperty.Register( public static readonly DependencyProperty CreateNodeCommandProperty = DependencyProperty.Register(
"CreateNodeCommand", typeof(ICommand), typeof(VisualScriptNodeCreationBox), new PropertyMetadata(default(ICommand))); "CreateNodeCommand", typeof(ICommand), typeof(VisualScriptNodeCreationBox), new PropertyMetadata(default(ICommand)));
public ICommand CreateNodeCommand public ICommand CreateNodeCommand
{ {
get => (ICommand)GetValue(CreateNodeCommandProperty); get => (ICommand) GetValue(CreateNodeCommandProperty);
set => SetValue(CreateNodeCommandProperty, value); set => SetValue(CreateNodeCommandProperty, value);
} }
@ -63,12 +74,23 @@ namespace Artemis.VisualScripting.Editor.Controls
_contentList.IsSynchronizedWithCurrentItem = false; _contentList.IsSynchronizedWithCurrentItem = false;
_contentList.SelectionChanged += OnContentListSelectionChanged; _contentList.SelectionChanged += OnContentListSelectionChanged;
_contentList.SelectionMode = SelectionMode.Single; _contentList.SelectionMode = SelectionMode.Single;
IsVisibleChanged += OnIsVisibleChanged;
_searchBox.Focus(); _searchBox.Focus();
_contentView?.Refresh(); _contentView?.Refresh();
ItemsSourceChanged(); ItemsSourceChanged();
} }
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is not true)
return;
_searchBox.Focus();
_searchBox.SelectionStart = 0;
_searchBox.SelectionLength = _searchBox.Text.Length;
}
private void OnSearchBoxTextChanged(object sender, TextChangedEventArgs args) private void OnSearchBoxTextChanged(object sender, TextChangedEventArgs args)
{ {
_contentView?.Refresh(); _contentView?.Refresh();
@ -86,7 +108,14 @@ namespace Artemis.VisualScripting.Editor.Controls
if (_searchBox == null) return false; if (_searchBox == null) return false;
if (o is not NodeData nodeData) return false; if (o is not NodeData nodeData) return false;
return nodeData.Name.Contains(_searchBox.Text, StringComparison.OrdinalIgnoreCase); bool nameContains = nodeData.Name.Contains(_searchBox.Text, StringComparison.OrdinalIgnoreCase);
if (SourcePin == null || SourcePin.Pin.Type == typeof(object))
return nameContains;
if (SourcePin.Pin.Direction == PinDirection.Input)
return nameContains && (nodeData.OutputType == typeof(object) || nodeData.OutputType == SourcePin.Pin.Type);
return nameContains && (nodeData.InputType == typeof(object) || nodeData.InputType == SourcePin.Pin.Type);
} }
private void ItemsSourceChanged() private void ItemsSourceChanged()
@ -100,7 +129,16 @@ namespace Artemis.VisualScripting.Editor.Controls
} }
else else
{ {
_collectionViewSource = new CollectionViewSource { Source = AvailableNodes, SortDescriptions = { new SortDescription("Name", ListSortDirection.Ascending)}}; _collectionViewSource = new CollectionViewSource
{
Source = AvailableNodes,
SortDescriptions =
{
new SortDescription(nameof(NodeData.Category), ListSortDirection.Ascending),
new SortDescription(nameof(NodeData.Name), ListSortDirection.Ascending)
},
GroupDescriptions = {new PropertyGroupDescription(nameof(NodeData.Category))}
};
_contentView = _collectionViewSource.View; _contentView = _collectionViewSource.View;
_contentView.Filter += Filter; _contentView.Filter += Filter;
} }
@ -110,6 +148,8 @@ namespace Artemis.VisualScripting.Editor.Controls
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs args) => (d as VisualScriptNodeCreationBox)?.ItemsSourceChanged(); private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs args) => (d as VisualScriptNodeCreationBox)?.ItemsSourceChanged();
private static void OnSourcePinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => (d as VisualScriptNodeCreationBox)?._contentView.Refresh();
#endregion #endregion
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@ -65,7 +66,7 @@ namespace Artemis.VisualScripting.Editor.Controls
public INodeScript Script public INodeScript Script
{ {
get => (INodeScript)GetValue(ScriptProperty); get => (INodeScript) GetValue(ScriptProperty);
set => SetValue(ScriptProperty, value); set => SetValue(ScriptProperty, value);
} }
@ -74,7 +75,7 @@ namespace Artemis.VisualScripting.Editor.Controls
public double Scale public double Scale
{ {
get => (double)GetValue(ScaleProperty); get => (double) GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value); set => SetValue(ScaleProperty, value);
} }
@ -83,7 +84,7 @@ namespace Artemis.VisualScripting.Editor.Controls
public double MinScale public double MinScale
{ {
get => (double)GetValue(MinScaleProperty); get => (double) GetValue(MinScaleProperty);
set => SetValue(MinScaleProperty, value); set => SetValue(MinScaleProperty, value);
} }
@ -92,7 +93,7 @@ namespace Artemis.VisualScripting.Editor.Controls
public double MaxScale public double MaxScale
{ {
get => (double)GetValue(MaxScaleProperty); get => (double) GetValue(MaxScaleProperty);
set => SetValue(MaxScaleProperty, value); set => SetValue(MaxScaleProperty, value);
} }
@ -101,7 +102,7 @@ namespace Artemis.VisualScripting.Editor.Controls
public double ScaleFactor public double ScaleFactor
{ {
get => (double)GetValue(ScaleFactorProperty); get => (double) GetValue(ScaleFactorProperty);
set => SetValue(ScaleFactorProperty, value); set => SetValue(ScaleFactorProperty, value);
} }
@ -110,7 +111,7 @@ namespace Artemis.VisualScripting.Editor.Controls
public IEnumerable<NodeData> AvailableNodes public IEnumerable<NodeData> AvailableNodes
{ {
get => (IEnumerable<NodeData>)GetValue(AvailableNodesProperty); get => (IEnumerable<NodeData>) GetValue(AvailableNodesProperty);
set => SetValue(AvailableNodesProperty, value); set => SetValue(AvailableNodesProperty, value);
} }
@ -119,16 +120,25 @@ namespace Artemis.VisualScripting.Editor.Controls
public bool AlwaysShowValues public bool AlwaysShowValues
{ {
get => (bool)GetValue(AlwaysShowValuesProperty); get => (bool) GetValue(AlwaysShowValuesProperty);
set => SetValue(AlwaysShowValuesProperty, value); set => SetValue(AlwaysShowValuesProperty, value);
} }
public static readonly DependencyProperty SourcePinProperty = DependencyProperty.Register(
"SourcePin", typeof(VisualScriptPin), typeof(VisualScriptPresenter), new PropertyMetadata(default(VisualScriptPin)));
public VisualScriptPin SourcePin
{
get => (VisualScriptPin) GetValue(SourcePinProperty);
set => SetValue(SourcePinProperty, value);
}
public static readonly DependencyProperty CreateNodeCommandProperty = DependencyProperty.Register( public static readonly DependencyProperty CreateNodeCommandProperty = DependencyProperty.Register(
"CreateNodeCommand", typeof(ICommand), typeof(VisualScriptPresenter), new PropertyMetadata(default(ICommand))); "CreateNodeCommand", typeof(ICommand), typeof(VisualScriptPresenter), new PropertyMetadata(default(ICommand)));
public ICommand CreateNodeCommand public ICommand CreateNodeCommand
{ {
get => (ICommand)GetValue(CreateNodeCommandProperty); get => (ICommand) GetValue(CreateNodeCommandProperty);
private set => SetValue(CreateNodeCommandProperty, value); private set => SetValue(CreateNodeCommandProperty, value);
} }
@ -137,7 +147,7 @@ namespace Artemis.VisualScripting.Editor.Controls
public int GridSize public int GridSize
{ {
get => (int)GetValue(GridSizeProperty); get => (int) GetValue(GridSizeProperty);
set => SetValue(GridSizeProperty, value); set => SetValue(GridSizeProperty, value);
} }
@ -146,7 +156,7 @@ namespace Artemis.VisualScripting.Editor.Controls
public int SurfaceSize public int SurfaceSize
{ {
get => (int)GetValue(SurfaceSizeProperty); get => (int) GetValue(SurfaceSizeProperty);
set => SetValue(SurfaceSizeProperty, value); set => SetValue(SurfaceSizeProperty, value);
} }
@ -155,7 +165,7 @@ namespace Artemis.VisualScripting.Editor.Controls
public bool AutoFitScript public bool AutoFitScript
{ {
get => (bool)GetValue(AutoFitScriptProperty); get => (bool) GetValue(AutoFitScriptProperty);
set => SetValue(AutoFitScriptProperty, value); set => SetValue(AutoFitScriptProperty, value);
} }
@ -194,7 +204,7 @@ namespace Artemis.VisualScripting.Editor.Controls
_canvas.MouseMove += OnCanvasMouseMove; _canvas.MouseMove += OnCanvasMouseMove;
_canvas.MouseWheel += OnCanvasMouseWheel; _canvas.MouseWheel += OnCanvasMouseWheel;
_canvas.DragOver += OnCanvasDragOver; _canvas.DragOver += OnCanvasDragOver;
_canvas.Drop += OnCanvasDrop;
_nodeList.ItemsSource = VisualScript?.Nodes; _nodeList.ItemsSource = VisualScript?.Nodes;
_cableList.ItemsSource = VisualScript?.Cables; _cableList.ItemsSource = VisualScript?.Cables;
} }
@ -387,6 +397,34 @@ namespace Artemis.VisualScripting.Editor.Controls
VisualScript.OnDragOver(args.GetPosition(_canvas)); VisualScript.OnDragOver(args.GetPosition(_canvas));
} }
private void OnCanvasDrop(object sender, DragEventArgs args)
{
if (!args.Data.GetDataPresent(typeof(VisualScriptPin))) return;
VisualScriptPin sourcePin = (VisualScriptPin) args.Data.GetData(typeof(VisualScriptPin));
if (sourcePin == null) return;
if (_creationBoxParent.ContextMenu != null)
{
SourcePin = sourcePin;
_lastRightClickLocation = args.GetPosition(_canvas);
_creationBoxParent.ContextMenu.IsOpen = true;
_creationBoxParent.ContextMenu.DataContext = this;
void ContextMenuOnClosed(object s, RoutedEventArgs e)
{
SourcePin = null;
if (_creationBoxParent.ContextMenu != null)
_creationBoxParent.ContextMenu.Closed -= ContextMenuOnClosed;
}
_creationBoxParent.ContextMenu.Closed += ContextMenuOnClosed;
}
args.Handled = true;
}
private void OnCanvasMouseWheel(object sender, MouseWheelEventArgs args) private void OnCanvasMouseWheel(object sender, MouseWheelEventArgs args)
{ {
if (AutoFitScript) if (AutoFitScript)
@ -415,8 +453,8 @@ namespace Artemis.VisualScripting.Editor.Controls
for (int i = 0; i < _nodeList.Items.Count; i++) for (int i = 0; i < _nodeList.Items.Count; i++)
{ {
ContentPresenter nodeControl = (ContentPresenter)_nodeList.ItemContainerGenerator.ContainerFromIndex(i); ContentPresenter nodeControl = (ContentPresenter) _nodeList.ItemContainerGenerator.ContainerFromIndex(i);
VisualScriptNode node = (VisualScriptNode)nodeControl.Content; VisualScriptNode node = (VisualScriptNode) nodeControl.Content;
double nodeWidth = nodeControl.ActualWidth; double nodeWidth = nodeControl.ActualWidth;
double nodeHeight = nodeControl.ActualHeight; double nodeHeight = nodeControl.ActualHeight;
@ -492,14 +530,32 @@ namespace Artemis.VisualScripting.Editor.Controls
{ {
if (nodeData == null) return; if (nodeData == null) return;
if (_creationBoxParent.ContextMenu != null)
_creationBoxParent.ContextMenu.IsOpen = false;
INode node = nodeData.CreateNode(Script, null); INode node = nodeData.CreateNode(Script, null);
node.Initialize(Script); node.Initialize(Script);
node.X = _lastRightClickLocation.X - VisualScript.LocationOffset; node.X = _lastRightClickLocation.X - VisualScript.LocationOffset;
node.Y = _lastRightClickLocation.Y - VisualScript.LocationOffset; node.Y = _lastRightClickLocation.Y - VisualScript.LocationOffset;
if (SourcePin != null)
{
// Connect to the first matching input or output pin
List<IPin> pins = node.Pins.ToList();
pins.AddRange(node.PinCollections.SelectMany(c => c));
pins = pins.Where(p => p.Type == typeof(object) || p.Type == SourcePin.Pin.Type).OrderBy(p => p.Type != typeof(object)).ToList();
IPin preferredPin = SourcePin.Pin.Direction == PinDirection.Input
? pins.FirstOrDefault(p => p.Direction == PinDirection.Output)
: pins.FirstOrDefault(p => p.Direction == PinDirection.Input);
if (preferredPin != null)
{
preferredPin.ConnectTo(SourcePin.Pin);
SourcePin.Pin.ConnectTo(preferredPin);
}
}
if (_creationBoxParent.ContextMenu != null)
_creationBoxParent.ContextMenu.IsOpen = false;
Script.AddNode(node); Script.AddNode(node);
} }
@ -549,9 +605,10 @@ namespace Artemis.VisualScripting.Editor.Controls
T result = (child as T) ?? GetChildOfType<T>(child); T result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result; if (result != null) return result;
} }
return null; return null;
} }
#endregion #endregion
} }
} }

View File

@ -1,28 +1,35 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls" xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"> xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:behaviors="clr-namespace:Artemis.VisualScripting.Behaviors">
<DataTemplate x:Key="TemplateNodeItem" <DataTemplate x:Key="TemplateNodeItem"
DataType="{x:Type core:NodeData}"> DataType="{x:Type core:NodeData}">
<TextBlock Text="{Binding Name}" /> <TextBlock Margin="8 0 0 0"
Text="{Binding Name}"
ToolTip="{Binding Description}"
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Text, ElementName=PART_SearchBox}"
behaviors:HighlightTermBehavior.Text="{Binding Name}"
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}"/>
</DataTemplate> </DataTemplate>
<ControlTemplate x:Key="TemplateVisualScriptNodeCreationBox" <ControlTemplate x:Key="TemplateVisualScriptNodeCreationBox"
TargetType="{x:Type controls:VisualScriptNodeCreationBox}"> TargetType="{x:Type controls:VisualScriptNodeCreationBox}">
<Border Background="{TemplateBinding Background}" <Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"> BorderThickness="{TemplateBinding BorderThickness}">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Border Padding="2" <Border Padding="2"
BorderThickness="1" BorderThickness="1"
BorderBrush="#A0FFFFFF"> BorderBrush="#A0FFFFFF">
<TextBox x:Name="PART_SearchBox" <TextBox x:Name="PART_SearchBox"
Background="Transparent" Background="Transparent"
Foreground="#FFFFFFFF" Foreground="#FFFFFFFF"
CaretBrush="#FFFFFFFF" CaretBrush="#FFFFFFFF"
@ -33,13 +40,43 @@
Grid.Row="1" Grid.Row="1"
Background="Transparent" Background="Transparent"
Foreground="#FFFFFFFF" Foreground="#FFFFFFFF"
BorderThickness="0" BorderThickness="0"
ItemTemplate="{StaticResource TemplateNodeItem}" /> ItemTemplate="{StaticResource TemplateNodeItem}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander Header="{Binding Name}" IsExpanded="True">
<Expander.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontWeight="Bold" FontSize="16" Margin="0 8 8 8" />
</DataTemplate>
</Expander.HeaderTemplate>
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="Foreground" Value="AliceBlue"></Setter>
</Style>
</Expander.Style>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
</Grid> </Grid>
</Border> </Border>
</ControlTemplate> </ControlTemplate>
<Style x:Key="StyleVisualScriptNodeCreationBox" <Style x:Key="StyleVisualScriptNodeCreationBox"
TargetType="{x:Type controls:VisualScriptNodeCreationBox}"> TargetType="{x:Type controls:VisualScriptNodeCreationBox}">
<Setter Property="Background" Value="#DD333333" /> <Setter Property="Background" Value="#DD333333" />
<Setter Property="BorderBrush" Value="#FF222222" /> <Setter Property="BorderBrush" Value="#FF222222" />

View File

@ -15,6 +15,7 @@
<ControlTemplate> <ControlTemplate>
<Border Width="700" Height="300"> <Border Width="700" Height="300">
<controls:VisualScriptNodeCreationBox AvailableNodes="{Binding AvailableNodes}" <controls:VisualScriptNodeCreationBox AvailableNodes="{Binding AvailableNodes}"
SourcePin="{Binding SourcePin}"
CreateNodeCommand="{Binding CreateNodeCommand}" /> CreateNodeCommand="{Binding CreateNodeCommand}" />
</Border> </Border>
</ControlTemplate> </ControlTemplate>

View File

@ -3,7 +3,7 @@ using Artemis.Core;
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("Greater than", "Checks if the first input is greater than the second.")] [Node("Greater than", "Checks if the first input is greater than the second.", "Operators", InputType = typeof(object), OutputType = typeof(bool))]
public class GreaterThanNode : Node public class GreaterThanNode : Node
{ {
#region Properties & Fields #region Properties & Fields
@ -44,7 +44,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("Less than", "Checks if the first input is less than the second.")] [Node("Less than", "Checks if the first input is less than the second.", "Operators", InputType = typeof(object), OutputType = typeof(bool))]
public class LessThanNode : Node public class LessThanNode : Node
{ {
#region Properties & Fields #region Properties & Fields
@ -85,7 +85,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("Equals", "Checks if the two inputs are equals.")] [Node("Equals", "Checks if the two inputs are equals.", "Operators", InputType = typeof(bool), OutputType = typeof(bool))]
public class EqualsNode : Node public class EqualsNode : Node
{ {
#region Properties & Fields #region Properties & Fields
@ -126,7 +126,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("Negate", "Negates the boolean.")] [Node("Negate", "Negates the boolean.", "Operators", InputType = typeof(bool), OutputType = typeof(bool))]
public class NegateNode : Node public class NegateNode : Node
{ {
#region Properties & Fields #region Properties & Fields

View File

@ -3,7 +3,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("Brighten Color", "Brightens a color by a specified amount in percent")] [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() : base("Brighten Color", "Brightens a color by a specified amount in percent")
@ -20,7 +20,7 @@ namespace Artemis.VisualScripting.Nodes.Color
public override void Evaluate() public override void Evaluate()
{ {
Input.Value.ToHsl(out float h, out float s, out float l); Input.Value.ToHsl(out float h, out float s, out float l);
l *= (Percentage.Value + 100f) / 100f; l += l * (Percentage.Value / 100f);
Output.Value = SKColor.FromHsl(h, s, l); Output.Value = SKColor.FromHsl(h, s, l);
} }
} }

View File

@ -3,7 +3,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("Darken Color", "Darkens a color by a specified amount in percent")] [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() : base("Darken Color", "Darkens a color by a specified amount in percent")
@ -20,7 +20,7 @@ namespace Artemis.VisualScripting.Nodes.Color
public override void Evaluate() public override void Evaluate()
{ {
Input.Value.ToHsl(out float h, out float s, out float l); Input.Value.ToHsl(out float h, out float s, out float l);
l *= (Percentage.Value * -1 + 100f) / 100f; l -= l * (Percentage.Value / 100f);
Output.Value = SKColor.FromHsl(h, s, l); Output.Value = SKColor.FromHsl(h, s, l);
} }
} }

View File

@ -4,7 +4,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("Desaturate Color", "Desaturates a color by a specified amount in percent")] [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() : base("Desaturate Color", "Desaturates a color by a specified amount in percent")
@ -21,9 +21,8 @@ namespace Artemis.VisualScripting.Nodes.Color
public override void Evaluate() public override void Evaluate()
{ {
Input.Value.ToHsl(out float h, out float s, out float l); Input.Value.ToHsl(out float h, out float s, out float l);
s -= Percentage.Value; s -= s * (Percentage.Value / 100f);
s = Math.Clamp(s, 0, 100); Output.Value = SKColor.FromHsl(h, Math.Clamp(s, 0f, 100f), l);
Output.Value = SKColor.FromHsl(h, s, l);
} }
} }
} }

View File

@ -3,7 +3,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("HSL Color", "Creates a color from hue, saturation and lightness values")] [Node("HSL Color", "Creates a color from hue, saturation and lightness values", "Color", InputType = typeof(float), 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() : base("HSL Color", "Creates a color from hue, saturation and lightness values")

View File

@ -3,7 +3,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("Invert Color", "Inverts a color by a specified amount in percent")] [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() : base("Invert Color", "Inverts a color")
@ -17,9 +17,12 @@ namespace Artemis.VisualScripting.Nodes.Color
public override void Evaluate() public override void Evaluate()
{ {
Input.Value.ToHsl(out float h, out float s, out float l); Output.Value = new SKColor(
h += 180; (byte) (255 - Input.Value.Red),
Output.Value = SKColor.FromHsl(h % 360, s, l); (byte) (255 - Input.Value.Green),
(byte) (255 - Input.Value.Blue),
Input.Value.Alpha
);
} }
} }
} }

View File

@ -3,7 +3,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("Rotate Color Hue", "Rotates the hue of a color by a specified amount in degrees")] [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() : base("Rotate Color Hue", "Rotates the hue of a color by a specified amount in degrees")

View File

@ -4,7 +4,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("Saturate Color", "Saturates a color by a specified amount in percent")] [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() : base("Saturate Color", "Saturates a color by a specified amount in percent")
@ -21,9 +21,8 @@ namespace Artemis.VisualScripting.Nodes.Color
public override void Evaluate() public override void Evaluate()
{ {
Input.Value.ToHsl(out float h, out float s, out float l); Input.Value.ToHsl(out float h, out float s, out float l);
s += Percentage.Value; s += s * (Percentage.Value / 100f);
s = Math.Clamp(s, 0, 100); Output.Value = SKColor.FromHsl(h, Math.Clamp(s, 0f, 100f), l);
Output.Value = SKColor.FromHsl(h, s, l);
} }
} }
} }

View File

@ -4,7 +4,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("Color-Value", "Outputs a configurable color value.")] [Node("Color-Value", "Outputs a configurable color value.", "Static", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
public class StaticSKColorValueNode : Node<StaticSKColorValueNodeCustomViewModel> public class StaticSKColorValueNode : Node<StaticSKColorValueNodeCustomViewModel>
{ {
#region Constructors #region Constructors

View File

@ -3,7 +3,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Color namespace Artemis.VisualScripting.Nodes.Color
{ {
[Node("Sum (Color)", "Sums the connected color values.")] [Node("Sum (Color)", "Sums the connected color values.", "Color", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
public class SumSKColorsNode : Node public class SumSKColorsNode : Node
{ {
#region Properties & Fields #region Properties & Fields

View File

@ -2,7 +2,7 @@
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("To String", "Converts the input to a string.")] [Node("To String", "Converts the input to a string.", "Conversion", InputType = typeof(object), OutputType = typeof(string))]
public class ConvertToStringNode : Node public class ConvertToStringNode : Node
{ {
#region Properties & Fields #region Properties & Fields
@ -34,7 +34,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("To Integer", "Converts the input to an integer.")] [Node("To Integer", "Converts the input to an integer.", "Conversion", InputType = typeof(object), OutputType = typeof(int))]
public class ConvertToIntegerNode : Node public class ConvertToIntegerNode : Node
{ {
#region Properties & Fields #region Properties & Fields
@ -80,7 +80,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("To Double", "Converts the input to a double.")] [Node("To Double", "Converts the input to a double.", "Conversion", InputType = typeof(object), OutputType = typeof(double))]
public class ConvertToDoubleNode : Node public class ConvertToDoubleNode : Node
{ {
#region Properties & Fields #region Properties & Fields
@ -126,7 +126,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("To Float", "Converts the input to a float.")] [Node("To Float", "Converts the input to a float.", "Conversion", InputType = typeof(object), OutputType = typeof(float))]
public class ConvertToFloatNode : Node public class ConvertToFloatNode : Node
{ {
#region Properties & Fields #region Properties & Fields

View File

@ -7,7 +7,7 @@ using Stylet;
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("Data Model-Value", "Outputs a selectable data model value.")] [Node("Data Model-Value", "Outputs a selectable data model value.", "External")]
public class DataModelNode : Node<DataModelNodeCustomViewModel>, IDisposable public class DataModelNode : Node<DataModelNodeCustomViewModel>, IDisposable
{ {
private DataModelPath _dataModelPath; private DataModelPath _dataModelPath;

View File

@ -3,7 +3,7 @@ using Artemis.Core;
namespace Artemis.VisualScripting.Nodes.Easing namespace Artemis.VisualScripting.Nodes.Easing
{ {
[Node("Double Easing", "Outputs an eased double value")] [Node("Double Easing", "Outputs an eased double value", "Easing", InputType = typeof(double), OutputType = typeof(double))]
public class DoubleEasingNode : Node public class DoubleEasingNode : Node
{ {
private DateTime _lastEvaluate = DateTime.MinValue; private DateTime _lastEvaluate = DateTime.MinValue;

View File

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

View File

@ -3,7 +3,7 @@ using Artemis.Core;
namespace Artemis.VisualScripting.Nodes.Easing namespace Artemis.VisualScripting.Nodes.Easing
{ {
[Node("Float Easing", "Outputs an eased float value")] [Node("Float Easing", "Outputs an eased float value", "Easing", InputType = typeof(float), OutputType = typeof(float))]
public class FloatEasingNode : Node public class FloatEasingNode : Node
{ {
private DateTime _lastEvaluate = DateTime.MinValue; private DateTime _lastEvaluate = DateTime.MinValue;

View File

@ -3,7 +3,7 @@ using Artemis.Core;
namespace Artemis.VisualScripting.Nodes.Easing namespace Artemis.VisualScripting.Nodes.Easing
{ {
[Node("Integer Easing", "Outputs an eased integer value")] [Node("Integer Easing", "Outputs an eased integer value", "Easing", InputType = typeof(int), OutputType = typeof(int))]
public class IntEasingNode : Node public class IntEasingNode : Node
{ {
private DateTime _lastEvaluate = DateTime.MinValue; private DateTime _lastEvaluate = DateTime.MinValue;

View File

@ -4,7 +4,7 @@ using SkiaSharp;
namespace Artemis.VisualScripting.Nodes.Easing namespace Artemis.VisualScripting.Nodes.Easing
{ {
[Node("Color Easing", "Outputs an eased color value")] [Node("Color Easing", "Outputs an eased color value", "Easing", InputType = typeof(SKColor), OutputType = typeof(SKColor))]
public class SKColorEasingNode : Node public class SKColorEasingNode : Node
{ {
private DateTime _lastEvaluate = DateTime.MinValue; private DateTime _lastEvaluate = DateTime.MinValue;

View File

@ -6,7 +6,7 @@ using Artemis.VisualScripting.Nodes.CustomViewModels;
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("Layer/Folder Property", "Outputs the property of a selected layer or folder")] [Node("Layer/Folder Property", "Outputs the property of a selected layer or folder", "External")]
public class LayerPropertyNode : Node<LayerPropertyNodeCustomViewModel> public class LayerPropertyNode : Node<LayerPropertyNodeCustomViewModel>
{ {
private readonly object _layerPropertyLock = new(); private readonly object _layerPropertyLock = new();

View File

@ -3,7 +3,7 @@ using Artemis.VisualScripting.Nodes.CustomViewModels;
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("Integer-Value", "Outputs an configurable integer value.")] [Node("Integer-Value", "Outputs an configurable integer value.", "Static", OutputType = typeof(int))]
public class StaticIntegerValueNode : Node<StaticIntegerValueNodeCustomViewModel> public class StaticIntegerValueNode : Node<StaticIntegerValueNodeCustomViewModel>
{ {
#region Properties & Fields #region Properties & Fields
@ -34,7 +34,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("Double-Value", "Outputs a configurable double value.")] [Node("Double-Value", "Outputs a configurable double value.", "Static", OutputType = typeof(double))]
public class StaticDoubleValueNode : Node<StaticDoubleValueNodeCustomViewModel> public class StaticDoubleValueNode : Node<StaticDoubleValueNodeCustomViewModel>
{ {
#region Properties & Fields #region Properties & Fields
@ -65,7 +65,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("Float-Value", "Outputs a configurable float value.")] [Node("Float-Value", "Outputs a configurable float value.", "Static", OutputType = typeof(float))]
public class StaticFloatValueNode : Node<StaticFloatValueNodeCustomViewModel> public class StaticFloatValueNode : Node<StaticFloatValueNodeCustomViewModel>
{ {
#region Properties & Fields #region Properties & Fields
@ -99,7 +99,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("String-Value", "Outputs a configurable string value.")] [Node("String-Value", "Outputs a configurable string value.", "Static", OutputType = typeof(string))]
public class StaticStringValueNode : Node<StaticStringValueNodeCustomViewModel> public class StaticStringValueNode : Node<StaticStringValueNodeCustomViewModel>
{ {
#region Properties & Fields #region Properties & Fields
@ -127,8 +127,4 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
#region CustomViewModels
#endregion
} }

View File

@ -3,7 +3,7 @@ using Artemis.Core;
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("Format", "Formats the input string.")] [Node("Format", "Formats the input string.", "Text", InputType = typeof(object), OutputType = typeof(string))]
public class StringFormatNode : Node public class StringFormatNode : Node
{ {
#region Properties & Fields #region Properties & Fields

View File

@ -3,7 +3,7 @@ using Artemis.Core;
namespace Artemis.VisualScripting.Nodes namespace Artemis.VisualScripting.Nodes
{ {
[Node("Sum (Integer)", "Sums the connected integer values.")] [Node("Sum (Integer)", "Sums the connected integer values.", "Mathematics", InputType = typeof(int), OutputType = typeof(int))]
public class SumIntegersNode : Node public class SumIntegersNode : Node
{ {
#region Properties & Fields #region Properties & Fields
@ -35,7 +35,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("Sum (Float)", "Sums the connected float values.")] [Node("Sum (Float)", "Sums the connected float values.", "Mathematics", InputType = typeof(float), OutputType = typeof(float))]
public class SumFloatsNode : Node public class SumFloatsNode : Node
{ {
#region Properties & Fields #region Properties & Fields
@ -67,7 +67,7 @@ namespace Artemis.VisualScripting.Nodes
#endregion #endregion
} }
[Node("Sum (Double)", "Sums the connected double values.")] [Node("Sum (Double)", "Sums the connected double values.", "Mathematics", InputType = typeof(double), OutputType = typeof(double))]
public class SumDoublesNode : Node public class SumDoublesNode : Node
{ {
#region Properties & Fields #region Properties & Fields