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

VisualScripting: Fixed Preview; Added auto-fit mode

This commit is contained in:
Darth Affe 2021-08-21 21:48:01 +02:00
parent 405d5b756c
commit 7422a9667d
14 changed files with 372 additions and 86 deletions

View File

@ -15,7 +15,10 @@ namespace Artemis.Core
Type ResultType { get; }
object? Context { get; set; }
event EventHandler<INode>? NodeAdded;
event EventHandler<INode>? NodeRemoved;
void Run();
void AddNode(INode node);
void RemoveNode(INode node);

View File

@ -16,6 +16,9 @@ namespace Artemis.Core
bool IsEvaluated { get; set; }
event EventHandler<IPin> PinConnected;
event EventHandler<IPin> PinDisconnected;
void ConnectTo(IPin pin);
void DisconnectFrom(IPin pin);
}

View File

@ -9,6 +9,9 @@ namespace Artemis.Core
PinDirection Direction { get; }
Type Type { get; }
event EventHandler<IPin> PinAdded;
event EventHandler<IPin> PinRemoved;
IPin AddPin();
bool Remove(IPin pin);
}

View File

@ -1,18 +1,14 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace Artemis.Core
{
public abstract class Node : CorePropertyChanged, INode
{
public event EventHandler Resetting;
#region Properties & Fields
private string _name;
public string Name
{
get => _name;
@ -20,7 +16,6 @@ namespace Artemis.Core
}
private string _description;
public string Description
{
get => _description;
@ -28,7 +23,6 @@ namespace Artemis.Core
}
private double _x;
public double X
{
get => _x;
@ -36,7 +30,6 @@ namespace Artemis.Core
}
private double _y;
public double Y
{
get => _y;
@ -44,7 +37,6 @@ namespace Artemis.Core
}
private object? _storage;
public object? Storage
{
get => _storage;
@ -61,11 +53,16 @@ namespace Artemis.Core
#endregion
#region Events
public event EventHandler Resetting;
#endregion
#region Construtors
protected Node()
{
}
{ }
protected Node(string name, string description)
{
@ -138,8 +135,7 @@ namespace Artemis.Core
}
public virtual void Initialize(INodeScript script)
{
}
{ }
public abstract void Evaluate();
@ -154,12 +150,10 @@ namespace Artemis.Core
public abstract class Node<T> : CustomViewModelNode where T : ICustomNodeViewModel
{
protected Node()
{
}
{ }
protected Node(string name, string description) : base(name, description)
{
}
{ }
public override Type CustomViewModelType => typeof(T);
public T CustomViewModel => (T) BaseCustomViewModel!;
@ -169,13 +163,11 @@ namespace Artemis.Core
{
/// <inheritdoc />
protected CustomViewModelNode()
{
}
{ }
/// <inheritdoc />
protected CustomViewModelNode(string name, string description) : base(name, description)
{
}
{ }
public abstract Type CustomViewModelType { get; }
public object? BaseCustomViewModel { get; set; }

View File

@ -28,6 +28,13 @@ namespace Artemis.Core
#endregion
#region Events
public event EventHandler<INode>? NodeAdded;
public event EventHandler<INode>? NodeRemoved;
#endregion
#region Constructors
public NodeScript(string name, string description, object? context = null)
@ -67,11 +74,15 @@ namespace Artemis.Core
public void AddNode(INode node)
{
_nodes.Add(node);
NodeAdded?.Invoke(this, node);
}
public void RemoveNode(INode node)
{
_nodes.Remove(node);
NodeRemoved?.Invoke(this, node);
}
public void Dispose()

View File

@ -27,6 +27,13 @@ namespace Artemis.Core
#endregion
#region Events
public event EventHandler<IPin> PinConnected;
public event EventHandler<IPin> PinDisconnected;
#endregion
#region Constructors
protected Pin(INode node, string name = "")
@ -46,18 +53,27 @@ namespace Artemis.Core
{
_connectedTo.Add(pin);
OnPropertyChanged(nameof(ConnectedTo));
PinConnected?.Invoke(this, pin);
}
public void DisconnectFrom(IPin pin)
{
_connectedTo.Remove(pin);
OnPropertyChanged(nameof(ConnectedTo));
PinDisconnected?.Invoke(this, pin);
}
public void DisconnectAll()
{
List<IPin> connectedPins = new(_connectedTo);
_connectedTo.Clear();
OnPropertyChanged(nameof(ConnectedTo));
foreach (IPin pin in connectedPins)
PinDisconnected?.Invoke(this, pin);
}
private void OnNodeResetting(object sender, EventArgs e)

View File

@ -20,6 +20,13 @@ namespace Artemis.Core
#endregion
#region Events
public event EventHandler<IPin> PinAdded;
public event EventHandler<IPin> PinRemoved;
#endregion
#region Constructors
protected PinCollection(INode node, string name, int initialCount)
@ -35,14 +42,26 @@ namespace Artemis.Core
#region Methods
public IPin AddPin()
{
IPin pin = CreatePin();
_pins.Add(pin);
PinAdded?.Invoke(this, pin);
return pin;
}
public bool Remove(IPin pin) => _pins.Remove(pin);
public bool Remove(IPin pin)
{
bool removed = _pins.Remove(pin);
if (removed)
PinRemoved?.Invoke(this, pin);
return removed;
}
protected abstract IPin CreatePin();

View File

@ -43,8 +43,9 @@
<Separator Grid.Row="1" Grid.Column="0" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="-2 0" />
<Grid Grid.Row="2" Grid.Column="0" MouseUp="{s:Action ScriptGridMouseUp}" Cursor="Hand">
<controls:VisualScriptEditor Script="{Binding RenderProfileElement.DisplayCondition}"
Visibility="{Binding RenderProfileElement.DisplayCondition, Converter={StaticResource NullToVisibilityConverter}}" />
<controls:VisualScriptPresenter Script="{Binding RenderProfileElement.DisplayCondition}"
AutoFitScript="True"
Visibility="{Binding RenderProfileElement.DisplayCondition, Converter={StaticResource NullToVisibilityConverter}}" />
<Border Opacity="0">
<Border.Background>
<SolidColorBrush Color="{Binding Color, Source={StaticResource MaterialDesignCardBackground}}" Opacity="0.75" />

View File

@ -7,10 +7,6 @@ namespace Artemis.VisualScripting.Editor.Controls
{
public class VisualScriptEditor : Control
{
#region Properties & Fields
#endregion
#region Dependency Properties
public static readonly DependencyProperty ScriptProperty = DependencyProperty.Register(
@ -32,9 +28,5 @@ namespace Artemis.VisualScripting.Editor.Controls
}
#endregion
#region Methods
#endregion
}
}

View File

@ -6,6 +6,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Artemis.Core;
using Artemis.VisualScripting.Editor.Controls.Wrapper;
using Artemis.VisualScripting.ViewModel;
@ -32,11 +33,13 @@ namespace Artemis.VisualScripting.Editor.Controls
#region Properties & Fields
private bool _fitPending = false;
private Canvas _canvas;
private ItemsControl _nodeList;
private ItemsControl _cableList;
private Border _selectionBorder;
private TranslateTransform _canvasViewPortTransform;
private ScaleTransform _canvasViewPortScale;
private Panel _creationBoxParent;
private Vector _viewportCenter = new(0, 0);
@ -121,7 +124,7 @@ namespace Artemis.VisualScripting.Editor.Controls
}
public static readonly DependencyProperty GridSizeProperty = DependencyProperty.Register(
"GridSize", typeof(int), typeof(VisualScriptPresenter), new PropertyMetadata(12));
"GridSize", typeof(int), typeof(VisualScriptPresenter), new PropertyMetadata(24));
public int GridSize
{
@ -138,6 +141,15 @@ namespace Artemis.VisualScripting.Editor.Controls
set => SetValue(SurfaceSizeProperty, value);
}
public static readonly DependencyProperty AutoFitScriptProperty = DependencyProperty.Register(
"AutoFitScript", typeof(bool), typeof(VisualScriptPresenter), new PropertyMetadata(false, AutoFitScriptChanged));
public bool AutoFitScript
{
get => (bool)GetValue(AutoFitScriptProperty);
set => SetValue(AutoFitScriptProperty, value);
}
#endregion
#region Constructors
@ -163,6 +175,7 @@ namespace Artemis.VisualScripting.Editor.Controls
_canvas.AllowDrop = true;
_canvas.LayoutTransform = _canvasViewPortScale = new ScaleTransform(MaxScale, MaxScale);
_canvas.RenderTransform = _canvasViewPortTransform = new TranslateTransform(0, 0);
_canvas.MouseLeftButtonDown += OnCanvasMouseLeftButtonDown;
_canvas.MouseLeftButtonUp += OnCanvasMouseLeftButtonUp;
@ -177,11 +190,22 @@ namespace Artemis.VisualScripting.Editor.Controls
_cableList.ItemsSource = VisualScript?.Cables;
}
private static void AutoFitScriptChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
if (d is not VisualScriptPresenter scriptPresenter) return;
if ((args.NewValue as bool?) == true)
scriptPresenter.FitScript();
}
private void OnSizeChanged(object sender, SizeChangedEventArgs args)
{
if (sender is not VisualScriptPresenter scriptPresenter) return;
scriptPresenter.UpdatePanning();
if (AutoFitScript)
scriptPresenter.FitScript();
else
scriptPresenter.UpdatePanning();
}
private static void ScriptChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
@ -194,28 +218,33 @@ namespace Artemis.VisualScripting.Editor.Controls
private void ScriptChanged(VisualScript newScript)
{
if (VisualScript != null)
{
VisualScript.PropertyChanged -= OnVisualScriptPropertyChanged;
VisualScript.NodeMoved -= OnVisualScriptNodeMoved;
VisualScript.NodeCollectionChanged -= OnVisualScriptNodeCollectionChanged;
}
VisualScript = newScript;
if (VisualScript != null)
{
VisualScript.PropertyChanged += OnVisualScriptPropertyChanged;
VisualScript.NodeMoved += OnVisualScriptNodeMoved;
VisualScript.NodeCollectionChanged += OnVisualScriptNodeCollectionChanged;
if (_nodeList != null)
_nodeList.ItemsSource = VisualScript?.Nodes;
if (_cableList != null)
_cableList.ItemsSource = VisualScript?.Cables;
VisualScript.Nodes.Clear();
foreach (INode node in VisualScript.Script.Nodes)
InitializeNode(node);
}
VisualScript?.RecreateCables();
CenterAt(new Vector(0, 0));
if (AutoFitScript)
FitScript();
else
CenterAt(new Vector(0, 0));
}
private void OnVisualScriptPropertyChanged(object sender, PropertyChangedEventArgs args)
@ -225,6 +254,18 @@ namespace Artemis.VisualScripting.Editor.Controls
_cableList.ItemsSource = VisualScript.Cables;
}
private void OnVisualScriptNodeMoved(object sender, EventArgs args)
{
if (AutoFitScript)
FitScript();
}
private void OnVisualScriptNodeCollectionChanged(object? sender, EventArgs e)
{
if (AutoFitScript)
FitScript();
}
private void OnCanvasPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs args)
{
_lastRightClickLocation = args.GetPosition(_canvas);
@ -261,6 +302,12 @@ namespace Artemis.VisualScripting.Editor.Controls
private void OnCanvasMouseRightButtonDown(object sender, MouseButtonEventArgs args)
{
if (AutoFitScript)
{
args.Handled = true;
return;
}
_dragCanvas = true;
_dragCanvasStartLocation = args.GetPosition(this);
_dragCanvasStartOffset = _viewportCenter;
@ -289,7 +336,7 @@ namespace Artemis.VisualScripting.Editor.Controls
{
if (args.RightButton == MouseButtonState.Pressed)
{
Vector newLocation = _dragCanvasStartOffset + (((args.GetPosition(this) - _dragCanvasStartLocation)) * (1.0 / Scale));
Vector newLocation = _dragCanvasStartOffset - (((args.GetPosition(this) - _dragCanvasStartLocation)) * (1.0 / Scale));
CenterAt(newLocation);
_movedDuringDrag = true;
@ -327,14 +374,18 @@ namespace Artemis.VisualScripting.Editor.Controls
private void OnCanvasDragOver(object sender, DragEventArgs args)
{
if (VisualScript == null) return;
if (VisualScript.IsConnecting)
VisualScript.OnDragOver(args.GetPosition(_canvas));
}
private void OnCanvasMouseWheel(object sender, MouseWheelEventArgs args)
{
if (AutoFitScript)
{
args.Handled = true;
return;
}
if (args.Delta < 0)
Scale /= ScaleFactor;
else
@ -342,8 +393,6 @@ namespace Artemis.VisualScripting.Editor.Controls
Scale = Clamp(Scale, MinScale, MaxScale);
_canvas.LayoutTransform = new ScaleTransform(Scale, Scale);
UpdatePanning();
args.Handled = true;
@ -373,9 +422,63 @@ namespace Artemis.VisualScripting.Editor.Controls
if (d is not VisualScriptPresenter presenter) return;
if (presenter.VisualScript == null) return;
presenter._canvasViewPortScale.ScaleX = presenter.Scale;
presenter._canvasViewPortScale.ScaleY = presenter.Scale;
presenter.VisualScript.NodeDragScale = 1.0 / presenter.Scale;
}
private void FitScript()
{
if (_fitPending) return;
_fitPending = true;
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(FitScriptAction));
}
private void FitScriptAction()
{
_fitPending = false;
if ((Script == null) || (_nodeList == null)) return;
double minX = double.MaxValue;
double maxX = double.MinValue;
double minY = double.MaxValue;
double maxY = double.MinValue;
for (int i = 0; i < _nodeList.Items.Count; i++)
{
DependencyObject container = _nodeList.ItemContainerGenerator.ContainerFromIndex(i);
VisualScriptNodePresenter nodePresenter = GetChildOfType<VisualScriptNodePresenter>(container);
if (nodePresenter != null)
{
minX = Math.Min(minX, nodePresenter.Node.Node.X);
minY = Math.Min(minY, nodePresenter.Node.Node.Y);
maxX = Math.Max(maxX, nodePresenter.Node.Node.X + nodePresenter.ActualWidth);
maxY = Math.Max(maxY, nodePresenter.Node.Node.Y + nodePresenter.ActualHeight);
}
}
if (minX >= (double.MaxValue - 1))
{
Scale = MaxScale;
CenterAt(new Vector(0, 0));
}
else
{
double width = maxX - minX;
double height = maxY - minY;
double scaleX = ActualWidth / width;
double scaleY = ActualHeight / height;
Scale = Clamp(Math.Min(scaleX, scaleY) / 1.05, 0, MaxScale); //DarthAffe 21.08.2021: 5% Border
CenterAt(new Vector(minX + (width / 2.0), minY + (height / 2.0)));
}
}
private void CreateNode(NodeData nodeData)
{
if (nodeData == null) return;
@ -385,21 +488,10 @@ namespace Artemis.VisualScripting.Editor.Controls
INode node = nodeData.CreateNode(Script, null);
node.Initialize(Script);
node.X = _lastRightClickLocation.X - VisualScript.LocationOffset;
node.Y = _lastRightClickLocation.Y - VisualScript.LocationOffset;
Script.AddNode(node);
InitializeNode(node, _lastRightClickLocation);
}
private void InitializeNode(INode node, Point? initialLocation = null)
{
VisualScriptNode visualScriptNode = new(VisualScript, node);
if (initialLocation != null)
{
visualScriptNode.X = initialLocation.Value.X;
visualScriptNode.Y = initialLocation.Value.Y;
}
visualScriptNode.SnapNodeToGrid();
VisualScript.Nodes.Add(visualScriptNode);
}
private void CenterAt(Vector vector)
@ -414,8 +506,8 @@ namespace Artemis.VisualScripting.Editor.Controls
if (_canvasViewPortTransform == null) return;
double surfaceOffset = (SurfaceSize / 2.0) * Scale;
_canvasViewPortTransform.X = (((_viewportCenter.X * Scale) + (ActualWidth / 2.0))) - surfaceOffset;
_canvasViewPortTransform.Y = (((_viewportCenter.Y * Scale) + (ActualHeight / 2.0))) - surfaceOffset;
_canvasViewPortTransform.X = (((-_viewportCenter.X * Scale) + (ActualWidth / 2.0))) - surfaceOffset;
_canvasViewPortTransform.Y = (((-_viewportCenter.Y * Scale) + (ActualHeight / 2.0))) - surfaceOffset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -436,6 +528,21 @@ namespace Artemis.VisualScripting.Editor.Controls
return new Vector(x, y);
}
public static T GetChildOfType<T>(DependencyObject obj)
where T : DependencyObject
{
if (obj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
T result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
#endregion
}
}

View File

@ -2,8 +2,10 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
using Artemis.Core;
using Artemis.VisualScripting.Events;
using Artemis.VisualScripting.ViewModel;
@ -14,6 +16,8 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
{
#region Properties & Fields
private bool _cableRecreationPending = false;
private readonly HashSet<VisualScriptNode> _selectedNodes = new();
private readonly Dictionary<VisualScriptNode, (double X, double Y)> _nodeStartPositions = new();
private double _nodeDragAccumulationX;
@ -31,6 +35,8 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
set => SetProperty(ref _nodeDragScale, value);
}
internal double LocationOffset { get; }
private VisualScriptPin _isConnectingPin;
private VisualScriptPin IsConnectingPin
{
@ -42,6 +48,7 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
}
}
private readonly Dictionary<INode, VisualScriptNode> _nodeMapping = new();
public ObservableCollection<VisualScriptNode> Nodes { get; } = new();
public IEnumerable<VisualScriptCable> Cables => Nodes.SelectMany(n => n.InputPins.SelectMany(p => p.InternalConnections))
@ -54,6 +61,13 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
#endregion
#region Events
public event EventHandler NodeMoved;
public event EventHandler NodeCollectionChanged;
#endregion
#region Constructors
public VisualScript(INodeScript script, int surfaceSize, int gridSize)
@ -62,7 +76,15 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
this.SurfaceSize = surfaceSize;
this.GridSize = gridSize;
LocationOffset = SurfaceSize / 2.0;
Nodes.CollectionChanged += OnNodeCollectionChanged;
script.NodeAdded += OnScriptNodeAdded;
script.NodeRemoved += OnScriptRemovedAdded;
foreach (INode node in Script.Nodes)
InitializeNode(node);
}
#endregion
@ -85,31 +107,69 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
RegisterNodes(args.NewItems.Cast<VisualScriptNode>());
}
private void OnScriptNodeAdded(object sender, INode node)
{
if (_nodeMapping.ContainsKey(node)) return;
InitializeNode(node);
}
private void OnScriptRemovedAdded(object sender, INode node)
{
if (!_nodeMapping.TryGetValue(node, out VisualScriptNode visualScriptNode)) return;
Nodes.Remove(visualScriptNode);
}
private void InitializeNode(INode node)
{
VisualScriptNode visualScriptNode = new(this, node);
visualScriptNode.SnapNodeToGrid();
Nodes.Add(visualScriptNode);
}
private void RegisterNodes(IEnumerable<VisualScriptNode> nodes)
{
foreach (VisualScriptNode node in nodes)
{
_nodeMapping.Add(node.Node, node);
node.IsSelectedChanged += OnNodeIsSelectedChanged;
node.DragStarting += OnNodeDragStarting;
node.DragEnding += OnNodeDragEnding;
node.DragMoving += OnNodeDragMoving;
node.PropertyChanged += OnNodePropertyChanged;
if (node.IsSelected)
_selectedNodes.Add(node);
}
NodeCollectionChanged?.Invoke(this, EventArgs.Empty);
}
private void UnregisterNodes(IEnumerable<VisualScriptNode> nodes)
{
foreach (VisualScriptNode node in nodes)
{
_nodeMapping.Remove(node.Node);
node.IsSelectedChanged -= OnNodeIsSelectedChanged;
node.DragStarting -= OnNodeDragStarting;
node.DragEnding -= OnNodeDragEnding;
node.DragMoving -= OnNodeDragMoving;
node.PropertyChanged -= OnNodePropertyChanged;
_selectedNodes.Remove(node);
}
NodeCollectionChanged?.Invoke(this, EventArgs.Empty);
}
private void OnNodePropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (string.Equals(args.PropertyName, nameof(VisualScriptNode.X), StringComparison.OrdinalIgnoreCase)
|| string.Equals(args.PropertyName, nameof(VisualScriptNode.Y), StringComparison.OrdinalIgnoreCase))
NodeMoved?.Invoke(this, EventArgs.Empty);
}
private void OnNodeIsSelectedChanged(object sender, VisualScriptNodeIsSelectedChangedEventArgs args)
@ -184,8 +244,18 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
Script.RemoveNode(node.Node);
}
internal void RequestCableRecreation()
{
if (_cableRecreationPending) return;
_cableRecreationPending = true;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(RecreateCables));
}
internal void RecreateCables()
{
_cableRecreationPending = false;
Dictionary<IPin, VisualScriptPin> pinMapping = Nodes.SelectMany(n => n.InputPins)
.Concat(Nodes.SelectMany(n => n.OutputPins))
.Concat(Nodes.SelectMany(n => n.InputPinCollections.SelectMany(p => p.Pins)))
@ -200,13 +270,13 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
{
foreach (IPin connectedPin in pin.ConnectedTo)
if (!connectedPins.Contains(connectedPin))
{
VisualScriptPin pin1 = pinMapping[pin];
VisualScriptPin pin2 = pinMapping[connectedPin];
VisualScriptCable cable = new(pin1, pin2, false);
pin1.InternalConnections.Add(cable);
pin2.InternalConnections.Add(cable);
}
if (pinMapping.TryGetValue(pin, out VisualScriptPin pin1)
&& pinMapping.TryGetValue(connectedPin, out VisualScriptPin pin2))
{
VisualScriptCable cable = new(pin1, pin2, false);
pin1.InternalConnections.Add(cable);
pin2.InternalConnections.Add(cable);
}
connectedPins.Add(pin);
}

View File

@ -13,7 +13,7 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
{
#region Properties & Fields
private double _locationOffset;
private readonly Dictionary<IPin, VisualScriptPin> _pinMapping = new();
public VisualScript Script { get; }
public INode Node { get; }
@ -55,20 +55,20 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
public double X
{
get => Node.X + _locationOffset;
get => Node.X + Script.LocationOffset;
set
{
Node.X = value - _locationOffset;
Node.X = value - Script.LocationOffset;
OnPropertyChanged();
}
}
public double Y
{
get => Node.Y + _locationOffset;
get => Node.Y + Script.LocationOffset;
set
{
Node.Y = value - _locationOffset;
Node.Y = value - Script.LocationOffset;
OnPropertyChanged();
}
}
@ -100,8 +100,6 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
Node.PropertyChanged += OnNodePropertyChanged;
_locationOffset = script.SurfaceSize / 2.0;
ValidatePins();
}
@ -114,6 +112,12 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
if (string.Equals(args.PropertyName, nameof(Node.Pins), StringComparison.OrdinalIgnoreCase)
|| string.Equals(args.PropertyName, nameof(Node.PinCollections), StringComparison.OrdinalIgnoreCase))
ValidatePins();
else if (string.Equals(args.PropertyName, nameof(Node.X), StringComparison.OrdinalIgnoreCase))
OnPropertyChanged(nameof(X));
else if (string.Equals(args.PropertyName, nameof(Node.Y), StringComparison.OrdinalIgnoreCase))
OnPropertyChanged(nameof(Y));
}
private void ValidatePins()
@ -199,8 +203,30 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
}
#endregion
#region Pin Mapping
_pinMapping.Clear();
foreach (VisualScriptPin pin in InputPins)
_pinMapping.Add(pin.Pin, pin);
foreach (VisualScriptPin pin in OutputPins)
_pinMapping.Add(pin.Pin, pin);
foreach (VisualScriptPinCollection pinCollection in InputPinCollections)
foreach (VisualScriptPin pin in pinCollection.Pins)
_pinMapping.Add(pin.Pin, pin);
foreach (VisualScriptPinCollection pinCollection in OutputPinCollections)
foreach (VisualScriptPin pin in pinCollection.Pins)
_pinMapping.Add(pin.Pin, pin);
#endregion
}
internal VisualScriptPin GetVisualPin(IPin pin) => ((pin != null) && _pinMapping.TryGetValue(pin, out VisualScriptPin visualScriptPin)) ? visualScriptPin : null;
public void SnapNodeToGrid()
{
X -= X % Script.GridSize;
@ -219,8 +245,8 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
OnIsSelectedChanged(IsSelected, alterSelection);
}
public void DragStart() => DragStarting?.Invoke(this, new EventArgs());
public void DragEnd() => DragEnding?.Invoke(this, new EventArgs());
public void DragStart() => DragStarting?.Invoke(this, EventArgs.Empty);
public void DragEnd() => DragEnding?.Invoke(this, EventArgs.Empty);
public void DragMove(double dx, double dy) => DragMoving?.Invoke(this, new VisualScriptNodeDragMovingEventArgs(dx, dy));
private void OnIsSelectedChanged(bool isSelected, bool alterSelection) => IsSelectedChanged?.Invoke(this, new VisualScriptNodeIsSelectedChangedEventArgs(isSelected, alterSelection));

View File

@ -19,6 +19,8 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
#region Properties & Fields
private bool _isConnectionUpdated = false;
private VisualScriptPin _isConnectingPin;
private VisualScriptCable _isConnectingCable;
@ -39,8 +41,7 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
}
}
public Point AbsoluteCableTargetPosition => Pin.Direction == PinDirection.Input ? new Point(AbsolutePosition.X - CABLE_OFFSET, AbsolutePosition.Y)
: new Point(AbsolutePosition.X + CABLE_OFFSET, AbsolutePosition.Y);
public Point AbsoluteCableTargetPosition => Pin.Direction == PinDirection.Input ? new Point(AbsolutePosition.X - CABLE_OFFSET, AbsolutePosition.Y) : new Point(AbsolutePosition.X + CABLE_OFFSET, AbsolutePosition.Y);
#endregion
@ -50,12 +51,22 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
{
this.Node = node;
this.Pin = pin;
pin.PinConnected += PinConnectionChanged;
pin.PinDisconnected += PinConnectionChanged;
}
#endregion
#region Methods
private void PinConnectionChanged(object sender, IPin pin)
{
if (_isConnectionUpdated) return;
Node?.Script?.RequestCableRecreation();
}
public void SetConnecting(bool isConnecting)
{
if (isConnecting)
@ -63,8 +74,7 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
if (_isConnectingCable != null)
SetConnecting(false);
_isConnectingPin = new VisualScriptPin(null, new IsConnectingPin(Pin.Direction == PinDirection.Input ? PinDirection.Output : PinDirection.Input, Pin.Type))
{ AbsolutePosition = AbsolutePosition };
_isConnectingPin = new VisualScriptPin(null, new IsConnectingPin(Pin.Direction == PinDirection.Input ? PinDirection.Output : PinDirection.Input, Pin.Type)) { AbsolutePosition = AbsolutePosition };
_isConnectingCable = new VisualScriptCable(this, _isConnectingPin);
Node.OnIsConnectingPinChanged(_isConnectingPin);
}
@ -81,6 +91,8 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
{
if (InternalConnections.Contains(cable)) return;
_isConnectionUpdated = true;
if (Pin.Direction == PinDirection.Input)
{
List<VisualScriptCable> cables = InternalConnections.ToList();
@ -92,21 +104,31 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
Pin.ConnectTo(cable.GetConnectedPin(Pin));
Node?.OnPinConnected(new PinConnectedEventArgs(this, cable));
_isConnectionUpdated = false;
}
public void DisconnectAll()
{
_isConnectionUpdated = true;
List<VisualScriptCable> cables = InternalConnections.ToList();
foreach (VisualScriptCable cable in cables)
cable.Disconnect();
_isConnectionUpdated = false;
}
internal void Disconnect(VisualScriptCable cable)
{
_isConnectionUpdated = true;
InternalConnections.Remove(cable);
Pin.DisconnectFrom(cable.GetConnectedPin(Pin));
Node?.OnPinDisconnected(new PinDisconnectedEventArgs(this, cable));
_isConnectionUpdated = false;
}
#endregion

View File

@ -12,6 +12,7 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
public VisualScriptNode Node { get; }
public IPinCollection PinCollection { get; }
private readonly Dictionary<IPin, VisualScriptPin> _pinMapping = new();
public ObservableCollection<VisualScriptPin> Pins { get; } = new();
#endregion
@ -33,27 +34,47 @@ namespace Artemis.VisualScripting.Editor.Controls.Wrapper
this.Node = node;
this.PinCollection = pinCollection;
pinCollection.PinAdded += OnPinCollectionPinAdded;
pinCollection.PinRemoved += OnPinCollectionPinRemoved;
foreach (IPin pin in PinCollection)
Pins.Add(new VisualScriptPin(node, pin));
{
VisualScriptPin visualScriptPin = new(node, pin);
_pinMapping.Add(pin, visualScriptPin);
Pins.Add(visualScriptPin);
}
}
#endregion
#region Methods
private void OnPinCollectionPinRemoved(object sender, IPin pin)
{
if (!_pinMapping.TryGetValue(pin, out VisualScriptPin visualScriptPin)) return;
visualScriptPin.DisconnectAll();
Pins.Remove(visualScriptPin);
}
private void OnPinCollectionPinAdded(object sender, IPin pin)
{
if (_pinMapping.ContainsKey(pin)) return;
VisualScriptPin visualScriptPin = new(Node, pin);
_pinMapping.Add(pin, visualScriptPin);
Pins.Add(visualScriptPin);
}
public void AddPin()
{
IPin pin = PinCollection.AddPin();
Pins.Add(new VisualScriptPin(Node, pin));
PinCollection.AddPin();
}
public void RemovePin(VisualScriptPin pin)
{
pin.DisconnectAll();
PinCollection.Remove(pin.Pin);
Pins.Remove(pin);
}
public void RemoveAll()
{
List<VisualScriptPin> pins = new(Pins);