mirror of
https://github.com/4sval/FModel.git
synced 2026-03-21 17:24:26 -05:00
added connections + buggy indexed node logic
This commit is contained in:
parent
eeb11321f7
commit
9d7a7661d4
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" : "")})";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ConnectorViewModel> 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<FScriptStruct> { 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<UScriptArray> { 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<FPackageIndex> { Value: not null } p:
|
||||
case FPropertyTagType<FPackageIndex> { 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<FSoftObjectPath> 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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<FPropertyTagType> properties, FlowNodeViewModel parent) : BaseIndexedNodeViewModel(properties.Count)
|
||||
{
|
||||
public IndexedNodeViewModel(List<FPropertyTagType> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,23 @@ public abstract class NodeViewModel : ViewModel
|
|||
|
||||
public Orientation Orientation { get; protected set; }
|
||||
|
||||
public NodifyObservableCollection<NodeViewModel> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,66 @@
|
|||
<SolidColorBrush x:Key="SquareConnectorOutline" Color="MediumSlateBlue" Opacity="0.15" />
|
||||
<SolidColorBrush x:Key="TriangleConnectorOutline" Color="MediumVioletRed" Opacity="0.15" />
|
||||
|
||||
<UIElement x:Key="ConnectionAnimationPlaceholder" Opacity="1" />
|
||||
|
||||
<Storyboard x:Key="HighlightConnection">
|
||||
<DoubleAnimation Storyboard.Target="{StaticResource ConnectionAnimationPlaceholder}"
|
||||
Storyboard.TargetProperty="(UIElement.Opacity)"
|
||||
Duration="0:0:0.1" From="1" To="0.3" />
|
||||
</Storyboard>
|
||||
|
||||
<Style x:Key="ConnectionStyle"
|
||||
TargetType="{x:Type nodify:BaseConnection}"
|
||||
BasedOn="{StaticResource {x:Type nodify:BaseConnection}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Target.Shape}" Value="{x:Static viewModels:ConnectorShape.Square}">
|
||||
<Setter Property="Stroke" Value="{StaticResource SquareConnectorColor}" />
|
||||
<Setter Property="Fill" Value="{StaticResource SquareConnectorColor}" />
|
||||
<Setter Property="OutlineBrush" Value="{StaticResource SquareConnectorOutline}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Target.Shape}" Value="{x:Static viewModels:ConnectorShape.Triangle}">
|
||||
<Setter Property="Stroke" Value="{StaticResource TriangleConnectorColor}" />
|
||||
<Setter Property="Fill" Value="{StaticResource TriangleConnectorColor}" />
|
||||
<Setter Property="OutlineBrush" Value="{StaticResource TriangleConnectorOutline}" />
|
||||
</DataTrigger>
|
||||
<Trigger Property="IsMouseDirectlyOver" Value="True">
|
||||
<Trigger.EnterActions>
|
||||
<BeginStoryboard Name="HighlightConnection" Storyboard="{StaticResource HighlightConnection}" />
|
||||
</Trigger.EnterActions>
|
||||
<Trigger.ExitActions>
|
||||
<RemoveStoryboard BeginStoryboardName="HighlightConnection" />
|
||||
</Trigger.ExitActions>
|
||||
<Setter Property="Opacity" Value="1" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsSelectable" Value="True">
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseDirectlyOver" Value="False" />
|
||||
<Condition Property="IsSelected" Value="False" />
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter Property="OutlineBrush" Value="Transparent" />
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Opacity" Value="{Binding Source={StaticResource ConnectionAnimationPlaceholder}, Path=Opacity}" />
|
||||
<Setter Property="Source" Value="{Binding Source.Anchor}" />
|
||||
<Setter Property="Target" Value="{Binding Target.Anchor}" />
|
||||
<Setter Property="SourceOrientation" Value="{Binding Source.Node.Orientation}" />
|
||||
<Setter Property="TargetOrientation" Value="{Binding Target.Node.Orientation}" />
|
||||
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="LineConnectionTemplate">
|
||||
<nodify:LineConnection Style="{StaticResource ConnectionStyle}" />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="ConnectionTemplate">
|
||||
<nodify:Connection Style="{StaticResource ConnectionStyle}" />
|
||||
</DataTemplate>
|
||||
|
||||
<ControlTemplate x:Key="SquareConnector" TargetType="Control">
|
||||
<Rectangle Width="14"
|
||||
Height="14"
|
||||
|
|
@ -78,6 +138,13 @@
|
|||
ViewportZoom="{Binding ViewportZoom, Mode=OneWayToSource}"
|
||||
Background="{StaticResource SmallGridLinesDrawingBrush}"
|
||||
ItemTemplateSelector="{DynamicResource NodeTemplateSelector}">
|
||||
<nodify:NodifyEditor.Style>
|
||||
<Style TargetType="{x:Type nodify:NodifyEditor}"
|
||||
BasedOn="{StaticResource {x:Type nodify:NodifyEditor}}">
|
||||
<Setter Property="ConnectionTemplate" Value="{StaticResource LineConnectionTemplate}" />
|
||||
</Style>
|
||||
</nodify:NodifyEditor.Style>
|
||||
|
||||
<nodify:NodifyEditor.Resources>
|
||||
<Style TargetType="{x:Type nodify:Connector}"
|
||||
BasedOn="{StaticResource {x:Type nodify:Connector}}">
|
||||
|
|
@ -190,13 +257,6 @@
|
|||
</Style.Triggers>
|
||||
</Style>
|
||||
</nodify:NodifyEditor.ItemContainerStyle>
|
||||
|
||||
<!-- <nodify:NodifyEditor.ConnectionTemplate> -->
|
||||
<!-- <DataTemplate DataType="{x:Type viewModels:ConnectionViewModel}"> -->
|
||||
<!-- <nodify:LineConnection Source="{Binding Input.Anchor}" -->
|
||||
<!-- Target="{Binding Output.Anchor}" /> -->
|
||||
<!-- </DataTemplate> -->
|
||||
<!-- </nodify:NodifyEditor.ConnectionTemplate> -->
|
||||
</nodify:NodifyEditor>
|
||||
|
||||
<Grid Background="{StaticResource LargeGridLinesDrawingBrush}" Panel.ZIndex="-2" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user