diff --git a/NodeGraph.NET6/Controls/CanvasMouseEventArgs.cs b/NodeGraph.NET6/Controls/CanvasMouseEventArgs.cs index 6115406..5db1c21 100644 --- a/NodeGraph.NET6/Controls/CanvasMouseEventArgs.cs +++ b/NodeGraph.NET6/Controls/CanvasMouseEventArgs.cs @@ -1,10 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; -using System.Windows.Input; namespace NodeGraph.NET6.Controls { diff --git a/NodeGraph.NET6/Controls/DefaultNode.cs b/NodeGraph.NET6/Controls/DefaultNode.cs index 6225ce7..3ffa9cb 100644 --- a/NodeGraph.NET6/Controls/DefaultNode.cs +++ b/NodeGraph.NET6/Controls/DefaultNode.cs @@ -1,21 +1,10 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Data; -using System.Windows.Documents; using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; namespace NodeGraph.NET6.Controls { diff --git a/NodeGraph.NET6/Controls/GridCanvas.cs b/NodeGraph.NET6/Controls/GridCanvas.cs index 2afd1be..7a2fbf1 100644 --- a/NodeGraph.NET6/Controls/GridCanvas.cs +++ b/NodeGraph.NET6/Controls/GridCanvas.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; diff --git a/NodeGraph.NET6/Controls/GroupNode.cs b/NodeGraph.NET6/Controls/GroupNode.cs index f27355e..047bd66 100644 --- a/NodeGraph.NET6/Controls/GroupNode.cs +++ b/NodeGraph.NET6/Controls/GroupNode.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; diff --git a/NodeGraph.NET6/Controls/ICanvasObject.cs b/NodeGraph.NET6/Controls/ICanvasObject.cs index f676444..8da7831 100644 --- a/NodeGraph.NET6/Controls/ICanvasObject.cs +++ b/NodeGraph.NET6/Controls/ICanvasObject.cs @@ -1,10 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; namespace NodeGraph.NET6.Controls { diff --git a/NodeGraph.NET6/Controls/ISelectableObject.cs b/NodeGraph.NET6/Controls/ISelectableObject.cs index 287f98d..c530aa0 100644 --- a/NodeGraph.NET6/Controls/ISelectableObject.cs +++ b/NodeGraph.NET6/Controls/ISelectableObject.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; namespace NodeGraph.NET6.Controls { diff --git a/NodeGraph.NET6/Controls/NodeBase.cs b/NodeGraph.NET6/Controls/NodeBase.cs index 0deb561..5a7734f 100644 --- a/NodeGraph.NET6/Controls/NodeBase.cs +++ b/NodeGraph.NET6/Controls/NodeBase.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; diff --git a/NodeGraph.NET6/Controls/NodeConnector.cs b/NodeGraph.NET6/Controls/NodeConnector.cs index bb904de..de8fac5 100644 --- a/NodeGraph.NET6/Controls/NodeConnector.cs +++ b/NodeGraph.NET6/Controls/NodeConnector.cs @@ -1,11 +1,7 @@ -using Livet; -using Livet.EventListeners; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; diff --git a/NodeGraph.NET6/Controls/NodeGraph.xaml b/NodeGraph.NET6/Controls/NodeGraph.xaml index bda13f2..e94a906 100644 --- a/NodeGraph.NET6/Controls/NodeGraph.xaml +++ b/NodeGraph.NET6/Controls/NodeGraph.xaml @@ -1,7 +1,6 @@ diff --git a/NodeGraph.NET6/Controls/NodeInput.cs b/NodeGraph.NET6/Controls/NodeInput.cs index 2c7af73..7ed4aa3 100644 --- a/NodeGraph.NET6/Controls/NodeInput.cs +++ b/NodeGraph.NET6/Controls/NodeInput.cs @@ -1,16 +1,6 @@ -using Livet; -using Livet.EventListeners; -using NodeGraph.NET6.Utilities; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using NodeGraph.NET6.Utilities; using System.Windows; using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Media; namespace NodeGraph.NET6.Controls { diff --git a/NodeGraph.NET6/Controls/NodeLink.cs b/NodeGraph.NET6/Controls/NodeLink.cs index 45991cf..b81e26b 100644 --- a/NodeGraph.NET6/Controls/NodeLink.cs +++ b/NodeGraph.NET6/Controls/NodeLink.cs @@ -1,14 +1,9 @@ using NodeGraph.NET6.Extensions; using System; -using System.Collections.Generic; using System.Data; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; -using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; diff --git a/NodeGraph.NET6/Controls/NodeOuput.cs b/NodeGraph.NET6/Controls/NodeOuput.cs index 0e3466f..48cb23d 100644 --- a/NodeGraph.NET6/Controls/NodeOuput.cs +++ b/NodeGraph.NET6/Controls/NodeOuput.cs @@ -1,13 +1,6 @@ using NodeGraph.NET6.Utilities; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using System.Windows.Controls.Primitives; namespace NodeGraph.NET6.Controls { diff --git a/NodeGraph.NET6/Controls/RangeSelector.cs b/NodeGraph.NET6/Controls/RangeSelector.cs index d34ea10..20cbe26 100644 --- a/NodeGraph.NET6/Controls/RangeSelector.cs +++ b/NodeGraph.NET6/Controls/RangeSelector.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; using System.Windows.Media; using System.Windows.Shapes; diff --git a/NodeGraph.NET6/Controls/Ruler.cs b/NodeGraph.NET6/Controls/Ruler.cs index fc27199..1624f16 100644 --- a/NodeGraph.NET6/Controls/Ruler.cs +++ b/NodeGraph.NET6/Controls/Ruler.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; diff --git a/NodeGraph.NET6/Controls/StaticDefinition.cs b/NodeGraph.NET6/Controls/StaticDefinition.cs index 9d0cc8a..e985d88 100644 --- a/NodeGraph.NET6/Controls/StaticDefinition.cs +++ b/NodeGraph.NET6/Controls/StaticDefinition.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace NodeGraph.NET6.Controls +namespace NodeGraph.NET6.Controls { public static class ControlSize { diff --git a/NodeGraph.NET6/Converters/InverseBooleanToVisibilityConverter.cs b/NodeGraph.NET6/Converters/InverseBooleanToVisibilityConverter.cs index 4edd083..147e866 100644 --- a/NodeGraph.NET6/Converters/InverseBooleanToVisibilityConverter.cs +++ b/NodeGraph.NET6/Converters/InverseBooleanToVisibilityConverter.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Data; diff --git a/NodeGraph.NET6/Extensions/PointExtension.cs b/NodeGraph.NET6/Extensions/PointExtension.cs index 26f40dc..172066c 100644 --- a/NodeGraph.NET6/Extensions/PointExtension.cs +++ b/NodeGraph.NET6/Extensions/PointExtension.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; namespace NodeGraph.NET6.Extensions { diff --git a/NodeGraph.NET6/Extensions/VectorExtension.cs b/NodeGraph.NET6/Extensions/VectorExtension.cs index ac6a5d9..bac356c 100644 --- a/NodeGraph.NET6/Extensions/VectorExtension.cs +++ b/NodeGraph.NET6/Extensions/VectorExtension.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; namespace NodeGraph.NET6.Extensions { diff --git a/NodeGraph.NET6/Operation/BeginMoveNodesOperationEventArgs.cs b/NodeGraph.NET6/Operation/BeginMoveNodesOperationEventArgs.cs index dd3ecda..141b610 100644 --- a/NodeGraph.NET6/Operation/BeginMoveNodesOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/BeginMoveNodesOperationEventArgs.cs @@ -1,14 +1,10 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.Operation { public class BeginMoveNodesOperationEventArgs : EventArgs { - public Guid[] NodeGuids { get; } = null; + public Guid[] NodeGuids { get; } = null!; public BeginMoveNodesOperationEventArgs(Guid[] nodeGuids) { diff --git a/NodeGraph.NET6/Operation/ConnectOperationEventArgs.cs b/NodeGraph.NET6/Operation/ConnectOperationEventArgs.cs index 191d42e..3144e3e 100644 --- a/NodeGraph.NET6/Operation/ConnectOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/ConnectOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.Operation { diff --git a/NodeGraph.NET6/Operation/ConnectedLinkOperationEventArgs.cs b/NodeGraph.NET6/Operation/ConnectedLinkOperationEventArgs.cs index 26a2bee..ffc38c8 100644 --- a/NodeGraph.NET6/Operation/ConnectedLinkOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/ConnectedLinkOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.Operation { diff --git a/NodeGraph.NET6/Operation/DisconnectOperationEventArgs.cs b/NodeGraph.NET6/Operation/DisconnectOperationEventArgs.cs index 1f7c8a8..787d672 100644 --- a/NodeGraph.NET6/Operation/DisconnectOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/DisconnectOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.Operation { diff --git a/NodeGraph.NET6/Operation/DisconnectedLinkOperationEventArgs.cs b/NodeGraph.NET6/Operation/DisconnectedLinkOperationEventArgs.cs index 87a9d77..5d36ff1 100644 --- a/NodeGraph.NET6/Operation/DisconnectedLinkOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/DisconnectedLinkOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.Operation { diff --git a/NodeGraph.NET6/Operation/EndMoveNodesOperationEventArgs.cs b/NodeGraph.NET6/Operation/EndMoveNodesOperationEventArgs.cs index 25e4892..e66c80b 100644 --- a/NodeGraph.NET6/Operation/EndMoveNodesOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/EndMoveNodesOperationEventArgs.cs @@ -1,9 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; namespace NodeGraph.NET6.Operation { diff --git a/NodeGraph.NET6/Operation/MovedNodesOperationEventArgs.cs b/NodeGraph.NET6/Operation/MovedNodesOperationEventArgs.cs index 58be98a..9c7561d 100644 --- a/NodeGraph.NET6/Operation/MovedNodesOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/MovedNodesOperationEventArgs.cs @@ -1,9 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; namespace NodeGraph.NET6.Operation { diff --git a/NodeGraph.NET6/Operation/PreviewConnectLinkOperationEventArgs.cs b/NodeGraph.NET6/Operation/PreviewConnectLinkOperationEventArgs.cs index d6e7572..eb212ef 100644 --- a/NodeGraph.NET6/Operation/PreviewConnectLinkOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/PreviewConnectLinkOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.Operation { diff --git a/NodeGraph.NET6/Operation/PreviewConnectOperationEventArgs.cs b/NodeGraph.NET6/Operation/PreviewConnectOperationEventArgs.cs index 23eecb8..869bfd4 100644 --- a/NodeGraph.NET6/Operation/PreviewConnectOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/PreviewConnectOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.Operation { diff --git a/NodeGraph.NET6/Operation/StartMoveNodesOperationEventArgs.cs b/NodeGraph.NET6/Operation/StartMoveNodesOperationEventArgs.cs index 4588452..64471b8 100644 --- a/NodeGraph.NET6/Operation/StartMoveNodesOperationEventArgs.cs +++ b/NodeGraph.NET6/Operation/StartMoveNodesOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.Operation { diff --git a/NodeGraph.NET6/OperationEventArgs/BeginMoveNodesOperationEventArgs.cs b/NodeGraph.NET6/OperationEventArgs/BeginMoveNodesOperationEventArgs.cs index 2fbf6e4..3de5c6f 100644 --- a/NodeGraph.NET6/OperationEventArgs/BeginMoveNodesOperationEventArgs.cs +++ b/NodeGraph.NET6/OperationEventArgs/BeginMoveNodesOperationEventArgs.cs @@ -1,14 +1,10 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.OperationEventArgs { public class BeginMoveNodesOperationEventArgs : EventArgs { - public Guid[] NodeGuids { get; } = null; + public Guid[] NodeGuids { get; } = null!; public BeginMoveNodesOperationEventArgs(Guid[] nodeGuids) { diff --git a/NodeGraph.NET6/OperationEventArgs/ConnectedLinkOperationEventArgs.cs b/NodeGraph.NET6/OperationEventArgs/ConnectedLinkOperationEventArgs.cs index e09ea11..7860c3b 100644 --- a/NodeGraph.NET6/OperationEventArgs/ConnectedLinkOperationEventArgs.cs +++ b/NodeGraph.NET6/OperationEventArgs/ConnectedLinkOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.OperationEventArgs { diff --git a/NodeGraph.NET6/OperationEventArgs/DisconnectedLinkOperationEventArgs.cs b/NodeGraph.NET6/OperationEventArgs/DisconnectedLinkOperationEventArgs.cs index add273d..2a6f011 100644 --- a/NodeGraph.NET6/OperationEventArgs/DisconnectedLinkOperationEventArgs.cs +++ b/NodeGraph.NET6/OperationEventArgs/DisconnectedLinkOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.OperationEventArgs { diff --git a/NodeGraph.NET6/OperationEventArgs/EndMoveNodesOperationEventArgs.cs b/NodeGraph.NET6/OperationEventArgs/EndMoveNodesOperationEventArgs.cs index 4280500..4940c49 100644 --- a/NodeGraph.NET6/OperationEventArgs/EndMoveNodesOperationEventArgs.cs +++ b/NodeGraph.NET6/OperationEventArgs/EndMoveNodesOperationEventArgs.cs @@ -1,15 +1,10 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; namespace NodeGraph.NET6.OperationEventArgs { public class EndMoveNodesOperationEventArgs : EventArgs { - public Guid[] NodeGuids { get; } = null; + public Guid[] NodeGuids { get; } = null!; public EndMoveNodesOperationEventArgs(Guid[] nodeGuids) { diff --git a/NodeGraph.NET6/OperationEventArgs/PreviewConnectLinkOperationEventArgs.cs b/NodeGraph.NET6/OperationEventArgs/PreviewConnectLinkOperationEventArgs.cs index 11a9c5f..f915214 100644 --- a/NodeGraph.NET6/OperationEventArgs/PreviewConnectLinkOperationEventArgs.cs +++ b/NodeGraph.NET6/OperationEventArgs/PreviewConnectLinkOperationEventArgs.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NodeGraph.NET6.OperationEventArgs { diff --git a/NodeGraph.NET6/Properties/Resources.Designer.cs b/NodeGraph.NET6/Properties/Resources.Designer.cs index c56ea3a..592c30a 100644 --- a/NodeGraph.NET6/Properties/Resources.Designer.cs +++ b/NodeGraph.NET6/Properties/Resources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace NodeGraph.Properties +namespace NodeGraph.NET6.Properties { diff --git a/NodeGraph.NET6/Properties/Settings.Designer.cs b/NodeGraph.NET6/Properties/Settings.Designer.cs index b01c872..2c78dcd 100644 --- a/NodeGraph.NET6/Properties/Settings.Designer.cs +++ b/NodeGraph.NET6/Properties/Settings.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace NodeGraph.Properties +namespace NodeGraph.NET6.Properties { diff --git a/NodeGraph.NET6/ResourceDictionary.xaml b/NodeGraph.NET6/ResourceDictionary.xaml index 758b1b5..1c3389b 100644 --- a/NodeGraph.NET6/ResourceDictionary.xaml +++ b/NodeGraph.NET6/ResourceDictionary.xaml @@ -1,6 +1,4 @@ - + diff --git a/NodeGraph.NET6/Utilities/ResourceInstance.cs b/NodeGraph.NET6/Utilities/ResourceInstance.cs index 1224539..0b6a85e 100644 --- a/NodeGraph.NET6/Utilities/ResourceInstance.cs +++ b/NodeGraph.NET6/Utilities/ResourceInstance.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; using System.Windows.Threading; namespace NodeGraph.NET6.Utilities @@ -14,12 +9,12 @@ public T Get(string resourceName) { if (_Resource == null) { - _Resource = Application.Current.TryFindResource(resourceName) as T; + _Resource = (Application.Current.TryFindResource(resourceName) as T)!; } - return _Resource; + return _Resource!; } - T _Resource = null; + T _Resource = null!; } } diff --git a/NodeGraph.NET6/packages.config b/NodeGraph.NET6/packages.config deleted file mode 100644 index 1c85274..0000000 --- a/NodeGraph.NET6/packages.config +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/NodeGraph.NET7/Controls/CanvasMouseEventArgs.cs b/NodeGraph.NET7/Controls/CanvasMouseEventArgs.cs new file mode 100644 index 0000000..9bf8533 --- /dev/null +++ b/NodeGraph.NET7/Controls/CanvasMouseEventArgs.cs @@ -0,0 +1,16 @@ +using System; +using System.Windows; + +namespace NodeGraph.NET7.Controls +{ + public class CanvasMouseEventArgs : EventArgs + { + // This position has taken scale and offset into account. + public Point TransformedPosition { get; } + + public CanvasMouseEventArgs(Point transformedPosition) + { + TransformedPosition = transformedPosition; + } + } +} diff --git a/NodeGraph.NET7/Controls/DefaultNode.cs b/NodeGraph.NET7/Controls/DefaultNode.cs new file mode 100644 index 0000000..8d7ba72 --- /dev/null +++ b/NodeGraph.NET7/Controls/DefaultNode.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace NodeGraph.NET7.Controls +{ + public class DefaultNode : NodeBase + { + public DataTemplate HeaderContentTemplate + { + get => (DataTemplate)GetValue(HeaderContentTemplateProperty); + set => SetValue(HeaderContentTemplateProperty, value); + } + public static readonly DependencyProperty HeaderContentTemplateProperty = DependencyProperty.Register( + nameof(HeaderContentTemplate), + typeof(DataTemplate), + typeof(DefaultNode), + new FrameworkPropertyMetadata(null)); + + public Thickness ContentMargin + { + get => (Thickness)GetValue(ContentMarginProperty); + set => SetValue(ContentMarginProperty, value); + } + public static readonly DependencyProperty ContentMarginProperty = DependencyProperty.Register( + nameof(ContentMargin), + typeof(Thickness), + typeof(DefaultNode), + new FrameworkPropertyMetadata(new Thickness(4, 0, 4, 0), FrameworkPropertyMetadataOptions.AffectsArrange)); + + public VerticalAlignment InputLayout + { + get => (VerticalAlignment)GetValue(InputLayoutProperty); + set => SetValue(InputLayoutProperty, value); + } + public static readonly DependencyProperty InputLayoutProperty = DependencyProperty.Register( + nameof(InputLayout), + typeof(VerticalAlignment), + typeof(DefaultNode), + new FrameworkPropertyMetadata(VerticalAlignment.Top)); + + public Thickness InputMargin + { + get => (Thickness)GetValue(InputMarginProperty); + set => SetValue(InputMarginProperty, value); + } + public static readonly DependencyProperty InputMarginProperty = DependencyProperty.Register( + nameof(InputMargin), + typeof(Thickness), + typeof(DefaultNode), + new FrameworkPropertyMetadata(new Thickness(2.0))); + + public IEnumerable Inputs + { + get => (IEnumerable)GetValue(InputsProperty); + set => SetValue(InputsProperty, value); + } + public static readonly DependencyProperty InputsProperty = DependencyProperty.Register( + nameof(Inputs), + typeof(IEnumerable), + typeof(DefaultNode), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsArrange, InputsPropertyChanged)); + + public Style InputStyle + { + get => (Style)GetValue(InputStyleProperty); + set => SetValue(InputStyleProperty, value); + } + public static readonly DependencyProperty InputStyleProperty = DependencyProperty.Register( + nameof(InputStyle), + typeof(Style), + typeof(DefaultNode), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + public VerticalAlignment OutputLayout + { + get => (VerticalAlignment)GetValue(OutputLayoutProperty); + set => SetValue(OutputLayoutProperty, value); + } + public static readonly DependencyProperty OutputLayoutProperty = DependencyProperty.Register( + nameof(OutputLayout), + typeof(VerticalAlignment), + typeof(DefaultNode), + new FrameworkPropertyMetadata(VerticalAlignment.Top)); + + public Thickness OutputMargin + { + get => (Thickness)GetValue(OutputMarginProperty); + set => SetValue(OutputMarginProperty, value); + } + public static readonly DependencyProperty OutputMarginProperty = DependencyProperty.Register( + nameof(OutputMargin), + typeof(Thickness), + typeof(DefaultNode), + new FrameworkPropertyMetadata(new Thickness(2.0))); + + public IEnumerable Outputs + { + get => (IEnumerable)GetValue(OutputsProperty); + set => SetValue(OutputsProperty, value); + } + public static readonly DependencyProperty OutputsProperty = DependencyProperty.Register( + nameof(Outputs), + typeof(IEnumerable), + typeof(DefaultNode), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsArrange, OutputsPropertyChanged)); + + public Style OutputStyle + { + get => (Style)GetValue(OutputStyleProperty); + set => SetValue(OutputStyleProperty, value); + } + public static readonly DependencyProperty OutputStyleProperty = DependencyProperty.Register( + nameof(OutputStyle), + typeof(Style), + typeof(DefaultNode), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + public ICommand SizeChangedCommand + { + get => (ICommand)GetValue(SizeChangedCommandProperty); + set => SetValue(SizeChangedCommandProperty, value); + } + public static readonly DependencyProperty SizeChangedCommandProperty = DependencyProperty.Register( + nameof(SizeChangedCommand), + typeof(ICommand), + typeof(DefaultNode), + new FrameworkPropertyMetadata(null)); + + NodeInput _NodeInput = null; + NodeOutput _NodeOutput = null; + + ColumnDefinition _NodeInputGridColumnDefinition; + ColumnDefinition _NodeContentTemplateGridColumnDefinition; + ColumnDefinition _NodeOutputGridColumnDefinition; + + static void OutputsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var node = d as DefaultNode; + + if (e.OldValue is INotifyCollectionChanged oldCollection) + { + oldCollection.CollectionChanged -= node.OutputCollectionChanged; + } + if (e.NewValue is INotifyCollectionChanged newCollection) + { + newCollection.CollectionChanged += node.OutputCollectionChanged; + } + } + + static void InputsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var node = d as DefaultNode; + + if (e.OldValue != null && e.OldValue is INotifyCollectionChanged oldCollection) + { + oldCollection.CollectionChanged -= node.InputCollectionChanged; + } + if (e.NewValue != null && e.NewValue is INotifyCollectionChanged newCollection) + { + newCollection.CollectionChanged += node.InputCollectionChanged; + } + } + + static DefaultNode() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(DefaultNode), new FrameworkPropertyMetadata(typeof(DefaultNode))); + } + + internal DefaultNode(Canvas canvas, Point offset, double scale) : base(canvas, offset) + { + SizeChanged += Node_SizeChanged; + } + + public override void OnApplyTemplate() + { + _NodeInput = (NodeInput)GetTemplateChild("__NodeInput__"); + _NodeInput.ApplyTemplate(); + + _NodeOutput = (NodeOutput)GetTemplateChild("__NodeOutput__"); + _NodeOutput.ApplyTemplate(); + + _NodeInputGridColumnDefinition = (ColumnDefinition)GetTemplateChild("__NodeInputGridColumnDefinition__"); + _NodeContentTemplateGridColumnDefinition = (ColumnDefinition)GetTemplateChild("__NodeContentTemplateGridColumnDefinition__"); + _NodeOutputGridColumnDefinition = (ColumnDefinition)GetTemplateChild("__NodeOutputGridColumnDefinition__"); + } + + internal void Initialize() + { + _NodeInput.Initialize(); + _NodeOutput.Initialize(); + } + + internal NodeLink[] EnumrateConnectedNodeLinks() + { + var inputNodeLinks = _NodeInput.EnumerateConnectedNodeLinks(); + var outputNodeLinks = _NodeOutput.EnumerateConnectedNodeLinks(); + + return inputNodeLinks.Concat(outputNodeLinks).ToArray(); + } + + public NodeConnectorContent FindNodeConnectorContent(Guid guid) + { + var input = _NodeInput.FindNodeConnector(guid); + if (input != null) + { + return input; + } + + return _NodeOutput.FindNodeConnector(guid); + } + + protected override void OnDisposing() + { + _NodeInput.Dispose(); + _NodeOutput.Dispose(); + SizeChanged -= Node_SizeChanged; + } + + protected override void OnUpdateTranslation() + { + _NodeInput.UpdateLinkPosition(Canvas); + _NodeOutput.UpdateLinkPosition(Canvas); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + } + + protected override void OnMouseUp(MouseButtonEventArgs e) + { + base.OnMouseUp(e); + } + + void Node_SizeChanged(object sender, SizeChangedEventArgs e) + { + _NodeInput.UpdateLinkPosition(Canvas); + _NodeOutput.UpdateLinkPosition(Canvas); + + // Collapse input/output controls if not placement input/output connectors. + _NodeInputGridColumnDefinition.Width = _NodeInput.HasItems ? new GridLength(1.0f, GridUnitType.Star) : new GridLength(0); + _NodeOutputGridColumnDefinition.Width = _NodeOutput.HasItems ? new GridLength(1.0f, GridUnitType.Star) : new GridLength(0); + + SizeChangedCommand?.Execute(e.NewSize); + } + + void OutputCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + // Implement if need anything. + } + + void InputCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + // Implement if need anything. + } + } +} diff --git a/NodeGraph.NET7/Controls/DefaultNode.xaml b/NodeGraph.NET7/Controls/DefaultNode.xaml new file mode 100644 index 0000000..8a911e7 --- /dev/null +++ b/NodeGraph.NET7/Controls/DefaultNode.xaml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NodeGraph.NET7/Controls/GridCanvas.cs b/NodeGraph.NET7/Controls/GridCanvas.cs new file mode 100644 index 0000000..7091934 --- /dev/null +++ b/NodeGraph.NET7/Controls/GridCanvas.cs @@ -0,0 +1,204 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace NodeGraph.NET7.Controls +{ + /// + /// Infinity grid rendering canvas. + /// + public class GridCanvas : Canvas + { + public Point Offset + { + get => (Point)GetValue(OffsetProperty); + set => SetValue(OffsetProperty, value); + } + public static readonly DependencyProperty OffsetProperty = + DependencyProperty.Register(nameof(Offset), typeof(Point), typeof(GridCanvas), new FrameworkPropertyMetadata(new Point(0, 0), OffsetPropertyChanged)); + + public double Scale + { + get => (double)GetValue(ScaleProperty); + set => SetValue(ScaleProperty, value); + } + public static readonly DependencyProperty ScaleProperty = + DependencyProperty.Register(nameof(Scale), typeof(double), typeof(GridCanvas), new FrameworkPropertyMetadata(1.0, ScalePropertyChanged)); + + public double Spacing + { + get => (double)GetValue(SpacingProperty); + set => SetValue(SpacingProperty, value); + } + public static readonly DependencyProperty SpacingProperty = + DependencyProperty.Register(nameof(Spacing), typeof(double), typeof(GridCanvas), new FrameworkPropertyMetadata(128.0)); + + public int SubDivisionCount + { + get => (int)GetValue(SubDivisionCountProperty); + set => SetValue(SubDivisionCountProperty, value); + } + public static readonly DependencyProperty SubDivisionCountProperty = + DependencyProperty.Register(nameof(SubDivisionCount), typeof(int), typeof(GridCanvas), new FrameworkPropertyMetadata(3)); + + public Brush GridMainBrush + { + get => (Brush)GetValue(GridMainBrushProperty); + set => SetValue(GridMainBrushProperty, value); + } + public static readonly DependencyProperty GridMainBrushProperty = + DependencyProperty.Register(nameof(GridMainBrush), typeof(Brush), typeof(GridCanvas), new FrameworkPropertyMetadata(new SolidColorBrush(Color.FromRgb(80, 80, 80)), GridMainBrushPropertyChanged)); + + public Brush GridSubBrush + { + get => (Brush)GetValue(GridSubBrushProperty); + set => SetValue(GridSubBrushProperty, value); + } + public static readonly DependencyProperty GridSubBrushProperty = + DependencyProperty.Register(nameof(GridSubBrush), typeof(Brush), typeof(GridCanvas), new FrameworkPropertyMetadata(new SolidColorBrush(Color.FromRgb(64, 64, 64)), GridSubBrushPropertyChanged)); + + public double GridMainThickness + { + get => (double)GetValue(GridMainThicknessProperty); + set => SetValue(GridMainThicknessProperty, value); + } + public static readonly DependencyProperty GridMainThicknessProperty = + DependencyProperty.Register(nameof(GridMainThickness), typeof(double), typeof(GridCanvas), new FrameworkPropertyMetadata(1.0, GridMainThicknessPropertyChanged)); + + public double GridSubThickness + { + get => (double)GetValue(GridSubThicknessProperty); + set => SetValue(GridSubThicknessProperty, value); + } + public static readonly DependencyProperty GridSubThicknessProperty = + DependencyProperty.Register(nameof(GridSubThickness), typeof(double), typeof(GridCanvas), new FrameworkPropertyMetadata(1.0, GridSubThicknessPropertyChanged)); + + Pen _GridMainPen = null; + Pen _GridSubPen = null; + ScaleTransform _Scale = new ScaleTransform(1.0, 1.0); + + static void OffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as GridCanvas).InvalidateVisual(); + } + + static void ScalePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var canvas = d as GridCanvas; + canvas.UpdateScale(); + canvas.InvalidateVisual(); + } + + static void GridMainBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as GridCanvas).UpdateMainGridPen(); + } + + static void GridSubBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as GridCanvas).UpdateMainGridPen(); + } + + static void GridMainThicknessPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as GridCanvas).UpdateSubGridPen(); + } + + static void GridSubThicknessPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as GridCanvas).UpdateSubGridPen(); + } + + static GridCanvas() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(GridCanvas), new FrameworkPropertyMetadata(typeof(GridCanvas))); + } + + public GridCanvas() + { + var transfromGroup = new TransformGroup(); + transfromGroup.Children.Add(_Scale); + RenderTransform = transfromGroup; + + UpdateMainGridPen(); + UpdateSubGridPen(); + } + + protected override void OnRender(DrawingContext dc) + { + base.OnRender(dc); + + // render grid only visual area. + int subGridCount = SubDivisionCount + 1; + double subGridSpace = Spacing / subGridCount; + double subGridOffset = subGridSpace * subGridCount; + + double MinHorizonOnCanvas = -ActualWidth * Math.Max(0.0, 1.0 - Scale) * 1.0 / Scale * 0.5; + double MaxHorizonOnCanvas = +ActualWidth * (1 + Math.Max(0.0, 1.0 - Scale) * 1.0 / Scale * 0.5); + + double MinVerticalOnCanvas = -ActualHeight * Math.Max(0.0, 1.0 - Scale) * 1.0 / Scale * 0.5; + double MaxVerticalOnCanvas = +ActualHeight * (1 + Math.Max(0.0, 1.0 - Scale) * 1.0 / Scale * 0.5); + + double invScale = 1.0 / Scale; + + int hCount = (int)(ActualHeight * invScale / Spacing + Math.Ceiling(Scale)); + int initHIndex = (int)(Math.Max(0, ActualHeight * invScale - ActualHeight) / Spacing); + + int vCount = (int)(ActualWidth * invScale / Spacing + Math.Ceiling(Scale)) + 2; + int initVIndex = (int)(Math.Max(0, ActualWidth * invScale - ActualWidth) / Spacing); + + // sub horizon + for (int i = -initHIndex; i < hCount; ++i) + { + for (int sub = 0; sub < subGridCount; ++sub) + { + double hSub = sub * subGridSpace + i * subGridOffset + Offset.Y % subGridSpace; + dc.DrawLine(_GridSubPen, new Point(MinHorizonOnCanvas, hSub), new Point(MaxHorizonOnCanvas, hSub)); + } + } + + // sub vertical + for (int i = -initVIndex; i < vCount; ++i) + { + for (uint sub = 0; sub < subGridCount; ++sub) + { + double vSub = sub * subGridSpace + i * subGridOffset + Offset.X % subGridSpace; + dc.DrawLine(_GridSubPen, new Point(vSub, MinVerticalOnCanvas), new Point(vSub, MaxVerticalOnCanvas)); + } + } + + // main horizontal + for (int i = -initHIndex; i < hCount; ++i) + { + double h = i * Spacing + Offset.Y % Spacing; + dc.DrawLine(_GridMainPen, new Point(MinHorizonOnCanvas, h), new Point(MaxHorizonOnCanvas, h)); + } + + // main vertical + for (int i = -initVIndex; i < vCount; ++i) + { + double v = i * Spacing + Offset.X % Spacing; + dc.DrawLine(_GridMainPen, new Point(v, MinVerticalOnCanvas), new Point(v, MaxVerticalOnCanvas)); + } + } + + void UpdateMainGridPen() + { + _GridMainPen = new Pen(GridMainBrush, GridMainThickness); + _GridMainPen.Freeze(); + } + + void UpdateSubGridPen() + { + _GridSubPen = new Pen(GridSubBrush, GridSubThickness); + _GridSubPen.Freeze(); + } + + void UpdateScale() + { + _Scale.ScaleX = Scale; + _Scale.ScaleY = Scale; + } + } +} diff --git a/NodeGraph.NET7/Controls/GroupNode.cs b/NodeGraph.NET7/Controls/GroupNode.cs new file mode 100644 index 0000000..a8e579c --- /dev/null +++ b/NodeGraph.NET7/Controls/GroupNode.cs @@ -0,0 +1,536 @@ +using System; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace NodeGraph.NET7.Controls +{ + enum DragResizeType + { + None, + LeftTop, + RightTop, + LeftBottom, + RightBottom, + Top, + Bottom, + Left, + Right, + } + + public class GroupNode : NodeBase + { + public DataTemplate HeaderContentTemplate + { + get => (DataTemplate)GetValue(HeaderContentTemplateProperty); + set => SetValue(HeaderContentTemplateProperty, value); + } + public static readonly DependencyProperty HeaderContentTemplateProperty = DependencyProperty.Register( + nameof(HeaderContentTemplate), + typeof(DataTemplate), + typeof(GroupNode), + new FrameworkPropertyMetadata(null)); + + /// + /// This property is set only where script. + /// Do not use at UI code. + /// + public Point InterlockPosition + { + get => (Point)GetValue(InterlockPositionProperty); + set => SetValue(InterlockPositionProperty, value); + } + public static readonly DependencyProperty InterlockPositionProperty = DependencyProperty.Register( + nameof(InterlockPosition), + typeof(Point), + typeof(GroupNode), + new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, InterlockPositionPropertyChanged)); + + public double BorderSize + { + get => (double)GetValue(BorderSizeProperty); + set => SetValue(BorderSizeProperty, value); + } + public static readonly DependencyProperty BorderSizeProperty = DependencyProperty.Register( + nameof(BorderSize), + typeof(double), + typeof(GroupNode), + new FrameworkPropertyMetadata(4.0)); + + public Point InnerPosition + { + get => (Point)GetValue(InnerPositionProperty); + set => SetValue(InnerPositionProperty, value); + } + public static readonly DependencyProperty InnerPositionProperty = DependencyProperty.Register( + nameof(InnerPosition), + typeof(Point), + typeof(GroupNode), + new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, InnerPositionPropertyChanged)); + + public double InnerWidth + { + get => (double)GetValue(InnerWidthProperty); + set => SetValue(InnerWidthProperty, value); + } + public static readonly DependencyProperty InnerWidthProperty = DependencyProperty.Register( + nameof(InnerWidth), + typeof(double), + typeof(GroupNode), + new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, InnerWidthPropertyChanged)); + + public double InnerHeight + { + get => (double)GetValue(InnerHeightProperty); + set => SetValue(InnerHeightProperty, value); + } + public static readonly DependencyProperty InnerHeightProperty = DependencyProperty.Register( + nameof(InnerHeight), + typeof(double), + typeof(GroupNode), + new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, InnerHeightPropertyChanged)); + + public SolidColorBrush InnerColor + { + get => (SolidColorBrush)GetValue(InnerColorProperty); + set => SetValue(InnerColorProperty, value); + } + public static readonly DependencyProperty InnerColorProperty = DependencyProperty.Register( + nameof(InnerColor), + typeof(SolidColorBrush), + typeof(GroupNode), + new FrameworkPropertyMetadata(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)))); + + public string Comment + { + get => (string)GetValue(CommentProperty); + set => SetValue(CommentProperty, value); + } + public static readonly DependencyProperty CommentProperty = DependencyProperty.Register( + nameof(Comment), + typeof(string), + typeof(GroupNode), + new FrameworkPropertyMetadata(string.Empty)); + + public double CommentSize + { + get => (double)GetValue(CommentSizeProperty); + set => SetValue(CommentSizeProperty, value); + } + public static readonly DependencyProperty CommentSizeProperty = DependencyProperty.Register( + nameof(CommentSize), + typeof(double), + typeof(GroupNode), + new FrameworkPropertyMetadata(12.0)); + + public SolidColorBrush CommentForeground + { + get => (SolidColorBrush)GetValue(CommentForegroundProperty); + set => SetValue(CommentForegroundProperty, value); + } + public static readonly DependencyProperty CommentForegroundProperty = DependencyProperty.Register( + nameof(CommentForeground), + typeof(SolidColorBrush), + typeof(GroupNode), + new FrameworkPropertyMetadata(new SolidColorBrush(Color.FromArgb(64, 255, 255, 255)))); + + public Thickness CommentMargin + { + get => (Thickness)GetValue(CommentMarginProperty); + set => SetValue(CommentMarginProperty, value); + } + public static readonly DependencyProperty CommentMarginProperty = DependencyProperty.Register( + nameof(CommentMargin), + typeof(Thickness), + typeof(GroupNode), + new FrameworkPropertyMetadata(new Thickness(8))); + + public ICommand SizeChangedCommand + { + get => GetValue(SizeChangedCommandProperty) as ICommand; + set => SetValue(SizeChangedCommandProperty, value); + } + public static readonly DependencyProperty SizeChangedCommandProperty = DependencyProperty.Register( + nameof(SizeChangedCommand), + typeof(ICommand), + typeof(GroupNode), + new FrameworkPropertyMetadata(null)); + + public bool IsDraggingToResize { get; private set; } = false; + + bool _IsInside = false; + bool _InternalPositionUpdating = false; + + Rect _CapturedNodeRect; + DragResizeType _IsDraggingToResizeType = DragResizeType.None; + Border _GroupNodeHeader = null; + + static GroupNode() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(GroupNode), new FrameworkPropertyMetadata(typeof(GroupNode))); + } + + internal GroupNode(Canvas canvas, Point offset, double scale) : base(canvas, offset) + { + SizeChanged += Group_SizeChanged; + } + + internal void Initialize() + { + // Implement if anything. + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _GroupNodeHeader = (Border)GetTemplateChild("__GroupNodeHeader__"); + _GroupNodeHeader.SizeChanged += GroupNodeHeader_SizeChanged; + } + + internal void ExpandSize(Rect rect) + { + var oldPosition = Position; + var maxX = Math.Max(oldPosition.X + Width, rect.Right + BorderSize); + var maxY = Math.Max(oldPosition.Y + Height, rect.Bottom + BorderSize); + + var x = rect.X - BorderSize; + var y = rect.Y - _GroupNodeHeader.ActualHeight - BorderSize; + UpdatePosition(Math.Min(x, Position.X), Math.Min(y, Position.Y)); + + var w = rect.Width + BorderSize * 2; + var h = rect.Height + BorderSize * 2 + _GroupNodeHeader.ActualHeight; + Width = Math.Max(w, maxX - Position.X); + Height = Math.Max(h, maxY - Position.Y); + + UpdateInnerSize(); + UpdateInnerPosition(); + } + + internal void CaptureToResizeDragging() + { + if (_IsDraggingToResizeType != DragResizeType.None) + { + IsDraggingToResize = true; + _CapturedNodeRect = new Rect(Position, new Size(ActualWidth, ActualHeight)); + } + } + + internal void ReleaseToResizeDragging() + { + IsDraggingToResize = false; + } + + internal void Resize(in Point pos) + { + switch (_IsDraggingToResizeType) + { + case DragResizeType.LeftTop: + { + var x = _CapturedNodeRect.Right - pos.X > MinHeight ? pos.X : Position.X; + var y = _CapturedNodeRect.Bottom - pos.Y > MinHeight ? pos.Y : Position.Y; + Position = new Point(x, y); + Width = Math.Max(MinWidth, _CapturedNodeRect.Right - Position.X); + Height = Math.Max(MinWidth, _CapturedNodeRect.Bottom - Position.Y); + } + Mouse.SetCursor(Cursors.SizeNWSE); + break; + case DragResizeType.RightTop: + { + var h = _CapturedNodeRect.Bottom - pos.Y; + if (h > MinHeight) + { + Position = new Point(Position.X, pos.Y); + Height = Math.Max(MinHeight, h); + } + } + Width = Math.Max(MinWidth, pos.X - _CapturedNodeRect.X); + Mouse.SetCursor(Cursors.SizeNESW); + break; + case DragResizeType.LeftBottom: + { + var w = _CapturedNodeRect.Right - pos.X; + if (w > MinWidth) + { + Position = new Point(pos.X, Position.Y); + Width = Math.Max(MinWidth, w); + } + } + Height = Math.Max(MinHeight, pos.Y - _CapturedNodeRect.Y); + Mouse.SetCursor(Cursors.SizeNESW); + break; + case DragResizeType.RightBottom: + Width = Math.Max(MinWidth, pos.X - _CapturedNodeRect.X); + Height = Math.Max(MinHeight, pos.Y - _CapturedNodeRect.Y); + Mouse.SetCursor(Cursors.SizeNWSE); + break; + case DragResizeType.Top: + { + var h = _CapturedNodeRect.Bottom - pos.Y; + if (h > MinHeight) + { + Position = new Point(Position.X, pos.Y); + Height = Math.Max(MinHeight, h); + } + } + Mouse.SetCursor(Cursors.SizeNS); + break; + case DragResizeType.Bottom: + Height = Math.Max(MinHeight, pos.Y - _CapturedNodeRect.Y); + Mouse.SetCursor(Cursors.SizeNS); + break; + case DragResizeType.Left: + { + var w = _CapturedNodeRect.Right - pos.X; + if (w > MinWidth) + { + Position = new Point(pos.X, Position.Y); + Width = Math.Max(MinWidth, w); + } + } + Mouse.SetCursor(Cursors.SizeWE); + break; + case DragResizeType.Right: + Width = Math.Max(MinWidth, pos.X - _CapturedNodeRect.X); + Mouse.SetCursor(Cursors.SizeWE); + break; + } + + UpdateInnerPosition(); + } + + internal void ChangeInnerColor(bool inside) + { + if (_IsInside != inside) + { + _IsInside = inside; + InnerColor = inside ? new SolidColorBrush(Color.FromArgb(128, 0, 255, 0)) : Brushes.Transparent; + } + } + + internal bool IsInsideCompletely(in Rect rect) + { + return InnerPosition.X <= rect.X && rect.Right <= InnerPosition.X + InnerWidth + && InnerPosition.Y <= rect.Y && rect.Bottom <= InnerPosition.Y + InnerHeight; + } + + internal bool IsInsideCompletely(Point pos) + { + return InnerPosition.X <= pos.X && pos.X <= InnerPosition.X + InnerWidth + && InnerPosition.Y <= pos.Y && pos.Y <= InnerPosition.Y + InnerHeight; + } + + internal Rect GetInnerBoundingBox() + { + return new Rect(InnerPosition, new Size(InnerWidth, InnerHeight)); + } + + protected override void OnUpdateTranslation() + { + _InternalPositionUpdating = true; + + InterlockPosition = Position; + UpdateInnerPosition(); + + _InternalPositionUpdating = false; + } + + protected override void OnDisposing() + { + SizeChanged -= Group_SizeChanged; + _GroupNodeHeader.SizeChanged -= GroupNodeHeader_SizeChanged; + } + + protected override void OnMouseEnter(MouseEventArgs e) + { + base.OnMouseEnter(e); + if (IsDraggingToResize == false) + { + UpdateMouseCursor(e); + } + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + if (IsDraggingToResize == false) + { + UpdateMouseCursor(e); + } + } + + void UpdateMouseCursor(MouseEventArgs e) + { + var pos = e.GetPosition(this); + if (pos.X < BorderSize) + { + // left + + // cursor is inside left vertical border. + if (pos.Y < BorderSize) + { + // top + _IsDraggingToResizeType = DragResizeType.LeftTop; + Mouse.SetCursor(Cursors.SizeNWSE); + } + else if (pos.Y > ActualHeight - BorderSize) + { + // bottom + _IsDraggingToResizeType = DragResizeType.LeftBottom; + Mouse.SetCursor(Cursors.SizeNESW); + } + else + { + // left + _IsDraggingToResizeType = DragResizeType.Left; + Mouse.SetCursor(Cursors.SizeWE); + } + } + else if (pos.X > ActualWidth - BorderSize) + { + // right + + // cursor is inside right vertical border. + if (pos.Y < BorderSize) + { + // top + _IsDraggingToResizeType = DragResizeType.RightTop; + Mouse.SetCursor(Cursors.SizeNESW); + } + else if (pos.Y > ActualHeight - BorderSize) + { + // bottom + _IsDraggingToResizeType = DragResizeType.RightBottom; + Mouse.SetCursor(Cursors.SizeNWSE); + } + else + { + // right + _IsDraggingToResizeType = DragResizeType.Right; + Mouse.SetCursor(Cursors.SizeWE); + } + } + else + { + // middle + if (pos.Y < BorderSize) + { + _IsDraggingToResizeType = DragResizeType.Top; + Mouse.SetCursor(Cursors.SizeNS); + } + else if (pos.Y > ActualHeight - BorderSize) + { + _IsDraggingToResizeType = DragResizeType.Bottom; + Mouse.SetCursor(Cursors.SizeNS); + } + else + { + _IsDraggingToResizeType = DragResizeType.None; + } + } + } + + void Group_SizeChanged(object sender, SizeChangedEventArgs e) + { + UpdateInnerSize(); + UpdateInnerPosition(); + SizeChangedCommand?.Execute(e.NewSize); + } + + void GroupNodeHeader_SizeChanged(object sender, SizeChangedEventArgs e) + { + UpdateInnerSize(); + UpdateInnerPosition(); + + Width = InnerWidth + BorderSize * 2; + Height = InnerHeight + _GroupNodeHeader.ActualHeight + BorderSize * 2; + UpdateLayout(); + } + + void UpdateInnerSize() + { + InnerWidth = Width - BorderSize * 2; + InnerHeight = Height - BorderSize * 2 - _GroupNodeHeader.ActualHeight; + + // for notifying Width/Height immediately. + UpdateLayout(); + } + + void UpdateWidthFromInnerWidth() + { + Width = InnerWidth + BorderSize * 2; + + // for notifying InnerWidth/InnerHeight immediately. + UpdateLayout(); + } + + void UpdateHeightFromInnerHeight() + { + Height = InnerHeight + _GroupNodeHeader.ActualHeight + BorderSize * 2; + + // for notifying InnerWidth/InnerHeight immediately. + UpdateLayout(); + } + + void UpdateInnerPosition() + { + var diff_x = BorderSize; + var diff_y = _GroupNodeHeader.ActualHeight + BorderSize; + InnerPosition = new Point(Position.X + diff_x, Position.Y + diff_y); + + // for notifying Position immediately. + UpdateLayout(); + } + + static void InterlockPositionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var groupNode = d as GroupNode; + if (groupNode._InternalPositionUpdating) + { + return; + } + + var move = groupNode.InterlockPosition - groupNode.Position; + + var otherNodes = groupNode.Canvas.Children.OfType().Where(arg => arg != groupNode).ToArray(); + foreach (var node in otherNodes) + { + if (groupNode.IsInsideCompletely(node.GetBoundingBox())) + { + node.Position = node.Position + move; + } + } + + groupNode.Position = groupNode.InterlockPosition; + } + + static void InnerPositionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var groupNode = d as GroupNode; + if (groupNode._InternalPositionUpdating) + { + return; + } + + var x = groupNode.InnerPosition.X - groupNode.BorderSize; + var y = groupNode.InnerPosition.Y - (groupNode._GroupNodeHeader.ActualHeight + groupNode.BorderSize); + groupNode.UpdatePosition(x, y); + } + + static void InnerWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var groupNode = d as GroupNode; + + groupNode.UpdateWidthFromInnerWidth(); + } + + static void InnerHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var groupNode = d as GroupNode; + + groupNode.UpdateHeightFromInnerHeight(); + } + } +} diff --git a/NodeGraph.NET7/Controls/GroupNode.xaml b/NodeGraph.NET7/Controls/GroupNode.xaml new file mode 100644 index 0000000..afc7ee8 --- /dev/null +++ b/NodeGraph.NET7/Controls/GroupNode.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + diff --git a/NodeGraph.NET7/Controls/ICanvasObject.cs b/NodeGraph.NET7/Controls/ICanvasObject.cs new file mode 100644 index 0000000..80e9852 --- /dev/null +++ b/NodeGraph.NET7/Controls/ICanvasObject.cs @@ -0,0 +1,11 @@ +using System; +using System.Windows; + +namespace NodeGraph.NET7.Controls +{ + public interface ICanvasObject : IDisposable + { + Guid Guid { get; set; } + void UpdateOffset(Point offset); + } +} diff --git a/NodeGraph.NET7/Controls/ISelectableObject.cs b/NodeGraph.NET7/Controls/ISelectableObject.cs new file mode 100644 index 0000000..b053e26 --- /dev/null +++ b/NodeGraph.NET7/Controls/ISelectableObject.cs @@ -0,0 +1,13 @@ +using System.Windows; + +namespace NodeGraph.NET7.Controls +{ + internal interface ISelectableObject + { + bool IsSelected { get; set; } + object DataContext { get; set; } + + bool Contains(Rect rect); + bool IntersectsWith(Rect rect); + } +} diff --git a/NodeGraph.NET7/Controls/NodeBase.cs b/NodeGraph.NET7/Controls/NodeBase.cs new file mode 100644 index 0000000..4f42e2b --- /dev/null +++ b/NodeGraph.NET7/Controls/NodeBase.cs @@ -0,0 +1,155 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Media; + +namespace NodeGraph.NET7.Controls +{ + public abstract class NodeBase : ContentControl, ICanvasObject, ISelectableObject, IDisposable + { + public Guid Guid + { + get => (Guid)GetValue(GuidProperty); + set => SetValue(GuidProperty, value); + } + public static readonly DependencyProperty GuidProperty = DependencyProperty.Register( + nameof(Guid), + typeof(Guid), + typeof(NodeBase), + new PropertyMetadata(Guid.NewGuid())); + + public bool IsSelected + { + get => (bool)GetValue(IsSelectedProperty); + set => UpdateSelectedState(value); + } + public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register( + nameof(IsSelected), + typeof(bool), + typeof(NodeBase), + new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsSelectedPropertyChanged)); + + public Point Position + { + get => (Point)GetValue(PositionProperty); + set => SetValue(PositionProperty, value); + } + public static readonly DependencyProperty PositionProperty = DependencyProperty.Register( + nameof(Position), + typeof(Point), + typeof(NodeBase), + new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PositionPropertyChanged)); + + public Point DragStartPosition { get; private set; } = new Point(0, 0); + + internal EventHandler BeginSelectionChanged { get; set; } = null; + internal EventHandler EndSelectionChanged { get; set; } = null; + + protected Canvas Canvas { get; } = null; + protected Point Offset { get; private set; } = new Point(0, 0); + protected TranslateTransform Translate { get; private set; } = new TranslateTransform(0, 0); + + + internal NodeBase(Canvas canvas, Point offset) + { + Canvas = canvas; + Offset = offset; + + Translate.X = Position.X + Offset.X; + Translate.Y = Position.Y + Offset.Y; + + var transformGroup = new TransformGroup(); + transformGroup.Children.Add(Translate); + + RenderTransform = transformGroup; + } + + internal Rect GetBoundingBox() + { + return new Rect(Position.X, Position.Y, ActualWidth, ActualHeight); + } + + internal void CaptureDragStartPosition() + { + DragStartPosition = Position; + } + + internal void UpdatePosition(double x, double y) + { + Position = new Point(x, y); + + UpdateTranslation(); + } + + public void UpdateOffset(Point offset) + { + Offset = offset; + + UpdateTranslation(); + + InvalidateVisual(); + } + + public bool Contains(Rect rect) + { + return rect.Contains(GetBoundingBox()); + } + + public bool IntersectsWith(Rect rect) + { + return GetBoundingBox().IntersectsWith(rect); + } + + public void Dispose() + { + // You need to clear Style. + // Because implemented on style for binding. + Style = null; + + // Clear binding for subscribing source changed event from old control. + // throw exception about visual tree ancestor different if you not clear binding. + BindingOperations.ClearAllBindings(this); + + // Clear binding myself first. + // Because children throw exception about visual tree ancestor different when my Style to be null. + OnDisposing(); + } + + void UpdateSelectedState(bool value) + { + SetValue(IsSelectedProperty, value); + Panel.SetZIndex(this, value ? 1 : 0); + } + + void UpdateTranslation() + { + Translate.X = Position.X + Offset.X; + Translate.Y = Position.Y + Offset.Y; + + OnUpdateTranslation(); + } + + static void IsSelectedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var node = (NodeBase)d; + + node.BeginSelectionChanged?.Invoke(node, EventArgs.Empty); + + if (node.Focusable) + { + node.Focus(); + } + + node.EndSelectionChanged?.Invoke(node, EventArgs.Empty); + } + + static void PositionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as NodeBase).UpdateTranslation(); + } + + protected abstract void OnDisposing(); + protected abstract void OnUpdateTranslation(); + } +} diff --git a/NodeGraph.NET7/Controls/NodeConnector.cs b/NodeGraph.NET7/Controls/NodeConnector.cs new file mode 100644 index 0000000..6179788 --- /dev/null +++ b/NodeGraph.NET7/Controls/NodeConnector.cs @@ -0,0 +1,378 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Media; + +namespace NodeGraph.NET7.Controls +{ + public abstract class NodeConnectorContent : ContentControl, IDisposable + { + public Guid Guid + { + get => (Guid)GetValue(GuidProperty); + set => SetValue(GuidProperty, value); + } + public static readonly DependencyProperty GuidProperty = DependencyProperty.Register( + nameof(Guid), + typeof(Guid), + typeof(NodeConnectorContent), + new PropertyMetadata(Guid.Empty)); + + public int ConnectedCount + { + get => (int)GetValue(ConnectedCountProperty); + private set => SetValue(ConnectedCountPropertyKey, value); + } + public static readonly DependencyPropertyKey ConnectedCountPropertyKey = DependencyProperty.RegisterReadOnly( + nameof(ConnectedCount), + typeof(int), + typeof(NodeConnectorContent), + new PropertyMetadata(0)); + + public static readonly DependencyProperty ConnectedCountProperty = ConnectedCountPropertyKey.DependencyProperty; + + public bool IsConnected + { + get => (bool)GetValue(IsConnectedProperty); + private set => SetValue(IsConnectedPropertyKey, value); + } + public static readonly DependencyPropertyKey IsConnectedPropertyKey = DependencyProperty.RegisterReadOnly( + nameof(IsConnected), + typeof(bool), + typeof(NodeConnectorContent), + new PropertyMetadata(false)); + + public static readonly DependencyProperty IsConnectedProperty = IsConnectedPropertyKey.DependencyProperty; + + public Brush Stroke + { + get => (Brush)GetValue(StrokeProperty); + set => SetValue(StrokeProperty, value); + } + public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( + nameof(Stroke), + typeof(Brush), + typeof(NodeConnectorContent), + new FrameworkPropertyMetadata(Brushes.Blue)); + + public double StrokeThickness + { + get => (double)GetValue(StrokeThicknessProperty); + set => SetValue(StrokeThicknessProperty, value); + } + public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( + nameof(StrokeThickness), + typeof(double), + typeof(NodeConnectorContent), + new FrameworkPropertyMetadata(1.0)); + + public Brush Fill + { + get => (Brush)GetValue(FillProperty); + set => SetValue(FillProperty, value); + } + public static readonly DependencyProperty FillProperty = DependencyProperty.Register( + nameof(Fill), + typeof(Brush), + typeof(NodeConnectorContent), + new FrameworkPropertyMetadata(Brushes.Gray)); + + public bool CanConnect + { + get => (bool)GetValue(CanConnectProperty); + set => SetValue(CanConnectProperty, value); + } + public static readonly DependencyProperty CanConnectProperty = DependencyProperty.Register( + nameof(CanConnect), + typeof(bool), + typeof(NodeConnectorContent), + new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender)); + + public Point Position + { + get => _Position; + set => UpdatePosition(value); + } + + public DefaultNode Node { get; private set; } = null; + + /// + /// connecting node links. + /// + public IEnumerable NodeLinks => _NodeLinks; + List _NodeLinks = new List(); + + protected abstract FrameworkElement ConnectorControl { get; } + + Point _Position = new Point(); + TranslateTransform _Translate = new TranslateTransform(); + + static public bool CanConnectEachOther(NodeConnectorContent start, NodeConnectorContent toEnd) + { + return start.CanConnectTo(toEnd) && toEnd.CanConnectTo(start); + } + + public NodeConnectorContent() + { + var transfromGroup = new TransformGroup(); + transfromGroup.Children.Add(_Translate); + RenderTransform = transfromGroup; + } + + public void Initialize() + { + var parent = VisualTreeHelper.GetParent(this); + while (parent.GetType() != typeof(DefaultNode)) + { + parent = VisualTreeHelper.GetParent(parent); + } + + Node = parent as DefaultNode; + } + + public void Dispose() + { + // You need to clear Style. + // Because implemented on style for binding. + Style = null; + + // Clear binding for subscribing source changed event from old control. + // throw exception about visual tree ancestor different if you not clear binding. + BindingOperations.ClearAllBindings(this); + + var nodeLinks = _NodeLinks.ToArray(); + + // it must instance to nodeLinks because change node link collection in NodeLink Dispose. + foreach (var nodeLink in nodeLinks) + { + nodeLink.Dispose(); + } + } + + public void Connect(NodeLink nodeLink) + { + _NodeLinks.Add(nodeLink); + ConnectedCount = _NodeLinks.Count; + IsConnected = ConnectedCount > 0; + } + + public void Disconnect(NodeLink nodeLink) + { + _NodeLinks.Remove(nodeLink); + ConnectedCount = _NodeLinks.Count; + IsConnected = ConnectedCount > 0; + } + + public Point GetContentPosition(Canvas canvas, double xScaleOffset = 0.5, double yScaleOffset = 0.5) + { + // it will be shifted Control position if not called UpdateLayout(). + ConnectorControl.UpdateLayout(); + var transformer = ConnectorControl.TransformToVisual(canvas); + + var x = ConnectorControl.ActualWidth * xScaleOffset; + var y = ConnectorControl.ActualHeight * yScaleOffset; + return transformer.Transform(new Point(x, y)); + } + + public abstract void UpdateLinkPosition(Canvas canvas); + public abstract bool CanConnectTo(NodeConnectorContent connector); + + void UpdatePosition(Point pos) + { + _Position = pos; + _Translate.X = _Position.X; + _Translate.Y = _Position.Y; + + InvalidateVisual(); + } + } + + public abstract class NodeConnector : MultiSelector, IDisposable where T : NodeConnectorContent, new() + { + public Thickness ConnectorMargin + { + get => (Thickness)GetValue(ConnectorMarginProperty); + set => SetValue(ConnectorMarginProperty, value); + } + public static readonly DependencyProperty ConnectorMarginProperty = DependencyProperty.Register( + nameof(ConnectorMargin), + typeof(Thickness), + typeof(NodeConnector), + new FrameworkPropertyMetadata(new Thickness(2.0), ConnectorMarginPropertyChanged)); + + /// + /// must implement connector canvas name. + /// + protected abstract string ConnectorCanvasName { get; } + + /// + /// must implement connector content template. + /// + protected abstract ControlTemplate NodeConnectorContentTemplate { get; } + + /// + /// must implement connector content style. + /// + protected abstract Style NodeConnectorContentBaseStyle { get; } + + Canvas _Canvas = null; + + + static void ConnectorMarginPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as NodeConnector).UpdateConnectorsLayout(); + } + + public void Initialize() + { + foreach (var connector in _Canvas.Children.OfType()) + { + // ItemContainerStyle is still null during applying template, so need to apply at initialize.(ApplyTemplate later) + connector.Style = ItemContainerStyle; + connector.Initialize(); + } + } + + public void Dispose() + { + // You need to clear Style. + // Because implemented on style for binding. + Style = null; + + // Clear binding for subscribing source changed event from old control. + // throw exception about visual tree ancestor different if you not clear binding. + BindingOperations.ClearAllBindings(this); + + var connectors = _Canvas.Children.OfType().ToArray(); + + foreach (var connector in connectors) + { + connector.Dispose(); + } + } + + public NodeLink[] EnumerateConnectedNodeLinks() + { + var connectors = _Canvas.Children.OfType().ToArray(); + return connectors.SelectMany(arg => arg.NodeLinks).ToArray(); + } + + public T FindNodeConnector(Guid guid) + { + var connectors = _Canvas.Children.OfType().ToArray(); + return connectors.FirstOrDefault(arg => arg.Guid == guid); + } + + public void UpdateLinkPosition(Canvas canvas) + { + if (_Canvas == null) + { + return; + } + + foreach (var connector in _Canvas.Children.OfType()) + { + connector.UpdateLinkPosition(canvas); + } + } + + public void UpdateConnectorsLayout() + { + if (_Canvas == null) + { + return; + } + + ReplaceConnectElements(0); + + var connectorContents = _Canvas.Children.OfType(); + + if (connectorContents.Count() > 0) + { + var numElement = connectorContents.Count(); + Width = connectorContents.Max(arg => arg.ActualWidth); + Height = connectorContents.Sum(arg => arg.ActualHeight) + numElement * (ConnectorMargin.Top + ConnectorMargin.Bottom) + numElement; // last adding numElement is 1px border each other. + } + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _Canvas = GetTemplateChild(ConnectorCanvasName) as Canvas; + } + + protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) + { + base.OnItemsSourceChanged(oldValue, newValue); + + if (oldValue != null) + { + RemoveConnectorFromCanvas(oldValue.OfType()); + } + if (newValue != null) + { + AddConnectorsToCanvas(newValue.OfType()); + } + + UpdateConnectorsLayout(); + } + + void RemoveConnectorFromCanvas(IEnumerable removeVMs) + { + var removeElements = new List(); + var children = _Canvas.Children.OfType(); + + foreach (var removeVM in removeVMs) + { + var removeElement = children.First(arg => arg.DataContext == removeVM); + removeElements.Add(removeElement); + } + + foreach (var element in removeElements) + { + element.SizeChanged -= Connector_SizeChanged; + element.Dispose(); + _Canvas.Children.Remove(element); + } + } + + void AddConnectorsToCanvas(IEnumerable addVMs) + { + foreach (var vm in addVMs) + { + var connector = new T() + { + DataContext = vm, + Template = NodeConnectorContentTemplate, + }; + connector.ApplyTemplate(); + + connector.SizeChanged += Connector_SizeChanged; + _Canvas.Children.Add(connector); + } + } + + void Connector_SizeChanged(object sender, SizeChangedEventArgs e) + { + UpdateConnectorsLayout(); + } + + void ReplaceConnectElements(double offset) + { + int index = 0; + foreach (var element in _Canvas.Children.OfType()) + { + double margin = index * (ConnectorMargin.Top + ConnectorMargin.Bottom) + ConnectorMargin.Top; + element.Padding = new Thickness(ConnectorMargin.Left, 0, ConnectorMargin.Right, 0); + element.Position = new Point(0, offset + element.ActualHeight * index + margin + index); // last adding index for non overlap other connector. + ++index; + } + } + } +} diff --git a/NodeGraph.NET7/Controls/NodeGraph.cs b/NodeGraph.NET7/Controls/NodeGraph.cs new file mode 100644 index 0000000..73ee916 --- /dev/null +++ b/NodeGraph.NET7/Controls/NodeGraph.cs @@ -0,0 +1,1541 @@ +using NodeGraph.NET7.Operation; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using NodeGraph.NET7.Utilities; +using NodeGraph.NET7.Extensions; + +namespace NodeGraph.NET7.Controls +{ + public enum GroupIntersectType + { + CursorPoint, + BoundingBox, + } + + public enum RangeSelectionMode + { + Contain, + Intersect, + } + + public class NodeGraph : MultiSelector + { + public Canvas Canvas { get; private set; } = null; + + public Key MoveWithKey + { + get => (Key)GetValue(MoveWithKeyProperty); + set => SetValue(MoveWithKeyProperty, value); + } + public static readonly DependencyProperty MoveWithKeyProperty = + DependencyProperty.Register(nameof(MoveWithKey), typeof(Key), typeof(NodeGraph), new FrameworkPropertyMetadata(Key.None)); + + public MouseButton MoveWithMouse + { + get => (MouseButton)GetValue(MoveWithMouseProperty); + set => SetValue(MoveWithMouseProperty, value); + } + public static readonly DependencyProperty MoveWithMouseProperty = + DependencyProperty.Register(nameof(MoveWithMouse), typeof(MouseButton), typeof(NodeGraph), new FrameworkPropertyMetadata(MouseButton.Middle)); + + public Key ScaleWithKey + { + get => (Key)GetValue(ScaleWithKeyProperty); + set => SetValue(ScaleWithKeyProperty, value); + } + public static readonly DependencyProperty ScaleWithKeyProperty = + DependencyProperty.Register(nameof(ScaleWithKey), typeof(Key), typeof(NodeGraph), new FrameworkPropertyMetadata(Key.None)); + + public double Scale + { + get => (double)GetValue(ScaleProperty); + set => SetValue(ScaleProperty, value); + } + public static readonly DependencyProperty ScaleProperty = + DependencyProperty.Register(nameof(Scale), typeof(double), typeof(NodeGraph), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + public double MinScale + { + get => (double)GetValue(MinScaleProperty); + set => SetValue(MinScaleProperty, value); + } + public static readonly DependencyProperty MinScaleProperty = + DependencyProperty.Register(nameof(MinScale), typeof(double), typeof(NodeGraph), new FrameworkPropertyMetadata(0.1)); + + public double ScaleRate + { + get => (double)GetValue(ScaleRateProperty); + set => SetValue(ScaleRateProperty, value); + } + public static readonly DependencyProperty ScaleRateProperty = + DependencyProperty.Register(nameof(ScaleRate), typeof(double), typeof(NodeGraph), new FrameworkPropertyMetadata(0.1)); + + public Point Offset + { + get => (Point)GetValue(OffsetProperty); + set => SetValue(OffsetProperty, value); + } + public static readonly DependencyProperty OffsetProperty = + DependencyProperty.Register(nameof(Offset), typeof(Point), typeof(NodeGraph), new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OffsetPropertyChanged)); + + public ICommand PreviewConnectLinkCommand + { + get => (ICommand)GetValue(PreviewConnectLinkCommandProperty); + set => SetValue(PreviewConnectLinkCommandProperty, value); + } + public static readonly DependencyProperty PreviewConnectLinkCommandProperty = + DependencyProperty.Register(nameof(PreviewConnectLinkCommand), typeof(ICommand), typeof(NodeGraph), new FrameworkPropertyMetadata(null)); + + public ICommand ConnectedLinkCommand + { + get => (ICommand)GetValue(ConnectedLinkCommandProperty); + set => SetValue(ConnectedLinkCommandProperty, value); + } + public static readonly DependencyProperty ConnectedLinkCommandProperty = + DependencyProperty.Register(nameof(ConnectedLinkCommand), typeof(ICommand), typeof(NodeGraph), new FrameworkPropertyMetadata(null)); + + public ICommand DisconnectedLinkCommand + { + get => (ICommand)GetValue(DisconnectedLinkCommandProperty); + set => SetValue(DisconnectedLinkCommandProperty, value); + } + public static readonly DependencyProperty DisconnectedLinkCommandProperty = + DependencyProperty.Register(nameof(DisconnectedLinkCommand), typeof(ICommand), typeof(NodeGraph), new FrameworkPropertyMetadata(null)); + + public ICommand BeginMoveNodesCommand + { + get => (ICommand)GetValue(BeginMoveNodesCommandProperty); + set => SetValue(BeginMoveNodesCommandProperty, value); + } + public static readonly DependencyProperty BeginMoveNodesCommandProperty = + DependencyProperty.Register(nameof(BeginMoveNodesCommand), typeof(ICommand), typeof(NodeGraph), new FrameworkPropertyMetadata(null)); + + public ICommand EndMoveNodesCommand + { + get => (ICommand)GetValue(EndMoveNodesCommandProperty); + set => SetValue(EndMoveNodesCommandProperty, value); + } + public static readonly DependencyProperty EndMoveNodesCommandProperty = + DependencyProperty.Register(nameof(EndMoveNodesCommand), typeof(ICommand), typeof(NodeGraph), new FrameworkPropertyMetadata(null)); + + public Style NodeLinkStyle + { + get => (Style)GetValue(NodeLinkStyleProperty); + set => SetValue(NodeLinkStyleProperty, value); + } + public static readonly DependencyProperty NodeLinkStyleProperty = + DependencyProperty.Register(nameof(NodeLinkStyle), typeof(Style), typeof(NodeGraph), new FrameworkPropertyMetadata(null, NodeLinkStylePropertyChanged)); + + public Style GroupNodeStyle + { + get => (Style)GetValue(GroupNodeStyleProperty); + set => SetValue(GroupNodeStyleProperty, value); + } + public static readonly DependencyProperty GroupNodeStyleProperty = + DependencyProperty.Register(nameof(GroupNodeStyle), typeof(Style), typeof(NodeGraph), new FrameworkPropertyMetadata(null)); + + public IEnumerable NodeLinks + { + get => (IEnumerable)GetValue(NodeLinksProperty); + set => SetValue(NodeLinksProperty, value); + } + public static readonly DependencyProperty NodeLinksProperty = + DependencyProperty.Register(nameof(NodeLinks), typeof(IEnumerable), typeof(NodeGraph), new FrameworkPropertyMetadata(null, NodeLinksPropertyChanged)); + + public IEnumerable GroupNodes + { + get => (IEnumerable)GetValue(GroupNodesProperty); + set => SetValue(GroupNodesProperty, value); + } + public static readonly DependencyProperty GroupNodesProperty = + DependencyProperty.Register(nameof(GroupNodes), typeof(IEnumerable), typeof(NodeGraph), new FrameworkPropertyMetadata(null, GroupNodesPropertyChanged)); + + public GroupIntersectType GroupIntersectType + { + get => (GroupIntersectType)GetValue(GroupIntersectTypeProperty); + set => SetValue(GroupIntersectTypeProperty, value); + } + public static readonly DependencyProperty GroupIntersectTypeProperty = + DependencyProperty.Register(nameof(GroupIntersectType), typeof(GroupIntersectType), typeof(NodeGraph), new FrameworkPropertyMetadata(GroupIntersectType.BoundingBox)); + + public bool AllowToOverrideConnection + { + get => (bool)GetValue(AllowToOverrideConnectionProperty); + set => SetValue(AllowToOverrideConnectionProperty, value); + } + public static readonly DependencyProperty AllowToOverrideConnectionProperty = + DependencyProperty.Register(nameof(AllowToOverrideConnection), typeof(bool), typeof(NodeGraph), new FrameworkPropertyMetadata(false, AllowToOverrideConnectionPropertyChanged)); + + public RangeSelectionMode RangeSelectionMdoe + { + get => (RangeSelectionMode)GetValue(RangeSelectionMdoeProperty); + set => SetValue(RangeSelectionMdoeProperty, value); + } + public static readonly DependencyProperty RangeSelectionMdoeProperty = + DependencyProperty.Register(nameof(RangeSelectionMdoe), typeof(RangeSelectionMode), typeof(NodeGraph), new FrameworkPropertyMetadata(RangeSelectionMode.Contain)); + + public ICommand PreviewDropOnCanvasCommand + { + get => (ICommand)GetValue(PreviewDropOnCanvasCommandProperty); + set => SetValue(PreviewDropOnCanvasCommandProperty, value); + } + public static readonly DependencyProperty PreviewDropOnCanvasCommandProperty = + DependencyProperty.Register(nameof(PreviewDropOnCanvasCommand), typeof(ICommand), typeof(NodeGraph), new FrameworkPropertyMetadata(null)); + + public ICommand MouseMoveOnCanvasCommand + { + get => (ICommand)GetValue(MouseMoveOnCanvasCommandProperty); + set => SetValue(MouseMoveOnCanvasCommandProperty, value); + } + public static readonly DependencyProperty MouseMoveOnCanvasCommandProperty = + DependencyProperty.Register(nameof(MouseMoveOnCanvasCommand), typeof(ICommand), typeof(NodeGraph), new FrameworkPropertyMetadata(null)); + + + ControlTemplate NodeTemplate => _NodeTemplate.Get("__NodeTemplate__"); + ResourceInstance _NodeTemplate = new ResourceInstance(); + + ControlTemplate GroupNodeTemplate => _GroupNodeTemplate.Get("__GroupNodeTemplate__"); + ResourceInstance _GroupNodeTemplate = new ResourceInstance(); + + Style NodeLinkAnimationStyle => _NodeLinkAnimationStyle.Get("__NodeLinkAnimationStyle__"); + ResourceInstance + diff --git a/NodeGraph.NET7/Controls/NodeInput.cs b/NodeGraph.NET7/Controls/NodeInput.cs new file mode 100644 index 0000000..eb85635 --- /dev/null +++ b/NodeGraph.NET7/Controls/NodeInput.cs @@ -0,0 +1,91 @@ +using NodeGraph.NET7.Utilities; +using System.Windows; +using System.Windows.Controls; + +namespace NodeGraph.NET7.Controls +{ + public class NodeInputContent : NodeConnectorContent + { + public bool AllowToConnectMultiple + { + get => (bool)GetValue(AllowToConnectMultipleProperty); + set => SetValue(AllowToConnectMultipleProperty, value); + } + public static readonly DependencyProperty AllowToConnectMultipleProperty = DependencyProperty.Register( + nameof(AllowToConnectMultiple), + typeof(bool), + typeof(NodeInputContent), + new PropertyMetadata(false)); + + public static bool AllowToOverrideConnection { get; set; } = false; + + protected override FrameworkElement ConnectorControl => _ConnectorControl; + FrameworkElement _ConnectorControl = null; + + + static NodeInputContent() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(NodeInputContent), new FrameworkPropertyMetadata(typeof(NodeInputContent))); + } + + public override void OnApplyTemplate() + { + _ConnectorControl = GetTemplateChild("__InputConnector__") as FrameworkElement; + + base.OnApplyTemplate(); + } + + public override void UpdateLinkPosition(Canvas canvas) + { + var transformer = _ConnectorControl.TransformToVisual(canvas); + var posOnCanvas = transformer.Transform(new Point(0, _ConnectorControl.ActualHeight * 0.5)); + + foreach (var nodeLink in NodeLinks) + { + nodeLink.UpdateInputEdge(posOnCanvas.X, posOnCanvas.Y); + } + } + + public override bool CanConnectTo(NodeConnectorContent connector) + { + if (AllowToOverrideConnection == false && ConnectedCount > 0 && AllowToConnectMultiple == false) + { + // already connected to other node link. + return false; + } + if ((CanConnect && connector is NodeOutputContent && Node != connector.Node) == false) + { + return false; + } + + // check for circulation connecting. + var nodeLinks = connector.Node.EnumrateConnectedNodeLinks(); + foreach (var nodeLink in nodeLinks) + { + if (nodeLink.Output?.Node == Node) + { + return false; + } + } + + return true; + } + } + + public class NodeInput : NodeConnector + { + protected override string ConnectorCanvasName => "__NodeInputCanvas__"; + + protected override ControlTemplate NodeConnectorContentTemplate => _NodeInputTemplate.Get("__NodeInputContentTemplate__"); + ResourceInstance _NodeInputTemplate = new ResourceInstance(); + + protected override Style NodeConnectorContentBaseStyle => _NodeConnectorContentBaseStyle.Get("__NodeInputBaseStyle__"); + ResourceInstance + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NodeGraph.NET7/Controls/NodeLink.cs b/NodeGraph.NET7/Controls/NodeLink.cs new file mode 100644 index 0000000..06daaeb --- /dev/null +++ b/NodeGraph.NET7/Controls/NodeLink.cs @@ -0,0 +1,422 @@ +using NodeGraph.NET7.Extensions; +using System; +using System.Data; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Media; +using System.Windows.Shapes; + +namespace NodeGraph.NET7.Controls +{ + public enum NodeLinkType + { + Line, + Curve + } + + public class NodeLink : Shape, ICanvasObject, ISelectableObject + { + public Guid Guid + { + get => (Guid)GetValue(GuidProperty); + set => SetValue(GuidProperty, value); + } + public static readonly DependencyProperty GuidProperty = DependencyProperty.Register( + nameof(Guid), + typeof(Guid), + typeof(NodeLink), + new FrameworkPropertyMetadata(Guid.NewGuid())); + + public Guid InputConnectorGuid + { + get => (Guid)GetValue(InputConnectorGuidProperty); + set => SetValue(InputConnectorGuidProperty, value); + } + public static readonly DependencyProperty InputConnectorGuidProperty = DependencyProperty.Register( + nameof(InputConnectorGuid), + typeof(Guid), + typeof(NodeLink), + new FrameworkPropertyMetadata(Guid.NewGuid())); + + public Guid OutputConnectorGuid + { + get => (Guid)GetValue(OutputConnectorGuidProperty); + set => SetValue(OutputConnectorGuidProperty, value); + } + public static readonly DependencyProperty OutputConnectorGuidProperty = DependencyProperty.Register( + nameof(OutputConnectorGuid), + typeof(Guid), + typeof(NodeLink), + new FrameworkPropertyMetadata(Guid.NewGuid())); + + public double LinkSize + { + get => (double)GetValue(LinkSizeProperty); + set => SetValue(LinkSizeProperty, value); + } + public static readonly DependencyProperty LinkSizeProperty = + DependencyProperty.Register(nameof(LinkSize), typeof(double), typeof(NodeLink), new FrameworkPropertyMetadata(2.0)); + + public double DashOffset + { + get => (double)GetValue(DashOffsetProperty); + set => SetValue(DashOffsetProperty, value); + } + public static readonly DependencyProperty DashOffsetProperty = + DependencyProperty.Register(nameof(DashOffset), typeof(double), typeof(NodeLink), new FrameworkPropertyMetadata(0.0, DashOffsetPropertyChanged)); + + public NodeLinkType LinkType + { + get => (NodeLinkType)GetValue(LinkTypeProperty); + set => SetValue(LinkTypeProperty, value); + } + public static readonly DependencyProperty LinkTypeProperty = + DependencyProperty.Register(nameof(LinkType), typeof(NodeLinkType), typeof(NodeLink), new FrameworkPropertyMetadata(NodeLinkType.Curve, LinkTypePropertyChanged)); + + public bool IsLocked + { + get => (bool)GetValue(IsLockedProperty); + set => SetValue(IsLockedProperty, value); + } + public static readonly DependencyProperty IsLockedProperty = + DependencyProperty.Register(nameof(IsLocked), typeof(bool), typeof(NodeLink), new FrameworkPropertyMetadata(false)); + + public bool IsSelected + { + get => (bool)GetValue(IsSelectedProperty); + set => SetValue(IsSelectedProperty, value); + } + public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register( + nameof(IsSelected), typeof(bool), typeof(NodeLink), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + static void DashOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var nodeLink = (NodeLink)d; + nodeLink.InvalidateVisual(); + } + + static void LinkTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var nodeLink = (NodeLink)d; + nodeLink.InvalidateVisual(); + } + + public bool IsConnecting => Input != null && Output != null; + + public NodeInputContent Input { get; private set; } = null; + public NodeOutputContent Output { get; private set; } = null; + + public NodeConnectorContent StartConnector { get; private set; } = null; + public NodeConnectorContent ToEndConnector { get; private set; } = null; + + protected override Geometry DefiningGeometry => StreamGeometry; + + double EndPointX => _EndPoint.X / _Scale; + double EndPointY => _EndPoint.Y / _Scale; + Point _EndPoint = new Point(0, 0); + + double StartPointX => _StartPoint.X / _Scale; + double StartPointY => _StartPoint.Y / _Scale; + Point _StartPoint = new Point(0, 0); + + Canvas Canvas { get; } = null; + + double _Scale = 1.0f; + Point _Offset = new Point(0, 0); + Point _RestoreEndPoint = new Point(); + + readonly StreamGeometry StreamGeometry = new StreamGeometry(); + + static NodeLink() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(NodeLink), new FrameworkPropertyMetadata(typeof(NodeLink))); + } + + internal NodeLink(Canvas canvas, double scale, Point offset) + { + // constructed from view model or other constructors. + Canvas = canvas; + + IsHitTestVisible = false; // no need to hit until connected + + _Scale = scale; + _Offset = offset; + + var transformGroup = new TransformGroup(); + transformGroup.Children.Add(new ScaleTransform() { ScaleX = scale, ScaleY = scale }); + RenderTransform = transformGroup; + RenderTransformOrigin = new Point(0.5, 0.5); + + Panel.SetZIndex(this, -1); + } + + internal NodeLink(NodeConnectorContent connector, double x, double y, Canvas canvas, double scale, Point offset) : this(x, y, canvas, scale, offset) + { + // constructed from view for previewing node link. + switch (connector) + { + case NodeInputContent input: + Input = input; + StartConnector = input; + break; + case NodeOutputContent output: + Output = output; + StartConnector = output; + break; + default: + throw new InvalidCastException($"Cannot cast this connector. {connector}"); + } + } + + NodeLink(double x, double y, Canvas canvas, double scale, Point offset) : this(canvas, scale, offset) + { + // create from view for preview reconnecting link. + var point = new Point(x, y); + _StartPoint = point; + _EndPoint = point; + } + + public bool Contains(Rect rect) + { + var test = Rect.Offset(RenderedGeometry.Bounds, -_Offset.X, -_Offset.Y); + return rect.Contains(test); + } + + public bool IntersectsWith(Rect rect) + { + var test = Rect.Offset(RenderedGeometry.Bounds, -_Offset.X, -_Offset.Y); + return rect.IntersectsWith(test); + } + + internal NodeLink CreateGhost() + { + var ghost = new NodeLink(Canvas, _Scale, _Offset); + ghost.DataContext = null; // ghost link cannot binding connector properties. so you have to bind null to DataContext.(otherwise, occur binding errors) + ghost._StartPoint = _StartPoint; + ghost._EndPoint = _EndPoint; + ghost.IsHitTestVisible = false; + ghost.Fill = new SolidColorBrush(Color.FromArgb(128, 128, 128, 128)); + + return ghost; + } + + internal void Validate() + { + if (Guid == Guid.Empty || InputConnectorGuid == Guid.Empty || OutputConnectorGuid == Guid.Empty) + { + throw new DataException("Does not assigned guid"); + } + } + + public void Dispose() + { + // You need to clear Style. + // Because implemented on style for binding. + Style = null; + + // Clear binding for subscribing source changed event from old control. + // throw exception about visual tree ancestor different if you not clear binding. + BindingOperations.ClearAllBindings(this); + + Input?.Disconnect(this); + Input = null; + Output?.Disconnect(this); + Output = null; + } + + internal void Connect(NodeInputContent input, NodeOutputContent output) + { + Input?.Disconnect(this); + Output?.Disconnect(this); + + Input = input; + Output = output; + + StartConnector = Output; + ToEndConnector = Input; + + IsHitTestVisible = true; + + UpdateConnectPosition(); + + InvalidateVisual(); + } + + public void UpdateOffset(Point offset) + { + _Offset = offset; + + UpdateConnectPosition(); + + InvalidateVisual(); + } + + internal void UpdateEdgePoint(double x, double y) + { + if (Input == null) + { + // first connected output to input. + _EndPoint = new Point(x, y); + } + else if (Output == null) + { + // first connected input to output. + _StartPoint = new Point(x, y); + } + else + { + // reconnected point. + _EndPoint = new Point(x, y); + } + + InvalidateVisual(); + } + + internal void ReleaseEndPoint() + { + IsHitTestVisible = false; + _RestoreEndPoint = _EndPoint; + } + + internal void RestoreEndPoint() + { + IsHitTestVisible = true; + UpdateEdgePoint(_RestoreEndPoint.X, _RestoreEndPoint.Y); + } + + internal void UpdateInputEdge(double x, double y) + { + _EndPoint = new Point(x, y); + InvalidateVisual(); + } + + internal void UpdateOutputEdge(double x, double y) + { + _StartPoint = new Point(x, y); + InvalidateVisual(); + } + + protected override void OnRender(DrawingContext drawingContext) + { + switch (LinkType) + { + case NodeLinkType.Line: + DrawLine(drawingContext); + break; + case NodeLinkType.Curve: + DrawCurve(drawingContext); + break; + } + } + + void UpdateConnectPosition() + { + _EndPoint = Input.GetContentPosition(Canvas, 0, 0.5); + _StartPoint = Output.GetContentPosition(Canvas, 0.5, 0.5); + } + + void DrawLine(DrawingContext drawingContext) + { + var start = new Point(StartPointX, StartPointY); + var end = new Point(EndPointX, EndPointY); + + double linkSize = LinkSize * (1.0f / _Scale); + + // collision + if (IsHitTestVisible) + { + var hitVisiblePen = new Pen(Brushes.Transparent, linkSize + StrokeThickness); + hitVisiblePen.Freeze(); + drawingContext.DrawLine(hitVisiblePen, start, end); + } + + // actually visual + var visualPen = new Pen(Fill, linkSize); + if (IsMouseOver) + { + visualPen.DashStyle = new DashStyle(new double[] { 2 }, DashOffset); + } + visualPen.Freeze(); + + drawingContext.DrawLine(visualPen, start, end); + + // arrow + var vEnd = new Vector(EndPointX, EndPointY); + var vStart = new Vector(StartPointX, StartPointY); + var degree = Vector.AngleBetween(vStart - vEnd, vStart); + + if (IsConnecting == false) + { + return; + } + + var segment = vStart - vEnd; + segment.Normalize(); + + Matrix rotMat = new Matrix(); + { + rotMat.Rotate(30); + + var arrow = Vector.Multiply(segment, rotMat); + drawingContext.DrawLine(visualPen, end, arrow * 12 + end); + } + { + rotMat.Rotate(-60); + + var arrow = Vector.Multiply(segment, rotMat); + drawingContext.DrawLine(visualPen, end, arrow * 12 + end); + } + } + + void DrawCurve(DrawingContext drawingContext) + { + var start = new Point(StartPointX, StartPointY); + var end = new Point(EndPointX, EndPointY); + + Point c0 = new Point(); + Point c1 = new Point(); + double power = 100; + + var pen = new Pen(Brushes.Green, 2); + + var axis = new Vector(1, 0); + var startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo(); + + var k = 1 - Math.Pow(Math.Max(0, axis.DotProduct(startToEnd)), 10.0); + var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0; + + c0 = new Point(+(power + bias) * k + start.X, start.Y); + c1 = new Point(-(power + bias) * k + end.X, end.Y); + + StreamGeometry.Clear(); + + using (var context = StreamGeometry.Open()) + { + context.BeginFigure(start, true, false); + context.BezierTo(c0, c1, end, true, false); + } + + double linkSize = LinkSize * (1.0f / _Scale); + + // collision + if (IsHitTestVisible) + { + var hitVisiblePen = new Pen(Brushes.Transparent, linkSize + StrokeThickness); + hitVisiblePen.Freeze(); + drawingContext.DrawGeometry(null, hitVisiblePen, StreamGeometry); + } + + // actually visual + var visualPen = new Pen(Fill, linkSize); + if (IsMouseOver) + { + visualPen.DashStyle = new DashStyle(new double[] { 2 }, DashOffset); + } + + visualPen.Freeze(); + + drawingContext.DrawGeometry(null, visualPen, StreamGeometry); + } + } +} diff --git a/NodeGraph.NET7/Controls/NodeLink.xaml b/NodeGraph.NET7/Controls/NodeLink.xaml new file mode 100644 index 0000000..5eaf9ae --- /dev/null +++ b/NodeGraph.NET7/Controls/NodeLink.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/NodeGraph.NET7/Controls/NodeOuput.cs b/NodeGraph.NET7/Controls/NodeOuput.cs new file mode 100644 index 0000000..eeb880a --- /dev/null +++ b/NodeGraph.NET7/Controls/NodeOuput.cs @@ -0,0 +1,72 @@ +using NodeGraph.NET7.Utilities; +using System.Windows; +using System.Windows.Controls; + +namespace NodeGraph.NET7.Controls +{ + public class NodeOutputContent : NodeConnectorContent + { + protected override FrameworkElement ConnectorControl => _ConnectorControl; + FrameworkElement _ConnectorControl = null; + + static NodeOutputContent() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(NodeOutputContent), new FrameworkPropertyMetadata(typeof(NodeOutputContent))); + } + + public override void OnApplyTemplate() + { + _ConnectorControl = GetTemplateChild("__OutputConnector__") as FrameworkElement; + + base.OnApplyTemplate(); + } + + public override void UpdateLinkPosition(Canvas canvas) + { + var transformer = ConnectorControl.TransformToVisual(canvas); + var posOnCanvas = transformer.Transform(new Point(ConnectorControl.ActualWidth * 0.5, ConnectorControl.ActualHeight * 0.5)); + + foreach (var nodeLink in NodeLinks) + { + nodeLink.UpdateOutputEdge(posOnCanvas.X, posOnCanvas.Y); + } + } + + public override bool CanConnectTo(NodeConnectorContent connector) + { + if ((CanConnect && connector is NodeInputContent && Node != connector.Node) == false) + { + return false; + } + + // check for circulation connecting. + var nodeLinks = connector.Node.EnumrateConnectedNodeLinks(); + foreach (var nodeLink in nodeLinks) + { + if (nodeLink.Input?.Node == Node) + { + return false; + } + } + + return true; + } + } + + public class NodeOutput : NodeConnector + { + protected override string ConnectorCanvasName => "__NodeOutputCanvas__"; + + protected override ControlTemplate NodeConnectorContentTemplate => _NodeOutputTemplate.Get("__NodeOutputContentTemplate__"); + + protected override Style NodeConnectorContentBaseStyle => _NodeConnectorContentBaseStyle.Get("__NodeOutputBaseStyle__"); + ResourceInstance + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NodeGraph.NET7/Controls/RangeSelector.cs b/NodeGraph.NET7/Controls/RangeSelector.cs new file mode 100644 index 0000000..055e640 --- /dev/null +++ b/NodeGraph.NET7/Controls/RangeSelector.cs @@ -0,0 +1,56 @@ +using System.Windows; +using System.Windows.Media; +using System.Windows.Shapes; + +namespace NodeGraph.NET7.Controls +{ + public class RangeSelector : Shape + { + public Rect RangeRect + { + get => (Rect)GetValue(RangeRectProperty); + set => SetValue(RangeRectProperty, value); + } + public static readonly DependencyProperty RangeRectProperty = DependencyProperty.Register( + nameof(RangeRect), + typeof(Rect), + typeof(RangeSelector), + new FrameworkPropertyMetadata(new Rect(), FrameworkPropertyMetadataOptions.AffectsRender)); + + public bool IsIntersects + { + get => (bool)GetValue(IsIntersectsProperty); + set => SetValue(IsIntersectsProperty, value); + } + public static readonly DependencyProperty IsIntersectsProperty = DependencyProperty.Register( + nameof(IsIntersects), + typeof(bool), + typeof(RangeSelector), + new FrameworkPropertyMetadata(false)); + + protected override Geometry DefiningGeometry => Geometry.Empty; + + Pen _StrokePen = null; + + static RangeSelector() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RangeSelector), new FrameworkPropertyMetadata(typeof(RangeSelector))); + } + + public void Reset(Point pos) + { + RangeRect = new Rect(pos, Size.Empty); + IsIntersects = false; + } + + protected override void OnRender(DrawingContext drawingContext) + { + base.OnRender(drawingContext); + + _StrokePen = new Pen(Stroke, StrokeThickness); + _StrokePen.Freeze(); + + drawingContext.DrawRectangle(Fill, _StrokePen, RangeRect); + } + } +} diff --git a/NodeGraph.NET7/Controls/RangeSelector.xaml b/NodeGraph.NET7/Controls/RangeSelector.xaml new file mode 100644 index 0000000..05a2e1e --- /dev/null +++ b/NodeGraph.NET7/Controls/RangeSelector.xaml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/NodeGraph.NET7/Controls/Ruler.cs b/NodeGraph.NET7/Controls/Ruler.cs new file mode 100644 index 0000000..1fd32be --- /dev/null +++ b/NodeGraph.NET7/Controls/Ruler.cs @@ -0,0 +1,143 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace NodeGraph.NET7.Controls +{ + internal class Ruler : ContentControl + { + public double Scale + { + get => (double)GetValue(ScaleProperty); + set => SetValue(ScaleProperty, value); + } + public static readonly DependencyProperty ScaleProperty = + DependencyProperty.Register(nameof(Scale), typeof(double), typeof(Ruler), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender)); + + public Point Offset + { + get => (Point)GetValue(OffsetProperty); + set => SetValue(OffsetProperty, value); + } + public static readonly DependencyProperty OffsetProperty = + DependencyProperty.Register(nameof(Offset), typeof(Point), typeof(Ruler), new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.AffectsRender)); + + public Brush Color + { + get => (Brush)GetValue(ColorProperty); + set => SetValue(ColorProperty, value); + } + public static readonly DependencyProperty ColorProperty = + DependencyProperty.Register(nameof(Color), typeof(Brush), typeof(Ruler), new FrameworkPropertyMetadata(Brushes.Black, ColorPropertyChanged)); + + public Orientation Orientation + { + get => (Orientation)GetValue(OrientationProperty); + set => SetValue(OrientationProperty, value); + } + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(Ruler), new FrameworkPropertyMetadata(Orientation.Horizontal)); + + Pen _Pen = null; + static Typeface Typeface = new Typeface("Verdana"); + static double LineOffset = 3; + static double LineLength = 5 + LineOffset; + static double LineDistance = 100; + static double SubLineOffset = 1; + static double SubLineLength = 5 + SubLineOffset; + static double SubLineDistance = LineDistance / 10; + + static void ColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as Ruler).UpdatePen(); + } + + static Ruler() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(Ruler), new FrameworkPropertyMetadata(typeof(Ruler))); + } + + public Ruler() + { + + } + + protected override void OnRender(DrawingContext drawingContext) + { + base.OnRender(drawingContext); + + if (_Pen == null) + { + UpdatePen(); + } + + switch (Orientation) + { + case Orientation.Horizontal: + OnRenderHorizontal(drawingContext); + break; + case Orientation.Vertical: + OnRenderVertical(drawingContext); + break; + } + } + + void OnRenderHorizontal(DrawingContext dc) + { + double s = Math.Max(Scale, 1); + double numScale = Math.Max(1.0 / Scale, 1); + + int init = (int)(-Offset.X / LineDistance) - 1; + int count = (int)((-Offset.X + ActualWidth) / LineDistance) + 1; + for (int i = init; i < count; ++i) + { + double num = i * LineDistance; + double x = (num + Offset.X) * s; + dc.DrawLine(_Pen, new Point(x, LineOffset), new Point(x, LineLength)); + + for (int j = 1; j < 10; ++j) + { + double sub_x = x + j * SubLineDistance * s; + dc.DrawLine(_Pen, new Point(sub_x, SubLineOffset), new Point(sub_x, SubLineLength)); + } + + int numText = (int)(num * numScale); + var text = new FormattedText($"{numText}", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, Typeface, 8, Color, 1.0); + dc.DrawText(text, new Point(x - text.Width * 0.5, LineLength)); + } + } + + void OnRenderVertical(DrawingContext dc) + { + double s = Math.Max(Scale, 1); + double numScale = Math.Max(1.0 / Scale, 1); + + int init = (int)(-Offset.Y / LineDistance) - 1; + int count = (int)((-Offset.Y + ActualHeight) / LineDistance) + 1; + for (int i = init; i < count; ++i) + { + double num = i * LineDistance; + double y = (num + Offset.Y) * s; + dc.DrawLine(_Pen, new Point(LineOffset, y), new Point(LineLength, y)); + + for (int j = 1; j < 10; ++j) + { + double sub_y = y + j * SubLineDistance * s; + dc.DrawLine(_Pen, new Point(SubLineOffset, sub_y), new Point(SubLineLength, sub_y)); + } + + int numText = (int)(num * numScale); + var text = new FormattedText($"{numText}", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, Typeface, 8, Color, 1.0); + dc.DrawText(text, new Point(LineLength + LineOffset, y - text.Height * 0.5)); + } + } + + void UpdatePen() + { + _Pen = new Pen(Color, 1); + _Pen.Freeze(); + } + } +} diff --git a/NodeGraph.NET7/Controls/StaticDefinition.cs b/NodeGraph.NET7/Controls/StaticDefinition.cs new file mode 100644 index 0000000..1304997 --- /dev/null +++ b/NodeGraph.NET7/Controls/StaticDefinition.cs @@ -0,0 +1,15 @@ +namespace NodeGraph.NET7.Controls +{ + public static class ControlSize + { + public const double ConnectorSize = 13; + } + + public static class GroupNodeDefault + { + public const double Width = 500; + public const double Height = 300; + public const double MinWidth = 50; + public const double MinHeight = 50; + } +} diff --git a/NodeGraph.NET7/Converters/InverseBooleanToVisibilityConverter.cs b/NodeGraph.NET7/Converters/InverseBooleanToVisibilityConverter.cs new file mode 100644 index 0000000..22b5fb5 --- /dev/null +++ b/NodeGraph.NET7/Converters/InverseBooleanToVisibilityConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace NodeGraph.NET7.Converters +{ + public class InverseBooleanToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var boolValue = (bool)value; + return boolValue ? Visibility.Collapsed : Visibility.Visible; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + var visibility = (Visibility)value; + return visibility == Visibility.Visible ? false : true; + } + } +} diff --git a/NodeGraph.NET7/Extensions/PointExtension.cs b/NodeGraph.NET7/Extensions/PointExtension.cs new file mode 100644 index 0000000..6bb0ddb --- /dev/null +++ b/NodeGraph.NET7/Extensions/PointExtension.cs @@ -0,0 +1,22 @@ +using System.Windows; + +namespace NodeGraph.NET7.Extensions +{ + public static class PointExtension + { + public static Point Add(this Point a, Point b) + { + return new Point(a.X + b.X, a.Y + b.Y); + } + + public static Point Sub(this Point a, Point b) + { + return new Point(a.X - b.X, a.Y - b.Y); + } + + public static Vector ToVector(this Point me) + { + return new Vector(me.X, me.Y); + } + } +} diff --git a/NodeGraph.NET7/Extensions/VectorExtension.cs b/NodeGraph.NET7/Extensions/VectorExtension.cs new file mode 100644 index 0000000..258d9c4 --- /dev/null +++ b/NodeGraph.NET7/Extensions/VectorExtension.cs @@ -0,0 +1,20 @@ +using System.Windows; + +namespace NodeGraph.NET7.Extensions +{ + public static class VectorExtension + { + public static double DotProduct(this Vector a, Vector b) + { + return a.X * b.X + a.Y * b.Y; + } + + public static Vector NormalizeTo(this Vector v) + { + var temp = v; + temp.Normalize(); + + return temp; + } + } +} diff --git a/NodeGraph.NET7/NodeGraph.NET7.csproj b/NodeGraph.NET7/NodeGraph.NET7.csproj new file mode 100644 index 0000000..2d1227e --- /dev/null +++ b/NodeGraph.NET7/NodeGraph.NET7.csproj @@ -0,0 +1,29 @@ + + + + net7.0-windows + enable + true + + + + + + + + + + True + True + Settings.settings + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + diff --git a/NodeGraph.NET7/Operation/BeginMoveNodesOperationEventArgs.cs b/NodeGraph.NET7/Operation/BeginMoveNodesOperationEventArgs.cs new file mode 100644 index 0000000..181e632 --- /dev/null +++ b/NodeGraph.NET7/Operation/BeginMoveNodesOperationEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class BeginMoveNodesOperationEventArgs : EventArgs + { + public Guid[] NodeGuids { get; } = null!; + + public BeginMoveNodesOperationEventArgs(Guid[] nodeGuids) + { + NodeGuids = nodeGuids; + } + } +} diff --git a/NodeGraph.NET7/Operation/ConnectOperationEventArgs.cs b/NodeGraph.NET7/Operation/ConnectOperationEventArgs.cs new file mode 100644 index 0000000..d0e596a --- /dev/null +++ b/NodeGraph.NET7/Operation/ConnectOperationEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class ConnectedOperationEventArgs : EventArgs + { + public Guid InputNodeGuid { get; } = Guid.Empty; + public Guid InputConnectorGuid { get; } = Guid.Empty; + public Guid OutputNodeGuid { get; } = Guid.Empty; + public Guid OutputConnectorGuid { get; } = Guid.Empty; + + public ConnectedOperationEventArgs( + Guid inputNodeGuid, + Guid inputConnectorGuid, + Guid outputNodeGuid, + Guid outputConnectorGuid) + { + InputNodeGuid = inputNodeGuid; + InputConnectorGuid = inputConnectorGuid; + OutputNodeGuid = outputNodeGuid; + OutputConnectorGuid = outputConnectorGuid; + } + } +} diff --git a/NodeGraph.NET7/Operation/ConnectedLinkOperationEventArgs.cs b/NodeGraph.NET7/Operation/ConnectedLinkOperationEventArgs.cs new file mode 100644 index 0000000..c2a58cb --- /dev/null +++ b/NodeGraph.NET7/Operation/ConnectedLinkOperationEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class ConnectedLinkOperationEventArgs : EventArgs + { + public Guid OutputConnectorGuid { get; } = Guid.Empty; + public Guid OutputConnectorNodeGuid { get; } = Guid.Empty; + public Guid InputConnectorGuid { get; } = Guid.Empty; + public Guid InputConnectorNodeGuid { get; } = Guid.Empty; + + public ConnectedLinkOperationEventArgs( + Guid outputConnectorGuid, + Guid outputConnectorNodeGuid, + Guid inputConnectorGuid, + Guid inputConnectorNodeGuid) + { + InputConnectorGuid = inputConnectorGuid; + InputConnectorNodeGuid = inputConnectorNodeGuid; + OutputConnectorGuid = outputConnectorGuid; + OutputConnectorNodeGuid = outputConnectorNodeGuid; + } + } +} diff --git a/NodeGraph.NET7/Operation/DisconnectOperationEventArgs.cs b/NodeGraph.NET7/Operation/DisconnectOperationEventArgs.cs new file mode 100644 index 0000000..35dfc6a --- /dev/null +++ b/NodeGraph.NET7/Operation/DisconnectOperationEventArgs.cs @@ -0,0 +1,27 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class DisconnectedOperationEventArgs : EventArgs + { + public Guid NodeLinkGuid { get; } = Guid.Empty; + public Guid OutputConnectorGuid { get; } = Guid.Empty; + public Guid OutputConnectorNodeGuid { get; } = Guid.Empty; + public Guid InputConnectorGuid { get; } = Guid.Empty; + public Guid InputConnectorNodeGuid { get; } = Guid.Empty; + + public DisconnectedOperationEventArgs( + Guid nodeLinkGuid, + Guid outputConnectorGuid, + Guid outputConnectorNodeGuid, + Guid inputConnectorGuid, + Guid inputConnectorNodeGuid) + { + NodeLinkGuid = nodeLinkGuid; + OutputConnectorGuid = outputConnectorGuid; + OutputConnectorNodeGuid = outputConnectorNodeGuid; + InputConnectorGuid = inputConnectorGuid; + InputConnectorNodeGuid = inputConnectorNodeGuid; + } + } +} diff --git a/NodeGraph.NET7/Operation/DisconnectedLinkOperationEventArgs.cs b/NodeGraph.NET7/Operation/DisconnectedLinkOperationEventArgs.cs new file mode 100644 index 0000000..9a13426 --- /dev/null +++ b/NodeGraph.NET7/Operation/DisconnectedLinkOperationEventArgs.cs @@ -0,0 +1,27 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class DisconnectedLinkOperationEventArgs : EventArgs + { + public Guid NodeLinkGuid { get; } = Guid.Empty; + public Guid OutputConnectorGuid { get; } = Guid.Empty; + public Guid OutputConnectorNodeGuid { get; } = Guid.Empty; + public Guid InputConnectorGuid { get; } = Guid.Empty; + public Guid InputConnectorNodeGuid { get; } = Guid.Empty; + + public DisconnectedLinkOperationEventArgs( + Guid nodeLinkGuid, + Guid outputConnectorGuid, + Guid outputConnectorNodeGuid, + Guid inputConnectorGuid, + Guid inputConnectorNodeGuid) + { + NodeLinkGuid = nodeLinkGuid; + InputConnectorNodeGuid = inputConnectorNodeGuid; + InputConnectorGuid = inputConnectorGuid; + OutputConnectorNodeGuid = outputConnectorNodeGuid; + OutputConnectorGuid = outputConnectorGuid; + } + } +} diff --git a/NodeGraph.NET7/Operation/EndMoveNodesOperationEventArgs.cs b/NodeGraph.NET7/Operation/EndMoveNodesOperationEventArgs.cs new file mode 100644 index 0000000..46e2e8d --- /dev/null +++ b/NodeGraph.NET7/Operation/EndMoveNodesOperationEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class EndMoveNodesOperationEventArgs : EventArgs + { + public Guid[] NodeGuids { get; } = null; + + public EndMoveNodesOperationEventArgs(Guid[] nodeGuids) + { + NodeGuids = nodeGuids; + } + } +} diff --git a/NodeGraph.NET7/Operation/MovedNodesOperationEventArgs.cs b/NodeGraph.NET7/Operation/MovedNodesOperationEventArgs.cs new file mode 100644 index 0000000..1b226a4 --- /dev/null +++ b/NodeGraph.NET7/Operation/MovedNodesOperationEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class NodesMovedOperationEventArgs : EventArgs + { + public Guid[] NodeGuids { get; } = null; + + public NodesMovedOperationEventArgs(Guid[] nodeGuids) + { + NodeGuids = nodeGuids; + } + } +} diff --git a/NodeGraph.NET7/Operation/PreviewConnectLinkOperationEventArgs.cs b/NodeGraph.NET7/Operation/PreviewConnectLinkOperationEventArgs.cs new file mode 100644 index 0000000..b132bc0 --- /dev/null +++ b/NodeGraph.NET7/Operation/PreviewConnectLinkOperationEventArgs.cs @@ -0,0 +1,26 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class PreviewConnectLinkOperationEventArgs + { + public bool CanConnect { get; set; } = true; + + public Guid ConnectStartNodeGuid { get; } = Guid.Empty; + public Guid ConnectStartConnectorGuid { get; } = Guid.Empty; + public Guid ConnectToEndNodeGuid { get; } = Guid.Empty; + public Guid ConnectToEndConnectorGuid { get; } = Guid.Empty; + + public PreviewConnectLinkOperationEventArgs( + Guid connectStartNodeGuid, + Guid connectStartConnectorGuid, + Guid connectToEndNodeGuid, + Guid connectToEndConnectorGuid) + { + ConnectStartNodeGuid = connectStartNodeGuid; + ConnectStartConnectorGuid = connectStartConnectorGuid; + ConnectToEndNodeGuid = connectToEndNodeGuid; + ConnectToEndConnectorGuid = connectToEndConnectorGuid; + } + } +} diff --git a/NodeGraph.NET7/Operation/PreviewConnectOperationEventArgs.cs b/NodeGraph.NET7/Operation/PreviewConnectOperationEventArgs.cs new file mode 100644 index 0000000..0934d46 --- /dev/null +++ b/NodeGraph.NET7/Operation/PreviewConnectOperationEventArgs.cs @@ -0,0 +1,26 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class PreviewConnectOperationEventArgs + { + public bool CanConnect { get; set; } = true; + + public Guid ConnectStartNodeGuid { get; } = Guid.Empty; + public Guid ConnectStartConnectorGuid { get; } = Guid.Empty; + public Guid ConnectToEndNodeGuid { get; } = Guid.Empty; + public Guid ConnectToEndConnectorGuid { get; } = Guid.Empty; + + public PreviewConnectOperationEventArgs( + Guid connectStartNodeGuid, + Guid connectStartConnectorGuid, + Guid connectToEndNodeGuid, + Guid connectToEndConnectorGuid) + { + ConnectStartNodeGuid = connectStartNodeGuid; + ConnectStartConnectorGuid = connectStartConnectorGuid; + ConnectToEndNodeGuid = connectToEndNodeGuid; + ConnectToEndConnectorGuid = connectToEndConnectorGuid; + } + } +} diff --git a/NodeGraph.NET7/Operation/StartMoveNodesOperationEventArgs.cs b/NodeGraph.NET7/Operation/StartMoveNodesOperationEventArgs.cs new file mode 100644 index 0000000..fedf5f4 --- /dev/null +++ b/NodeGraph.NET7/Operation/StartMoveNodesOperationEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace NodeGraph.NET7.Operation +{ + public class StartMoveNodesOperationEventArgs : EventArgs + { + public Guid[] NodeGuids { get; } = null; + + public StartMoveNodesOperationEventArgs(Guid[] nodeGuids) + { + NodeGuids = nodeGuids; + } + } +} diff --git a/NodeGraph.NET7/OperationEventArgs/BeginMoveNodesOperationEventArgs.cs b/NodeGraph.NET7/OperationEventArgs/BeginMoveNodesOperationEventArgs.cs new file mode 100644 index 0000000..e2522f8 --- /dev/null +++ b/NodeGraph.NET7/OperationEventArgs/BeginMoveNodesOperationEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace NodeGraph.NET7.OperationEventArgs +{ + public class BeginMoveNodesOperationEventArgs : EventArgs + { + public Guid[] NodeGuids { get; } = null!; + + public BeginMoveNodesOperationEventArgs(Guid[] nodeGuids) + { + NodeGuids = nodeGuids; + } + } +} diff --git a/NodeGraph.NET7/OperationEventArgs/ConnectedLinkOperationEventArgs.cs b/NodeGraph.NET7/OperationEventArgs/ConnectedLinkOperationEventArgs.cs new file mode 100644 index 0000000..94a2b38 --- /dev/null +++ b/NodeGraph.NET7/OperationEventArgs/ConnectedLinkOperationEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace NodeGraph.NET7.OperationEventArgs +{ + public class ConnectedLinkOperationEventArgs : EventArgs + { + public Guid OutputConnectorGuid { get; } = Guid.Empty; + public Guid OutputConnectorNodeGuid { get; } = Guid.Empty; + public Guid InputConnectorGuid { get; } = Guid.Empty; + public Guid InputConnectorNodeGuid { get; } = Guid.Empty; + + public ConnectedLinkOperationEventArgs( + Guid outputConnectorGuid, + Guid outputConnectorNodeGuid, + Guid inputConnectorGuid, + Guid inputConnectorNodeGuid) + { + InputConnectorGuid = inputConnectorGuid; + InputConnectorNodeGuid = inputConnectorNodeGuid; + OutputConnectorGuid = outputConnectorGuid; + OutputConnectorNodeGuid = outputConnectorNodeGuid; + } + } +} diff --git a/NodeGraph.NET7/OperationEventArgs/DisconnectedLinkOperationEventArgs.cs b/NodeGraph.NET7/OperationEventArgs/DisconnectedLinkOperationEventArgs.cs new file mode 100644 index 0000000..f4ba280 --- /dev/null +++ b/NodeGraph.NET7/OperationEventArgs/DisconnectedLinkOperationEventArgs.cs @@ -0,0 +1,27 @@ +using System; + +namespace NodeGraph.NET7.OperationEventArgs +{ + public class DisconnectedLinkOperationEventArgs : EventArgs + { + public Guid NodeLinkGuid { get; } = Guid.Empty; + public Guid OutputConnectorGuid { get; } = Guid.Empty; + public Guid OutputConnectorNodeGuid { get; } = Guid.Empty; + public Guid InputConnectorGuid { get; } = Guid.Empty; + public Guid InputConnectorNodeGuid { get; } = Guid.Empty; + + public DisconnectedLinkOperationEventArgs( + Guid nodeLinkGuid, + Guid outputConnectorGuid, + Guid outputConnectorNodeGuid, + Guid inputConnectorGuid, + Guid inputConnectorNodeGuid) + { + NodeLinkGuid = nodeLinkGuid; + InputConnectorNodeGuid = inputConnectorNodeGuid; + InputConnectorGuid = inputConnectorGuid; + OutputConnectorNodeGuid = outputConnectorNodeGuid; + OutputConnectorGuid = outputConnectorGuid; + } + } +} diff --git a/NodeGraph.NET7/OperationEventArgs/EndMoveNodesOperationEventArgs.cs b/NodeGraph.NET7/OperationEventArgs/EndMoveNodesOperationEventArgs.cs new file mode 100644 index 0000000..7d9c048 --- /dev/null +++ b/NodeGraph.NET7/OperationEventArgs/EndMoveNodesOperationEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace NodeGraph.NET7.OperationEventArgs +{ + public class EndMoveNodesOperationEventArgs : EventArgs + { + public Guid[] NodeGuids { get; } = null!; + + public EndMoveNodesOperationEventArgs(Guid[] nodeGuids) + { + NodeGuids = nodeGuids; + } + } +} diff --git a/NodeGraph.NET7/OperationEventArgs/PreviewConnectLinkOperationEventArgs.cs b/NodeGraph.NET7/OperationEventArgs/PreviewConnectLinkOperationEventArgs.cs new file mode 100644 index 0000000..2f507ee --- /dev/null +++ b/NodeGraph.NET7/OperationEventArgs/PreviewConnectLinkOperationEventArgs.cs @@ -0,0 +1,26 @@ +using System; + +namespace NodeGraph.NET7.OperationEventArgs +{ + public class PreviewConnectLinkOperationEventArgs + { + public bool CanConnect { get; set; } = true; + + public Guid ConnectStartNodeGuid { get; } = Guid.Empty; + public Guid ConnectStartConnectorGuid { get; } = Guid.Empty; + public Guid ConnectToEndNodeGuid { get; } = Guid.Empty; + public Guid ConnectToEndConnectorGuid { get; } = Guid.Empty; + + public PreviewConnectLinkOperationEventArgs( + Guid connectStartNodeGuid, + Guid connectStartConnectorGuid, + Guid connectToEndNodeGuid, + Guid connectToEndConnectorGuid) + { + ConnectStartNodeGuid = connectStartNodeGuid; + ConnectStartConnectorGuid = connectStartConnectorGuid; + ConnectToEndNodeGuid = connectToEndNodeGuid; + ConnectToEndConnectorGuid = connectToEndConnectorGuid; + } + } +} diff --git a/NodeGraph.NET7/Properties/Resources.Designer.cs b/NodeGraph.NET7/Properties/Resources.Designer.cs new file mode 100644 index 0000000..dd5135e --- /dev/null +++ b/NodeGraph.NET7/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// このコードはツールによって生成されました。 +// ランタイム バージョン:4.0.30319.42000 +// +// このファイルへの変更は、正しくない動作の原因になったり、 +// コードが再生成されるときに失われたりします。 +// +//------------------------------------------------------------------------------ + +namespace NodeGraph.NET7.Properties +{ + + + /// + /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 + /// + // このクラスは StronglyTypedResourceBuilder クラスによって ResGen + // または Visual Studio のようなツールを使用して自動生成されました。 + // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に + // ResGen を実行し直すか、または VS プロジェクトをリビルドします。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// このクラスで使用されるキャッシュされた ResourceManager インスタンスを返します。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NodeGraph.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// すべてについて、現在のスレッドの CurrentUICulture プロパティをオーバーライドします + /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/NodeGraph.NET7/Properties/Resources.resx b/NodeGraph.NET7/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/NodeGraph.NET7/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NodeGraph.NET7/Properties/Settings.Designer.cs b/NodeGraph.NET7/Properties/Settings.Designer.cs new file mode 100644 index 0000000..a1b5c97 --- /dev/null +++ b/NodeGraph.NET7/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NodeGraph.NET7.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/NodeGraph.NET7/Properties/Settings.settings b/NodeGraph.NET7/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/NodeGraph.NET7/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/NodeGraph.NET7/ResourceDictionary.xaml b/NodeGraph.NET7/ResourceDictionary.xaml new file mode 100644 index 0000000..9c52d20 --- /dev/null +++ b/NodeGraph.NET7/ResourceDictionary.xaml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/NodeGraph.NET7/Utilities/ResourceInstance.cs b/NodeGraph.NET7/Utilities/ResourceInstance.cs new file mode 100644 index 0000000..edb6448 --- /dev/null +++ b/NodeGraph.NET7/Utilities/ResourceInstance.cs @@ -0,0 +1,20 @@ +using System.Windows; +using System.Windows.Threading; + +namespace NodeGraph.NET7.Utilities +{ + public class ResourceInstance where T : DispatcherObject + { + public T Get(string resourceName) + { + if (_Resource == null) + { + _Resource = (Application.Current.TryFindResource(resourceName) as T)!; + } + + return _Resource!; + } + + T _Resource = null!; + } +} diff --git a/NodeGraph.PreviewTest.NET6/App.config b/NodeGraph.PreviewTest.NET6/App.config deleted file mode 100644 index 56efbc7..0000000 --- a/NodeGraph.PreviewTest.NET6/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/NodeGraph.PreviewTest.NET6/App.xaml b/NodeGraph.PreviewTest.NET6/App.xaml index b9db7fa..2b62348 100644 --- a/NodeGraph.PreviewTest.NET6/App.xaml +++ b/NodeGraph.PreviewTest.NET6/App.xaml @@ -1,7 +1,6 @@  diff --git a/NodeGraph.PreviewTest.NET6/App.xaml.cs b/NodeGraph.PreviewTest.NET6/App.xaml.cs index 18f3466..867d14e 100644 --- a/NodeGraph.PreviewTest.NET6/App.xaml.cs +++ b/NodeGraph.PreviewTest.NET6/App.xaml.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; namespace NodeGraph.PreviewTest { diff --git a/NodeGraph.PreviewTest.NET6/MainWindow.xaml b/NodeGraph.PreviewTest.NET6/MainWindow.xaml deleted file mode 100644 index 8515e4a..0000000 --- a/NodeGraph.PreviewTest.NET6/MainWindow.xaml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/NodeGraph.PreviewTest.NET6/MainWindow.xaml.cs b/NodeGraph.PreviewTest.NET6/MainWindow.xaml.cs deleted file mode 100644 index 9bc0b30..0000000 --- a/NodeGraph.PreviewTest.NET6/MainWindow.xaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace NodeGraph.PreviewTest.NET6 -{ - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : Window - { - public MainWindow() - { - InitializeComponent(); - } - } -} diff --git a/NodeGraph.PreviewTest.NET6/NodeGraph.PreviewTest.NET6.csproj b/NodeGraph.PreviewTest.NET6/NodeGraph.PreviewTest.NET6.csproj index e83fae3..de35f03 100644 --- a/NodeGraph.PreviewTest.NET6/NodeGraph.PreviewTest.NET6.csproj +++ b/NodeGraph.PreviewTest.NET6/NodeGraph.PreviewTest.NET6.csproj @@ -7,11 +7,6 @@ true - - - - - diff --git a/NodeGraph.PreviewTest.NET6/Utilities/ViewModelCommandHandler.cs b/NodeGraph.PreviewTest.NET6/Utilities/ViewModelCommandHandler.cs index af339e6..1ef9822 100644 --- a/NodeGraph.PreviewTest.NET6/Utilities/ViewModelCommandHandler.cs +++ b/NodeGraph.PreviewTest.NET6/Utilities/ViewModelCommandHandler.cs @@ -1,16 +1,11 @@ using Livet.Commands; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Input; -namespace NodeGraph.Utilities +namespace NodeGraph.PreviewTest.NET6.Utilities { public class ViewModelCommandHandler { - public ViewModelCommand Get(Action execute, Func canExecute = null) + public ViewModelCommand Get(Action execute, Func canExecute = null!) { if (_Command == null) { @@ -20,14 +15,14 @@ public ViewModelCommand Get(Action execute, Func canExecute = null) return _Command; } - ViewModelCommand _Command; + ViewModelCommand? _Command; } public class ViewModelCommandHandler { - public ListenerCommand Get(Action execute, Func canExecute = null) + public ListenerCommand Get(Action execute, Func canExecute = null!) { - if(_Command == null) + if (_Command == null) { _Command = new ListenerCommand(execute); } @@ -35,6 +30,6 @@ public ListenerCommand Get(Action execute, Func canExecute = null) return _Command; } - ListenerCommand _Command; + ListenerCommand? _Command; } } diff --git a/NodeGraph.PreviewTest.NET6/ViewModels/MainWindowViewModel.cs b/NodeGraph.PreviewTest.NET6/ViewModels/MainWindowViewModel.cs index 619a683..49a3d94 100644 --- a/NodeGraph.PreviewTest.NET6/ViewModels/MainWindowViewModel.cs +++ b/NodeGraph.PreviewTest.NET6/ViewModels/MainWindowViewModel.cs @@ -1,18 +1,15 @@ using Livet; using Livet.Commands; using NodeGraph.NET6.Operation; -using NodeGraph.PreviewTest.ViewModels; -using NodeGraph.Utilities; +using NodeGraph.PreviewTest.NET6.Utilities; using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; -namespace NodeGraph.PreviewTest.ViewModels +namespace NodeGraph.PreviewTest.NET6.ViewModels { public enum GroupIntersectType { diff --git a/NodeGraph.PreviewTest.NET6/ViewModels/NodeConnectorViewModel.cs b/NodeGraph.PreviewTest.NET6/ViewModels/NodeConnectorViewModel.cs index ae59661..ac9926a 100644 --- a/NodeGraph.PreviewTest.NET6/ViewModels/NodeConnectorViewModel.cs +++ b/NodeGraph.PreviewTest.NET6/ViewModels/NodeConnectorViewModel.cs @@ -1,20 +1,16 @@ using Livet; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace NodeGraph.PreviewTest.ViewModels +namespace NodeGraph.PreviewTest.NET6.ViewModels { - public interface NodeConnectorViewModel + public interface INodeConnectorViewModel { Guid Guid { get; set; } string Label { get; set; } bool IsEnable { get; set; } } - public class NodeInputViewModel : ViewModel, NodeConnectorViewModel + public class NodeInputViewModel : ViewModel, INodeConnectorViewModel { public Guid Guid { @@ -51,7 +47,7 @@ public NodeInputViewModel(string label, bool allowToConnectMultiple) } } - public class NodeOutputViewModel : ViewModel, NodeConnectorViewModel + public class NodeOutputViewModel : ViewModel, INodeConnectorViewModel { public Guid Guid { diff --git a/NodeGraph.PreviewTest.NET6/ViewModels/NodeLinkViewModel.cs b/NodeGraph.PreviewTest.NET6/ViewModels/NodeLinkViewModel.cs index 038957e..63727fc 100644 --- a/NodeGraph.PreviewTest.NET6/ViewModels/NodeLinkViewModel.cs +++ b/NodeGraph.PreviewTest.NET6/ViewModels/NodeLinkViewModel.cs @@ -1,11 +1,7 @@ using Livet; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace NodeGraph.PreviewTest.ViewModels +namespace NodeGraph.PreviewTest.NET6.ViewModels { public class NodeLinkViewModel : ViewModel { diff --git a/NodeGraph.PreviewTest.NET6/ViewModels/NodeViewModel.cs b/NodeGraph.PreviewTest.NET6/ViewModels/NodeViewModel.cs index f4cbffa..efa0af7 100644 --- a/NodeGraph.PreviewTest.NET6/ViewModels/NodeViewModel.cs +++ b/NodeGraph.PreviewTest.NET6/ViewModels/NodeViewModel.cs @@ -1,15 +1,13 @@ using Livet; -using NodeGraph.Utilities; +using NodeGraph.PreviewTest.NET6.Utilities; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Input; -namespace NodeGraph.PreviewTest.ViewModels +namespace NodeGraph.PreviewTest.NET6.ViewModels { public interface INodeViewModel { @@ -130,10 +128,10 @@ public bool IsSelected public ICommand SizeChangedCommand => _SizeChangedCommand.Get(SizeChanged); ViewModelCommandHandler _SizeChangedCommand = new ViewModelCommandHandler(); - public abstract IEnumerable Inputs { get; } - public abstract IEnumerable Outputs { get; } + public abstract IEnumerable Inputs { get; } + public abstract IEnumerable Outputs { get; } - public abstract NodeConnectorViewModel FindConnector(Guid guid); + public abstract INodeConnectorViewModel FindConnector(Guid guid); void SizeChanged(Size newSize) { @@ -158,10 +156,10 @@ public string Body } string _Body = string.Empty; - public override IEnumerable Inputs => _Inputs; + public override IEnumerable Inputs => _Inputs; readonly ObservableCollection _Inputs = new ObservableCollection(); - public override IEnumerable Outputs => _Outputs; + public override IEnumerable Outputs => _Outputs; readonly ObservableCollection _Outputs = new ObservableCollection(); public Test1DefaultNodeViewModel() @@ -171,7 +169,7 @@ public Test1DefaultNodeViewModel() if (i % 2 == 0) { var label = $"Input{i}"; - if(i > 1) + if (i > 1) { label += " Allow to connect multiple"; } @@ -189,7 +187,7 @@ public Test1DefaultNodeViewModel() } } - public override NodeConnectorViewModel FindConnector(Guid guid) + public override INodeConnectorViewModel FindConnector(Guid guid) { var input = Inputs.FirstOrDefault(arg => arg.Guid == guid); if (input != null) @@ -218,10 +216,10 @@ public string Body } string _Body = string.Empty; - public override IEnumerable Inputs => _Inputs; + public override IEnumerable Inputs => _Inputs; readonly ObservableCollection _Inputs = new ObservableCollection(); - public override IEnumerable Outputs => _Outputs; + public override IEnumerable Outputs => _Outputs; readonly ObservableCollection _Outputs = new ObservableCollection(); public Test2DefaultNodeViewModel() @@ -242,7 +240,7 @@ public Test2DefaultNodeViewModel() } } - public override NodeConnectorViewModel FindConnector(Guid guid) + public override INodeConnectorViewModel FindConnector(Guid guid) { var input = Inputs.FirstOrDefault(arg => arg.Guid == guid); if (input != null) @@ -271,10 +269,10 @@ public string Body } string _Body = string.Empty; - public override IEnumerable Inputs => _Inputs; + public override IEnumerable Inputs => _Inputs; readonly ObservableCollection _Inputs = new ObservableCollection(); - public override IEnumerable Outputs => _Outputs; + public override IEnumerable Outputs => _Outputs; readonly ObservableCollection _Outputs = new ObservableCollection(); public Test3DefaultNodeViewModel() @@ -285,7 +283,7 @@ public Test3DefaultNodeViewModel() } } - public override NodeConnectorViewModel FindConnector(Guid guid) + public override INodeConnectorViewModel FindConnector(Guid guid) { return Outputs.FirstOrDefault(arg => arg.Guid == guid); } @@ -307,10 +305,10 @@ public string Body } string _Body = string.Empty; - public override IEnumerable Inputs => _Inputs; + public override IEnumerable Inputs => _Inputs; readonly ObservableCollection _Inputs = new ObservableCollection(); - public override IEnumerable Outputs => _Outputs; + public override IEnumerable Outputs => _Outputs; readonly ObservableCollection _Outputs = new ObservableCollection(); public Test4DefaultNodeViewModel() @@ -326,7 +324,7 @@ public Test4DefaultNodeViewModel() } } - public override NodeConnectorViewModel FindConnector(Guid guid) + public override INodeConnectorViewModel FindConnector(Guid guid) { return Inputs.FirstOrDefault(arg => arg.Guid == guid); } diff --git a/NodeGraph.PreviewTest.NET6/Views/MainWindow.xaml b/NodeGraph.PreviewTest.NET6/Views/MainWindow.xaml index d8456f4..242fda8 100644 --- a/NodeGraph.PreviewTest.NET6/Views/MainWindow.xaml +++ b/NodeGraph.PreviewTest.NET6/Views/MainWindow.xaml @@ -4,9 +4,9 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:vm="clr-namespace:NodeGraph.PreviewTest.ViewModels" + xmlns:vm="clr-namespace:NodeGraph.PreviewTest.NET6.ViewModels" xmlns:ctrl="clr-namespace:NodeGraph.NET6.Controls;assembly=NodeGraph.NET6" - xmlns:local="clr-namespace:NodeGraph.PreviewTest" + mc:Ignorable="d" Title="NodeGraph - prototype" Height="600" Width="1000"> diff --git a/NodeGraph.PreviewTest.NET6/Views/MainWindow.xaml.cs b/NodeGraph.PreviewTest.NET6/Views/MainWindow.xaml.cs index d1ec78b..bc479fc 100644 --- a/NodeGraph.PreviewTest.NET6/Views/MainWindow.xaml.cs +++ b/NodeGraph.PreviewTest.NET6/Views/MainWindow.xaml.cs @@ -1,17 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; +using System.Windows; namespace NodeGraph.PreviewTest.Views { diff --git a/NodeGraph.PreviewTest.NET7/App.xaml b/NodeGraph.PreviewTest.NET7/App.xaml new file mode 100644 index 0000000..62c333e --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/NodeGraph.PreviewTest.NET7/App.xaml.cs b/NodeGraph.PreviewTest.NET7/App.xaml.cs new file mode 100644 index 0000000..867d14e --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/App.xaml.cs @@ -0,0 +1,11 @@ +using System.Windows; + +namespace NodeGraph.PreviewTest +{ + /// + /// App.xaml の相互作用ロジック + /// + public partial class App : Application + { + } +} diff --git a/NodeGraph.PreviewTest.NET7/AssemblyInfo.cs b/NodeGraph.PreviewTest.NET7/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/NodeGraph.PreviewTest.NET7/NodeGraph.PreviewTest.NET7.csproj b/NodeGraph.PreviewTest.NET7/NodeGraph.PreviewTest.NET7.csproj new file mode 100644 index 0000000..91eaa67 --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/NodeGraph.PreviewTest.NET7.csproj @@ -0,0 +1,14 @@ + + + + WinExe + net7.0-windows + enable + true + + + + + + + diff --git a/NodeGraph.PreviewTest.NET7/Properties/Resources.Designer.cs b/NodeGraph.PreviewTest.NET7/Properties/Resources.Designer.cs new file mode 100644 index 0000000..e82203c --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// このコードはツールによって生成されました。 +// ランタイム バージョン:4.0.30319.42000 +// +// このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 +// コードが再生成されるときに損失したりします +// +//------------------------------------------------------------------------------ + +namespace NodeGraph.PreviewTest.NET7.Properties +{ + + + /// + /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 + /// + // このクラスは StronglyTypedResourceBuilder クラスによって ResGen + // または Visual Studio のようなツールを使用して自動生成されました。 + // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に + // ResGen を実行し直すか、または VS プロジェクトをリビルドします。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// このクラスで使用されるキャッシュされた ResourceManager インスタンスを返します。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NodeGraph.PreviewTest.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// すべてについて、現在のスレッドの CurrentUICulture プロパティをオーバーライドします + /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/NodeGraph.PreviewTest.NET7/Properties/Resources.resx b/NodeGraph.PreviewTest.NET7/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NodeGraph.PreviewTest.NET7/Properties/Settings.Designer.cs b/NodeGraph.PreviewTest.NET7/Properties/Settings.Designer.cs new file mode 100644 index 0000000..35b992e --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NodeGraph.PreviewTest.NET7.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/NodeGraph.PreviewTest.NET7/Properties/Settings.settings b/NodeGraph.PreviewTest.NET7/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/NodeGraph.PreviewTest.NET7/Utilities/ViewModelCommandHandler.cs b/NodeGraph.PreviewTest.NET7/Utilities/ViewModelCommandHandler.cs new file mode 100644 index 0000000..1570e28 --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/Utilities/ViewModelCommandHandler.cs @@ -0,0 +1,35 @@ +using Livet.Commands; +using System; + +namespace NodeGraph.PreviewTest.NET7.Utilities +{ + public class ViewModelCommandHandler + { + public ViewModelCommand Get(Action execute, Func canExecute = null!) + { + if (_Command == null) + { + _Command = new ViewModelCommand(execute, canExecute); + } + + return _Command; + } + + ViewModelCommand? _Command; + } + + public class ViewModelCommandHandler + { + public ListenerCommand Get(Action execute, Func canExecute = null!) + { + if (_Command == null) + { + _Command = new ListenerCommand(execute); + } + + return _Command; + } + + ListenerCommand? _Command; + } +} diff --git a/NodeGraph.PreviewTest.NET7/ViewModels/MainWindowViewModel.cs b/NodeGraph.PreviewTest.NET7/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..76d3374 --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,288 @@ +using Livet; +using Livet.Commands; +using NodeGraph.NET7.Operation; +using NodeGraph.PreviewTest.NET7.Utilities; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows; + +namespace NodeGraph.PreviewTest.NET7.ViewModels +{ + public enum GroupIntersectType + { + CursorPointVMDefine, + BoundingBoxVMDefine, + } + + public enum RangeSelectionMode + { + ContainVMDefine, + IntersectVMDefine, + } + + public class MainWindowViewModel : ViewModel + { + public double Scale + { + get => _Scale; + set => RaisePropertyChangedIfSet(ref _Scale, value); + } + double _Scale = 1.0f; + + public ViewModelCommand AddNodeCommand => _AddNodeCommand.Get(AddNode); + ViewModelCommandHandler _AddNodeCommand = new ViewModelCommandHandler(); + + public ViewModelCommand AddGroupNodeCommand => _AddGroupNodeCommand.Get(AddGroupNode); + ViewModelCommandHandler _AddGroupNodeCommand = new ViewModelCommandHandler(); + + public ViewModelCommand RemoveNodesCommand => _RemoveNodesCommand.Get(RemoveNodes); + ViewModelCommandHandler _RemoveNodesCommand = new ViewModelCommandHandler(); + + public ListenerCommand PreviewConnectLinkCommand => _PreviewConnectLinkCommand.Get(PreviewConnect); + ViewModelCommandHandler _PreviewConnectLinkCommand = new ViewModelCommandHandler(); + + public ListenerCommand ConnectedLinkCommand => _ConnectedLinkCommand.Get(Connected); + ViewModelCommandHandler _ConnectedLinkCommand = new ViewModelCommandHandler(); + + public ListenerCommand DisconnectedLinkCommand => _DisconnectedLinkCommand.Get(Disconnected); + ViewModelCommandHandler _DisconnectedLinkCommand = new ViewModelCommandHandler(); + + public ListenerCommand EndMoveNodesCommand => _EndMoveNodesCommand.Get(NodesMoved); + ViewModelCommandHandler _EndMoveNodesCommand = new ViewModelCommandHandler(); + + public ListenerCommand SelectionChangedCommand => _SelectionChangedCommand.Get(SelectionChanged); + ViewModelCommandHandler _SelectionChangedCommand = new ViewModelCommandHandler(); + + public ViewModelCommand AddTestNodeLinkCommand => _AddTestNodeLinkCommand.Get(AddTestNodeLink); + ViewModelCommandHandler _AddTestNodeLinkCommand = new ViewModelCommandHandler(); + + public ViewModelCommand MoveTestNodesCommand => _MoveTestNodesCommand.Get(MoveTestNodes); + ViewModelCommandHandler _MoveTestNodesCommand = new ViewModelCommandHandler(); + + public ViewModelCommand ClearNodesCommand => _ClearNodesCommand.Get(ClearNodes); + ViewModelCommandHandler _ClearNodesCommand = new ViewModelCommandHandler(); + + public ViewModelCommand ClearNodeLinksCommand => _ClearNodeLinksCommand.Get(ClearNodeLinks); + ViewModelCommandHandler _ClearNodeLinksCommand = new ViewModelCommandHandler(); + + public ViewModelCommand MoveGroupNodeCommand => _MoveGroupNodeCommand.Get(MoveGroupNode); + ViewModelCommandHandler _MoveGroupNodeCommand = new ViewModelCommandHandler(); + + public ViewModelCommand ChangeGroupInnerSizeCommand => _ChangeGroupInnerSizeCommand.Get(ChangeGroupInnerSize); + ViewModelCommandHandler _ChangeGroupInnerSizeCommand = new ViewModelCommandHandler(); + + public ViewModelCommand ChangeGroupInnerPositionCommand => _ChangeGroupInnerPositionCommand.Get(ChangeGroupInnerPosition); + ViewModelCommandHandler _ChangeGroupInnerPositionCommand = new ViewModelCommandHandler(); + + public ViewModelCommand ResetScaleCommand => _ResetScaleCommand.Get(ResetScale); + ViewModelCommandHandler _ResetScaleCommand = new ViewModelCommandHandler(); + + public IEnumerable NodeViewModels => _NodeViewModels; + ObservableCollection _NodeViewModels = new ObservableCollection(); + + public IEnumerable NodeLinkViewModels => _NodeLinkViewModels; + ObservableCollection _NodeLinkViewModels = new ObservableCollection(); + + public IEnumerable GroupNodeViewModels => _GroupNodeViewModels; + ObservableCollection _GroupNodeViewModels = new ObservableCollection(); + + public GroupIntersectType[] GroupIntersectTypes { get; } = Enum.GetValues(typeof(GroupIntersectType)).OfType().ToArray(); + public RangeSelectionMode[] RangeSelectionModes { get; } = Enum.GetValues(typeof(RangeSelectionMode)).OfType().ToArray(); + + public GroupIntersectType SelectedGroupIntersectType + { + get => _SelectedGroupIntersectType; + set => RaisePropertyChangedIfSet(ref _SelectedGroupIntersectType, value); + } + GroupIntersectType _SelectedGroupIntersectType; + + public RangeSelectionMode SelectedRangeSelectionMode + { + get => _SelectedRangeSelectionMode; + set => RaisePropertyChangedIfSet(ref _SelectedRangeSelectionMode, value); + } + RangeSelectionMode _SelectedRangeSelectionMode = RangeSelectionMode.ContainVMDefine; + + public bool IsLockedAllNodeLinks + { + get => _IsLockedAllNodeLinks; + set => UpdateIsLockedAllNodeLinksProperty(value); + } + bool _IsLockedAllNodeLinks = false; + + public bool IsEnableAllNodeConnectors + { + get => _IsEnableAllNodeConnectors; + set => UpdateIsEnableAllNodeConnectorsProperty(value); + } + bool _IsEnableAllNodeConnectors = true; + + public bool AllowToOverrideConnection + { + get => _AllowToOverrideConnection; + set => RaisePropertyChangedIfSet(ref _AllowToOverrideConnection, value); + } + bool _AllowToOverrideConnection = true; + + public bool ClipToBounds + { + get => _ClipToBounds; + set => RaisePropertyChangedIfSet(ref _ClipToBounds, value); + } + bool _ClipToBounds = true; + + public MainWindowViewModel() + { + _GroupNodeViewModels.Add(new GroupNodeViewModel() { Name = "Group1" }); + _NodeViewModels.Add(new Test1DefaultNodeViewModel() { Name = "Node1", Body = "Content1", Position = new Point(0, 100) }); + _NodeViewModels.Add(new Test1DefaultNodeViewModel() { Name = "Node2", Body = "Content2", Position = new Point(100, 200) }); + _NodeViewModels.Add(new Test1DefaultNodeViewModel() { Name = "Node3", Body = "Content3", Position = new Point(200, 300) }); + _NodeViewModels.Add(new Test3DefaultNodeViewModel() { Name = "Node4", Body = "OutputsOnlyNode", Position = new Point(500, 100) }); + _NodeViewModels.Add(new Test4DefaultNodeViewModel() { Name = "Node5", Body = "InputsOnlyNode", Position = new Point(600, 200) }); + } + + void AddNode() + { + _NodeViewModels.Add(new Test2DefaultNodeViewModel() { Name = "NewNode", Body = "NewContent" }); + } + + void AddGroupNode() + { + _GroupNodeViewModels.Add(new GroupNodeViewModel() { Name = "NewGroupNode" }); + } + + void RemoveNodes() + { + var removeNodes = _NodeViewModels.Where(arg => arg.IsSelected).ToArray(); + foreach (var removeNode in removeNodes) + { + _NodeViewModels.Remove(removeNode); + + var removeNodeLink = NodeLinkViewModels.FirstOrDefault(arg => arg.InputConnectorNodeGuid == removeNode.Guid || arg.OutputConnectorNodeGuid == removeNode.Guid); + _NodeLinkViewModels.Remove(removeNodeLink); + } + } + + void ClearNodes() + { + _NodeLinkViewModels.Clear(); + _NodeViewModels.Clear(); + } + + void ClearNodeLinks() + { + _NodeLinkViewModels.Clear(); + } + + void MoveGroupNode() + { + _GroupNodeViewModels[0].InterlockPosition = new Point(0, 0); + } + + void ChangeGroupInnerSize() + { + _GroupNodeViewModels[0].InnerWidth = 300; + _GroupNodeViewModels[0].InnerHeight = 300; + } + + void ChangeGroupInnerPosition() + { + _GroupNodeViewModels[0].InnerPosition = new Point(0, 0); + } + + void ResetScale() + { + Scale = 1.0f; + } + + void UpdateIsLockedAllNodeLinksProperty(bool value) + { + _IsLockedAllNodeLinks = !_IsLockedAllNodeLinks; + + foreach (var nodeLink in _NodeLinkViewModels) + { + nodeLink.IsLocked = _IsLockedAllNodeLinks; + } + + RaisePropertyChanged(nameof(IsLockedAllNodeLinks)); + } + + void UpdateIsEnableAllNodeConnectorsProperty(bool value) + { + _IsEnableAllNodeConnectors = !_IsEnableAllNodeConnectors; + + foreach (var node in _NodeViewModels) + { + foreach (var input in node.Inputs) + { + input.IsEnable = _IsEnableAllNodeConnectors; + } + foreach (var output in node.Outputs) + { + output.IsEnable = _IsEnableAllNodeConnectors; + } + } + + RaisePropertyChanged(nameof(IsEnableAllNodeConnectors)); + } + + void PreviewConnect(PreviewConnectLinkOperationEventArgs args) + { + var inputNode = NodeViewModels.First(arg => arg.Guid == args.ConnectToEndNodeGuid); + var inputConnector = inputNode.FindConnector(args.ConnectToEndConnectorGuid); + args.CanConnect = inputConnector.Label == "Limited Input" == false; + } + + void Connected(ConnectedLinkOperationEventArgs param) + { + var nodeLink = new NodeLinkViewModel() + { + OutputConnectorGuid = param.OutputConnectorGuid, + OutputConnectorNodeGuid = param.OutputConnectorNodeGuid, + InputConnectorGuid = param.InputConnectorGuid, + InputConnectorNodeGuid = param.InputConnectorNodeGuid, + IsLocked = IsLockedAllNodeLinks, + }; + _NodeLinkViewModels.Add(nodeLink); + } + + void Disconnected(DisconnectedLinkOperationEventArgs param) + { + var nodeLink = _NodeLinkViewModels.First(arg => arg.Guid == param.NodeLinkGuid); + _NodeLinkViewModels.Remove(nodeLink); + } + + void NodesMoved(EndMoveNodesOperationEventArgs param) + { + + } + + void SelectionChanged(IList list) + { + + } + + void AddTestNodeLink() + { + if (_NodeViewModels.Count < 2) + { + return; + } + var nodeLink = new NodeLinkViewModel(); + nodeLink.OutputConnectorGuid = _NodeViewModels[0].Outputs.ElementAt(0).Guid; + nodeLink.InputConnectorGuid = _NodeViewModels[1].Inputs.ElementAt(0).Guid; + _NodeLinkViewModels.Add(nodeLink); + } + + void MoveTestNodes() + { + if (_NodeLinkViewModels.Count > 0) + { + _NodeViewModels[0].Position = new Point(0, 0); + } + } + } +} diff --git a/NodeGraph.PreviewTest.NET7/ViewModels/NodeConnectorViewModel.cs b/NodeGraph.PreviewTest.NET7/ViewModels/NodeConnectorViewModel.cs new file mode 100644 index 0000000..d7f7fa1 --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/ViewModels/NodeConnectorViewModel.cs @@ -0,0 +1,78 @@ +using Livet; +using System; + +namespace NodeGraph.PreviewTest.NET7.ViewModels +{ + public interface INodeConnectorViewModel + { + Guid Guid { get; set; } + string Label { get; set; } + bool IsEnable { get; set; } + } + + public class NodeInputViewModel : ViewModel, INodeConnectorViewModel + { + public Guid Guid + { + get => _Guid; + set => RaisePropertyChangedIfSet(ref _Guid, value); + } + Guid _Guid = Guid.NewGuid(); + + public string Label + { + get => _Label; + set => RaisePropertyChangedIfSet(ref _Label, value); + } + string _Label = string.Empty; + + public bool IsEnable + { + get => _IsEnable; + set => RaisePropertyChangedIfSet(ref _IsEnable, value); + } + bool _IsEnable = true; + + public bool AllowToConnectMultiple + { + get => _AllowToConnectMultiple; + set => RaisePropertyChangedIfSet(ref _AllowToConnectMultiple, value); + } + bool _AllowToConnectMultiple = false; + + public NodeInputViewModel(string label, bool allowToConnectMultiple) + { + Label = label; + AllowToConnectMultiple = allowToConnectMultiple; + } + } + + public class NodeOutputViewModel : ViewModel, INodeConnectorViewModel + { + public Guid Guid + { + get => _Guid; + set => RaisePropertyChangedIfSet(ref _Guid, value); + } + Guid _Guid = Guid.NewGuid(); + + public string Label + { + get => _Label; + set => RaisePropertyChangedIfSet(ref _Label, value); + } + string _Label = string.Empty; + + public bool IsEnable + { + get => _IsEnable; + set => RaisePropertyChangedIfSet(ref _IsEnable, value); + } + bool _IsEnable = true; + + public NodeOutputViewModel(string label) + { + Label = label; + } + } +} diff --git a/NodeGraph.PreviewTest.NET7/ViewModels/NodeLinkViewModel.cs b/NodeGraph.PreviewTest.NET7/ViewModels/NodeLinkViewModel.cs new file mode 100644 index 0000000..8643386 --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/ViewModels/NodeLinkViewModel.cs @@ -0,0 +1,57 @@ +using Livet; +using System; + +namespace NodeGraph.PreviewTest.NET7.ViewModels +{ + public class NodeLinkViewModel : ViewModel + { + public Guid Guid + { + get => _Guid; + set => RaisePropertyChangedIfSet(ref _Guid, value); + } + Guid _Guid = Guid.NewGuid(); + + public Guid InputConnectorGuid + { + get => _InputConnectorGuid; + set => RaisePropertyChangedIfSet(ref _InputConnectorGuid, value); + } + Guid _InputConnectorGuid = Guid.NewGuid(); + + public Guid OutputConnectorGuid + { + get => _OutputConnectorGuid; + set => RaisePropertyChangedIfSet(ref _OutputConnectorGuid, value); + } + Guid _OutputConnectorGuid = Guid.NewGuid(); + + public Guid InputConnectorNodeGuid + { + get => _InputNodeGuid; + set => RaisePropertyChangedIfSet(ref _InputNodeGuid, value); + } + Guid _InputNodeGuid = Guid.NewGuid(); + + public Guid OutputConnectorNodeGuid + { + get => _OutputNodeGuid; + set => RaisePropertyChangedIfSet(ref _OutputNodeGuid, value); + } + Guid _OutputNodeGuid = Guid.NewGuid(); + + public bool IsLocked + { + get => _IsLocked; + set => RaisePropertyChangedIfSet(ref _IsLocked, value); + } + bool _IsLocked = false; + + public bool IsSelected + { + get => _IsSelected; + set => RaisePropertyChangedIfSet(ref _IsSelected, value); + } + bool _IsSelected = false; + } +} diff --git a/NodeGraph.PreviewTest.NET7/ViewModels/NodeViewModel.cs b/NodeGraph.PreviewTest.NET7/ViewModels/NodeViewModel.cs new file mode 100644 index 0000000..252dbc6 --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/ViewModels/NodeViewModel.cs @@ -0,0 +1,332 @@ +using Livet; +using NodeGraph.PreviewTest.NET7.Utilities; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows; +using System.Windows.Input; + +namespace NodeGraph.PreviewTest.NET7.ViewModels +{ + public interface INodeViewModel + { + Guid Guid { get; set; } + Point Position { get; set; } + bool IsSelected { get; set; } + } + + public class GroupNodeViewModel : ViewModel, INodeViewModel + { + public Guid Guid + { + get => _Guid; + set => RaisePropertyChangedIfSet(ref _Guid, value); + } + Guid _Guid = Guid.NewGuid(); + + public string Name + { + get => _Name; + set => RaisePropertyChangedIfSet(ref _Name, value); + } + string _Name = string.Empty; + + public Point Position + { + get => _Position; + set => RaisePropertyChangedIfSet(ref _Position, value, nameof(Comment)); + } + Point _Position = new Point(0, 0); + + public Point InterlockPosition + { + get => _InterlockPosition; + set => RaisePropertyChangedIfSet(ref _InterlockPosition, value); + } + Point _InterlockPosition = new Point(0, 0); + + public Point InnerPosition + { + get => _InnerPosition; + set => RaisePropertyChangedIfSet(ref _InnerPosition, value, nameof(Comment)); + } + Point _InnerPosition = new Point(0, 0); + + public double InnerWidth + { + get => _InnerWidth; + set => RaisePropertyChangedIfSet(ref _InnerWidth, value, nameof(Comment)); + } + double _InnerWidth = 100; + + public double InnerHeight + { + get => _InnerHeight; + set => RaisePropertyChangedIfSet(ref _InnerHeight, value, nameof(Comment)); + } + double _InnerHeight = 100; + + public string Comment + { + get => $"InnerWidth = {InnerWidth:F2}, InnerHeight = {InnerHeight:F2},\n Position = {Position:F2}, InnerPosition = {InnerPosition:F2}"; + } + + public bool IsSelected + { + get => _IsSelected; + set => RaisePropertyChangedIfSet(ref _IsSelected, value); + } + bool _IsSelected = false; + + public ICommand SizeChangedCommand => _SizeChangedCommand.Get(SizeChanged); + ViewModelCommandHandler _SizeChangedCommand = new ViewModelCommandHandler(); + + void SizeChanged(Size newSize) + { + + } + } + + public abstract class DefaultNodeViewModel : ViewModel, INodeViewModel + { + public double Width + { + get => _Width; + set => RaisePropertyChangedIfSet(ref _Width, value); + } + double _Width = 0; + + public double Height + { + get => _Height; + set => RaisePropertyChangedIfSet(ref _Height, value); + } + double _Height = 0; + + public Guid Guid + { + get => _Guid; + set => RaisePropertyChangedIfSet(ref _Guid, value); + } + Guid _Guid = Guid.NewGuid(); + + public Point Position + { + get => _Position; + set => RaisePropertyChangedIfSet(ref _Position, value); + } + Point _Position = new Point(0, 0); + + public bool IsSelected + { + get => _IsSelected; + set => RaisePropertyChangedIfSet(ref _IsSelected, value); + } + bool _IsSelected = false; + + public ICommand SizeChangedCommand => _SizeChangedCommand.Get(SizeChanged); + ViewModelCommandHandler _SizeChangedCommand = new ViewModelCommandHandler(); + + public abstract IEnumerable Inputs { get; } + public abstract IEnumerable Outputs { get; } + + public abstract INodeConnectorViewModel FindConnector(Guid guid); + + void SizeChanged(Size newSize) + { + Width = newSize.Width; + Height = newSize.Height; + } + } + + public class Test1DefaultNodeViewModel : DefaultNodeViewModel + { + public string Name + { + get => _Name; + set => RaisePropertyChangedIfSet(ref _Name, value); + } + string _Name = string.Empty; + + public string Body + { + get => _Body; + set => RaisePropertyChangedIfSet(ref _Body, value); + } + string _Body = string.Empty; + + public override IEnumerable Inputs => _Inputs; + readonly ObservableCollection _Inputs = new ObservableCollection(); + + public override IEnumerable Outputs => _Outputs; + readonly ObservableCollection _Outputs = new ObservableCollection(); + + public Test1DefaultNodeViewModel() + { + for (int i = 0; i < 4; ++i) + { + if (i % 2 == 0) + { + var label = $"Input{i}"; + if (i > 1) + { + label += " Allow to connect multiple"; + } + _Inputs.Add(new NodeInputViewModel(label, i > 1)); + } + else + { + _Inputs.Add(new NodeInputViewModel($"Limited Input", false)); + } + } + + for (int i = 0; i < 5; ++i) + { + _Outputs.Add(new NodeOutputViewModel($"Output{i}")); + } + } + + public override INodeConnectorViewModel FindConnector(Guid guid) + { + var input = Inputs.FirstOrDefault(arg => arg.Guid == guid); + if (input != null) + { + return input; + } + + var output = Outputs.FirstOrDefault(arg => arg.Guid == guid); + return output; + } + } + + public class Test2DefaultNodeViewModel : DefaultNodeViewModel + { + public string Name + { + get => _Name; + set => RaisePropertyChangedIfSet(ref _Name, value); + } + string _Name = string.Empty; + + public string Body + { + get => _Body; + set => RaisePropertyChangedIfSet(ref _Body, value); + } + string _Body = string.Empty; + + public override IEnumerable Inputs => _Inputs; + readonly ObservableCollection _Inputs = new ObservableCollection(); + + public override IEnumerable Outputs => _Outputs; + readonly ObservableCollection _Outputs = new ObservableCollection(); + + public Test2DefaultNodeViewModel() + { + for (int i = 0; i < 5; ++i) + { + var label = $"Input{i}"; + if (i > 2) + { + label += " Allow to connect multiple"; + } + _Inputs.Add(new NodeInputViewModel(label, i > 2)); + } + + for (int i = 0; i < 2; ++i) + { + _Outputs.Add(new NodeOutputViewModel($"Output{i}")); + } + } + + public override INodeConnectorViewModel FindConnector(Guid guid) + { + var input = Inputs.FirstOrDefault(arg => arg.Guid == guid); + if (input != null) + { + return input; + } + + var output = Outputs.FirstOrDefault(arg => arg.Guid == guid); + return output; + } + } + + public class Test3DefaultNodeViewModel : DefaultNodeViewModel + { + public string Name + { + get => _Name; + set => RaisePropertyChangedIfSet(ref _Name, value); + } + string _Name = string.Empty; + + public string Body + { + get => _Body; + set => RaisePropertyChangedIfSet(ref _Body, value); + } + string _Body = string.Empty; + + public override IEnumerable Inputs => _Inputs; + readonly ObservableCollection _Inputs = new ObservableCollection(); + + public override IEnumerable Outputs => _Outputs; + readonly ObservableCollection _Outputs = new ObservableCollection(); + + public Test3DefaultNodeViewModel() + { + for (int i = 0; i < 2; ++i) + { + _Outputs.Add(new NodeOutputViewModel($"Output{i}")); + } + } + + public override INodeConnectorViewModel FindConnector(Guid guid) + { + return Outputs.FirstOrDefault(arg => arg.Guid == guid); + } + } + + public class Test4DefaultNodeViewModel : DefaultNodeViewModel + { + public string Name + { + get => _Name; + set => RaisePropertyChangedIfSet(ref _Name, value); + } + string _Name = string.Empty; + + public string Body + { + get => _Body; + set => RaisePropertyChangedIfSet(ref _Body, value); + } + string _Body = string.Empty; + + public override IEnumerable Inputs => _Inputs; + readonly ObservableCollection _Inputs = new ObservableCollection(); + + public override IEnumerable Outputs => _Outputs; + readonly ObservableCollection _Outputs = new ObservableCollection(); + + public Test4DefaultNodeViewModel() + { + for (int i = 0; i < 5; ++i) + { + var label = $"Input{i}"; + if (i > 2) + { + label += " Allow to connect multiple"; + } + _Inputs.Add(new NodeInputViewModel(label, i > 2)); + } + } + + public override INodeConnectorViewModel FindConnector(Guid guid) + { + return Inputs.FirstOrDefault(arg => arg.Guid == guid); + } + } +} diff --git a/NodeGraph.PreviewTest.NET7/Views/MainWindow.xaml b/NodeGraph.PreviewTest.NET7/Views/MainWindow.xaml new file mode 100644 index 0000000..8afce66 --- /dev/null +++ b/NodeGraph.PreviewTest.NET7/Views/MainWindow.xaml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +