diff --git a/FModel/ViewModels/Nodify/ConnectionViewModel.cs b/FModel/ViewModels/Nodify/ConnectionViewModel.cs index 3086e612..d9d9f853 100644 --- a/FModel/ViewModels/Nodify/ConnectionViewModel.cs +++ b/FModel/ViewModels/Nodify/ConnectionViewModel.cs @@ -4,18 +4,18 @@ namespace FModel.ViewModels.Nodify; public class ConnectionViewModel : ViewModel { - private ConnectorViewModel _input; - public ConnectorViewModel Input + private ConnectorViewModel _source; + public ConnectorViewModel Source { - get => _input; - set => SetProperty(ref _input, value); + get => _source; + set => SetProperty(ref _source, value); } - private ConnectorViewModel _output; - public ConnectorViewModel Output + private ConnectorViewModel _target; + public ConnectorViewModel Target { - get => _output; - set => SetProperty(ref _output, value); + get => _target; + set => SetProperty(ref _target, value); } private bool _isSelected; @@ -25,8 +25,27 @@ public class ConnectionViewModel : ViewModel set => SetProperty(ref _isSelected, value); } - public ConnectionViewModel() + public ConnectionViewModel(ConnectorViewModel connector) { + switch (connector.Flow) + { + case ConnectorFlow.Input: + Target = connector; + break; + case ConnectorFlow.Output: + Source = connector; + break; + } + } + public ConnectionViewModel(ConnectorViewModel source, ConnectorViewModel target) + { + Source = source; + Target = target; + } + + public override string ToString() + { + return $"{Source.Title} -> {Target.Title}"; } } diff --git a/FModel/ViewModels/Nodify/ConnectorViewModel.cs b/FModel/ViewModels/Nodify/ConnectorViewModel.cs index 9a0d1611..7d82cc7c 100644 --- a/FModel/ViewModels/Nodify/ConnectorViewModel.cs +++ b/FModel/ViewModels/Nodify/ConnectorViewModel.cs @@ -1,4 +1,5 @@ using System.Windows; +using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Assets.Objects.Properties; using FModel.Framework; @@ -54,7 +55,40 @@ public class ConnectorViewModel : ViewModel public ConnectorViewModel() { - Connections.WhenAdded(c => c.Input = this); + void Remove(ConnectionViewModel connection) + { + if (Flow is not ConnectorFlow.Input) return; + Node.Graph.Connections.Remove(connection); + } + + Connections.WhenAdded(c => + { + switch (Flow) + { + case ConnectorFlow.Input when c.Target is null: + c.Target = this; + c.Source.Connections.Add(c); + break; + case ConnectorFlow.Output when c.Source is null: + c.Source = this; + c.Target.Connections.Add(c); + + // only connect outputs to inputs + Node.Graph.Connections.Add(c); + break; + } + + c.Source.IsConnected = true; + c.Target.IsConnected = true; + }) + .WhenRemoved(Remove) + .WhenCleared(c => + { + foreach (var connection in c) + { + Remove(connection); + } + }); } public ConnectorViewModel(string title) : this() @@ -62,8 +96,13 @@ public class ConnectorViewModel : ViewModel Title = title; } - public ConnectorViewModel(FPropertyTagType? tag) : this(tag?.ToString()) + public ConnectorViewModel(FPropertyTagData? type) : this(type?.ToString()) { } + + public override string ToString() + { + return $"{Title} ({Connections.Count} connection{(Connections.Count > 0 ? "s" : "")})"; + } } diff --git a/FModel/ViewModels/Nodify/FlowNodeViewModel.cs b/FModel/ViewModels/Nodify/FlowNodeViewModel.cs index 31e86287..f7051ba1 100644 --- a/FModel/ViewModels/Nodify/FlowNodeViewModel.cs +++ b/FModel/ViewModels/Nodify/FlowNodeViewModel.cs @@ -1,7 +1,7 @@ -using System.Windows.Controls; +using System.Collections.Generic; +using System.Windows.Controls; using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Assets.Objects.Properties; -using CUE4Parse.UE4.Objects.Core.i18N; using CUE4Parse.UE4.Objects.UObject; using FModel.Framework; @@ -21,40 +21,84 @@ public class FlowNodeViewModel : NodeViewModel public FlowNodeViewModel() { + void Clear(IList connectors) + { + foreach (var connector in connectors) + { + connector.Connections.Clear(); + } + } + Input.WhenAdded(c => { c.Flow = ConnectorFlow.Input; c.Node = this; - }); + }).WhenCleared(Clear); Output.WhenAdded(c => { c.Flow = ConnectorFlow.Output; c.Node = this; - }); + }).WhenCleared(Clear); Orientation = Orientation.Horizontal; } - protected void ConnectOutput(ConnectorViewModel output, FPropertyTagType? tag) + public FlowNodeViewModel(string title) : this() { + Title = title; + } + + protected void ConnectOutput(FPropertyTagType? tag) + { + var bSameNode = this is IndexedNodeViewModel { Output.Count: 1 }; + FlowNodeViewModel node = null; + switch (tag) { case FPropertyTagType { Value: not null } s: switch (s.Value.StructType) { case FStructFallback fallback: + node = bSameNode ? this : new FlowNodeViewModel("Properties"); + foreach (var property in fallback.Properties) + { + node.Input.Add(new ConnectorViewModel(property.Name.Text)); + node.Output.Add(new ConnectorViewModel(property.TagData)); + node.ConnectOutput(property.Tag); + } break; } break; case FPropertyTagType { Value.Properties.Count: > 0 } a: + node = new IndexedNodeViewModel(a.Value.Properties, this) { Title = "Properties" }; + node.Input.Add(new ConnectorViewModel("In")); + node.Output.Add(new ConnectorViewModel(a.Value.InnerTagData)); break; - case FPropertyTagType { Value: not null } p: + case FPropertyTagType { Value.IsNull: false } p: + node = bSameNode ? this : new FlowNodeViewModel("Properties"); + node.Input.Add(new ConnectorViewModel("In")); + node.Output.Add(new ConnectorViewModel(p.Value.Index.ToString())); break; case FPropertyTagType s: + node = bSameNode ? this : new FlowNodeViewModel("Properties"); + node.Input.Add(new ConnectorViewModel("In")); + node.Output.Add(new ConnectorViewModel(s.Value.ToString())); break; case { } t: - output.Title = t.GenericValue?.ToString(); + if (Output.Count == 0) Output.Add(new ConnectorViewModel()); + Output[^1].Title = t.GenericValue?.ToString(); break; } + + node?.PostConnectOutput(this); + } + + protected virtual void PostConnectOutput(FlowNodeViewModel parent) + { + parent.Children.Add(this); + if (Input.Count > 0 && parent.Output.Count > 0) + { + parent.Output[^1].Connections.Add(new ConnectionViewModel(Input[^1])); + } } } diff --git a/FModel/ViewModels/Nodify/IndexedNodeViewModel.cs b/FModel/ViewModels/Nodify/IndexedNodeViewModel.cs index c7ef104a..3917146d 100644 --- a/FModel/ViewModels/Nodify/IndexedNodeViewModel.cs +++ b/FModel/ViewModels/Nodify/IndexedNodeViewModel.cs @@ -1,16 +1,18 @@ using System.Collections.Generic; +using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Assets.Objects.Properties; using FModel.Framework; namespace FModel.ViewModels.Nodify; -public class IndexedNodeViewModel : BaseIndexedNodeViewModel +public class IndexedNodeViewModel(List properties, FlowNodeViewModel parent) : BaseIndexedNodeViewModel(properties.Count) { - public IndexedNodeViewModel(List properties) : base(properties.Count) { } - protected override void OnArrayIndexChanged() { + for (int i = 1; i < Input.Count; i++) Input.RemoveAt(i); + for (int i = 1; i < Output.Count; i++) Output.RemoveAt(i); + ConnectOutput(properties[ArrayIndex]); } } @@ -22,14 +24,14 @@ public abstract class BaseIndexedNodeViewModel : FlowNodeViewModel get => _arrayIndex; set { - if (!SetProperty(ref _arrayIndex, value)) return; + if (CanUpdateArrayIndex(value) && !SetProperty(ref _arrayIndex, value)) + return; RaisePropertyChanged(nameof(DisplayIndex)); NextCommand.RaiseCanExecuteChanged(); PreviousCommand.RaiseCanExecuteChanged(); - Input.Clear(); - Output.Clear(); + Children.Clear(); OnArrayIndexChanged(); } } @@ -44,10 +46,25 @@ public abstract class BaseIndexedNodeViewModel : FlowNodeViewModel PreviousCommand = new DelegateCommand(() => ArrayIndex--, () => ArrayIndex > 0); ArrayMax = arrayMax; + } + + public void Initialize() + { ArrayIndex = 0; } protected abstract void OnArrayIndexChanged(); + protected override void PostConnectOutput(FlowNodeViewModel parent) + { + if (Output.Count <= 1) + { + base.PostConnectOutput(parent); + Initialize(); + } + } + + protected bool CanUpdateArrayIndex(int value) => value >= 0 && value < ArrayMax; + public int DisplayIndex => ArrayIndex + 1; } diff --git a/FModel/ViewModels/Nodify/NodeViewModel.cs b/FModel/ViewModels/Nodify/NodeViewModel.cs index a4704e5b..4e9d9bbb 100644 --- a/FModel/ViewModels/Nodify/NodeViewModel.cs +++ b/FModel/ViewModels/Nodify/NodeViewModel.cs @@ -17,8 +17,23 @@ public abstract class NodeViewModel : ViewModel public Orientation Orientation { get; protected set; } + public NodifyObservableCollection Children { get; } = new(); + protected NodeViewModel() { + void Remove(NodeViewModel node) + { + Graph.Nodes.Remove(node); + } + Children.WhenAdded(c => Graph.Nodes.Add(c)); + Children.WhenRemoved(Remove); + Children.WhenCleared(c => + { + foreach (var child in c) + { + Remove(child); + } + }); } } diff --git a/FModel/ViewModels/Nodify/NodifyEditorViewModel.cs b/FModel/ViewModels/Nodify/NodifyEditorViewModel.cs index 78aec7ed..2990a641 100644 --- a/FModel/ViewModels/Nodify/NodifyEditorViewModel.cs +++ b/FModel/ViewModels/Nodify/NodifyEditorViewModel.cs @@ -11,6 +11,14 @@ public class NodifyEditorViewModel : ViewModel { Nodes .WhenAdded(n => n.Graph = this) + .WhenRemoved(n => + { + if (n is not FlowNodeViewModel flowNode) return; + foreach (var input in flowNode.Input) + { + input.Connections.Clear(); + } + }) .WhenCleared(_ => Connections.Clear()); } @@ -18,6 +26,7 @@ public class NodifyEditorViewModel : ViewModel { var root = new PackagedNodeViewModel(package); Nodes.Add(root); + root.Initialize(); } // private void AddToCollection(NodeViewModel parent, NodeViewModel node) diff --git a/FModel/ViewModels/Nodify/PackagedNodeViewModel.cs b/FModel/ViewModels/Nodify/PackagedNodeViewModel.cs index f5243f22..65e68893 100644 --- a/FModel/ViewModels/Nodify/PackagedNodeViewModel.cs +++ b/FModel/ViewModels/Nodify/PackagedNodeViewModel.cs @@ -6,14 +6,17 @@ public class PackagedNodeViewModel(IPackage package) : BaseIndexedNodeViewModel( { protected override void OnArrayIndexChanged() { + Input.Clear(); + Output.Clear(); + var export = package.ExportsLazy[ArrayIndex].Value; Title = export.Name; foreach (var property in export.Properties) { Input.Add(new ConnectorViewModel(property.Name.Text)); - Output.Add(new ConnectorViewModel(property.TagData?.ToString())); - ConnectOutput(Output[^1], property.Tag); + Output.Add(new ConnectorViewModel(property.TagData)); + ConnectOutput(property.Tag); } } } diff --git a/FModel/Views/Resources/Controls/NodifyEditor.xaml b/FModel/Views/Resources/Controls/NodifyEditor.xaml index 39b545ed..704c4d15 100644 --- a/FModel/Views/Resources/Controls/NodifyEditor.xaml +++ b/FModel/Views/Resources/Controls/NodifyEditor.xaml @@ -39,6 +39,66 @@ + + + + + + + + + + + + + + + + + + + + - - - - - - -