From 5cf760ebcf2d6499e6e35f47c70896c13d58ff15 Mon Sep 17 00:00:00 2001 From: Robin Kahlow Date: Tue, 14 May 2019 14:12:06 +0100 Subject: [PATCH 1/4] added graph interfaces, removed a lot of pin connection related view(-model) code --- NetPrints/Base/INode.cs | 28 ++ NetPrints/Base/INodeDataPin.cs | 10 + NetPrints/Base/INodeExecutionPin.cs | 6 + NetPrints/Base/INodeGraph.cs | 15 + NetPrints/Base/INodeInputPin.cs | 10 + NetPrints/Base/INodeOutputPin.cs | 10 + NetPrints/Base/INodePin.cs | 29 ++ NetPrints/Base/INodeTypePin.cs | 10 + .../Core/FilteredObservableCollection.cs | 128 +++++++++ NetPrints/Core/INodeInputButtons.cs | 8 + NetPrints/Core/INodeOutputButtons.cs | 8 + NetPrints/Core/MethodGraph.cs | 11 +- NetPrints/Core/NetPrintsUtil.cs | 24 ++ NetPrints/Core/NodeGraph.cs | 120 +++++++- NetPrints/Core/PinConnection.cs | 42 +++ NetPrints/Core/Variable.cs | 2 +- NetPrints/Graph/AwaitNode.cs | 6 +- NetPrints/Graph/CallMethodNode.cs | 16 +- NetPrints/Graph/ClassReturnNode.cs | 14 +- NetPrints/Graph/ConstructorNode.cs | 2 +- NetPrints/Graph/ExecNode.cs | 11 +- NetPrints/Graph/ExplicitCastNode.cs | 8 +- NetPrints/Graph/GraphUtil.cs | 260 +++++------------- NetPrints/Graph/LiteralNode.cs | 4 +- NetPrints/Graph/MakeArrayNode.cs | 31 +-- NetPrints/Graph/MethodEntryNode.cs | 22 +- NetPrints/Graph/Node.cs | 56 ++-- NetPrints/Graph/NodeDataPin.cs | 5 +- NetPrints/Graph/NodeExecPin.cs | 5 +- NetPrints/Graph/NodeInputDataPin.cs | 33 +-- NetPrints/Graph/NodeInputExecPin.cs | 20 +- NetPrints/Graph/NodeInputTypePin.cs | 34 +-- NetPrints/Graph/NodeOutputDataPin.cs | 15 +- NetPrints/Graph/NodeOutputExecPin.cs | 32 +-- NetPrints/Graph/NodeOutputTypePin.cs | 25 +- NetPrints/Graph/NodePin.cs | 39 ++- NetPrints/Graph/NodeTypePin.cs | 5 +- NetPrints/Graph/ReturnNode.cs | 26 +- .../Translator/ExecutionGraphTranslator.cs | 19 +- NetPrints/Translator/TranslatorUtil.cs | 8 +- NetPrintsEditor/Controls/GraphEditorView.xaml | 19 +- .../Controls/GraphEditorView.xaml.cs | 20 +- NetPrintsEditor/Controls/PinControl.xaml.cs | 2 +- NetPrintsEditor/Converters/PointConverter.cs | 20 ++ NetPrintsEditor/ViewModels/ClassEditorVM.cs | 2 +- .../ViewModels/MemberVariableVM.cs | 8 +- NetPrintsEditor/ViewModels/NodeGraphVM.cs | 257 +---------------- NetPrintsEditor/ViewModels/NodePinVM.cs | 109 ++------ NetPrintsEditor/ViewModels/NodeVM.cs | 177 ++---------- NetPrintsUnitTests/ClassTranslatorTests.cs | 20 +- NetPrintsUnitTests/DelegateTranslatorTests.cs | 6 +- NetPrintsUnitTests/GenericsTests.cs | 18 +- NetPrintsUnitTests/MethodTranslatorTests.cs | 32 +-- 53 files changed, 891 insertions(+), 956 deletions(-) create mode 100644 NetPrints/Base/INode.cs create mode 100644 NetPrints/Base/INodeDataPin.cs create mode 100644 NetPrints/Base/INodeExecutionPin.cs create mode 100644 NetPrints/Base/INodeGraph.cs create mode 100644 NetPrints/Base/INodeInputPin.cs create mode 100644 NetPrints/Base/INodeOutputPin.cs create mode 100644 NetPrints/Base/INodePin.cs create mode 100644 NetPrints/Base/INodeTypePin.cs create mode 100644 NetPrints/Core/FilteredObservableCollection.cs create mode 100644 NetPrints/Core/INodeInputButtons.cs create mode 100644 NetPrints/Core/INodeOutputButtons.cs create mode 100644 NetPrints/Core/PinConnection.cs create mode 100644 NetPrintsEditor/Converters/PointConverter.cs diff --git a/NetPrints/Base/INode.cs b/NetPrints/Base/INode.cs new file mode 100644 index 0000000..5b98901 --- /dev/null +++ b/NetPrints/Base/INode.cs @@ -0,0 +1,28 @@ +using NetPrints.Core; + +namespace NetPrints.Base +{ + /// + /// Interface for all node types. + /// + public interface INode + { + /// + /// All pins on this node. + /// + public ObservableRangeCollection Pins { get; } + + /// + /// Name of this node. + /// + public string Name { get; set; } + + /// + /// Graph this node is contained in. + /// + public INodeGraph Graph + { + get; + } + } +} diff --git a/NetPrints/Base/INodeDataPin.cs b/NetPrints/Base/INodeDataPin.cs new file mode 100644 index 0000000..547a47a --- /dev/null +++ b/NetPrints/Base/INodeDataPin.cs @@ -0,0 +1,10 @@ +using NetPrints.Core; +using NetPrints.Graph; + +namespace NetPrints.Base +{ + public interface INodeDataPin : INodePin + { + public ObservableValue PinType { get; } + } +} diff --git a/NetPrints/Base/INodeExecutionPin.cs b/NetPrints/Base/INodeExecutionPin.cs new file mode 100644 index 0000000..bd5ce3b --- /dev/null +++ b/NetPrints/Base/INodeExecutionPin.cs @@ -0,0 +1,6 @@ +namespace NetPrints.Base +{ + public interface INodeExecutionPin : INodePin + { + } +} diff --git a/NetPrints/Base/INodeGraph.cs b/NetPrints/Base/INodeGraph.cs new file mode 100644 index 0000000..04e5ad2 --- /dev/null +++ b/NetPrints/Base/INodeGraph.cs @@ -0,0 +1,15 @@ +using NetPrints.Core; + +namespace NetPrints.Base +{ + public interface INodeGraph + { + /// + /// List of nodes in this graph. + /// + public ObservableRangeCollection Nodes + { + get; + } + } +} diff --git a/NetPrints/Base/INodeInputPin.cs b/NetPrints/Base/INodeInputPin.cs new file mode 100644 index 0000000..2e29047 --- /dev/null +++ b/NetPrints/Base/INodeInputPin.cs @@ -0,0 +1,10 @@ +using NetPrints.Core; +using System.Collections.ObjectModel; + +namespace NetPrints.Base +{ + public interface INodeInputPin : INodePin + { + public IObservableCollectionView IncomingPins { get; } + } +} diff --git a/NetPrints/Base/INodeOutputPin.cs b/NetPrints/Base/INodeOutputPin.cs new file mode 100644 index 0000000..04c85da --- /dev/null +++ b/NetPrints/Base/INodeOutputPin.cs @@ -0,0 +1,10 @@ +using NetPrints.Core; +using System.Collections.ObjectModel; + +namespace NetPrints.Base +{ + public interface INodeOutputPin : INodePin + { + public IObservableCollectionView OutgoingPins { get; } + } +} diff --git a/NetPrints/Base/INodePin.cs b/NetPrints/Base/INodePin.cs new file mode 100644 index 0000000..da2a668 --- /dev/null +++ b/NetPrints/Base/INodePin.cs @@ -0,0 +1,29 @@ +using NetPrints.Core; + +namespace NetPrints.Base +{ + public enum NodePinConnectionType + { + Single, + Multiple + } + + /// + /// Interface for node pins. + /// + public interface INodePin + { + /// + /// Name of the pin. + /// + public string Name { get; } + + /// + /// Node this pin is contained in. + /// + public INode Node { get; } + + public NodePinConnectionType ConnectionType { get; } + public ObservableRangeCollection ConnectedPins { get; } + } +} diff --git a/NetPrints/Base/INodeTypePin.cs b/NetPrints/Base/INodeTypePin.cs new file mode 100644 index 0000000..71a5c7c --- /dev/null +++ b/NetPrints/Base/INodeTypePin.cs @@ -0,0 +1,10 @@ +using NetPrints.Core; +using NetPrints.Graph; + +namespace NetPrints.Base +{ + public interface INodeTypePin : INodePin + { + public ObservableValue InferredType { get; } + } +} diff --git a/NetPrints/Core/FilteredObservableCollection.cs b/NetPrints/Core/FilteredObservableCollection.cs new file mode 100644 index 0000000..77f999a --- /dev/null +++ b/NetPrints/Core/FilteredObservableCollection.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Linq; + +namespace NetPrints.Core +{ + public interface IObservableCollectionView : IReadOnlyList, INotifyCollectionChanged + { + public bool Contains(T item); + + public int IndexOf(T item); + } + + public class FilteredObservableCollection : IObservableCollectionView where TFiltered : TOriginal + { + public event NotifyCollectionChangedEventHandler CollectionChanged; + + private readonly ObservableCollection original; + private readonly Func filter; + + private readonly List subset; + + public int Count => subset.Count; + + TFiltered IReadOnlyList.this[int index] => subset[index]; + + public TFiltered this[int index] => subset[index]; + + public FilteredObservableCollection(ObservableCollection original, Func filter) + { + Debug.Assert(original != null, "Original collection was null."); + Debug.Assert(filter != null, "Filter was null."); + + this.original = original; + this.filter = filter; + + original.CollectionChanged += OnOriginalCollectionChanged; + + subset = original.OfType().Where(item => filter(item)).ToList(); + } + + public static FilteredObservableCollection TypeFilter(ObservableCollection original) + { + return new FilteredObservableCollection(original, item => item is TFiltered); + } + + private void OnOriginalCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + subset.Clear(); + CollectionChanged?.Invoke(sender, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + + subset.AddRange(original.OfType().Where(item => filter(item))); + CollectionChanged?.Invoke(sender, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, subset, 0)); + + // TODO: Handle inserts / replace correctly. + + /*if (e.Action != NotifyCollectionChangedAction.Reset) + { + List oldItems = null, newItems = null; + int oldIndex = -1; + + // Remove old items + if (e.OldItems != null) + { + oldIndex = IndexOf(e.OldItems.OfType().Last()); + oldItems = e.OldItems.OfType().Where(item => subset.Remove(item)).ToList(); + } + + // Add new items if filter applies + if (e.NewItems != null) + { + newItems = e.NewItems.OfType().Where(item => filter(item)).ToList(); + + foreach (var newItem in newItems) + { + subset.Add(newItem); + } + } + + // Trigger collection change event if any items were added or removed. + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + if (newItems != null && newItems.Count > 0) + { + CollectionChanged?.Invoke(sender, new NotifyCollectionChangedEventArgs(e.Action, newItems, subset.Count - newItems.Count)); + } + break; + case NotifyCollectionChangedAction.Remove: + if (oldItems != null && oldItems.Count > 0) + { + CollectionChanged?.Invoke(sender, new NotifyCollectionChangedEventArgs(e.Action, oldItems, oldIndex)); + } + break; + case NotifyCollectionChangedAction.Replace: + if ((oldItems != null && oldItems.Count > 0) || (newItems != null && newItems.Count > 0)) + { + CollectionChanged?.Invoke(sender, new NotifyCollectionChangedEventArgs(e.Action, oldItems, newItems)); + } + break; + default: + throw new NotImplementedException(); + } + + } + else + { + if (subset.Count > 0) + { + subset.Clear(); + CollectionChanged?.Invoke(sender, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + }*/ + } + + public IEnumerator GetEnumerator() => subset.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => subset.GetEnumerator(); + + public bool Contains(TFiltered item) => subset.Contains(item); + + public int IndexOf(TFiltered item) => subset.IndexOf(item); + } +} diff --git a/NetPrints/Core/INodeInputButtons.cs b/NetPrints/Core/INodeInputButtons.cs new file mode 100644 index 0000000..4a5acc3 --- /dev/null +++ b/NetPrints/Core/INodeInputButtons.cs @@ -0,0 +1,8 @@ +namespace NetPrints.Core +{ + public interface INodeInputButtons + { + public void InputPlusClicked(); + public void InputMinusClicked(); + } +} diff --git a/NetPrints/Core/INodeOutputButtons.cs b/NetPrints/Core/INodeOutputButtons.cs new file mode 100644 index 0000000..5ba7761 --- /dev/null +++ b/NetPrints/Core/INodeOutputButtons.cs @@ -0,0 +1,8 @@ +namespace NetPrints.Core +{ + public interface INodeOutputButtons + { + public void OutputPlusClicked(); + public void OutputMinusClicked(); + } +} diff --git a/NetPrints/Core/MethodGraph.cs b/NetPrints/Core/MethodGraph.cs index ddff2b1..1b4b161 100644 --- a/NetPrints/Core/MethodGraph.cs +++ b/NetPrints/Core/MethodGraph.cs @@ -1,4 +1,5 @@ -using NetPrints.Graph; +using NetPrints.Base; +using NetPrints.Graph; using System; using System.Collections.Generic; using System.Linq; @@ -128,11 +129,13 @@ private void OnDeserialized(StreamingContext context) foreach (var node in Nodes) { - node.InputTypePins.ToList().ForEach(p => pinTypes.Add(p, p.InferredType?.Value)); + var inputTypePins = node.Pins.OfType().ToList(); - node.OnMethodDeserialized(); + inputTypePins.ForEach(p => pinTypes.Add(p, p.InferredType?.Value)); - if (node.InputTypePins.Any(p => pinTypes[p] != p.InferredType?.Value)) + ((Node)node).OnMethodDeserialized(); + + if (inputTypePins.Any(p => pinTypes[p] != p.InferredType?.Value)) { anyTypeChanged = true; } diff --git a/NetPrints/Core/NetPrintsUtil.cs b/NetPrints/Core/NetPrintsUtil.cs index 96f33f7..5ec4620 100644 --- a/NetPrints/Core/NetPrintsUtil.cs +++ b/NetPrints/Core/NetPrintsUtil.cs @@ -1,10 +1,34 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; namespace NetPrints.Core { public static class NetPrintsUtil { + public static int IndexOf(this IReadOnlyList list, T element) + { + for (int i = 0; i < list.Count; i++) + { + if (list[i].Equals(element)) + { + return i; + } + } + + return -1; + } + + public static FilteredObservableCollection ObservableOfType(this ObservableCollection original) where TFiltered : TOriginal + { + return new FilteredObservableCollection(original, item => item is TFiltered); + } + + public static FilteredObservableCollection ObservableWhere(this ObservableCollection observableCollection, Func predicate) + { + return new FilteredObservableCollection(observableCollection, predicate); + } + /// /// Returns the first name not already contained in a list of names by /// trying to generate a unique name based on the given name. diff --git a/NetPrints/Core/NodeGraph.cs b/NetPrints/Core/NodeGraph.cs index ec198ee..a5a134a 100644 --- a/NetPrints/Core/NodeGraph.cs +++ b/NetPrints/Core/NodeGraph.cs @@ -1,4 +1,8 @@ -using NetPrints.Graph; +using NetPrints.Base; +using NetPrints.Graph; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; using System.Runtime.Serialization; namespace NetPrints.Core @@ -8,17 +12,17 @@ namespace NetPrints.Core [KnownType(typeof(ConstructorGraph))] [KnownType(typeof(ClassGraph))] [KnownType(typeof(TypeGraph))] - public abstract class NodeGraph + public abstract class NodeGraph : INodeGraph { /// /// Collection of nodes in this graph. /// [DataMember] - public ObservableRangeCollection Nodes + public ObservableRangeCollection Nodes { get; private set; - } = new ObservableRangeCollection(); + } = new ObservableRangeCollection(); /// /// Class this graph is contained in. @@ -38,5 +42,113 @@ public Project Project get; set; } + + public ObservableRangeCollection Connections { get; } = new ObservableRangeCollection(); + + public NodeGraph() + { + Nodes.CollectionChanged += OnNodeCollectionChanged; + } + + [OnDeserialized] + private void OnDeserialized() + { + SetupInitialConnections(); + } + + private void SetupInitialConnections() + { + Connections.Clear(); + + Connections.AddRange(Nodes.SelectMany( + node => node.Pins.SelectMany( + pin => pin.ConnectedPins.Select( + connPin =>new PinConnection((NodePin)pin, (NodePin)connPin))))); + } + + private void OnNodeCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.OldItems != null) + { + foreach (var node in e.OldItems.Cast()) + { + SetupNodeEvents(node, false); + } + } + + if (e.NewItems != null) + { + foreach (var node in e.NewItems.Cast()) + { + SetupNodeEvents(node, true); + } + } + } + + private void OnNodePinsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.OldItems != null) + { + foreach (var pin in e.OldItems.Cast()) + { + SetupPinEvents(pin, false); + } + } + + if (e.NewItems != null) + { + foreach (var pin in e.NewItems.Cast()) + { + SetupPinEvents(pin, true); + } + } + } + + private void OnPinConnectionsCollectionChanged(NodePin fromPin, object sender, NotifyCollectionChangedEventArgs e) + { + if (e.OldItems != null) + { + Connections.RemoveRange(Connections.Where(conn => e.OldItems.Contains(conn.PinA) || e.OldItems.Contains(conn.PinB)).ToArray()); + } + + if (e.NewItems != null) + { + Connections.AddRange(e.NewItems.Cast() + .Where(pin => pin.ConnectionType == NodePinConnectionType.Single) + .Select(toPin => new PinConnection(fromPin, toPin))); + } + } + + private void SetupNodeEvents(INode node, bool add) + { + if (!add) + { + node.Pins.CollectionChanged -= OnNodePinsCollectionChanged; + } + + foreach (var pin in node.Pins.Cast()) + { + SetupPinEvents(pin, add); + } + + if (add) + { + node.Pins.CollectionChanged += OnNodePinsCollectionChanged; + } + } + + private void SetupPinEvents(NodePin pin, bool add) + { + if (add) + { + Connections.AddRange(pin.ConnectedPins.Select(toPin => new PinConnection((NodePin)pin, (NodePin)toPin))); + pin.ConnectedPins.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) => OnPinConnectionsCollectionChanged(pin, sender, e); + } + else + { + //pin.ConnectedPins.CollectionChanged -= (object sender, NotifyCollectionChangedEventArgs e) => OnPinConnectionsCollectionChanged(pin, sender, e); + Connections.RemoveRange(Connections.Where(conn => conn.PinA == pin || conn.PinB == pin).ToArray()); + } + } } } diff --git a/NetPrints/Core/PinConnection.cs b/NetPrints/Core/PinConnection.cs new file mode 100644 index 0000000..8e1b03e --- /dev/null +++ b/NetPrints/Core/PinConnection.cs @@ -0,0 +1,42 @@ +using NetPrints.Graph; +using PropertyChanged; +using System.Drawing; + +namespace NetPrints.Base +{ + [AddINotifyPropertyChangedInterface] + public class PinConnection + { + public NodePin PinA { get; } + public NodePin PinB { get; } + + public Point PointA { get; private set; } + public Point PointB { get; private set; } + public Point PointC { get; private set; } + public Point PointD { get; private set; } + + private const double CPOffset = 100; + + public PinConnection(NodePin a, NodePin b) + { + PinA = a; + PinB = b; + + PinA.PositionChanged += (sender, e) => RecalculatePoints(); + PinB.PositionChanged += (sender, e) => RecalculatePoints(); + + RecalculatePoints(); + } + + private void RecalculatePoints() + { + PointA = new Point((int)PinA.PositionX, (int)PinA.PositionY); + PointD = new Point((int)PinB.PositionX, (int)PinB.PositionY); + + int sign = PinA is INodeOutputPin ? 1 : -1; + + PointB = new Point((int)(PinA.PositionX + sign * CPOffset), (int)PinA.PositionY); + PointC = new Point((int)(PinB.PositionX - sign * CPOffset), (int)PinB.PositionY); + } + } +} diff --git a/NetPrints/Core/Variable.cs b/NetPrints/Core/Variable.cs index fa671ad..304cf50 100644 --- a/NetPrints/Core/Variable.cs +++ b/NetPrints/Core/Variable.cs @@ -189,7 +189,7 @@ public Variable(ClassGraph cls, string name, TypeSpecifier type, MethodGraph get NodeOutputTypePin typePin = GraphUtil.CreateNestedTypeNode(TypeGraph, type, 500, 300).OutputTypePins[0]; TypeGraph.ReturnNode.PositionX = 800; TypeGraph.ReturnNode.PositionY = 300; - GraphUtil.ConnectTypePins(typePin, TypeGraph.ReturnNode.TypePin); + GraphUtil.ConnectPins(typePin, TypeGraph.ReturnNode.TypePin); } [OnDeserialized] diff --git a/NetPrints/Graph/AwaitNode.cs b/NetPrints/Graph/AwaitNode.cs index 544856e..534b7e5 100644 --- a/NetPrints/Graph/AwaitNode.cs +++ b/NetPrints/Graph/AwaitNode.cs @@ -63,7 +63,7 @@ private void UpdateResultPin() // to determine if the types are still compatible. foreach (var outgoingPin in ResultPin.OutgoingPins) { - GraphUtil.DisconnectOutputDataPin(ResultPin); + GraphUtil.DisconnectPin(ResultPin); } } else @@ -76,8 +76,8 @@ private void UpdateResultPin() // Remove existing result pin if any if (ResultPin != null) { - GraphUtil.DisconnectOutputDataPin(ResultPin); - OutputDataPins.Remove(ResultPin); + GraphUtil.DisconnectPin(ResultPin); + Pins.Remove(ResultPin); } } } diff --git a/NetPrints/Graph/CallMethodNode.cs b/NetPrints/Graph/CallMethodNode.cs index 140163f..ba510c3 100644 --- a/NetPrints/Graph/CallMethodNode.cs +++ b/NetPrints/Graph/CallMethodNode.cs @@ -126,13 +126,13 @@ public NodeOutputExecPin CatchPin /// public bool HandlesExceptions { - get => !IsPure && OutputExecPins.Any(p => p.Name == CatchPinName) && CatchPin.OutgoingPin != null; + get => !IsPure && OutputExecPins.Any(p => p.Name == CatchPinName) && CatchPin.OutgoingExecPin != null; } /// /// List of node pins, one for each argument the method takes. /// - public IList ArgumentPins + public IReadOnlyList ArgumentPins { get { @@ -218,12 +218,12 @@ private void AddCatchPinChangedEvent() /// private void UpdateExceptionPin() { - if (CatchPin?.OutgoingPin == null && ExceptionPin != null) + if (CatchPin?.OutgoingExecPin == null && ExceptionPin != null) { - GraphUtil.DisconnectOutputDataPin(ExceptionPin); - OutputDataPins.Remove(ExceptionPin); + GraphUtil.DisconnectPin(ExceptionPin); + Pins.Remove(ExceptionPin); } - else if (CatchPin?.OutgoingPin != null && ExceptionPin == null) + else if (CatchPin?.OutgoingExecPin != null && ExceptionPin == null) { AddOutputDataPin(ExceptionPinName, TypeSpecifier.FromType()); } @@ -244,8 +244,8 @@ protected override void SetPurity(bool pure) { // Remove catch pin. Exception pin gets automatically removed because // of its pin changed ev ent. - GraphUtil.DisconnectOutputExecPin(CatchPin); - OutputExecPins.Remove(CatchPin); + GraphUtil.DisconnectPin(CatchPin); + Pins.Remove(CatchPin); } else { diff --git a/NetPrints/Graph/ClassReturnNode.cs b/NetPrints/Graph/ClassReturnNode.cs index e327564..ea1d9c2 100644 --- a/NetPrints/Graph/ClassReturnNode.cs +++ b/NetPrints/Graph/ClassReturnNode.cs @@ -8,7 +8,7 @@ namespace NetPrints.Graph { [DataContract] - public class ClassReturnNode : Node + public class ClassReturnNode : Node, INodeInputButtons { public NodeInputTypePin SuperTypePin { @@ -26,20 +26,24 @@ public ClassReturnNode(ClassGraph graph) AddInputTypePin("BaseType"); } - public void AddInterfacePin() + public void AddArgument() { AddInputTypePin($"Interface{InputTypePins.Count}"); } - public void RemoveInterfacePin() + public void RemoveArgument() { var interfacePin = InterfacePins.LastOrDefault(); if (interfacePin != null) { - GraphUtil.DisconnectInputTypePin(interfacePin); - InputTypePins.Remove(interfacePin); + GraphUtil.DisconnectPin(interfacePin); + Pins.Remove(interfacePin); } } + + public void InputPlusClicked() => AddArgument(); + + public void InputMinusClicked() => RemoveArgument(); } } diff --git a/NetPrints/Graph/ConstructorNode.cs b/NetPrints/Graph/ConstructorNode.cs index 2a4f148..8f4ad98 100644 --- a/NetPrints/Graph/ConstructorNode.cs +++ b/NetPrints/Graph/ConstructorNode.cs @@ -46,7 +46,7 @@ public IReadOnlyList ArgumentTypes /// /// List of node pins, one for each argument the constructor takes. /// - public IList ArgumentPins + public IReadOnlyList ArgumentPins { get { return InputDataPins; } } diff --git a/NetPrints/Graph/ExecNode.cs b/NetPrints/Graph/ExecNode.cs index 5be1d97..1906d45 100644 --- a/NetPrints/Graph/ExecNode.cs +++ b/NetPrints/Graph/ExecNode.cs @@ -1,4 +1,5 @@ using NetPrints.Core; +using System.Linq; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -29,11 +30,11 @@ protected override void SetPurity(bool pure) if (pure) { - GraphUtil.DisconnectInputExecPin(InputExecPins[0]); - InputExecPins.RemoveAt(0); - - GraphUtil.DisconnectOutputExecPin(OutputExecPins[0]); - OutputExecPins.RemoveAt(0); + GraphUtil.DisconnectPin(InputExecPins[0]); + Pins.Remove(Pins.First(pin => pin is NodeInputExecPin)); + + GraphUtil.DisconnectPin(OutputExecPins[0]); + Pins.Remove(Pins.First(pin => pin is NodeOutputExecPin)); } else if (!pure) { diff --git a/NetPrints/Graph/ExplicitCastNode.cs b/NetPrints/Graph/ExplicitCastNode.cs index d31fbf7..0638839 100644 --- a/NetPrints/Graph/ExplicitCastNode.cs +++ b/NetPrints/Graph/ExplicitCastNode.cs @@ -94,13 +94,13 @@ protected override void SetPurity(bool pure) foreach (var execPin in outExecPins) { - GraphUtil.DisconnectOutputExecPin(execPin); - OutputExecPins.Remove(execPin); + GraphUtil.DisconnectPin(execPin); + Pins.Remove(execPin); } var inExecPin = InputExecPins.Single(p => p.Name == "Exec"); - GraphUtil.DisconnectInputExecPin(inExecPin); - InputExecPins.Remove(inExecPin); + GraphUtil.DisconnectPin(inExecPin); + Pins.Remove(inExecPin); } else { diff --git a/NetPrints/Graph/GraphUtil.cs b/NetPrints/Graph/GraphUtil.cs index e85b97d..28806ed 100644 --- a/NetPrints/Graph/GraphUtil.cs +++ b/NetPrints/Graph/GraphUtil.cs @@ -1,4 +1,5 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using System; using System.Collections.Generic; using System.Linq; @@ -26,7 +27,7 @@ public static string SplitCamelCase(string input) /// Function for determining whether one type is the subclass of another type. /// Whether we want pinB to be the first pin and vice versa. /// - public static bool CanConnectNodePins(NodePin pinA, NodePin pinB, Func isSubclassOf, Func hasImplicitCast, bool swapped=false) + public static bool CanConnectNodePins(INodePin pinA, INodePin pinB, Func isSubclassOf, Func hasImplicitCast, bool swapped=false) { if (pinA is NodeInputExecPin && pinB is NodeOutputExecPin) { @@ -85,203 +86,68 @@ public static bool CanConnectNodePins(NodePin pinA, NodePin pinB, Func - /// Connects two node pins together. Makes sure any previous connections will be disconnected. - /// If the pin types are not compatible an ArgumentException will be thrown. - /// - /// First pin. - /// Second pin. - /// Whether we want pinB to be the first pin and vice versa. - public static void ConnectNodePins(NodePin pinA, NodePin pinB, bool swapped=false) + public static void ConnectPins(INodePin pinA, INodePin pinB) { - if (pinA is NodeInputExecPin exA && pinB is NodeOutputExecPin exB) - { - ConnectExecPins(exB, exA); - } - else if (pinA is NodeInputDataPin datA && pinB is NodeOutputDataPin datB) - { - ConnectDataPins(datB, datA); - } - else if (pinA is NodeInputTypePin typA && pinB is NodeOutputTypePin typB) - { - ConnectTypePins(typB, typA); - } - else if (!swapped) + // Connect from -> to + if (pinA.ConnectionType == NodePinConnectionType.Single) { - ConnectNodePins(pinB, pinA, true); + // Remove other pins' connection to this pin + if (pinA.ConnectedPins.Count > 0) + { + foreach (var otherPin in pinA.ConnectedPins) + { + otherPin.ConnectedPins.Clear(); + } + } + + pinA.ConnectedPins.Replace(pinB); } else { - throw new ArgumentException("The passed pins can not be connected because their types are incompatible."); + pinA.ConnectedPins.Add(pinB); } - } - /// - /// Connects two node execution pins. Removes any previous connection. - /// - /// Output execution pin to connect. - /// Input execution pin to connect. - public static void ConnectExecPins(NodeOutputExecPin fromPin, NodeInputExecPin toPin) - { - // Remove from old pin if any - if (fromPin.OutgoingPin != null) + // Connect to -> from + if (pinB.ConnectionType == NodePinConnectionType.Single) { - fromPin.OutgoingPin.IncomingPins.Remove(fromPin); - } - - fromPin.OutgoingPin = toPin; - toPin.IncomingPins.Add(fromPin); - } + // Remove other pins' connection to this pin + if (pinB.ConnectedPins.Count > 0) + { + foreach (var otherPin in pinB.ConnectedPins) + { + otherPin.ConnectedPins.Clear(); + } + } - /// - /// Connects two node data pins. Removes any previous connection. - /// - /// Output data pin to connect. - /// Input data pin to connect. - public static void ConnectDataPins(NodeOutputDataPin fromPin, NodeInputDataPin toPin) - { - // Remove from old pin if any - if (toPin.IncomingPin != null) + pinB.ConnectedPins.Replace(pinA); + } + else { - toPin.IncomingPin.OutgoingPins.Remove(toPin); + pinB.ConnectedPins.Add(pinA); } - - fromPin.OutgoingPins.Add(toPin); - toPin.IncomingPin = fromPin; } - /// - /// Connects two node type pins. Removes any previous connection. - /// - /// Output type pin to connect. - /// Input type pin to connect. - public static void ConnectTypePins(NodeOutputTypePin fromPin, NodeInputTypePin toPin) + public static void DisconnectPin(INodePin nodePin) { - // Remove from old pin if any - if (toPin.IncomingPin != null) - { - toPin.IncomingPin.OutgoingPins.Remove(toPin); - } - - fromPin.OutgoingPins.Add(toPin); - toPin.IncomingPin = fromPin; + nodePin.ConnectedPins.Clear(); } /// /// Disconnects all pins of a node. /// /// Node to have all its pins disconnected. - public static void DisconnectNodePins(Node node) + public static void DisconnectNodePins(INode node) { - foreach (NodeInputDataPin pin in node.InputDataPins) - { - DisconnectInputDataPin(pin); - } - - foreach (NodeOutputDataPin pin in node.OutputDataPins) - { - DisconnectOutputDataPin(pin); - } - - foreach (NodeInputExecPin pin in node.InputExecPins) - { - DisconnectInputExecPin(pin); - } - - foreach (NodeOutputExecPin pin in node.OutputExecPins) + foreach (var pin in node.Pins) { - DisconnectOutputExecPin(pin); - } - - foreach (NodeInputTypePin pin in node.InputTypePins) - { - DisconnectInputTypePin(pin); - } - - foreach (NodeOutputTypePin pin in node.OutputTypePins) - { - DisconnectOutputTypePin(pin); - } - } - - public static void DisconnectPin(NodePin nodePin) - { - if (nodePin is NodeInputDataPin idp) - { - DisconnectInputDataPin(idp); - } - else if (nodePin is NodeOutputDataPin odp) - { - DisconnectOutputDataPin(odp); - } - else if (nodePin is NodeInputExecPin ixp) - { - DisconnectInputExecPin(ixp); - } - else if (nodePin is NodeOutputExecPin oxp) - { - DisconnectOutputExecPin(oxp); - } - else if (nodePin is NodeInputTypePin itp) - { - DisconnectInputTypePin(itp); - } - else if (nodePin is NodeOutputTypePin otp) - { - DisconnectOutputTypePin(otp); - } - else - { - throw new NotImplementedException("Unknown pin type to disconnect."); + DisconnectPin(pin); } } - public static void DisconnectInputDataPin(NodeInputDataPin pin) + public static void DisconnectPins(INodePin a, INodePin b) { - pin.IncomingPin?.OutgoingPins.Remove(pin); - pin.IncomingPin = null; - } - - public static void DisconnectOutputDataPin(NodeOutputDataPin pin) - { - foreach(NodeInputDataPin outgoingPin in pin.OutgoingPins) - { - outgoingPin.IncomingPin = null; - } - - pin.OutgoingPins.Clear(); - } - - public static void DisconnectInputTypePin(NodeInputTypePin pin) - { - pin.IncomingPin?.OutgoingPins.Remove(pin); - pin.IncomingPin = null; - } - - public static void DisconnectOutputTypePin(NodeOutputTypePin pin) - { - foreach (NodeInputTypePin outgoingPin in pin.OutgoingPins) - { - outgoingPin.IncomingPin = null; - } - - pin.OutgoingPins.Clear(); - } - - public static void DisconnectOutputExecPin(NodeOutputExecPin pin) - { - pin.OutgoingPin?.IncomingPins.Remove(pin); - pin.OutgoingPin = null; - } - - public static void DisconnectInputExecPin(NodeInputExecPin pin) - { - foreach (NodeOutputExecPin incomingPin in pin.IncomingPins) - { - incomingPin.OutgoingPin = null; - } - - pin.IncomingPins.Clear(); + a.ConnectedPins.Remove(b); + b.ConnectedPins.Remove(a); } /// @@ -301,8 +167,8 @@ public static RerouteNode AddRerouteNode(NodeInputDataPin pin) new Tuple(pin.PinType, pin.IncomingPin.PinType) }); - GraphUtil.ConnectDataPins(pin.IncomingPin, rerouteNode.InputDataPins[0]); - GraphUtil.ConnectDataPins(rerouteNode.OutputDataPins[0], pin); + GraphUtil.ConnectPins(pin.IncomingPin, rerouteNode.InputDataPins[0]); + GraphUtil.ConnectPins(rerouteNode.OutputDataPins[0], pin); return rerouteNode; } @@ -314,15 +180,15 @@ public static RerouteNode AddRerouteNode(NodeInputDataPin pin) /// Reroute node created for the execution pin. public static RerouteNode AddRerouteNode(NodeOutputExecPin pin) { - if (pin?.OutgoingPin == null) + if (pin?.OutgoingExecPin == null) { throw new ArgumentException("Pin or its connected pin were null"); } var rerouteNode = RerouteNode.MakeExecution(pin.Node.Graph, 1); - GraphUtil.ConnectExecPins(rerouteNode.OutputExecPins[0], pin.OutgoingPin); - GraphUtil.ConnectExecPins(pin, rerouteNode.InputExecPins[0]); + GraphUtil.ConnectPins(rerouteNode.OutputExecPins[0], pin.OutgoingExecPin); + GraphUtil.ConnectPins(pin, rerouteNode.InputExecPins[0]); return rerouteNode; } @@ -341,8 +207,8 @@ public static RerouteNode AddRerouteNode(NodeInputTypePin pin) var rerouteNode = RerouteNode.MakeType(pin.Node.Graph, 1); - GraphUtil.ConnectTypePins(pin.IncomingPin, rerouteNode.InputTypePins[0]); - GraphUtil.ConnectTypePins(rerouteNode.OutputTypePins[0], pin); + GraphUtil.ConnectPins(pin.IncomingPin, rerouteNode.InputTypePins[0]); + GraphUtil.ConnectPins(rerouteNode.OutputTypePins[0], pin); return rerouteNode; } @@ -376,7 +242,7 @@ public static TypeNode CreateNestedTypeNode(NodeGraph graph, BaseType type, doub foreach (TypeNode genericArgNode in genericArgNodes) { - GraphUtil.ConnectTypePins(genericArgNode.OutputTypePins[0], typeNode.InputTypePins[0]); + GraphUtil.ConnectPins(genericArgNode.OutputTypePins[0], typeNode.InputTypePins[0]); } } @@ -420,7 +286,7 @@ public static MethodGraph AddOverrideMethod(ClassGraph cls, MethodSpecifier meth newMethod.ReturnNodes.First().PositionY = newMethod.EntryNode.PositionY; // Connect entry and return node execution pins - GraphUtil.ConnectExecPins(newMethod.EntryNode.InitialExecutionPin, newMethod.MainReturnNode.ReturnPin); + GraphUtil.ConnectPins(newMethod.EntryNode.InitialExecutionPin, newMethod.MainReturnNode.ReturnPin); // Add generic arguments for (var i = 0; i < methodSpecifier.GenericArguments.Count; i++) @@ -439,7 +305,7 @@ public static MethodGraph AddOverrideMethod(ClassGraph cls, MethodSpecifier meth newMethod.MethodEntryNode.AddArgument(); - ConnectTypePins(argTypeNode.OutputTypePins[0], newMethod.EntryNode.InputTypePins[i]); + ConnectPins(argTypeNode.OutputTypePins[0], newMethod.EntryNode.InputTypePins[i]); } // Add return types, their type nodes and connect them @@ -450,7 +316,7 @@ public static MethodGraph AddOverrideMethod(ClassGraph cls, MethodSpecifier meth newMethod.MainReturnNode.AddReturnType(); - ConnectTypePins(returnTypeNode.OutputTypePins[0], newMethod.MainReturnNode.InputTypePins[i]); + ConnectPins(returnTypeNode.OutputTypePins[0], newMethod.MainReturnNode.InputTypePins[i]); } cls.Methods.Add(newMethod); @@ -468,11 +334,11 @@ public static void ConnectRelevantPins(NodePin pin, Node node, Func 0 && node.OutputExecPins.Count > 0) { - var oldConnected = pin.Node.InputExecPins[0].IncomingPins.FirstOrDefault(); + var oldConnected = pin.Node.InputExecPins[0].IncomingExecutionPins.FirstOrDefault(); if (oldConnected != null) { - GraphUtil.DisconnectOutputExecPin(oldConnected); + GraphUtil.DisconnectPin(oldConnected); } - GraphUtil.ConnectExecPins(node.OutputExecPins[0], pin.Node.InputExecPins[0]); + GraphUtil.ConnectPins(node.OutputExecPins[0], pin.Node.InputExecPins[0]); if (oldConnected != null && node.InputExecPins.Count > 0) { - GraphUtil.ConnectExecPins(oldConnected, node.InputExecPins[0]); + GraphUtil.ConnectPins(oldConnected, node.InputExecPins[0]); } } @@ -511,19 +377,19 @@ public static void ConnectRelevantPins(NodePin pin, Node node, Func 0 && pin.Node.OutputExecPins.Count > 0) { - var oldConnected = pin.Node.OutputExecPins[0].OutgoingPin; + var oldConnected = pin.Node.OutputExecPins[0].OutgoingExecPin; - GraphUtil.ConnectExecPins(pin.Node.OutputExecPins[0], node.InputExecPins[0]); + GraphUtil.ConnectPins(pin.Node.OutputExecPins[0], node.InputExecPins[0]); if (oldConnected != null && node.OutputExecPins.Count > 0) { - GraphUtil.ConnectExecPins(node.OutputExecPins[0], oldConnected); + GraphUtil.ConnectPins(node.OutputExecPins[0], oldConnected); } } @@ -535,14 +401,14 @@ public static void ConnectRelevantPins(NodePin pin, Node node, Func 0) { - GraphUtil.ConnectTypePins(node.OutputTypePins[0], itp); + GraphUtil.ConnectPins(node.OutputTypePins[0], itp); } } else if (pin is NodeOutputTypePin otp) { if (node.InputTypePins.Count > 0) { - GraphUtil.ConnectTypePins(otp, node.InputTypePins[0]); + GraphUtil.ConnectPins(otp, node.InputTypePins[0]); } } } diff --git a/NetPrints/Graph/LiteralNode.cs b/NetPrints/Graph/LiteralNode.cs index 6b7c8ec..a00db90 100644 --- a/NetPrints/Graph/LiteralNode.cs +++ b/NetPrints/Graph/LiteralNode.cs @@ -68,13 +68,13 @@ private void UpdatePinTypes() if (constructedType != InputValuePin.PinType.Value) { - GraphUtil.DisconnectInputDataPin(InputValuePin); + GraphUtil.DisconnectPin(InputValuePin); InputValuePin.PinType.Value = constructedType; } if (constructedType != ValuePin.PinType.Value) { - GraphUtil.DisconnectOutputDataPin(ValuePin); + GraphUtil.DisconnectPin(ValuePin); ValuePin.PinType.Value = constructedType; } } diff --git a/NetPrints/Graph/MakeArrayNode.cs b/NetPrints/Graph/MakeArrayNode.cs index 703b167..b2a2842 100644 --- a/NetPrints/Graph/MakeArrayNode.cs +++ b/NetPrints/Graph/MakeArrayNode.cs @@ -1,5 +1,7 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using System; +using System.Linq; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -8,7 +10,7 @@ namespace NetPrints.Graph /// Node representing the creation of an array. /// [DataContract] - public class MakeArrayNode : Node + public class MakeArrayNode : Node, INodeInputButtons { /// /// Whether this node is in predefined-size @@ -108,8 +110,8 @@ private void UpdateInputDataPins() else { // Remove size pin - GraphUtil.DisconnectInputDataPin(InputDataPins[0]); - InputDataPins.RemoveAt(0); + GraphUtil.DisconnectPin(InputDataPins[0]); + Pins.Remove(Pins.First(p => p is NodeDataPin)); } } @@ -127,34 +129,31 @@ protected override void OnInputTypeChanged(object sender, EventArgs eventArgs) /// /// Adds an input data pin for an array element. /// - public void AddElementPin() - { - AddInputDataPin($"Element{InputDataPins.Count}", ElementType); - } - + public void AddElementPin() => AddInputDataPin($"Element{InputDataPins.Count}", ElementType); + /// /// Removes the last input data pin for an array element. /// Returns whether one was actually removed. /// /// Whether a pin was removed. - public bool RemoveElementPin() + public void RemoveElementPin() { if (InputDataPins.Count > 0) { // TODO: Add method for removing pins on Node NodeInputDataPin inputDataPin = InputDataPins[InputDataPins.Count - 1]; - GraphUtil.DisconnectInputDataPin(inputDataPin); - InputDataPins.Remove(inputDataPin); - - return true; + GraphUtil.DisconnectPin(inputDataPin); + Pins.Remove(inputDataPin); } - - return false; } public override string ToString() { return $"Make {ElementType.Name} Array"; } + + public void InputPlusClicked() => AddElementPin(); + + public void InputMinusClicked() => RemoveElementPin(); } } diff --git a/NetPrints/Graph/MethodEntryNode.cs b/NetPrints/Graph/MethodEntryNode.cs index a394c01..7dcf9eb 100644 --- a/NetPrints/Graph/MethodEntryNode.cs +++ b/NetPrints/Graph/MethodEntryNode.cs @@ -9,7 +9,7 @@ namespace NetPrints.Graph /// Node representing the initial execution node of a method. /// [DataContract] - public class MethodEntryNode : ExecutionEntryNode + public class MethodEntryNode : ExecutionEntryNode, INodeInputButtons, INodeOutputButtons { public MethodEntryNode(MethodGraph graph) : base(graph) @@ -46,11 +46,11 @@ public void RemoveArgument() NodeOutputDataPin odpToRemove = OutputDataPins.Last(); NodeInputTypePin itpToRemove = InputTypePins.Last(); - GraphUtil.DisconnectOutputDataPin(odpToRemove); - GraphUtil.DisconnectInputTypePin(itpToRemove); + GraphUtil.DisconnectPin(odpToRemove); + GraphUtil.DisconnectPin(itpToRemove); - OutputDataPins.Remove(odpToRemove); - InputTypePins.Remove(itpToRemove); + Pins.Remove(odpToRemove); + Pins.Remove(itpToRemove); } } @@ -66,10 +66,18 @@ public void RemoveGenericArgument() { NodeOutputTypePin otpToRemove = OutputTypePins.Last(); - GraphUtil.DisconnectOutputTypePin(otpToRemove); + GraphUtil.DisconnectPin(otpToRemove); - OutputTypePins.Remove(otpToRemove); + Pins.Remove(otpToRemove); } } + + public void InputPlusClicked() => AddArgument(); + + public void InputMinusClicked() => RemoveArgument(); + + public void OutputPlusClicked() => AddGenericArgument(); + + public void OutputMinusClicked() => RemoveGenericArgument(); } } diff --git a/NetPrints/Graph/Node.cs b/NetPrints/Graph/Node.cs index 559b6e5..c202a48 100644 --- a/NetPrints/Graph/Node.cs +++ b/NetPrints/Graph/Node.cs @@ -1,6 +1,9 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using PropertyChanged; using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Runtime.Serialization; @@ -35,43 +38,41 @@ namespace NetPrints.Graph [KnownType(typeof(TypeReturnNode))] [KnownType(typeof(DefaultNode))] [AddINotifyPropertyChangedInterface] - public abstract class Node + public abstract class Node : INode { + [DataMember] + public ObservableRangeCollection Pins { get; private set; } = new ObservableRangeCollection(); + /// /// Input data pins of this node. /// - [DataMember] - public ObservableRangeCollection InputDataPins { get; private set; } = new ObservableRangeCollection(); + + public IObservableCollectionView InputDataPins { get; } /// /// Output data pins of this node. /// - [DataMember] - public ObservableRangeCollection OutputDataPins { get; private set; } = new ObservableRangeCollection(); + public IObservableCollectionView OutputDataPins { get; } /// /// Input execution pins of this node. /// - [DataMember] - public ObservableRangeCollection InputExecPins { get; private set; } = new ObservableRangeCollection(); + public IObservableCollectionView InputExecPins { get; } /// /// Output execution pins of this node. /// - [DataMember] - public ObservableRangeCollection OutputExecPins { get; private set; } = new ObservableRangeCollection(); + public IObservableCollectionView OutputExecPins { get; } /// /// Input type pins of this node. /// - [DataMember] - public ObservableRangeCollection InputTypePins { get; private set; } = new ObservableRangeCollection(); + public IObservableCollectionView InputTypePins { get; } /// /// Output type pins of this node. /// - [DataMember] - public ObservableRangeCollection OutputTypePins { get; private set; } = new ObservableRangeCollection(); + public IObservableCollectionView OutputTypePins { get; } /// /// Delegate for the event of a position change of a node. @@ -180,12 +181,23 @@ public NodeGraph Graph private set; } + INodeGraph INode.Graph => Graph; + protected Node(NodeGraph graph) { + static bool isType(object x) => x is T; + + InputDataPins = new FilteredObservableCollection(Pins, isType); + OutputDataPins = new FilteredObservableCollection(Pins, isType); + InputExecPins = new FilteredObservableCollection(Pins, isType); + OutputExecPins = new FilteredObservableCollection(Pins, isType); + InputTypePins = new FilteredObservableCollection(Pins, isType); + OutputTypePins = new FilteredObservableCollection(Pins, isType); + + Name = NetPrintsUtil.GetUniqueName(GetType().Name, graph.Nodes.Select(n => n.Name).ToList()); + Graph = graph; Graph.Nodes.Add(this); - - Name = NetPrintsUtil.GetUniqueName(GetType().Name, Graph.Nodes.Select(n => n.Name).ToList()); } public override string ToString() @@ -200,7 +212,7 @@ public override string ToString() /// Specifier for the type of this pin. protected void AddInputDataPin(string pinName, ObservableValue pinType) { - InputDataPins.Add(new NodeInputDataPin(this, pinName, pinType)); + Pins.Add(new NodeInputDataPin(this, pinName, pinType)); } /// @@ -210,7 +222,7 @@ protected void AddInputDataPin(string pinName, ObservableValue pinType /// Specifier for the type of this pin. protected void AddOutputDataPin(string pinName, ObservableValue pinType) { - OutputDataPins.Add(new NodeOutputDataPin(this, pinName, pinType)); + Pins.Add(new NodeOutputDataPin(this, pinName, pinType)); } /// @@ -219,7 +231,7 @@ protected void AddOutputDataPin(string pinName, ObservableValue pinTyp /// Name of the pin. protected void AddInputExecPin(string pinName) { - InputExecPins.Add(new NodeInputExecPin(this, pinName)); + Pins.Add(new NodeInputExecPin(this, pinName)); } /// @@ -228,7 +240,7 @@ protected void AddInputExecPin(string pinName) /// Name of the pin. protected void AddOutputExecPin(string pinName) { - OutputExecPins.Add(new NodeOutputExecPin(this, pinName)); + Pins.Add(new NodeOutputExecPin(this, pinName)); } /// @@ -239,7 +251,7 @@ protected void AddInputTypePin(string pinName) { var typePin = new NodeInputTypePin(this, pinName); typePin.IncomingPinChanged += OnIncomingTypePinChanged; - InputTypePins.Add(typePin); + Pins.Add(typePin); } /// @@ -249,7 +261,7 @@ protected void AddInputTypePin(string pinName) /// Function that generates the output type. protected void AddOutputTypePin(string pinName, ObservableValue outputType) { - OutputTypePins.Add(new NodeOutputTypePin(this, pinName, outputType)); + Pins.Add(new NodeOutputTypePin(this, pinName, outputType)); } private void OnIncomingTypePinChanged(NodeInputTypePin pin, NodeOutputTypePin oldPin, NodeOutputTypePin newPin) diff --git a/NetPrints/Graph/NodeDataPin.cs b/NetPrints/Graph/NodeDataPin.cs index 4e1d80a..9369093 100644 --- a/NetPrints/Graph/NodeDataPin.cs +++ b/NetPrints/Graph/NodeDataPin.cs @@ -1,4 +1,5 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -7,7 +8,7 @@ namespace NetPrints.Graph /// Abstract class for data pins. /// [DataContract] - public abstract class NodeDataPin : NodePin + public abstract class NodeDataPin : NodePin, INodeDataPin { /// /// Specifier for the type of this data pin. diff --git a/NetPrints/Graph/NodeExecPin.cs b/NetPrints/Graph/NodeExecPin.cs index 93a4c4f..6dcefd2 100644 --- a/NetPrints/Graph/NodeExecPin.cs +++ b/NetPrints/Graph/NodeExecPin.cs @@ -1,4 +1,5 @@ -using System.Runtime.Serialization; +using NetPrints.Base; +using System.Runtime.Serialization; namespace NetPrints.Graph { @@ -6,7 +7,7 @@ namespace NetPrints.Graph /// Abstract class for execution pins. /// [DataContract] - public abstract class NodeExecPin : NodePin + public abstract class NodeExecPin : NodePin, INodeExecutionPin { protected NodeExecPin(Node node, string name) : base(node, name) diff --git a/NetPrints/Graph/NodeInputDataPin.cs b/NetPrints/Graph/NodeInputDataPin.cs index 978df79..6687363 100644 --- a/NetPrints/Graph/NodeInputDataPin.cs +++ b/NetPrints/Graph/NodeInputDataPin.cs @@ -1,5 +1,7 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using System; +using System.Collections.ObjectModel; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -11,7 +13,7 @@ public delegate void InputDataPinIncomingPinChangedDelegate( /// Input data pin which can be connected to up to one output data pin to receive a value. /// [DataContract] - public class NodeInputDataPin : NodeDataPin + public class NodeInputDataPin : NodeDataPin, INodeInputPin { /// /// Called when the node's incoming pin changed. @@ -22,22 +24,7 @@ public class NodeInputDataPin : NodeDataPin /// Incoming data pin for this pin. Null when not connected. /// Can trigger IncomingPinChanged when set. /// - [DataMember] - public NodeOutputDataPin IncomingPin - { - get => incomingPin; - set - { - if (incomingPin != value) - { - var oldPin = incomingPin; - - incomingPin = value; - - IncomingPinChanged?.Invoke(this, oldPin, incomingPin); - } - } - } + public NodeOutputDataPin IncomingPin => IncomingPins.Count > 0 ? (NodeOutputDataPin)IncomingPins[0] : null; /// /// Whether this pin uses its unconnected value to output a value @@ -48,8 +35,6 @@ public bool UsesUnconnectedValue get => PinType.Value is TypeSpecifier t && t.IsPrimitive; } - private NodeOutputDataPin incomingPin; - /// /// Unconnected value of this pin when no pin is connected to it. /// Setting this for types that don't support unconnected values will throw @@ -91,8 +76,12 @@ public bool UsesExplicitDefaultValue { get; set; - } - + } + + public IObservableCollectionView IncomingPins => ConnectedPins.ObservableOfType(); + + public override NodePinConnectionType ConnectionType => NodePinConnectionType.Single; + public NodeInputDataPin(Node node, string name, ObservableValue pinType) : base(node, name, pinType) { diff --git a/NetPrints/Graph/NodeInputExecPin.cs b/NetPrints/Graph/NodeInputExecPin.cs index e2c32cf..fc65c47 100644 --- a/NetPrints/Graph/NodeInputExecPin.cs +++ b/NetPrints/Graph/NodeInputExecPin.cs @@ -1,4 +1,6 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; +using System.Collections.ObjectModel; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -7,15 +9,17 @@ namespace NetPrints.Graph /// Pin that can be connected to output execution pins to receive execution. /// [DataContract] - public class NodeInputExecPin : NodeExecPin - { + public class NodeInputExecPin : NodeExecPin, INodeInputPin + { /// /// Output execution pins connected to this pin. - /// - [DataMember] - public ObservableRangeCollection IncomingPins { get; private set; } = - new ObservableRangeCollection(); - + /// + public IObservableCollectionView IncomingExecutionPins => ConnectedPins.ObservableOfType(); + + public IObservableCollectionView IncomingPins => ConnectedPins.ObservableOfType(); + + public override NodePinConnectionType ConnectionType => NodePinConnectionType.Multiple; + public NodeInputExecPin(Node node, string name) : base(node, name) { diff --git a/NetPrints/Graph/NodeInputTypePin.cs b/NetPrints/Graph/NodeInputTypePin.cs index dc5a670..2eb89fc 100644 --- a/NetPrints/Graph/NodeInputTypePin.cs +++ b/NetPrints/Graph/NodeInputTypePin.cs @@ -1,4 +1,5 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -10,41 +11,28 @@ public delegate void InputTypePinIncomingPinChangedDelegate( /// Pin which can receive types. /// [DataContract] - public class NodeInputTypePin : NodeTypePin + public class NodeInputTypePin : NodeTypePin, INodeInputPin { /// /// Called when the node's incoming pin changed. /// - public event InputTypePinIncomingPinChangedDelegate IncomingPinChanged; - + public event InputTypePinIncomingPinChangedDelegate IncomingPinChanged; + /// /// Incoming type pin for this pin. Null when not connected. /// Can trigger IncomingPinChanged when set. - /// - [DataMember] - public NodeOutputTypePin IncomingPin - { - get => incomingPin; - set - { - if (incomingPin != value) - { - var oldPin = incomingPin; - - incomingPin = value; - - IncomingPinChanged?.Invoke(this, oldPin, incomingPin); - } - } - } + /// + public NodeOutputTypePin IncomingPin => IncomingPins.Count > 0 ? (NodeOutputTypePin)IncomingPins[0] : null; public override ObservableValue InferredType { get => IncomingPin?.InferredType; } - private NodeOutputTypePin incomingPin; - + public IObservableCollectionView IncomingPins => ConnectedPins.ObservableOfType(); + + public override NodePinConnectionType ConnectionType => NodePinConnectionType.Single; + public NodeInputTypePin(Node node, string name) : base(node, name) { diff --git a/NetPrints/Graph/NodeOutputDataPin.cs b/NetPrints/Graph/NodeOutputDataPin.cs index a1341f9..9209d15 100644 --- a/NetPrints/Graph/NodeOutputDataPin.cs +++ b/NetPrints/Graph/NodeOutputDataPin.cs @@ -1,4 +1,5 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -7,15 +8,17 @@ namespace NetPrints.Graph /// Pin which outputs a value. Can be connected to input data pins. /// [DataContract] - public class NodeOutputDataPin : NodeDataPin + public class NodeOutputDataPin : NodeDataPin, INodeOutputPin { /// /// Connected input data pins. /// - [DataMember] - public ObservableRangeCollection OutgoingPins { get; private set; } - = new ObservableRangeCollection(); - + public IObservableCollectionView OutgoingDataPins => ConnectedPins.ObservableOfType(); + + public IObservableCollectionView OutgoingPins => ConnectedPins.ObservableOfType(); + + public override NodePinConnectionType ConnectionType => NodePinConnectionType.Multiple; + public NodeOutputDataPin(Node node, string name, ObservableValue pinType) : base(node, name, pinType) { diff --git a/NetPrints/Graph/NodeOutputExecPin.cs b/NetPrints/Graph/NodeOutputExecPin.cs index 2776284..2e0d3ec 100644 --- a/NetPrints/Graph/NodeOutputExecPin.cs +++ b/NetPrints/Graph/NodeOutputExecPin.cs @@ -1,4 +1,6 @@ -using System.Runtime.Serialization; +using NetPrints.Base; +using NetPrints.Core; +using System.Runtime.Serialization; namespace NetPrints.Graph { @@ -9,7 +11,7 @@ public delegate void OutputExecPinOutgoingPinChangedDelegate( /// Pin which can be connected to an input execution pin to pass along execution. /// [DataContract] - public class NodeOutputExecPin : NodeExecPin + public class NodeOutputExecPin : NodeExecPin, INodeOutputPin { /// /// Called when the connected outgoing pin changed. @@ -20,25 +22,13 @@ public class NodeOutputExecPin : NodeExecPin /// Connected input execution pin. Null if not connected. /// Can trigger OutgoingPinChanged when set. /// - [DataMember] - public NodeInputExecPin OutgoingPin - { - get => outgoingPin; - set - { - if (outgoingPin != value) - { - var oldPin = outgoingPin; - - outgoingPin = value; - - OutgoingPinChanged?.Invoke(this, oldPin, outgoingPin); - } - } - } - - private NodeInputExecPin outgoingPin; - + + public NodeInputExecPin OutgoingExecPin => OutgoingPins.Count > 0 ? (NodeInputExecPin)OutgoingPins[0] : null; + + public IObservableCollectionView OutgoingPins => ConnectedPins.ObservableOfType(); + + public override NodePinConnectionType ConnectionType => NodePinConnectionType.Single; + public NodeOutputExecPin(Node node, string name) : base(node, name) { diff --git a/NetPrints/Graph/NodeOutputTypePin.cs b/NetPrints/Graph/NodeOutputTypePin.cs index ad4085a..5f7f63b 100644 --- a/NetPrints/Graph/NodeOutputTypePin.cs +++ b/NetPrints/Graph/NodeOutputTypePin.cs @@ -1,4 +1,6 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; +using System.Linq; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -7,32 +9,29 @@ namespace NetPrints.Graph /// Pin which outputs a type. Can be connected to input type pins. /// [DataContract] - public class NodeOutputTypePin : NodeTypePin + public class NodeOutputTypePin : NodeTypePin, INodeOutputPin { /// /// Connected input data pins. /// - [DataMember] - public ObservableRangeCollection OutgoingPins { get; private set; } - = new ObservableRangeCollection(); - - public override ObservableValue InferredType - { - get => outputType; - } + public IObservableCollectionView OutgoingTypePins => ConnectedPins.ObservableOfType(); [DataMember] - private ObservableValue outputType; + public override ObservableValue InferredType { get; } + + public IObservableCollectionView OutgoingPins => ConnectedPins.ObservableOfType(); + + public override NodePinConnectionType ConnectionType => NodePinConnectionType.Multiple; public NodeOutputTypePin(Node node, string name, ObservableValue outputType) : base(node, name) { - this.outputType = outputType; + InferredType = outputType; } public override string ToString() { - return outputType.Value?.ShortName ?? "None"; + return InferredType.Value?.ShortName ?? "None"; } } } diff --git a/NetPrints/Graph/NodePin.cs b/NetPrints/Graph/NodePin.cs index fee7345..9495234 100644 --- a/NetPrints/Graph/NodePin.cs +++ b/NetPrints/Graph/NodePin.cs @@ -1,4 +1,8 @@ -using PropertyChanged; +using NetPrints.Base; +using NetPrints.Core; +using PropertyChanged; +using System; +using System.ComponentModel; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -14,8 +18,11 @@ namespace NetPrints.Graph [KnownType(typeof(NodeInputTypePin))] [KnownType(typeof(NodeOutputTypePin))] [AddINotifyPropertyChangedInterface] - public abstract class NodePin - { + public abstract class NodePin : INodePin + { + private double positionX; + private double positionY; + /// /// Name of the pin. /// @@ -34,8 +41,30 @@ public Node Node { get; private set; - } - + } + + public double PositionX + { + get { return positionX; } + set { positionX = value; PositionChanged?.Invoke(this, EventArgs.Empty); } + } + public double PositionY + { + get { return positionY; } + set { positionY = value; PositionChanged?.Invoke(this, EventArgs.Empty); } + } + + public event EventHandler PositionChanged; + + INode INodePin.Node => Node; + + public abstract NodePinConnectionType ConnectionType { get; } + + public ObservableRangeCollection ConnectedPins => connectedPins; + + [DataMember] + private ObservableRangeCollection connectedPins = new ObservableRangeCollection(); + protected NodePin(Node node, string name) { Node = node; diff --git a/NetPrints/Graph/NodeTypePin.cs b/NetPrints/Graph/NodeTypePin.cs index 81de5dc..1d5b467 100644 --- a/NetPrints/Graph/NodeTypePin.cs +++ b/NetPrints/Graph/NodeTypePin.cs @@ -1,4 +1,5 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using System.Runtime.Serialization; namespace NetPrints.Graph @@ -7,7 +8,7 @@ namespace NetPrints.Graph /// Abstract class for type pins. /// [DataContract] - public abstract class NodeTypePin : NodePin + public abstract class NodeTypePin : NodePin, INodeTypePin { public abstract ObservableValue InferredType { diff --git a/NetPrints/Graph/ReturnNode.cs b/NetPrints/Graph/ReturnNode.cs index faf2445..64f3876 100644 --- a/NetPrints/Graph/ReturnNode.cs +++ b/NetPrints/Graph/ReturnNode.cs @@ -1,4 +1,5 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using System; using System.Collections.Generic; using System.Linq; @@ -10,7 +11,7 @@ namespace NetPrints.Graph /// Represents a node which returns from a method. /// [DataContract] - public class ReturnNode : Node + public class ReturnNode : Node, INodeInputButtons { /// /// Execution pin that returns from the method when executed. @@ -52,10 +53,11 @@ private void ReplicateMainNodeInputTypes() oldConnections.Add(i, pin.IncomingPin); } - GraphUtil.DisconnectInputDataPin(pin); + GraphUtil.DisconnectPin(pin); } - InputDataPins.Clear(); + // Remove old data pins + Pins.RemoveRange(Pins.Where(pin => pin is INodeInputPin && pin is INodeDataPin)); foreach (NodeInputDataPin mainInputPin in mainInputPins) { @@ -65,7 +67,7 @@ private void ReplicateMainNodeInputTypes() // Restore old connections foreach (var oldConn in oldConnections) { - GraphUtil.ConnectDataPins(oldConn.Value, InputDataPins[oldConn.Key]); + GraphUtil.ConnectPins(oldConn.Value, InputDataPins[oldConn.Key]); } } @@ -116,11 +118,11 @@ public void RemoveReturnType() NodeInputDataPin idpToRemove = InputDataPins.Last(); NodeInputTypePin itpToRemove = InputTypePins.Last(); - GraphUtil.DisconnectInputDataPin(idpToRemove); - GraphUtil.DisconnectInputTypePin(itpToRemove); + GraphUtil.DisconnectPin(idpToRemove); + GraphUtil.DisconnectPin(itpToRemove); - InputDataPins.Remove(idpToRemove); - InputTypePins.Remove(itpToRemove); + Pins.Remove(idpToRemove); + Pins.Remove(itpToRemove); } } @@ -144,6 +146,10 @@ private void SetupSecondaryNodeEvents() public override string ToString() { return "Return"; - } + } + + public void InputPlusClicked() => AddReturnType(); + + public void InputMinusClicked() => RemoveReturnType(); } } diff --git a/NetPrints/Translator/ExecutionGraphTranslator.cs b/NetPrints/Translator/ExecutionGraphTranslator.cs index 847a427..a45e770 100644 --- a/NetPrints/Translator/ExecutionGraphTranslator.cs +++ b/NetPrints/Translator/ExecutionGraphTranslator.cs @@ -332,7 +332,7 @@ public string Translate(ExecutionGraph graph, bool withSignature) builder.AppendLine(); // Start at node after method entry if necessary (id!=0) - if (graph.EntryNode.OutputExecPins[0].OutgoingPin != null && GetExecPinStateId(graph.EntryNode.OutputExecPins[0].OutgoingPin) != 0) + if (graph.EntryNode.OutputExecPins[0].OutgoingExecPin != null && GetExecPinStateId(graph.EntryNode.OutputExecPins[0].OutgoingExecPin) != 0) { WriteGotoOutputPin(graph.EntryNode.OutputExecPins[0]); } @@ -423,13 +423,13 @@ private void WriteGotoInputPin(NodeInputExecPin pin) private void WriteGotoOutputPin(NodeOutputExecPin pin) { - if (pin.OutgoingPin == null) + if (pin.OutgoingExecPin == null) { WriteGotoJumpStack(); } else { - WriteGotoInputPin(pin.OutgoingPin); + WriteGotoInputPin(pin.OutgoingExecPin); } } @@ -438,7 +438,7 @@ private void WriteGotoOutputPinIfNecessary(NodeOutputExecPin pin, NodeInputExecP int fromId = GetExecPinStateId(fromPin); int nextId = fromId + 1; - if (pin.OutgoingPin == null) + if (pin.OutgoingExecPin == null) { if (nextId != jumpStackStateId) { @@ -447,13 +447,13 @@ private void WriteGotoOutputPinIfNecessary(NodeOutputExecPin pin, NodeInputExecP } else { - int toId = GetExecPinStateId(pin.OutgoingPin); + int toId = GetExecPinStateId(pin.OutgoingExecPin); // Only write the goto if the next state is not // the state we want to go to. if (nextId != toId) { - WriteGotoInputPin(pin.OutgoingPin); + WriteGotoInputPin(pin.OutgoingExecPin); } } } @@ -747,7 +747,7 @@ public void TranslateExplicitCastNode(ExplicitCastNode node) // If failure pin is not connected write explicit cast that throws. // Otherwise check if cast object is null and execute failure // path if it is. - if (node.IsPure || node.CastFailedPin.OutgoingPin == null) + if (node.IsPure || node.CastFailedPin.OutgoingExecPin == null) { builder.AppendLine($"{outputName} = ({node.CastType.FullCodeNameUnbound}){pinToCastName};"); @@ -918,7 +918,7 @@ public void TranslateIfElseNode(IfElseNode node) builder.AppendLine($"if ({conditionVar})"); builder.AppendLine("{"); - if (node.TruePin.OutgoingPin != null) + if (node.TruePin.OutgoingExecPin != null) { WriteGotoOutputPinIfNecessary(node.TruePin, node.InputExecPins[0]); } @@ -932,7 +932,7 @@ public void TranslateIfElseNode(IfElseNode node) builder.AppendLine("else"); builder.AppendLine("{"); - if (node.FalsePin.OutgoingPin != null) + if (node.FalsePin.OutgoingExecPin != null) { WriteGotoOutputPinIfNecessary(node.FalsePin, node.InputExecPins[0]); } @@ -1083,6 +1083,7 @@ public void PureTranslateMakeArrayNode(MakeArrayNode node) builder.AppendLine("};"); } } + public void PureTranslateDefaultNode(DefaultNode node) { builder.AppendLine($"{GetOrCreatePinName(node.DefaultValuePin)} = default({node.Type.FullCodeName});"); diff --git a/NetPrints/Translator/TranslatorUtil.cs b/NetPrints/Translator/TranslatorUtil.cs index cacfdc3..eb47f87 100644 --- a/NetPrints/Translator/TranslatorUtil.cs +++ b/NetPrints/Translator/TranslatorUtil.cs @@ -140,9 +140,9 @@ private static void AddAllNodes(Node node, ref HashSet nodes) foreach (NodeOutputExecPin pin in node.OutputExecPins) { - if (pin.OutgoingPin != null && !nodes.Contains(pin.OutgoingPin.Node)) + if (pin.OutgoingExecPin != null && !nodes.Contains(pin.OutgoingExecPin.Node)) { - AddAllNodes(pin.OutgoingPin.Node, ref nodes); + AddAllNodes(pin.OutgoingExecPin.Node, ref nodes); } } @@ -183,9 +183,9 @@ private static void AddExecNodes(Node node, ref HashSet nodes) foreach (NodeOutputExecPin pin in node.OutputExecPins) { - if (pin.OutgoingPin != null && !nodes.Contains(pin.OutgoingPin.Node)) + if (pin.OutgoingExecPin != null && !nodes.Contains(pin.OutgoingExecPin.Node)) { - AddExecNodes(pin.OutgoingPin.Node, ref nodes); + AddExecNodes(pin.OutgoingExecPin.Node, ref nodes); } } } diff --git a/NetPrintsEditor/Controls/GraphEditorView.xaml b/NetPrintsEditor/Controls/GraphEditorView.xaml index 1c90d1a..00b8d3f 100644 --- a/NetPrintsEditor/Controls/GraphEditorView.xaml +++ b/NetPrintsEditor/Controls/GraphEditorView.xaml @@ -12,6 +12,7 @@ + - + @@ -77,20 +78,24 @@ + + + - + + Point1="{Binding PointB, Converter={StaticResource PointConverter}}" + Point2="{Binding PointC, Converter={StaticResource PointConverter}}" + Point3="{Binding PointD, Converter={StaticResource PointConverter}}" /> diff --git a/NetPrintsEditor/Controls/GraphEditorView.xaml.cs b/NetPrintsEditor/Controls/GraphEditorView.xaml.cs index 869867e..096e99b 100644 --- a/NetPrintsEditor/Controls/GraphEditorView.xaml.cs +++ b/NetPrintsEditor/Controls/GraphEditorView.xaml.cs @@ -1,4 +1,5 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using NetPrints.Graph; using NetPrintsEditor.Commands; using NetPrintsEditor.Dialogs; @@ -475,18 +476,29 @@ private void CablePath_MouseLeave(object sender, MouseEventArgs e) private void CablePath_MouseDown(object sender, MouseButtonEventArgs e) { var element = sender as FrameworkElement; - if (!(element?.DataContext is NodePinVM pin)) + if (!(element?.DataContext is PinConnection conn)) { throw new Exception("Could not find cable's pin."); } if (e.ChangedButton == MouseButton.Left && e.LeftButton == MouseButtonState.Pressed && e.ClickCount == 2) { - pin.AddRerouteNode(); + if (conn.PinA is NodeInputDataPin idp) + { + GraphUtil.AddRerouteNode(idp); + } + else if (conn.PinA is NodeInputTypePin itp) + { + GraphUtil.AddRerouteNode(itp); + } + else if (conn.PinA is NodeOutputExecPin oep) + { + GraphUtil.AddRerouteNode(oep); + } } else if (e.ChangedButton == MouseButton.Middle) { - pin.DisconnectAll(); + GraphUtil.DisconnectPins(conn.PinA, conn.PinB); } } diff --git a/NetPrintsEditor/Controls/PinControl.xaml.cs b/NetPrintsEditor/Controls/PinControl.xaml.cs index e152d01..e1ea6f5 100644 --- a/NetPrintsEditor/Controls/PinControl.xaml.cs +++ b/NetPrintsEditor/Controls/PinControl.xaml.cs @@ -96,7 +96,7 @@ private void OnPinElementDrop(object sender, DragEventArgs e) // Another pin was dropped on this pin, link it NodePinVM droppedPin = (NodePinVM)e.Data.GetData(typeof(NodePinVM)); - droppedPin.ConnectTo(Pin); + GraphUtil.ConnectPins(droppedPin.Pin, Pin.Pin); e.Handled = true; } diff --git a/NetPrintsEditor/Converters/PointConverter.cs b/NetPrintsEditor/Converters/PointConverter.cs new file mode 100644 index 0000000..a5d52ab --- /dev/null +++ b/NetPrintsEditor/Converters/PointConverter.cs @@ -0,0 +1,20 @@ +using System; +using System.Globalization; + +namespace NetPrintsEditor.Converters +{ + public class PointConverter : System.Windows.Data.IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + System.Drawing.Point dp = (System.Drawing.Point)value; + return new System.Windows.Point(dp.X, dp.Y); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + System.Windows.Point wp = (System.Windows.Point)value; + return new System.Drawing.Point((int)wp.X, (int)wp.Y); + } + } +} diff --git a/NetPrintsEditor/ViewModels/ClassEditorVM.cs b/NetPrintsEditor/ViewModels/ClassEditorVM.cs index d6bde39..f227f43 100644 --- a/NetPrintsEditor/ViewModels/ClassEditorVM.cs +++ b/NetPrintsEditor/ViewModels/ClassEditorVM.cs @@ -167,7 +167,7 @@ public void CreateMethod(string name, double gridCellSize) newMethod.EntryNode.PositionY = gridCellSize * 4; newMethod.ReturnNodes.First().PositionX = newMethod.EntryNode.PositionX + gridCellSize * 15; newMethod.ReturnNodes.First().PositionY = newMethod.EntryNode.PositionY; - GraphUtil.ConnectExecPins(newMethod.EntryNode.InitialExecutionPin, newMethod.MainReturnNode.ReturnPin); + GraphUtil.ConnectPins(newMethod.EntryNode.InitialExecutionPin, newMethod.MainReturnNode.ReturnPin); Class.Methods.Add(newMethod); diff --git a/NetPrintsEditor/ViewModels/MemberVariableVM.cs b/NetPrintsEditor/ViewModels/MemberVariableVM.cs index 4276b06..4cd6a03 100644 --- a/NetPrintsEditor/ViewModels/MemberVariableVM.cs +++ b/NetPrintsEditor/ViewModels/MemberVariableVM.cs @@ -107,7 +107,7 @@ public void AddGetter() method.MainReturnNode.PositionY = method.EntryNode.PositionY; // Connect entry and return node execution pins - GraphUtil.ConnectExecPins(method.EntryNode.InitialExecutionPin, method.MainReturnNode.ReturnPin); + GraphUtil.ConnectPins(method.EntryNode.InitialExecutionPin, method.MainReturnNode.ReturnPin); // Create return input pin with correct type // TODO: Make sure we can't delete type pins. @@ -115,7 +115,7 @@ public void AddGetter() const int offsetY = -112; TypeNode returnTypeNode = GraphUtil.CreateNestedTypeNode(method, Type, method.MainReturnNode.PositionX + offsetX, method.MainReturnNode.PositionY + offsetY); method.MainReturnNode.AddReturnType(); - GraphUtil.ConnectTypePins(returnTypeNode.OutputTypePins[0], method.MainReturnNode.InputTypePins[0]); + GraphUtil.ConnectPins(returnTypeNode.OutputTypePins[0], method.MainReturnNode.InputTypePins[0]); Getter = method; } @@ -140,7 +140,7 @@ public void AddSetter() method.MainReturnNode.PositionY = method.EntryNode.PositionY; // Connect entry and return node execution pins - GraphUtil.ConnectExecPins(method.EntryNode.InitialExecutionPin, method.MainReturnNode.ReturnPin); + GraphUtil.ConnectPins(method.EntryNode.InitialExecutionPin, method.MainReturnNode.ReturnPin); // Create argument output pin with correct type // TODO: Make sure we can't delete type pins. @@ -148,7 +148,7 @@ public void AddSetter() const int offsetY = -112; TypeNode argTypeNode = GraphUtil.CreateNestedTypeNode(method, Type, method.EntryNode.PositionX + offsetX, method.EntryNode.PositionY + offsetY); method.MethodEntryNode.AddArgument(); - GraphUtil.ConnectTypePins(argTypeNode.OutputTypePins[0], method.EntryNode.InputTypePins[0]); + GraphUtil.ConnectPins(argTypeNode.OutputTypePins[0], method.EntryNode.InputTypePins[0]); Setter = method; } diff --git a/NetPrintsEditor/ViewModels/NodeGraphVM.cs b/NetPrintsEditor/ViewModels/NodeGraphVM.cs index d4ce460..2828d57 100644 --- a/NetPrintsEditor/ViewModels/NodeGraphVM.cs +++ b/NetPrintsEditor/ViewModels/NodeGraphVM.cs @@ -1,4 +1,5 @@ using GalaSoft.MvvmLight; +using NetPrints.Base; using NetPrints.Core; using NetPrints.Graph; using NetPrintsEditor.Commands; @@ -166,7 +167,7 @@ void AddSuggestionsWithCategory(string category, IEnumerable newSuggesti } else if (SuggestionPin is NodeOutputExecPin oxp) { - GraphUtil.DisconnectOutputExecPin(oxp); + GraphUtil.DisconnectPin(oxp); AddSuggestionsWithCategory("NetPrints", GetBuiltInNodes(Graph)); @@ -322,7 +323,7 @@ public string Name public bool IsConstructor => graph is ConstructorGraph; - public ObservableViewModelCollection Nodes { get; set; } + public ObservableViewModelCollection Nodes { get; set; } public IEnumerable ArgumentTypes => graph is ExecutionGraph execGraph ? execGraph.ArgumentTypes : null; @@ -367,62 +368,20 @@ public MemberVisibility Visibility MemberVisibility.Public, }; - /* - Scenarios: - - Method changed [0]: - (Un)assign (old) nodes changed events [1] - (Un)assign (old)new pins changed events [2] - (Un)assign (old)new pin connection changed events [3] - (Un)setup (old)new method pins' connections in VM - - Node changed [1]: - (Un)assign (old)new pins changed events [2] - (Un)assign (old)new pin connection changed events [3] - (Un)setup (old)new node pins' connections in VM - - Pin changed [2]: - (Un)assign (old)new pin connection changed events [3] - (Un)setup (old)new pin connection in VM - - Pin connection changed [3]: - (Un)setup (old)new connection in VM - */ - private void SetupNodeEvents(NodeVM node, bool add) { - // (Un)assign (old)new pins changed events [2] if (add) { - node.InputDataPins.CollectionChanged += OnPinCollectionChanged; - node.OutputDataPins.CollectionChanged += OnPinCollectionChanged; - node.InputExecPins.CollectionChanged += OnPinCollectionChanged; - node.OutputExecPins.CollectionChanged += OnPinCollectionChanged; - node.InputTypePins.CollectionChanged += OnPinCollectionChanged; - node.OutputTypePins.CollectionChanged += OnPinCollectionChanged; - node.OnDragStart += OnNodeDragStart; node.OnDragEnd += OnNodeDragEnd; node.OnDragMove += OnNodeDragMove; } else { - node.InputDataPins.CollectionChanged -= OnPinCollectionChanged; - node.OutputDataPins.CollectionChanged -= OnPinCollectionChanged; - node.InputExecPins.CollectionChanged -= OnPinCollectionChanged; - node.OutputExecPins.CollectionChanged -= OnPinCollectionChanged; - node.InputTypePins.CollectionChanged -= OnPinCollectionChanged; - node.OutputTypePins.CollectionChanged -= OnPinCollectionChanged; - node.OnDragStart -= OnNodeDragStart; node.OnDragEnd -= OnNodeDragEnd; node.OnDragMove -= OnNodeDragMove; } - - // (Un)assign (old)new pin connection changed events [3] - node.InputDataPins.ToList().ForEach(p => SetupPinEvents(p, add)); - node.OutputExecPins.ToList().ForEach(p => SetupPinEvents(p, add)); - node.InputTypePins.ToList().ForEach(p => SetupPinEvents(p, add)); } #region Node dragging @@ -492,122 +451,6 @@ private void OnNodeDragMove(NodeVM node, double dx, double dy) } } #endregion - - private void SetupPinEvents(NodePinVM pin, bool add) - { - // (Un)assign (old)new pin connection changed events [3] - - if (pin.Pin is NodeInputDataPin idp) - { - if (add) - { - idp.IncomingPinChanged += OnInputDataPinIncomingPinChanged; - } - else - { - idp.IncomingPinChanged -= OnInputDataPinIncomingPinChanged; - } - } - else if (pin.Pin is NodeOutputExecPin oxp) - { - if (add) - { - oxp.OutgoingPinChanged += OnOutputExecPinIncomingPinChanged; - } - else - { - oxp.OutgoingPinChanged -= OnOutputExecPinIncomingPinChanged; - } - } - else if (pin.Pin is NodeInputTypePin itp) - { - if (add) - { - itp.IncomingPinChanged += OnInputTypePinIncomingPinChanged; - } - else - { - itp.IncomingPinChanged -= OnInputTypePinIncomingPinChanged; - } - } - } - - private void SetupPinConnection(NodePinVM pin, bool add) - { - if (pin.Pin is NodeInputDataPin idp) - { - if (idp.IncomingPin != null) - { - if (add) - { - NodeOutputDataPin connPin = idp.IncomingPin as NodeOutputDataPin; - pin.ConnectedPin = Nodes - .SingleOrDefault(n => n.Node == connPin.Node) - ?.OutputDataPins - ?.Single(x => x.Pin == connPin); - } - else - { - pin.ConnectedPin = null; - } - } - } - else if (pin.Pin is NodeOutputExecPin oxp) - { - if (oxp.OutgoingPin != null) - { - if (add) - { - NodeInputExecPin connPin = oxp.OutgoingPin as NodeInputExecPin; - pin.ConnectedPin = Nodes - .Single(n => n.Node == connPin.Node) - .InputExecPins - .Single(x => x.Pin == connPin); - } - else - { - pin.ConnectedPin = null; - } - } - } - else if (pin.Pin is NodeInputTypePin itp) - { - if (itp.IncomingPin != null) - { - if (add) - { - NodeOutputTypePin connPin = itp.IncomingPin as NodeOutputTypePin; - pin.ConnectedPin = Nodes - .Single(n => n.Node == connPin.Node) - .OutputTypePins - .Single(x => x.Pin == connPin); - } - else - { - pin.ConnectedPin = null; - } - } - } - } - - private void SetupNodeConnections(NodeVM node, bool add) - { - node.OutputExecPins.ToList().ForEach(p => SetupPinConnection(p, add)); - node.InputDataPins.ToList().ForEach(p => SetupPinConnection(p, add)); - node.InputTypePins.ToList().ForEach(p => SetupPinConnection(p, add)); - - // Make sure to remove the IXP and ODP connections - // any other nodes are connecting to too - - if (!add) - { - AllPins.Where(p => - node.InputExecPins.Contains(p.ConnectedPin) - || node.OutputDataPins.Contains(p.ConnectedPin) - || node.OutputTypePins.Contains(p.ConnectedPin) - ).ToList().ForEach(p => p.ConnectedPin = null); - } - } public NodeGraph Graph { @@ -620,24 +463,18 @@ public NodeGraph Graph { Nodes.CollectionChanged -= OnNodeCollectionChanged; Nodes.ToList().ForEach(n => SetupNodeEvents(n, false)); - - Nodes.ToList().ForEach(n => SetupNodeConnections(n, false)); } graph = value; RaisePropertyChanged(nameof(AllPins)); RaisePropertyChanged(nameof(Visibility)); - Nodes = new ObservableViewModelCollection(Graph.Nodes, n => new NodeVM(n)); + Nodes = new ObservableViewModelCollection(Graph.Nodes, n => new NodeVM((Node)n)); if (graph != null) { Nodes.CollectionChanged += OnNodeCollectionChanged; Nodes.ToList().ForEach(n => SetupNodeEvents(n, true)); - - // Connections on normal pins already exist, - // Set them up for the viewmodels of the pins - Nodes.ToList().ForEach(n => SetupNodeConnections(n, true)); } } } @@ -651,102 +488,18 @@ private void OnNodeCollectionChanged(object sender, NotifyCollectionChangedEvent { var removedNodes = e.OldItems.Cast(); removedNodes.ToList().ForEach(n => SetupNodeEvents(n, false)); - - removedNodes.ToList().ForEach(n => SetupNodeConnections(n, false)); } if (e.NewItems != null) { var addedNodes = e.NewItems.Cast(); addedNodes.ToList().ForEach(n => SetupNodeEvents(n, true)); - - addedNodes.ToList().ForEach(n => SetupNodeConnections(n, true)); - } - - RaisePropertyChanged(nameof(AllPins)); - } - - private void OnPinCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (e.OldItems != null) - { - var removedPins = e.OldItems.Cast(); - - // Unsetup initial connections of added pins - removedPins.ToList().ForEach(p => SetupPinConnection(p, false)); - - // Remove events for removed pins - removedPins.ToList().ForEach(p => SetupPinEvents(p, false)); - } - - if (e.NewItems != null) - { - var addedPins = e.NewItems.Cast(); - - // Setup initial connections of added pins - addedPins.ToList().ForEach(p => SetupPinConnection(p, true)); - - // Add events for added pins - addedPins.ToList().ForEach(p => SetupPinEvents(p, true)); } RaisePropertyChanged(nameof(AllPins)); } - private void OnInputDataPinIncomingPinChanged(NodeInputDataPin pin, NodeOutputDataPin oldPin, NodeOutputDataPin newPin) - { - // Connect pinVM newPinVM (or null if newPin is null) - - NodePinVM pinVM = FindPinVMFromPin(pin); - pinVM.ConnectedPin = newPin == null ? null : FindPinVMFromPin(newPin); - } - - private void OnOutputExecPinIncomingPinChanged(NodeOutputExecPin pin, NodeInputExecPin oldPin, NodeInputExecPin newPin) - { - // Connect pinVM newPinVM (or null if newPin is null) - - NodePinVM pinVM = FindPinVMFromPin(pin); - pinVM.ConnectedPin = newPin == null ? null : FindPinVMFromPin(newPin); - } - - private void OnInputTypePinIncomingPinChanged(NodeInputTypePin pin, NodeOutputTypePin oldPin, NodeOutputTypePin newPin) - { - // Connect pinVM newPinVM (or null if newPin is null) - - NodePinVM pinVM = FindPinVMFromPin(pin); - if (pinVM != null) - { - pinVM.ConnectedPin = newPin == null ? null : FindPinVMFromPin(newPin); - } - } - - private NodePinVM FindPinVMFromPin(NodePin pin) - { - return AllPins.SingleOrDefault(p => p.Pin == pin); - } - - public IEnumerable AllPins - { - get - { - List pins = new List(); - - if (Graph != null) - { - foreach (NodeVM node in Nodes) - { - pins.AddRange(node.InputDataPins); - pins.AddRange(node.OutputDataPins); - pins.AddRange(node.InputExecPins); - pins.AddRange(node.OutputExecPins); - pins.AddRange(node.InputTypePins); - pins.AddRange(node.OutputTypePins); - } - } - - return pins; - } - } + public IEnumerable AllPins => Graph != null ? Nodes.SelectMany(node => node.Pins) : new NodePinVM[0]; /// /// Sends a message to deselect all nodes and select the given nodes. diff --git a/NetPrintsEditor/ViewModels/NodePinVM.cs b/NetPrintsEditor/ViewModels/NodePinVM.cs index 9ba59ea..8512618 100644 --- a/NetPrintsEditor/ViewModels/NodePinVM.cs +++ b/NetPrintsEditor/ViewModels/NodePinVM.cs @@ -1,4 +1,5 @@ using GalaSoft.MvvmLight; +using NetPrints.Base; using NetPrints.Core; using NetPrints.Graph; using NetPrints.Translator; @@ -105,13 +106,6 @@ public bool IsBeingConnected { isBeingConnected = value; - // Disconnect pin if its being connected - // and is an IDP, OXP or ITP - if (value && (Pin is NodeInputDataPin || Pin is NodeOutputExecPin || Pin is NodeInputTypePin)) - { - ConnectedPin = null; - } - RaisePropertyChanged(); RaisePropertyChanged(nameof(IsCableVisible)); OnConnectionPositionUpdate(); @@ -351,10 +345,10 @@ public Brush FillBrush // Check if the pin is connected to anything if ((pin is NodeInputDataPin idp && idp.IncomingPin != null) || (pin is NodeOutputDataPin odp && odp.OutgoingPins.Count > 0) - || (pin is NodeInputExecPin iep && iep.IncomingPins.Count > 0) - || (pin is NodeOutputExecPin oep && oep.OutgoingPin != null) + || (pin is NodeInputExecPin iep && iep.IncomingExecutionPins.Count > 0) + || (pin is NodeOutputExecPin oep && oep.OutgoingExecPin != null) || (pin is NodeInputTypePin itp && itp.IncomingPin != null) - || (pin is NodeOutputTypePin otp && otp.OutgoingPins.Count > 0)) + || (pin is NodeOutputTypePin otp && otp.OutgoingTypePins.Count > 0)) { return BorderBrush; } @@ -383,6 +377,12 @@ pin is NodeInputDataPin idp Node.PositionX + NodeRelativePosition.X, Node.PositionY + NodeRelativePosition.Y); + private void OnAbsolutePositionChanged() + { + Pin.PositionX = AbsolutePosition.X; + Pin.PositionY = AbsolutePosition.Y; + } + public Point NodeRelativePosition { get => nodeRelativePosition; @@ -405,68 +405,7 @@ private void OnConnectionPositionUpdate() RaisePropertyChanged(nameof(ConnectedCP1)); RaisePropertyChanged(nameof(ConnectedCP2)); RaisePropertyChanged(nameof(AbsolutePosition)); - } - - // = Incoming pin for data input - // = Outgoing pin for exec output - // = Incoming pin for type input - // = null for rest - public NodePinVM ConnectedPin - { - get => connectedPin; - set - { - if (!(Pin is NodeOutputExecPin || Pin is NodeInputDataPin || Pin is NodeInputTypePin)) - { - throw new Exception("Can only set connected pin of NodeOutputExecPin and NodeInputDataPin"); - } - - if (connectedPin != value) - { - if (connectedPin != null) - { - connectedPin.Node.OnPositionChanged -= OnConnectedPinNodePositionChanged; - connectedPin.PropertyChanged -= OnConnectedPinPropertyChanged; - } - - if (value != null) - { - GraphUtil.ConnectNodePins(Pin, value.Pin); - } - else - { - if (Pin is NodeOutputExecPin oxp) - { - GraphUtil.DisconnectOutputExecPin(oxp); - } - else if (Pin is NodeInputDataPin idp) - { - GraphUtil.DisconnectInputDataPin(idp); - } - else if (Pin is NodeInputTypePin itp) - { - GraphUtil.DisconnectInputTypePin(itp); - } - } - - connectedPin = value; - RaisePropertyChanged(); - RaisePropertyChanged(nameof(IsConnected)); - RaisePropertyChanged(nameof(IsCableVisible)); - RaisePropertyChanged(nameof(ShowUnconnectedValue)); - RaisePropertyChanged(nameof(ShowEnumValue)); - RaisePropertyChanged(nameof(PossibleEnumNames)); - RaisePropertyChanged(nameof(FillBrush)); - RaisePropertyChanged(nameof(DefaultValueIndicatorBrush)); - OnConnectionPositionUpdate(); - - if (connectedPin != null) - { - connectedPin.Node.OnPositionChanged += OnConnectedPinNodePositionChanged; - connectedPin.PropertyChanged += OnConnectedPinPropertyChanged; - } - } - } + OnAbsolutePositionChanged(); } private void OnConnectedPinPropertyChanged(object sender, PropertyChangedEventArgs e) @@ -479,7 +418,7 @@ private void OnConnectedPinPropertyChanged(object sender, PropertyChangedEventAr public bool IsConnected { - get => connectedPin != null; + get => pin.ConnectedPins.Count > 0; } public bool IsCableVisible @@ -534,13 +473,13 @@ public Point ConnectedAbsolutePosition } else { - return IsConnected ? ConnectedPin.AbsolutePosition : AbsolutePosition; + return AbsolutePosition; + // TODO: return IsConnected ? ConnectedPin.AbsolutePosition : AbsolutePosition; } } } private NodePin pin; - private NodePinVM connectedPin; private double positionX; private double positionY; @@ -565,8 +504,8 @@ public void AddRerouteNode() else if (Pin is NodeOutputExecPin execPin) { RerouteNode rerouteNode = GraphUtil.AddRerouteNode(execPin); - rerouteNode.PositionX = (Pin.Node.PositionX + execPin.OutgoingPin.Node.PositionX) / 2; - rerouteNode.PositionY = (Pin.Node.PositionY + execPin.OutgoingPin.Node.PositionY) / 2; + rerouteNode.PositionX = (Pin.Node.PositionX + execPin.OutgoingExecPin.Node.PositionX) / 2; + rerouteNode.PositionY = (Pin.Node.PositionY + execPin.OutgoingExecPin.Node.PositionY) / 2; } else if (Pin is NodeInputTypePin typePin) { @@ -579,22 +518,6 @@ public void AddRerouteNode() throw new Exception("Can't add reroute node for invalid pin type"); } } - - /// - /// Connects this pin to another pin. - /// - /// Pin to connect to. - public void ConnectTo(NodePinVM other) - { - if (Pin is NodeInputDataPin || Pin is NodeOutputExecPin || Pin is NodeInputTypePin) - { - ConnectedPin = other; - } - else - { - other.ConnectedPin = this; - } - } private void OnPinChanged() { diff --git a/NetPrintsEditor/ViewModels/NodeVM.cs b/NetPrintsEditor/ViewModels/NodeVM.cs index 9231dde..6e46230 100644 --- a/NetPrintsEditor/ViewModels/NodeVM.cs +++ b/NetPrintsEditor/ViewModels/NodeVM.cs @@ -1,13 +1,13 @@ using NetPrints.Core; using NetPrints.Graph; using System.ComponentModel; -using System.Runtime.CompilerServices; using System.Windows.Media; using System.Linq; using System; using GalaSoft.MvvmLight; using NetPrintsEditor.Messages; - +using NetPrints.Base; + namespace NetPrintsEditor.ViewModels { public class NodeVM : ViewModelBase @@ -271,90 +271,13 @@ public string Name } } - public ObservableViewModelCollection InputDataPins - { - get => inputDataPins; - set - { - if (inputDataPins != value) - { - inputDataPins = value; - RaisePropertyChanged(); - } - } - } - - public ObservableViewModelCollection OutputDataPins - { - get => outputDataPins; - set - { - if (outputDataPins != value) - { - outputDataPins = value; - RaisePropertyChanged(); - } - } - } - - public ObservableViewModelCollection InputExecPins - { - get => inputExecPins; - set - { - if (inputExecPins != value) - { - inputExecPins = value; - RaisePropertyChanged(); - } - } - } - - public ObservableViewModelCollection OutputExecPins - { - get => outputExecPins; - set - { - if (outputExecPins != value) - { - outputExecPins = value; - RaisePropertyChanged(); - } - } - } - - public ObservableViewModelCollection InputTypePins - { - get => inputTypePins; - set - { - if (inputTypePins != value) - { - inputTypePins = value; - RaisePropertyChanged(); - } - } - } - - public ObservableViewModelCollection OutputTypePins - { - get => outputTypePins; - set - { - if (outputTypePins != value) - { - outputTypePins = value; - RaisePropertyChanged(); - } - } - } - - private ObservableViewModelCollection inputDataPins; - private ObservableViewModelCollection outputDataPins; - private ObservableViewModelCollection inputExecPins; - private ObservableViewModelCollection outputExecPins; - private ObservableViewModelCollection inputTypePins; - private ObservableViewModelCollection outputTypePins; + public ObservableViewModelCollection Pins { get; private set; } + public IObservableCollectionView InputExecPins { get; private set; } + public IObservableCollectionView OutputExecPins { get; private set; } + public IObservableCollectionView InputDataPins { get; private set; } + public IObservableCollectionView OutputDataPins { get; private set; } + public IObservableCollectionView InputTypePins { get; private set; } + public IObservableCollectionView OutputTypePins { get; private set; } public bool IsPure { @@ -386,23 +309,13 @@ public Node Node { node.InputTypeChanged += OnInputTypeChanged; - InputDataPins = new ObservableViewModelCollection( - Node.InputDataPins, p => new NodePinVM(p)); - - OutputDataPins = new ObservableViewModelCollection( - Node.OutputDataPins, p => new NodePinVM(p)); - - InputExecPins = new ObservableViewModelCollection( - Node.InputExecPins, p => new NodePinVM(p)); - - OutputExecPins = new ObservableViewModelCollection( - Node.OutputExecPins, p => new NodePinVM(p)); - - InputTypePins = new ObservableViewModelCollection( - Node.InputTypePins, p => new NodePinVM(p)); - - OutputTypePins = new ObservableViewModelCollection( - Node.OutputTypePins, p => new NodePinVM(p)); + Pins = new ObservableViewModelCollection(Node.Pins, p => new NodePinVM((NodePin)p)); + InputExecPins = Pins.ObservableWhere(pinViewModel => pinViewModel.Pin is NodeInputExecPin); + OutputExecPins = Pins.ObservableWhere(pinViewModel => pinViewModel.Pin is NodeOutputExecPin); + InputDataPins = Pins.ObservableWhere(pinViewModel => pinViewModel.Pin is NodeInputDataPin); + OutputDataPins = Pins.ObservableWhere(pinViewModel => pinViewModel.Pin is NodeOutputDataPin); + InputTypePins = Pins.ObservableWhere(pinViewModel => pinViewModel.Pin is NodeInputTypePin); + OutputTypePins = Pins.ObservableWhere(pinViewModel => pinViewModel.Pin is NodeOutputTypePin); } UpdateOverloads(); @@ -520,8 +433,8 @@ public void ChangeOverload(object overload) bool oldPurity = Node.IsPure; if (!oldPurity) { - oldIncomingPins = Node.InputExecPins[0].IncomingPins.ToArray(); - oldOutgoingPin = Node.OutputExecPins[0].OutgoingPin; + oldIncomingPins = Node.InputExecPins[0].IncomingExecutionPins.ToArray(); + oldOutgoingPin = Node.OutputExecPins[0].OutgoingExecPin; } // Disconnect the old node from other nodes and remove it @@ -543,14 +456,14 @@ public void ChangeOverload(object overload) { if (oldOutgoingPin != null) { - GraphUtil.ConnectExecPins(newNode.OutputExecPins[0], oldOutgoingPin); + GraphUtil.ConnectPins(newNode.OutputExecPins[0], oldOutgoingPin); } if (oldIncomingPins != null) { foreach (NodeOutputExecPin oldIncomingPin in oldIncomingPins) { - GraphUtil.ConnectExecPins(oldIncomingPin, newNode.InputExecPins[0]); + GraphUtil.ConnectPins(oldIncomingPin, newNode.InputExecPins[0]); } } } @@ -576,7 +489,7 @@ public NodeGraph Graph /// public bool ShowLeftPinButtons { - get => node is MakeArrayNode || node is MethodEntryNode || (node is ReturnNode && node == Method.MainReturnNode) || node is ClassReturnNode; + get => node is INodeInputButtons; } /// @@ -585,7 +498,7 @@ public bool ShowLeftPinButtons /// public bool ShowRightPinButtons { - get => node is MethodEntryNode; + get => node is INodeOutputButtons; } /// @@ -593,45 +506,15 @@ public bool ShowRightPinButtons /// public void LeftPinsPlusClicked() { - if (node is MakeArrayNode makeArrayNode) - { - makeArrayNode.AddElementPin(); - } - else if (node is MethodEntryNode entryNode) - { - entryNode.AddArgument(); - } - else if (node is ReturnNode returnNode) - { - returnNode.AddReturnType(); - } - else if (node is ClassReturnNode classReturnNode) - { - classReturnNode.AddInterfacePin(); - } + (node as INodeInputButtons)?.InputPlusClicked(); } /// /// Called when the left pins' minus button was clicked. /// public void LeftPinsMinusClicked() - { - if (node is MakeArrayNode makeArrayNode) - { - makeArrayNode.RemoveElementPin(); - } - else if (node is MethodEntryNode entryNode) - { - entryNode.RemoveArgument(); - } - else if (node is ReturnNode returnNode) - { - returnNode.RemoveReturnType(); - } - else if (node is ClassReturnNode classReturnNode) - { - classReturnNode.RemoveInterfacePin(); - } + { + (node as INodeInputButtons)?.InputMinusClicked(); } /// @@ -639,10 +522,7 @@ public void LeftPinsMinusClicked() /// public void RightPinsPlusClicked() { - if (node is MethodEntryNode entryNode) - { - entryNode.AddGenericArgument(); - } + (node as INodeOutputButtons)?.OutputPlusClicked(); } /// @@ -650,10 +530,7 @@ public void RightPinsPlusClicked() /// public void RightPinsMinusClicked() { - if (node is MethodEntryNode entryNode) - { - entryNode.RemoveGenericArgument(); - } + (node as INodeOutputButtons)?.OutputMinusClicked(); } private Node node; diff --git a/NetPrintsUnitTests/ClassTranslatorTests.cs b/NetPrintsUnitTests/ClassTranslatorTests.cs index 6c492a5..1b651d8 100644 --- a/NetPrintsUnitTests/ClassTranslatorTests.cs +++ b/NetPrintsUnitTests/ClassTranslatorTests.cs @@ -34,7 +34,7 @@ public void CreateStringLengthMethod() for (int i = 0; i < returnTypeNodes.Count; i++) { stringLengthMethod.MainReturnNode.AddReturnType(); - GraphUtil.ConnectTypePins(returnTypeNodes[i].OutputTypePins[0], stringLengthMethod.MainReturnNode.InputTypePins[i]); + GraphUtil.ConnectPins(returnTypeNodes[i].OutputTypePins[0], stringLengthMethod.MainReturnNode.InputTypePins[i]); } TypeSpecifier stringType = TypeSpecifier.FromType(); @@ -45,11 +45,11 @@ public void CreateStringLengthMethod() VariableGetterNode getLengthNode = new VariableGetterNode(stringLengthMethod, new VariableSpecifier("Length", intType, MemberVisibility.Public, MemberVisibility.Public, stringType, VariableModifiers.None)); // Connect node execs - GraphUtil.ConnectExecPins(stringLengthMethod.EntryNode.InitialExecutionPin, stringLengthMethod.ReturnNodes.First().ReturnPin); + GraphUtil.ConnectPins(stringLengthMethod.EntryNode.InitialExecutionPin, stringLengthMethod.ReturnNodes.First().ReturnPin); // Connect node data - GraphUtil.ConnectDataPins(getStringNode.ValuePin, getLengthNode.TargetPin); - GraphUtil.ConnectDataPins(getLengthNode.ValuePin, stringLengthMethod.ReturnNodes.First().InputDataPins[0]); + GraphUtil.ConnectPins(getStringNode.ValuePin, getLengthNode.TargetPin); + GraphUtil.ConnectPins(getLengthNode.ValuePin, stringLengthMethod.ReturnNodes.First().InputDataPins[0]); } public void CreateMainMethod() @@ -74,14 +74,14 @@ public void CreateMainMethod() CallMethodNode writeConsoleNode = new CallMethodNode(mainMethod, writeConsoleSpecifier); // Connect node execs - GraphUtil.ConnectExecPins(mainMethod.EntryNode.InitialExecutionPin, setStringNode.InputExecPins[0]); - GraphUtil.ConnectExecPins(setStringNode.OutputExecPins[0], getStringLengthNode.InputExecPins[0]); - GraphUtil.ConnectExecPins(getStringLengthNode.OutputExecPins[0], writeConsoleNode.InputExecPins[0]); - GraphUtil.ConnectExecPins(writeConsoleNode.OutputExecPins[0], mainMethod.ReturnNodes.First().InputExecPins[0]); + GraphUtil.ConnectPins(mainMethod.EntryNode.InitialExecutionPin, setStringNode.InputExecPins[0]); + GraphUtil.ConnectPins(setStringNode.OutputExecPins[0], getStringLengthNode.InputExecPins[0]); + GraphUtil.ConnectPins(getStringLengthNode.OutputExecPins[0], writeConsoleNode.InputExecPins[0]); + GraphUtil.ConnectPins(writeConsoleNode.OutputExecPins[0], mainMethod.ReturnNodes.First().InputExecPins[0]); // Connect node data - GraphUtil.ConnectDataPins(stringLiteralNode.ValuePin, setStringNode.NewValuePin); - GraphUtil.ConnectDataPins(getStringLengthNode.OutputDataPins[0], writeConsoleNode.ArgumentPins[0]); + GraphUtil.ConnectPins(stringLiteralNode.ValuePin, setStringNode.NewValuePin); + GraphUtil.ConnectPins(getStringLengthNode.OutputDataPins[0], writeConsoleNode.ArgumentPins[0]); } [TestInitialize] diff --git a/NetPrintsUnitTests/DelegateTranslatorTests.cs b/NetPrintsUnitTests/DelegateTranslatorTests.cs index 23c2ea7..496900b 100644 --- a/NetPrintsUnitTests/DelegateTranslatorTests.cs +++ b/NetPrintsUnitTests/DelegateTranslatorTests.cs @@ -36,7 +36,7 @@ public void TestDelegate() for (int i = 0; i < returnTypeNodes.Count; i++) { delegateMethod.MainReturnNode.AddReturnType(); - GraphUtil.ConnectTypePins(returnTypeNodes[i].OutputTypePins[0], delegateMethod.MainReturnNode.InputTypePins[i]); + GraphUtil.ConnectPins(returnTypeNodes[i].OutputTypePins[0], delegateMethod.MainReturnNode.InputTypePins[i]); } MethodSpecifier delegateMethodSpecifier = new MethodSpecifier("TestMethod", @@ -53,10 +53,10 @@ public void TestDelegate() MakeDelegateNode makeDelegateNode = new MakeDelegateNode(delegateMethod, delegateMethodSpecifier); // Connect node execs - GraphUtil.ConnectExecPins(delegateMethod.EntryNode.InitialExecutionPin, delegateMethod.ReturnNodes.First().ReturnPin); + GraphUtil.ConnectPins(delegateMethod.EntryNode.InitialExecutionPin, delegateMethod.ReturnNodes.First().ReturnPin); // Connect node data - GraphUtil.ConnectDataPins(makeDelegateNode.OutputDataPins[0], delegateMethod.ReturnNodes.First().InputDataPins[0]); + GraphUtil.ConnectPins(makeDelegateNode.OutputDataPins[0], delegateMethod.ReturnNodes.First().InputDataPins[0]); string translated = methodTranslator.Translate(delegateMethod, true); } diff --git a/NetPrintsUnitTests/GenericsTests.cs b/NetPrintsUnitTests/GenericsTests.cs index 4e0622a..7d4e2ab 100644 --- a/NetPrintsUnitTests/GenericsTests.cs +++ b/NetPrintsUnitTests/GenericsTests.cs @@ -34,13 +34,13 @@ public void TestGenerics() TypeNode listTypeNode = new TypeNode(openMethod, listType); openMethod.MainReturnNode.AddReturnType(); - GraphUtil.ConnectTypePins(listTypeNode.OutputTypePins[0], openMethod.MainReturnNode.InputTypePins[0]); + GraphUtil.ConnectPins(listTypeNode.OutputTypePins[0], openMethod.MainReturnNode.InputTypePins[0]); DefaultNode defaultNode = new DefaultNode(openMethod); - GraphUtil.ConnectTypePins(listTypeNode.OutputTypePins[0], defaultNode.TypePin); - GraphUtil.ConnectDataPins(defaultNode.DefaultValuePin, openMethod.MainReturnNode.InputDataPins[0]); + GraphUtil.ConnectPins(listTypeNode.OutputTypePins[0], defaultNode.TypePin); + GraphUtil.ConnectPins(defaultNode.DefaultValuePin, openMethod.MainReturnNode.InputDataPins[0]); - GraphUtil.ConnectExecPins(openMethod.EntryNode.InitialExecutionPin, openMethod.ReturnNodes.First().ReturnPin); + GraphUtil.ConnectPins(openMethod.EntryNode.InitialExecutionPin, openMethod.ReturnNodes.First().ReturnPin); openClass.Methods.Add(openMethod); @@ -57,13 +57,13 @@ public void TestGenerics() // Add closed list parameter TypeNode closedListTypeNode = new TypeNode(closedMethod, closedListType); closedMethod.MainReturnNode.AddReturnType(); - GraphUtil.ConnectTypePins(closedListTypeNode.OutputTypePins[0], closedMethod.MainReturnNode.InputTypePins[0]); + GraphUtil.ConnectPins(closedListTypeNode.OutputTypePins[0], closedMethod.MainReturnNode.InputTypePins[0]); DefaultNode closedDefaultNode = new DefaultNode(closedMethod); - GraphUtil.ConnectTypePins(closedListTypeNode.OutputTypePins[0], closedDefaultNode.TypePin); - GraphUtil.ConnectDataPins(closedDefaultNode.DefaultValuePin, closedMethod.MainReturnNode.InputDataPins[0]); + GraphUtil.ConnectPins(closedListTypeNode.OutputTypePins[0], closedDefaultNode.TypePin); + GraphUtil.ConnectPins(closedDefaultNode.DefaultValuePin, closedMethod.MainReturnNode.InputDataPins[0]); - GraphUtil.ConnectExecPins(closedMethod.EntryNode.InitialExecutionPin, closedMethod.ReturnNodes.First().ReturnPin); + GraphUtil.ConnectPins(closedMethod.EntryNode.InitialExecutionPin, closedMethod.ReturnNodes.First().ReturnPin); closedClass.Methods.Add(closedMethod); @@ -89,7 +89,7 @@ public void TestTypeGraph() var typeNode = new TypeNode(method, TypeSpecifier.FromType()); Assert.AreEqual(literalNode.InputTypePins.Count, 1); - GraphUtil.ConnectTypePins(typeNode.OutputTypePins[0], literalNode.InputTypePins[0]); + GraphUtil.ConnectPins(typeNode.OutputTypePins[0], literalNode.InputTypePins[0]); Assert.AreEqual(literalNode.InputTypePins[0].InferredType.Value, new TypeSpecifier("System.Int32")); Assert.AreEqual(literalNode.OutputDataPins[0].PinType.Value, new TypeSpecifier("System.Collections.Generic.List", genericArguments: new BaseType[] { new TypeSpecifier("System.Int32") })); } diff --git a/NetPrintsUnitTests/MethodTranslatorTests.cs b/NetPrintsUnitTests/MethodTranslatorTests.cs index 8d3a14e..96086be 100644 --- a/NetPrintsUnitTests/MethodTranslatorTests.cs +++ b/NetPrintsUnitTests/MethodTranslatorTests.cs @@ -47,7 +47,7 @@ public void CreateStringLengthMethod() for (int i = 0; i < argTypeNodes.Count; i++) { ((MethodEntryNode)stringLengthMethod.EntryNode).AddArgument(); - GraphUtil.ConnectTypePins(argTypeNodes[i].OutputTypePins[0], stringLengthMethod.EntryNode.InputTypePins[i]); + GraphUtil.ConnectPins(argTypeNodes[i].OutputTypePins[0], stringLengthMethod.EntryNode.InputTypePins[i]); } // Add return types @@ -59,7 +59,7 @@ public void CreateStringLengthMethod() for (int i = 0; i < returnTypeNodes.Count; i++) { stringLengthMethod.MainReturnNode.AddReturnType(); - GraphUtil.ConnectTypePins(returnTypeNodes[i].OutputTypePins[0], stringLengthMethod.MainReturnNode.InputTypePins[i]); + GraphUtil.ConnectPins(returnTypeNodes[i].OutputTypePins[0], stringLengthMethod.MainReturnNode.InputTypePins[i]); } // Create nodes @@ -67,11 +67,11 @@ public void CreateStringLengthMethod() MemberVisibility.Public,MemberVisibility.Public, TypeSpecifier.FromType(), VariableModifiers.None)); // Connect node execs - GraphUtil.ConnectExecPins(stringLengthMethod.EntryNode.InitialExecutionPin, stringLengthMethod.ReturnNodes.First().ReturnPin); + GraphUtil.ConnectPins(stringLengthMethod.EntryNode.InitialExecutionPin, stringLengthMethod.ReturnNodes.First().ReturnPin); // Connect node data - GraphUtil.ConnectDataPins(stringLengthMethod.EntryNode.OutputDataPins[0], getLengthNode.InputDataPins[0]); - GraphUtil.ConnectDataPins(getLengthNode.OutputDataPins[0], stringLengthMethod.ReturnNodes.First().InputDataPins[0]); + GraphUtil.ConnectPins(stringLengthMethod.EntryNode.OutputDataPins[0], getLengthNode.InputDataPins[0]); + GraphUtil.ConnectPins(getLengthNode.OutputDataPins[0], stringLengthMethod.ReturnNodes.First().InputDataPins[0]); } public void CreateIfElseMethod() @@ -92,7 +92,7 @@ public void CreateIfElseMethod() for (int i = 0; i < argTypeNodes.Count; i++) { ((MethodEntryNode)ifElseMethod.EntryNode).AddArgument(); - GraphUtil.ConnectTypePins(argTypeNodes[i].OutputTypePins[0], ifElseMethod.EntryNode.InputTypePins[i]); + GraphUtil.ConnectPins(argTypeNodes[i].OutputTypePins[0], ifElseMethod.EntryNode.InputTypePins[i]); } // Add return types @@ -104,7 +104,7 @@ public void CreateIfElseMethod() for (int i = 0; i < returnTypeNodes.Count; i++) { ifElseMethod.MainReturnNode.AddReturnType(); - GraphUtil.ConnectTypePins(returnTypeNodes[i].OutputTypePins[0], ifElseMethod.MainReturnNode.InputTypePins[i]); + GraphUtil.ConnectPins(returnTypeNodes[i].OutputTypePins[0], ifElseMethod.MainReturnNode.InputTypePins[i]); } // Create nodes @@ -112,14 +112,14 @@ public void CreateIfElseMethod() LiteralNode literalNode = LiteralNode.WithValue(ifElseMethod, 123); // Connect exec nodes - GraphUtil.ConnectExecPins(ifElseMethod.EntryNode.InitialExecutionPin, ifElseNode.ExecutionPin); - GraphUtil.ConnectExecPins(ifElseNode.TruePin, ifElseMethod.ReturnNodes.First().ReturnPin); - GraphUtil.ConnectExecPins(ifElseNode.FalsePin, ifElseMethod.ReturnNodes.First().ReturnPin); + GraphUtil.ConnectPins(ifElseMethod.EntryNode.InitialExecutionPin, ifElseNode.ExecutionPin); + GraphUtil.ConnectPins(ifElseNode.TruePin, ifElseMethod.ReturnNodes.First().ReturnPin); + GraphUtil.ConnectPins(ifElseNode.FalsePin, ifElseMethod.ReturnNodes.First().ReturnPin); // Connect node data - GraphUtil.ConnectDataPins(ifElseMethod.EntryNode.OutputDataPins[1], ifElseNode.ConditionPin); - GraphUtil.ConnectDataPins(ifElseMethod.EntryNode.OutputDataPins[0], ifElseMethod.ReturnNodes.First().InputDataPins[0]); - GraphUtil.ConnectDataPins(literalNode.ValuePin, ifElseMethod.ReturnNodes.First().InputDataPins[0]); + GraphUtil.ConnectPins(ifElseMethod.EntryNode.OutputDataPins[1], ifElseNode.ConditionPin); + GraphUtil.ConnectPins(ifElseMethod.EntryNode.OutputDataPins[0], ifElseMethod.ReturnNodes.First().InputDataPins[0]); + GraphUtil.ConnectPins(literalNode.ValuePin, ifElseMethod.ReturnNodes.First().InputDataPins[0]); } public void CreateForLoopMethod() @@ -135,11 +135,11 @@ public void CreateForLoopMethod() ForLoopNode forLoopNode = new ForLoopNode(forLoopMethod); // Connect exec nodes - GraphUtil.ConnectExecPins(forLoopMethod.EntryNode.InitialExecutionPin, forLoopNode.ExecutionPin); - GraphUtil.ConnectExecPins(forLoopNode.CompletedPin, forLoopMethod.ReturnNodes.First().ReturnPin); + GraphUtil.ConnectPins(forLoopMethod.EntryNode.InitialExecutionPin, forLoopNode.ExecutionPin); + GraphUtil.ConnectPins(forLoopNode.CompletedPin, forLoopMethod.ReturnNodes.First().ReturnPin); // Connect node data - GraphUtil.ConnectDataPins(maxIndexLiteralNode.ValuePin, forLoopNode.MaxIndexPin); + GraphUtil.ConnectPins(maxIndexLiteralNode.ValuePin, forLoopNode.MaxIndexPin); } [TestMethod] From 730b9de8c9ca4337dfedb1bf4927885ac9bbdf5c Mon Sep 17 00:00:00 2001 From: Robin Kahlow Date: Tue, 14 May 2019 15:50:43 +0100 Subject: [PATCH 2/4] added pin connection viewmodel, fixed bugs with pin connection changes --- NetPrints/Core/NodeGraph.cs | 28 ++-- NetPrints/Graph/GraphUtil.cs | 46 +++---- NetPrints/Graph/Node.cs | 29 +++-- .../Serialization/SerializationHelper.cs | 9 ++ NetPrintsEditor/Controls/GraphEditorView.xaml | 18 ++- .../Controls/GraphEditorView.xaml.cs | 14 +- .../Converters/ModelToViewModelConverter.cs | 7 +- NetPrintsEditor/ViewModels/NodePinVM.cs | 122 +----------------- NetPrintsEditor/ViewModels/PinConnectionVM.cs | 31 +++++ 9 files changed, 124 insertions(+), 180 deletions(-) create mode 100644 NetPrintsEditor/ViewModels/PinConnectionVM.cs diff --git a/NetPrints/Core/NodeGraph.cs b/NetPrints/Core/NodeGraph.cs index a5a134a..e42f4e6 100644 --- a/NetPrints/Core/NodeGraph.cs +++ b/NetPrints/Core/NodeGraph.cs @@ -43,7 +43,7 @@ public Project Project set; } - public ObservableRangeCollection Connections { get; } = new ObservableRangeCollection(); + public ObservableRangeCollection Connections { get; private set; } = new ObservableRangeCollection(); public NodeGraph() { @@ -51,19 +51,20 @@ public NodeGraph() } [OnDeserialized] - private void OnDeserialized() + private void OnDeserialized(StreamingContext context) { SetupInitialConnections(); } private void SetupInitialConnections() { - Connections.Clear(); + if (Connections is null) + { + Connections = new ObservableRangeCollection(); + } - Connections.AddRange(Nodes.SelectMany( - node => node.Pins.SelectMany( - pin => pin.ConnectedPins.Select( - connPin =>new PinConnection((NodePin)pin, (NodePin)connPin))))); + OnNodeCollectionChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, Nodes, 0)); + Nodes.CollectionChanged += OnNodeCollectionChanged; } private void OnNodeCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -108,7 +109,12 @@ private void OnPinConnectionsCollectionChanged(NodePin fromPin, object sender, N { if (e.OldItems != null) { - Connections.RemoveRange(Connections.Where(conn => e.OldItems.Contains(conn.PinA) || e.OldItems.Contains(conn.PinB)).ToArray()); + var removedConnections = Connections.Where(conn => + (conn.PinA == fromPin && e.OldItems.Contains(conn.PinB)) + || (conn.PinB == fromPin && e.OldItems.Contains(conn.PinA))) + .ToArray(); + + Connections.RemoveRange(removedConnections); } if (e.NewItems != null) @@ -141,7 +147,11 @@ private void SetupPinEvents(NodePin pin, bool add) { if (add) { - Connections.AddRange(pin.ConnectedPins.Select(toPin => new PinConnection((NodePin)pin, (NodePin)toPin))); + if (pin.ConnectionType == NodePinConnectionType.Single) + { + Connections.AddRange(pin.ConnectedPins.Select(toPin => new PinConnection((NodePin)pin, (NodePin)toPin))); + } + pin.ConnectedPins.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) => OnPinConnectionsCollectionChanged(pin, sender, e); } else diff --git a/NetPrints/Graph/GraphUtil.cs b/NetPrints/Graph/GraphUtil.cs index 28806ed..c3eba58 100644 --- a/NetPrints/Graph/GraphUtil.cs +++ b/NetPrints/Graph/GraphUtil.cs @@ -2,6 +2,7 @@ using NetPrints.Core; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; @@ -88,47 +89,30 @@ public static bool CanConnectNodePins(INodePin pinA, INodePin pinB, Func to - if (pinA.ConnectionType == NodePinConnectionType.Single) - { - // Remove other pins' connection to this pin - if (pinA.ConnectedPins.Count > 0) - { - foreach (var otherPin in pinA.ConnectedPins) - { - otherPin.ConnectedPins.Clear(); - } - } + // Remove old connections for single connection pins - pinA.ConnectedPins.Replace(pinB); - } - else + if (pinA.ConnectionType == NodePinConnectionType.Single) { - pinA.ConnectedPins.Add(pinB); + DisconnectPin(pinA); } - // Connect to -> from if (pinB.ConnectionType == NodePinConnectionType.Single) { - // Remove other pins' connection to this pin - if (pinB.ConnectedPins.Count > 0) - { - foreach (var otherPin in pinB.ConnectedPins) - { - otherPin.ConnectedPins.Clear(); - } - } - - pinB.ConnectedPins.Replace(pinA); - } - else - { - pinB.ConnectedPins.Add(pinA); + DisconnectPin(pinB); } + + // Add new connections + pinA.ConnectedPins.Add(pinB); + pinB.ConnectedPins.Add(pinA); } public static void DisconnectPin(INodePin nodePin) { + foreach (var otherPin in nodePin.ConnectedPins) + { + otherPin.ConnectedPins.Remove(nodePin); + } + nodePin.ConnectedPins.Clear(); } @@ -146,6 +130,8 @@ public static void DisconnectNodePins(INode node) public static void DisconnectPins(INodePin a, INodePin b) { + Debug.Assert(a.ConnectedPins.Contains(b) && b.ConnectedPins.Contains(a), "Tried to disconnect two pins which don't connect to each other."); + a.ConnectedPins.Remove(b); b.ConnectedPins.Remove(a); } diff --git a/NetPrints/Graph/Node.cs b/NetPrints/Graph/Node.cs index c202a48..70fc52a 100644 --- a/NetPrints/Graph/Node.cs +++ b/NetPrints/Graph/Node.cs @@ -47,32 +47,32 @@ public abstract class Node : INode /// Input data pins of this node. /// - public IObservableCollectionView InputDataPins { get; } + public IObservableCollectionView InputDataPins { get; private set; } /// /// Output data pins of this node. /// - public IObservableCollectionView OutputDataPins { get; } + public IObservableCollectionView OutputDataPins { get; private set; } /// /// Input execution pins of this node. /// - public IObservableCollectionView InputExecPins { get; } + public IObservableCollectionView InputExecPins { get; private set; } /// /// Output execution pins of this node. /// - public IObservableCollectionView OutputExecPins { get; } + public IObservableCollectionView OutputExecPins { get; private set; } /// /// Input type pins of this node. /// - public IObservableCollectionView InputTypePins { get; } + public IObservableCollectionView InputTypePins { get; private set; } /// /// Output type pins of this node. /// - public IObservableCollectionView OutputTypePins { get; } + public IObservableCollectionView OutputTypePins { get; private set; } /// /// Delegate for the event of a position change of a node. @@ -184,6 +184,16 @@ public NodeGraph Graph INodeGraph INode.Graph => Graph; protected Node(NodeGraph graph) + { + SetupPinViews(); + + Name = NetPrintsUtil.GetUniqueName(GetType().Name, graph.Nodes.Select(n => n.Name).ToList()); + + Graph = graph; + Graph.Nodes.Add(this); + } + + private void SetupPinViews() { static bool isType(object x) => x is T; @@ -193,11 +203,6 @@ protected Node(NodeGraph graph) OutputExecPins = new FilteredObservableCollection(Pins, isType); InputTypePins = new FilteredObservableCollection(Pins, isType); OutputTypePins = new FilteredObservableCollection(Pins, isType); - - Name = NetPrintsUtil.GetUniqueName(GetType().Name, graph.Nodes.Select(n => n.Name).ToList()); - - Graph = graph; - Graph.Nodes.Add(this); } public override string ToString() @@ -295,6 +300,8 @@ protected virtual void OnInputTypeChanged(object sender, EventArgs eventArgs) [OnDeserialized] private void OnDeserializing(StreamingContext context) { + SetupPinViews(); + foreach (var inputTypePin in InputTypePins) { if (inputTypePin.InferredType != null) diff --git a/NetPrints/Serialization/SerializationHelper.cs b/NetPrints/Serialization/SerializationHelper.cs index 825f988..bfbae02 100644 --- a/NetPrints/Serialization/SerializationHelper.cs +++ b/NetPrints/Serialization/SerializationHelper.cs @@ -1,4 +1,7 @@ using NetPrints.Core; +using NetPrints.Graph; +using System; +using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; @@ -10,6 +13,12 @@ public static class SerializationHelper { PreserveObjectReferences = true, MaxItemsInObjectGraph = int.MaxValue, + KnownTypes = new Type[] + { + typeof(Node), + typeof(NodeGraph), + typeof(NodePin) + }, }); /// diff --git a/NetPrintsEditor/Controls/GraphEditorView.xaml b/NetPrintsEditor/Controls/GraphEditorView.xaml index 00b8d3f..ab5bac6 100644 --- a/NetPrintsEditor/Controls/GraphEditorView.xaml +++ b/NetPrintsEditor/Controls/GraphEditorView.xaml @@ -13,6 +13,7 @@ + - + - + + Point1="{Binding Connection.PointB, Converter={StaticResource PointConverter}}" + Point2="{Binding Connection.PointC, Converter={StaticResource PointConverter}}" + Point3="{Binding Connection.PointD, Converter={StaticResource PointConverter}}" /> diff --git a/NetPrintsEditor/Controls/GraphEditorView.xaml.cs b/NetPrintsEditor/Controls/GraphEditorView.xaml.cs index 096e99b..2daec87 100644 --- a/NetPrintsEditor/Controls/GraphEditorView.xaml.cs +++ b/NetPrintsEditor/Controls/GraphEditorView.xaml.cs @@ -476,43 +476,43 @@ private void CablePath_MouseLeave(object sender, MouseEventArgs e) private void CablePath_MouseDown(object sender, MouseButtonEventArgs e) { var element = sender as FrameworkElement; - if (!(element?.DataContext is PinConnection conn)) + if (!(element?.DataContext is PinConnectionVM connViewModel)) { throw new Exception("Could not find cable's pin."); } if (e.ChangedButton == MouseButton.Left && e.LeftButton == MouseButtonState.Pressed && e.ClickCount == 2) { - if (conn.PinA is NodeInputDataPin idp) + if (connViewModel.Connection.PinA is NodeInputDataPin idp) { GraphUtil.AddRerouteNode(idp); } - else if (conn.PinA is NodeInputTypePin itp) + else if (connViewModel.Connection.PinA is NodeInputTypePin itp) { GraphUtil.AddRerouteNode(itp); } - else if (conn.PinA is NodeOutputExecPin oep) + else if (connViewModel.Connection.PinA is NodeOutputExecPin oep) { GraphUtil.AddRerouteNode(oep); } } else if (e.ChangedButton == MouseButton.Middle) { - GraphUtil.DisconnectPins(conn.PinA, conn.PinB); + GraphUtil.DisconnectPins(connViewModel.Connection.PinA, connViewModel.Connection.PinB); } } private void CablePath_MouseUp(object sender, MouseButtonEventArgs e) { var element = sender as FrameworkElement; - if (!(element?.DataContext is NodePinVM pin)) + if (!(element?.DataContext is PinConnectionVM connection)) { throw new Exception("Could not find cable's pin."); } if (e.ChangedButton == MouseButton.XButton1 && e.RightButton == MouseButtonState.Released) { - pin.IsFaint = !pin.IsFaint; + //connection.IsFaint = !connection.IsFaint; } } } diff --git a/NetPrintsEditor/Converters/ModelToViewModelConverter.cs b/NetPrintsEditor/Converters/ModelToViewModelConverter.cs index ff6c11a..c7a7d89 100644 --- a/NetPrintsEditor/Converters/ModelToViewModelConverter.cs +++ b/NetPrintsEditor/Converters/ModelToViewModelConverter.cs @@ -1,4 +1,5 @@ -using NetPrints.Core; +using NetPrints.Base; +using NetPrints.Core; using NetPrints.Graph; using NetPrintsEditor.ViewModels; using System; @@ -31,6 +32,10 @@ public object Convert(object value, Type targetType, object parameter, CultureIn { return new CompilationReferenceVM(reference); } + else if (value is PinConnection connection) + { + return new PinConnectionVM(connection); + } throw new ArgumentException(); } diff --git a/NetPrintsEditor/ViewModels/NodePinVM.cs b/NetPrintsEditor/ViewModels/NodePinVM.cs index 8512618..6701d6b 100644 --- a/NetPrintsEditor/ViewModels/NodePinVM.cs +++ b/NetPrintsEditor/ViewModels/NodePinVM.cs @@ -162,10 +162,7 @@ private void OnInputTypeChanged(object sender, EventArgs eventArgs) RaisePropertyChanged(nameof(ToolTip)); } - public bool IsRerouteNodePin - { - get => Node is RerouteNode; - } + public bool IsRerouteNodePin => Node is RerouteNode; /// /// Disconnects the pin from all of its connections. @@ -343,12 +340,7 @@ public Brush FillBrush get { // Check if the pin is connected to anything - if ((pin is NodeInputDataPin idp && idp.IncomingPin != null) - || (pin is NodeOutputDataPin odp && odp.OutgoingPins.Count > 0) - || (pin is NodeInputExecPin iep && iep.IncomingExecutionPins.Count > 0) - || (pin is NodeOutputExecPin oep && oep.OutgoingExecPin != null) - || (pin is NodeInputTypePin itp && itp.IncomingPin != null) - || (pin is NodeOutputTypePin otp && otp.OutgoingTypePins.Count > 0)) + if (pin.ConnectedPins.Count > 0) { return BorderBrush; } @@ -359,11 +351,11 @@ public Brush FillBrush } } - public bool ShowRectangle => pin is NodeExecPin; + public bool ShowRectangle => pin is INodeExecutionPin; - public bool ShowCircle => pin is NodeDataPin; + public bool ShowCircle => pin is INodeDataPin; - public bool ShowTriangle => pin is NodeTypePin; + public bool ShowTriangle => pin is INodeTypePin; public bool ShowDefaultValueIndicator => pin is NodeInputDataPin idp && idp.UsesExplicitDefaultValue; @@ -401,83 +393,13 @@ public Point NodeRelativePosition private void OnConnectionPositionUpdate() { RaisePropertyChanged(nameof(NodeRelativePosition)); - RaisePropertyChanged(nameof(ConnectedAbsolutePosition)); - RaisePropertyChanged(nameof(ConnectedCP1)); - RaisePropertyChanged(nameof(ConnectedCP2)); RaisePropertyChanged(nameof(AbsolutePosition)); OnAbsolutePositionChanged(); } - private void OnConnectedPinPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(Position) || e.PropertyName == nameof(NodeRelativePosition)) - { - OnConnectionPositionUpdate(); - } - } - - public bool IsConnected - { - get => pin.ConnectedPins.Count > 0; - } - - public bool IsCableVisible - { - get => IsConnected || IsBeingConnected; - } - - private void OnConnectedPinNodePositionChanged(Node node, double posX, double posY) - { - OnConnectionPositionUpdate(); - } - - private const double CPOffset = 100; - - public Point ConnectedCP1 - { - get - { - if (Pin is NodeOutputExecPin || Pin is NodeOutputDataPin || Pin is NodeOutputTypePin) - { - return new Point(AbsolutePosition.X + CPOffset, AbsolutePosition.Y); - } - else - { - return new Point(AbsolutePosition.X - CPOffset, AbsolutePosition.Y); - } - } - } - - public Point ConnectedCP2 - { - get - { - if (Pin is NodeOutputExecPin || Pin is NodeOutputDataPin || Pin is NodeOutputTypePin) - { - return new Point(ConnectedAbsolutePosition.X - CPOffset, ConnectedAbsolutePosition.Y); - } - else - { - return new Point(ConnectedAbsolutePosition.X + CPOffset, ConnectedAbsolutePosition.Y); - } - } - } + public bool IsConnected => pin.ConnectedPins.Count > 0; - public Point ConnectedAbsolutePosition - { - get - { - if (IsBeingConnected) - { - return ConnectingAbsolutePosition; - } - else - { - return AbsolutePosition; - // TODO: return IsConnected ? ConnectedPin.AbsolutePosition : AbsolutePosition; - } - } - } + public bool IsCableVisible => IsBeingConnected; private NodePin pin; @@ -489,36 +411,6 @@ public NodePinVM(NodePin pin) Pin = pin; } - /// - /// Adds a reroute node for this pin. Only valid - /// for input data pins and output execution pins. - /// - public void AddRerouteNode() - { - if (Pin is NodeInputDataPin dataPin) - { - RerouteNode rerouteNode = GraphUtil.AddRerouteNode(dataPin); - rerouteNode.PositionX = (Pin.Node.PositionX + dataPin.IncomingPin.Node.PositionX) / 2; - rerouteNode.PositionY = (Pin.Node.PositionY + dataPin.IncomingPin.Node.PositionY) / 2; - } - else if (Pin is NodeOutputExecPin execPin) - { - RerouteNode rerouteNode = GraphUtil.AddRerouteNode(execPin); - rerouteNode.PositionX = (Pin.Node.PositionX + execPin.OutgoingExecPin.Node.PositionX) / 2; - rerouteNode.PositionY = (Pin.Node.PositionY + execPin.OutgoingExecPin.Node.PositionY) / 2; - } - else if (Pin is NodeInputTypePin typePin) - { - RerouteNode rerouteNode = GraphUtil.AddRerouteNode(typePin); - rerouteNode.PositionX = (Pin.Node.PositionX + typePin.IncomingPin.Node.PositionX) / 2; - rerouteNode.PositionY = (Pin.Node.PositionY + typePin.IncomingPin.Node.PositionY) / 2; - } - else - { - throw new Exception("Can't add reroute node for invalid pin type"); - } - } - private void OnPinChanged() { pin.PropertyChanged += OnPinPropertyChanged; diff --git a/NetPrintsEditor/ViewModels/PinConnectionVM.cs b/NetPrintsEditor/ViewModels/PinConnectionVM.cs new file mode 100644 index 0000000..d99333f --- /dev/null +++ b/NetPrintsEditor/ViewModels/PinConnectionVM.cs @@ -0,0 +1,31 @@ +using GalaSoft.MvvmLight; +using NetPrints.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Media; + +namespace NetPrintsEditor.ViewModels +{ + public class PinConnectionVM : ViewModelBase + { + public PinConnection Connection { get; } + + private static readonly Dictionary typeBrushes = new Dictionary() + { + [typeof(INodeExecutionPin)] = new SolidColorBrush(Color.FromArgb(0xFF, 0xE0, 0xFF, 0xE0)), + [typeof(INodeDataPin)] = new SolidColorBrush(Color.FromArgb(0xFF, 0xE0, 0xE0, 0xFF)), + [typeof(INodeTypePin)] = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xE0, 0xE0)), + }; + + public Brush Brush + { + get => typeBrushes.Single(x => (Connection.PinA.ConnectionType == NodePinConnectionType.Single ? Connection.PinA : Connection.PinB).GetType().GetInterfaces().Any(interf => interf == x.Key)).Value; + } + + public PinConnectionVM(PinConnection connection) + { + Connection = connection; + } + } +} From 59805059d81adf25f412dc5540382e262645dd8e Mon Sep 17 00:00:00 2001 From: Robin Kahlow Date: Tue, 14 May 2019 16:02:32 +0100 Subject: [PATCH 3/4] moved pin connection tracking into class, moved tracking from NodeGraph into its viewmodel --- NetPrints/Core/GraphConnectionTracker.cs | 126 ++++++++++++++++++ NetPrints/Core/NodeGraph.cs | 118 ---------------- NetPrintsEditor/Controls/GraphEditorView.xaml | 2 +- NetPrintsEditor/ViewModels/NodeGraphVM.cs | 8 ++ 4 files changed, 135 insertions(+), 119 deletions(-) create mode 100644 NetPrints/Core/GraphConnectionTracker.cs diff --git a/NetPrints/Core/GraphConnectionTracker.cs b/NetPrints/Core/GraphConnectionTracker.cs new file mode 100644 index 0000000..f82b775 --- /dev/null +++ b/NetPrints/Core/GraphConnectionTracker.cs @@ -0,0 +1,126 @@ +using NetPrints.Base; +using NetPrints.Graph; +using System.Collections.Specialized; +using System.Linq; + +namespace NetPrints.Core +{ + public class GraphConnectionTracker + { + public ObservableRangeCollection Connections { get; private set; } + + private INodeGraph graph; + + public GraphConnectionTracker(INodeGraph graph) + { + this.graph = graph; + + SetupInitialConnections(); + } + + private void SetupInitialConnections() + { + if (Connections is null) + { + Connections = new ObservableRangeCollection(); + } + + OnNodeCollectionChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, graph.Nodes, 0)); + graph.Nodes.CollectionChanged += OnNodeCollectionChanged; + } + + private void OnNodeCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.OldItems != null) + { + foreach (var node in e.OldItems.Cast()) + { + SetupNodeEvents(node, false); + } + } + + if (e.NewItems != null) + { + foreach (var node in e.NewItems.Cast()) + { + SetupNodeEvents(node, true); + } + } + } + + private void OnNodePinsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.OldItems != null) + { + foreach (var pin in e.OldItems.Cast()) + { + SetupPinEvents(pin, false); + } + } + + if (e.NewItems != null) + { + foreach (var pin in e.NewItems.Cast()) + { + SetupPinEvents(pin, true); + } + } + } + + private void OnPinConnectionsCollectionChanged(NodePin fromPin, object sender, NotifyCollectionChangedEventArgs e) + { + if (e.OldItems != null) + { + var removedConnections = Connections.Where(conn => + (conn.PinA == fromPin && e.OldItems.Contains(conn.PinB)) + || (conn.PinB == fromPin && e.OldItems.Contains(conn.PinA))) + .ToArray(); + + Connections.RemoveRange(removedConnections); + } + + if (e.NewItems != null) + { + Connections.AddRange(e.NewItems.Cast() + .Where(pin => pin.ConnectionType == NodePinConnectionType.Single) + .Select(toPin => new PinConnection(fromPin, toPin))); + } + } + + private void SetupNodeEvents(INode node, bool add) + { + if (!add) + { + node.Pins.CollectionChanged -= OnNodePinsCollectionChanged; + } + + foreach (var pin in node.Pins.Cast()) + { + SetupPinEvents(pin, add); + } + + if (add) + { + node.Pins.CollectionChanged += OnNodePinsCollectionChanged; + } + } + + private void SetupPinEvents(NodePin pin, bool add) + { + if (add) + { + if (pin.ConnectionType == NodePinConnectionType.Single) + { + Connections.AddRange(pin.ConnectedPins.Select(toPin => new PinConnection((NodePin)pin, (NodePin)toPin))); + } + + pin.ConnectedPins.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) => OnPinConnectionsCollectionChanged(pin, sender, e); + } + else + { + //pin.ConnectedPins.CollectionChanged -= (object sender, NotifyCollectionChangedEventArgs e) => OnPinConnectionsCollectionChanged(pin, sender, e); + Connections.RemoveRange(Connections.Where(conn => conn.PinA == pin || conn.PinB == pin).ToArray()); + } + } + } +} diff --git a/NetPrints/Core/NodeGraph.cs b/NetPrints/Core/NodeGraph.cs index e42f4e6..a35c15b 100644 --- a/NetPrints/Core/NodeGraph.cs +++ b/NetPrints/Core/NodeGraph.cs @@ -42,123 +42,5 @@ public Project Project get; set; } - - public ObservableRangeCollection Connections { get; private set; } = new ObservableRangeCollection(); - - public NodeGraph() - { - Nodes.CollectionChanged += OnNodeCollectionChanged; - } - - [OnDeserialized] - private void OnDeserialized(StreamingContext context) - { - SetupInitialConnections(); - } - - private void SetupInitialConnections() - { - if (Connections is null) - { - Connections = new ObservableRangeCollection(); - } - - OnNodeCollectionChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, Nodes, 0)); - Nodes.CollectionChanged += OnNodeCollectionChanged; - } - - private void OnNodeCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (e.OldItems != null) - { - foreach (var node in e.OldItems.Cast()) - { - SetupNodeEvents(node, false); - } - } - - if (e.NewItems != null) - { - foreach (var node in e.NewItems.Cast()) - { - SetupNodeEvents(node, true); - } - } - } - - private void OnNodePinsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (e.OldItems != null) - { - foreach (var pin in e.OldItems.Cast()) - { - SetupPinEvents(pin, false); - } - } - - if (e.NewItems != null) - { - foreach (var pin in e.NewItems.Cast()) - { - SetupPinEvents(pin, true); - } - } - } - - private void OnPinConnectionsCollectionChanged(NodePin fromPin, object sender, NotifyCollectionChangedEventArgs e) - { - if (e.OldItems != null) - { - var removedConnections = Connections.Where(conn => - (conn.PinA == fromPin && e.OldItems.Contains(conn.PinB)) - || (conn.PinB == fromPin && e.OldItems.Contains(conn.PinA))) - .ToArray(); - - Connections.RemoveRange(removedConnections); - } - - if (e.NewItems != null) - { - Connections.AddRange(e.NewItems.Cast() - .Where(pin => pin.ConnectionType == NodePinConnectionType.Single) - .Select(toPin => new PinConnection(fromPin, toPin))); - } - } - - private void SetupNodeEvents(INode node, bool add) - { - if (!add) - { - node.Pins.CollectionChanged -= OnNodePinsCollectionChanged; - } - - foreach (var pin in node.Pins.Cast()) - { - SetupPinEvents(pin, add); - } - - if (add) - { - node.Pins.CollectionChanged += OnNodePinsCollectionChanged; - } - } - - private void SetupPinEvents(NodePin pin, bool add) - { - if (add) - { - if (pin.ConnectionType == NodePinConnectionType.Single) - { - Connections.AddRange(pin.ConnectedPins.Select(toPin => new PinConnection((NodePin)pin, (NodePin)toPin))); - } - - pin.ConnectedPins.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) => OnPinConnectionsCollectionChanged(pin, sender, e); - } - else - { - //pin.ConnectedPins.CollectionChanged -= (object sender, NotifyCollectionChangedEventArgs e) => OnPinConnectionsCollectionChanged(pin, sender, e); - Connections.RemoveRange(Connections.Where(conn => conn.PinA == pin || conn.PinB == pin).ToArray()); - } - } } } diff --git a/NetPrintsEditor/Controls/GraphEditorView.xaml b/NetPrintsEditor/Controls/GraphEditorView.xaml index ab5bac6..b9b8905 100644 --- a/NetPrintsEditor/Controls/GraphEditorView.xaml +++ b/NetPrintsEditor/Controls/GraphEditorView.xaml @@ -69,7 +69,7 @@ - + diff --git a/NetPrintsEditor/ViewModels/NodeGraphVM.cs b/NetPrintsEditor/ViewModels/NodeGraphVM.cs index 2828d57..f3ceabd 100644 --- a/NetPrintsEditor/ViewModels/NodeGraphVM.cs +++ b/NetPrintsEditor/ViewModels/NodeGraphVM.cs @@ -71,6 +71,11 @@ public IEnumerable Suggestions }, }; + public ObservableRangeCollection Connections => connectionTracker.Connections; + + private GraphConnectionTracker connectionTracker; + + public SuggestionListVM SuggestionViewModel { get; } = new SuggestionListVM(); public event EventHandler OnHideContextMenu; @@ -476,6 +481,9 @@ public NodeGraph Graph Nodes.CollectionChanged += OnNodeCollectionChanged; Nodes.ToList().ForEach(n => SetupNodeEvents(n, true)); } + + connectionTracker = new GraphConnectionTracker(graph); + RaisePropertyChanged(nameof(Connections)); } } } From fede4437c2204387d023a5a01c16c5e3a80debac Mon Sep 17 00:00:00 2001 From: Robin Kahlow Date: Tue, 14 May 2019 16:23:35 +0100 Subject: [PATCH 4/4] moved suggestion generation from viewmodel into new class --- NetPrints/Graph/GraphUtil.cs | 2 +- .../Reflection/ISuggestionGenerator.cs | 10 + .../Reflection/SuggestionGenerator.cs | 250 ++++++++++++++++ NetPrintsEditor/ViewModels/NodeGraphVM.cs | 271 ++---------------- 4 files changed, 281 insertions(+), 252 deletions(-) create mode 100644 NetPrintsEditor/Reflection/ISuggestionGenerator.cs create mode 100644 NetPrintsEditor/Reflection/SuggestionGenerator.cs diff --git a/NetPrints/Graph/GraphUtil.cs b/NetPrints/Graph/GraphUtil.cs index c3eba58..611174a 100644 --- a/NetPrints/Graph/GraphUtil.cs +++ b/NetPrints/Graph/GraphUtil.cs @@ -330,7 +330,7 @@ public static void ConnectRelevantPins(NodePin pin, Node node, Func GetSuggestions(INodeGraph graph, INodePin pin = null); + } +} diff --git a/NetPrintsEditor/Reflection/SuggestionGenerator.cs b/NetPrintsEditor/Reflection/SuggestionGenerator.cs new file mode 100644 index 0000000..f283c67 --- /dev/null +++ b/NetPrintsEditor/Reflection/SuggestionGenerator.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NetPrints.Base; +using NetPrints.Core; +using NetPrints.Graph; +using NetPrintsEditor.Dialogs; + +namespace NetPrintsEditor.Reflection +{ + public class SuggestionGenerator : ISuggestionGenerator + { + private readonly Dictionary> builtInNodes = new Dictionary>() + { + [typeof(MethodGraph)] = new List() + { + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + }, + [typeof(ConstructorGraph)] = new List() + { + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + }, + [typeof(ClassGraph)] = new List() + { + TypeSpecifier.FromType(), + TypeSpecifier.FromType(), + }, + }; + + private List GetBuiltInNodes(NodeGraph graph) + { + if (builtInNodes.TryGetValue(graph.GetType(), out var nodes)) + { + return nodes; + } + + return new List(); + } + + public IEnumerable<(string, object)> GetSuggestions(INodeGraph graph, INodePin pin = null) + { + // Show all relevant methods for the type of the pin + IEnumerable<(string, object)> suggestions = new (string, object)[0]; + + void AddSuggestionsWithCategory(string category, IEnumerable newSuggestions) + { + suggestions = suggestions.Concat(newSuggestions.Select(suggestion => (category, suggestion))); + } + + NodeGraph nodeGraph = (NodeGraph)graph; + + if (pin != null) + { + if (pin is NodeOutputDataPin odp) + { + if (odp.PinType.Value is TypeSpecifier pinTypeSpec) + { + // Add make delegate + AddSuggestionsWithCategory("NetPrints", new[] { new MakeDelegateTypeInfo(pinTypeSpec, nodeGraph.Class.Type) }); + + // Add variables and methods of the pin type + AddSuggestionsWithCategory("Pin Variables", + App.ReflectionProvider.GetVariables( + new ReflectionProviderVariableQuery() + .WithType(pinTypeSpec) + .WithVisibleFrom(nodeGraph.Class.Type) + .WithStatic(false))); + + AddSuggestionsWithCategory("Pin Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithVisibleFrom(nodeGraph.Class.Type) + .WithStatic(false) + .WithType(pinTypeSpec))); + + // Add methods of the base types that can accept the pin type as argument + foreach (var baseType in nodeGraph.Class.AllBaseTypes) + { + AddSuggestionsWithCategory("This Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithVisibleFrom(nodeGraph.Class.Type) + .WithStatic(false) + .WithArgumentType(pinTypeSpec) + .WithType(baseType))); + } + + // Add static functions taking the type of the pin + AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithArgumentType(pinTypeSpec) + .WithVisibleFrom(nodeGraph.Class.Type) + .WithStatic(true))); + } + } + else if (pin is NodeInputDataPin idp) + { + if (idp.PinType.Value is TypeSpecifier pinTypeSpec) + { + // Variables of base classes that inherit from needed type + foreach (var baseType in nodeGraph.Class.AllBaseTypes) + { + AddSuggestionsWithCategory("This Variables", App.ReflectionProvider.GetVariables( + new ReflectionProviderVariableQuery() + .WithType(baseType) + .WithVisibleFrom(nodeGraph.Class.Type) + .WithVariableType(pinTypeSpec, true))); + } + + // Add static functions returning the type of the pin + AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithStatic(true) + .WithVisibleFrom(nodeGraph.Class.Type) + .WithReturnType(pinTypeSpec))); + } + } + else if (pin is NodeOutputExecPin oxp) + { + GraphUtil.DisconnectPin(oxp); + + AddSuggestionsWithCategory("NetPrints", GetBuiltInNodes(nodeGraph)); + + foreach (var baseType in nodeGraph.Class.AllBaseTypes) + { + AddSuggestionsWithCategory("This Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithType(baseType) + .WithStatic(false) + .WithVisibleFrom(nodeGraph.Class.Type))); + } + + AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithStatic(true) + .WithVisibleFrom(nodeGraph.Class.Type))); + } + else if (pin is NodeInputExecPin ixp) + { + AddSuggestionsWithCategory("NetPrints", GetBuiltInNodes(nodeGraph)); + + foreach (var baseType in nodeGraph.Class.AllBaseTypes) + { + AddSuggestionsWithCategory("This Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithType(baseType) + .WithStatic(false) + .WithVisibleFrom(nodeGraph.Class.Type))); + } + + AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithStatic(true) + .WithVisibleFrom(nodeGraph.Class.Type))); + } + else if (pin is NodeInputTypePin itp) + { + // TODO: Consider static types + AddSuggestionsWithCategory("Types", App.ReflectionProvider.GetNonStaticTypes()); + } + else if (pin is NodeOutputTypePin otp) + { + if (nodeGraph is ExecutionGraph && otp.InferredType.Value is TypeSpecifier typeSpecifier) + { + AddSuggestionsWithCategory("Pin Static Methods", App.ReflectionProvider + .GetMethods(new ReflectionProviderMethodQuery() + .WithType(typeSpecifier) + .WithStatic(true) + .WithVisibleFrom(nodeGraph.Class.Type))); + } + + // Types with type parameters + AddSuggestionsWithCategory("Generic Types", App.ReflectionProvider.GetNonStaticTypes() + .Where(t => t.GenericArguments.Any())); + + if (nodeGraph is ExecutionGraph) + { + // Public static methods that have type parameters + AddSuggestionsWithCategory("Generic Static Methods", App.ReflectionProvider + .GetMethods(new ReflectionProviderMethodQuery() + .WithStatic(true) + .WithHasGenericArguments(true) + .WithVisibleFrom(nodeGraph.Class.Type))); + } + } + } + else + { + AddSuggestionsWithCategory("NetPrints", GetBuiltInNodes(nodeGraph)); + + if (nodeGraph is ExecutionGraph) + { + // Get properties and methods of base class. + foreach (var baseType in nodeGraph.Class.AllBaseTypes) + { + AddSuggestionsWithCategory("This Variables", App.ReflectionProvider.GetVariables( + new ReflectionProviderVariableQuery() + .WithVisibleFrom(nodeGraph.Class.Type) + .WithType(baseType) + .WithStatic(false))); + + AddSuggestionsWithCategory("This Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithType(baseType) + .WithVisibleFrom(nodeGraph.Class.Type) + .WithStatic(false))); + } + + AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( + new ReflectionProviderMethodQuery() + .WithStatic(true) + .WithVisibleFrom(nodeGraph.Class.Type))); + + AddSuggestionsWithCategory("Static Variables", App.ReflectionProvider.GetVariables( + new ReflectionProviderVariableQuery() + .WithStatic(true) + .WithVisibleFrom(nodeGraph.Class.Type))); + } + else if (nodeGraph is ClassGraph) + { + AddSuggestionsWithCategory("Types", App.ReflectionProvider.GetNonStaticTypes()); + } + } + + return suggestions.Distinct(); + } + } +} diff --git a/NetPrintsEditor/ViewModels/NodeGraphVM.cs b/NetPrintsEditor/ViewModels/NodeGraphVM.cs index f3ceabd..fda3307 100644 --- a/NetPrintsEditor/ViewModels/NodeGraphVM.cs +++ b/NetPrintsEditor/ViewModels/NodeGraphVM.cs @@ -30,65 +30,15 @@ public IEnumerable Suggestions /// public NodePin SuggestionPin { get; set; } - private readonly Dictionary> builtInNodes = new Dictionary>() - { - [typeof(MethodGraph)] = new List() - { - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - }, - [typeof(ConstructorGraph)] = new List() - { - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - }, - [typeof(ClassGraph)] = new List() - { - TypeSpecifier.FromType(), - TypeSpecifier.FromType(), - }, - }; - public ObservableRangeCollection Connections => connectionTracker.Connections; private GraphConnectionTracker connectionTracker; - public SuggestionListVM SuggestionViewModel { get; } = new SuggestionListVM(); public event EventHandler OnHideContextMenu; - private List GetBuiltInNodes(NodeGraph graph) - { - if (builtInNodes.TryGetValue(graph.GetType(), out var nodes)) - { - return nodes; - } - - return new List(); - } + private readonly ISuggestionGenerator suggestionGenerator = new SuggestionGenerator(); public void UpdateSuggestions(double mouseX, double mouseY) { @@ -98,188 +48,7 @@ public void UpdateSuggestions(double mouseX, double mouseY) SuggestionViewModel.SuggestionPin = SuggestionPin; SuggestionViewModel.HideContextMenu = () => OnHideContextMenu?.Invoke(this, EventArgs.Empty); - // Show all relevant methods for the type of the pin - IEnumerable<(string, object)> suggestions = new (string, object)[0]; - - void AddSuggestionsWithCategory(string category, IEnumerable newSuggestions) - { - suggestions = suggestions.Concat(newSuggestions.Select(suggestion => (category, suggestion))); - } - - if (SuggestionPin != null) - { - if (SuggestionPin is NodeOutputDataPin odp) - { - if (odp.PinType.Value is TypeSpecifier pinTypeSpec) - { - // Add make delegate - AddSuggestionsWithCategory("NetPrints", new[] { new MakeDelegateTypeInfo(pinTypeSpec, Graph.Class.Type) }); - - // Add variables and methods of the pin type - AddSuggestionsWithCategory("Pin Variables", - App.ReflectionProvider.GetVariables( - new ReflectionProviderVariableQuery() - .WithType(pinTypeSpec) - .WithVisibleFrom(Graph.Class.Type) - .WithStatic(false))); - - AddSuggestionsWithCategory("Pin Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithVisibleFrom(Graph.Class.Type) - .WithStatic(false) - .WithType(pinTypeSpec))); - - // Add methods of the base types that can accept the pin type as argument - foreach (var baseType in Graph.Class.AllBaseTypes) - { - AddSuggestionsWithCategory("This Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithVisibleFrom(Graph.Class.Type) - .WithStatic(false) - .WithArgumentType(pinTypeSpec) - .WithType(baseType))); - } - - // Add static functions taking the type of the pin - AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithArgumentType(pinTypeSpec) - .WithVisibleFrom(Graph.Class.Type) - .WithStatic(true))); - } - } - else if (SuggestionPin is NodeInputDataPin idp) - { - if (idp.PinType.Value is TypeSpecifier pinTypeSpec) - { - // Variables of base classes that inherit from needed type - foreach (var baseType in Graph.Class.AllBaseTypes) - { - AddSuggestionsWithCategory("This Variables", App.ReflectionProvider.GetVariables( - new ReflectionProviderVariableQuery() - .WithType(baseType) - .WithVisibleFrom(Graph.Class.Type) - .WithVariableType(pinTypeSpec, true))); - } - - // Add static functions returning the type of the pin - AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithStatic(true) - .WithVisibleFrom(Graph.Class.Type) - .WithReturnType(pinTypeSpec))); - } - } - else if (SuggestionPin is NodeOutputExecPin oxp) - { - GraphUtil.DisconnectPin(oxp); - - AddSuggestionsWithCategory("NetPrints", GetBuiltInNodes(Graph)); - - foreach (var baseType in Graph.Class.AllBaseTypes) - { - AddSuggestionsWithCategory("This Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithType(baseType) - .WithStatic(false) - .WithVisibleFrom(Graph.Class.Type))); - } - - AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithStatic(true) - .WithVisibleFrom(Graph.Class.Type))); - } - else if (SuggestionPin is NodeInputExecPin ixp) - { - AddSuggestionsWithCategory("NetPrints", GetBuiltInNodes(Graph)); - - foreach (var baseType in Graph.Class.AllBaseTypes) - { - AddSuggestionsWithCategory("This Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithType(baseType) - .WithStatic(false) - .WithVisibleFrom(Graph.Class.Type))); - } - - AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithStatic(true) - .WithVisibleFrom(Graph.Class.Type))); - } - else if (SuggestionPin is NodeInputTypePin itp) - { - // TODO: Consider static types - AddSuggestionsWithCategory("Types", App.ReflectionProvider.GetNonStaticTypes()); - } - else if (SuggestionPin is NodeOutputTypePin otp) - { - if (Graph is ExecutionGraph && otp.InferredType.Value is TypeSpecifier typeSpecifier) - { - AddSuggestionsWithCategory("Pin Static Methods", App.ReflectionProvider - .GetMethods(new ReflectionProviderMethodQuery() - .WithType(typeSpecifier) - .WithStatic(true) - .WithVisibleFrom(Graph.Class.Type))); - } - - // Types with type parameters - AddSuggestionsWithCategory("Generic Types", App.ReflectionProvider.GetNonStaticTypes() - .Where(t => t.GenericArguments.Any())); - - if (Graph is ExecutionGraph) - { - // Public static methods that have type parameters - AddSuggestionsWithCategory("Generic Static Methods", App.ReflectionProvider - .GetMethods(new ReflectionProviderMethodQuery() - .WithStatic(true) - .WithHasGenericArguments(true) - .WithVisibleFrom(Graph.Class.Type))); - } - } - - Suggestions = suggestions.Distinct().Select(x => new SearchableComboBoxItem(x.Item1, x.Item2)); - } - else - { - AddSuggestionsWithCategory("NetPrints", GetBuiltInNodes(Graph)); - - if (Graph is ExecutionGraph) - { - // Get properties and methods of base class. - foreach (var baseType in Graph.Class.AllBaseTypes) - { - AddSuggestionsWithCategory("This Variables", App.ReflectionProvider.GetVariables( - new ReflectionProviderVariableQuery() - .WithVisibleFrom(Graph.Class.Type) - .WithType(baseType) - .WithStatic(false))); - - AddSuggestionsWithCategory("This Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithType(baseType) - .WithVisibleFrom(Graph.Class.Type) - .WithStatic(false))); - } - - AddSuggestionsWithCategory("Static Methods", App.ReflectionProvider.GetMethods( - new ReflectionProviderMethodQuery() - .WithStatic(true) - .WithVisibleFrom(Graph.Class.Type))); - - AddSuggestionsWithCategory("Static Variables", App.ReflectionProvider.GetVariables( - new ReflectionProviderVariableQuery() - .WithStatic(true) - .WithVisibleFrom(Graph.Class.Type))); - } - else if (Graph is ClassGraph) - { - AddSuggestionsWithCategory("Types", App.ReflectionProvider.GetNonStaticTypes()); - } - } - - Suggestions = suggestions.Distinct().Select(x => new SearchableComboBoxItem(x.Item1, x.Item2)); + Suggestions = suggestionGenerator.GetSuggestions(Graph, SuggestionPin).Select(x => new SearchableComboBoxItem(x.Item1, x.Item2)); } public IEnumerable SelectedNodes @@ -372,8 +141,20 @@ public MemberVisibility Visibility MemberVisibility.Protected, MemberVisibility.Public, }; + + #region Node dragging + public double NodeDragScale + { + get; + set; + } = 1; + + private double nodeDragAccumX; + private double nodeDragAccumY; + + private readonly Dictionary nodeStartPositions = new Dictionary(); - private void SetupNodeEvents(NodeVM node, bool add) + private void SetupNodeDragEvents(NodeVM node, bool add) { if (add) { @@ -387,19 +168,7 @@ private void SetupNodeEvents(NodeVM node, bool add) node.OnDragEnd -= OnNodeDragEnd; node.OnDragMove -= OnNodeDragMove; } - } - - #region Node dragging - public double NodeDragScale - { - get; - set; - } = 1; - - private double nodeDragAccumX; - private double nodeDragAccumY; - - private readonly Dictionary nodeStartPositions = new Dictionary(); + } /// /// Called when a node starts dragging. @@ -467,7 +236,7 @@ public NodeGraph Graph if (graph != null) { Nodes.CollectionChanged -= OnNodeCollectionChanged; - Nodes.ToList().ForEach(n => SetupNodeEvents(n, false)); + Nodes.ToList().ForEach(n => SetupNodeDragEvents(n, false)); } graph = value; @@ -479,7 +248,7 @@ public NodeGraph Graph if (graph != null) { Nodes.CollectionChanged += OnNodeCollectionChanged; - Nodes.ToList().ForEach(n => SetupNodeEvents(n, true)); + Nodes.ToList().ForEach(n => SetupNodeDragEvents(n, true)); } connectionTracker = new GraphConnectionTracker(graph); @@ -495,13 +264,13 @@ private void OnNodeCollectionChanged(object sender, NotifyCollectionChangedEvent if (e.OldItems != null) { var removedNodes = e.OldItems.Cast(); - removedNodes.ToList().ForEach(n => SetupNodeEvents(n, false)); + removedNodes.ToList().ForEach(n => SetupNodeDragEvents(n, false)); } if (e.NewItems != null) { var addedNodes = e.NewItems.Cast(); - addedNodes.ToList().ForEach(n => SetupNodeEvents(n, true)); + addedNodes.ToList().ForEach(n => SetupNodeDragEvents(n, true)); } RaisePropertyChanged(nameof(AllPins));