mirror of
https://github.com/4sval/FModel.git
synced 2026-03-21 17:24:26 -05:00
poc
This commit is contained in:
parent
b846878e54
commit
eeb11321f7
|
|
@ -156,6 +156,7 @@
|
|||
<PackageReference Include="ImGui.NET" Version="1.91.0.1" />
|
||||
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.3.8" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Nodify" Version="6.5.0" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.5" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
|
||||
<PackageReference Include="OpenTK" Version="4.8.2" />
|
||||
|
|
|
|||
|
|
@ -15,4 +15,19 @@ public abstract class Command : ICommand
|
|||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public class DelegateCommand : Command
|
||||
{
|
||||
private readonly Action _action;
|
||||
private readonly Func<bool>? _condition;
|
||||
|
||||
public DelegateCommand(Action action, Func<bool>? executeCondition = default)
|
||||
{
|
||||
_action = action ?? throw new ArgumentNullException(nameof(action));
|
||||
_condition = executeCondition;
|
||||
}
|
||||
|
||||
public override void Execute(object parameter) => _action();
|
||||
public override bool CanExecute(object parameter) => _condition?.Invoke() ?? true;
|
||||
}
|
||||
|
|
|
|||
192
FModel/Framework/NodifyObservableCollection.cs
Normal file
192
FModel/Framework/NodifyObservableCollection.cs
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public interface INodifyObservableCollection<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when a new item is added
|
||||
/// </summary>
|
||||
/// <param name="added">The callback to execute when an item is added</param>
|
||||
/// <returns>Returns self</returns>
|
||||
INodifyObservableCollection<T> WhenAdded(Action<T> added);
|
||||
|
||||
/// <summary>
|
||||
/// Called when an existing item is removed
|
||||
/// Note: It is not called when items are cleared if <see cref="WhenCleared(Action{IList{T}})"/> is used
|
||||
/// </summary>
|
||||
/// <param name="added">The callback to execute when an item is removed</param>
|
||||
/// <returns>Returns self</returns>
|
||||
INodifyObservableCollection<T> WhenRemoved(Action<T> removed);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the collection is cleared
|
||||
/// NOTE: It does not call <see cref="WhenRemoved(Action{T})"/> on each item
|
||||
/// </summary>
|
||||
/// <param name="added">The callback to execute when the collection is cleared</param>
|
||||
/// <returns>Returns self</returns>
|
||||
INodifyObservableCollection<T> WhenCleared(Action<IList<T>> cleared);
|
||||
}
|
||||
|
||||
public class NodifyObservableCollection<T> : Collection<T>, INodifyObservableCollection<T>, INotifyPropertyChanged, INotifyCollectionChanged
|
||||
{
|
||||
protected static readonly PropertyChangedEventArgs IndexerPropertyChanged = new("Item[]");
|
||||
protected static readonly PropertyChangedEventArgs CountPropertyChanged = new("Count");
|
||||
protected static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new(NotifyCollectionChangedAction.Reset);
|
||||
|
||||
private readonly List<Action<T>> _added = [];
|
||||
private readonly List<Action<T>> _removed = [];
|
||||
private readonly List<Action<IList<T>>> _cleared = [];
|
||||
|
||||
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public NodifyObservableCollection()
|
||||
{
|
||||
}
|
||||
|
||||
public NodifyObservableCollection(IEnumerable<T> collection)
|
||||
: base(new List<T>(collection))
|
||||
{
|
||||
}
|
||||
|
||||
#region Collection Events
|
||||
|
||||
public INodifyObservableCollection<T> WhenAdded(Action<T> added)
|
||||
{
|
||||
if (added != null)
|
||||
{
|
||||
_added.Add(added);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public INodifyObservableCollection<T> WhenRemoved(Action<T> removed)
|
||||
{
|
||||
if (removed != null)
|
||||
{
|
||||
_removed.Add(removed);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public INodifyObservableCollection<T> WhenCleared(Action<IList<T>> cleared)
|
||||
{
|
||||
if (cleared != null)
|
||||
{
|
||||
_cleared.Add(cleared);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected virtual void NotifyOnItemAdded(T item)
|
||||
{
|
||||
for (int i = 0; i < _added.Count; i++)
|
||||
{
|
||||
_added[i](item);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void NotifyOnItemRemoved(T item)
|
||||
{
|
||||
for (int i = 0; i < _removed.Count; i++)
|
||||
{
|
||||
_removed[i](item);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void NotifyOnItemsCleared(IList<T> items)
|
||||
{
|
||||
for (int i = 0; i < _cleared.Count; i++)
|
||||
{
|
||||
_cleared[i](items);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Collection Handlers
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
var items = _cleared.Count > 0 || _removed.Count > 0 ? new List<T>(this) : new List<T>();
|
||||
base.ClearItems();
|
||||
|
||||
if (_cleared.Count > 0)
|
||||
{
|
||||
NotifyOnItemsCleared(items);
|
||||
}
|
||||
else if (_removed.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
NotifyOnItemRemoved(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
OnPropertyChanged(CountPropertyChanged);
|
||||
OnPropertyChanged(IndexerPropertyChanged);
|
||||
OnCollectionChanged(ResetCollectionChanged);
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, T item)
|
||||
{
|
||||
base.InsertItem(index, item);
|
||||
|
||||
OnPropertyChanged(CountPropertyChanged);
|
||||
OnPropertyChanged(IndexerPropertyChanged);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
|
||||
NotifyOnItemAdded(item);
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
var item = base[index];
|
||||
base.RemoveItem(index);
|
||||
|
||||
OnPropertyChanged(CountPropertyChanged);
|
||||
OnPropertyChanged(IndexerPropertyChanged);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index);
|
||||
NotifyOnItemRemoved(item);
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, T item)
|
||||
{
|
||||
T prev = base[index];
|
||||
base.SetItem(index, item);
|
||||
OnPropertyChanged(IndexerPropertyChanged);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Replace, prev, item, index);
|
||||
NotifyOnItemRemoved(prev);
|
||||
NotifyOnItemAdded(item);
|
||||
}
|
||||
|
||||
public void Move(int oldIndex, int newIndex)
|
||||
{
|
||||
T prev = base[oldIndex];
|
||||
base.RemoveItem(oldIndex);
|
||||
base.InsertItem(newIndex, prev);
|
||||
OnPropertyChanged(IndexerPropertyChanged);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Move, prev, newIndex, oldIndex);
|
||||
}
|
||||
|
||||
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
=> CollectionChanged?.Invoke(this, e);
|
||||
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
|
||||
=> PropertyChanged?.Invoke(this, args);
|
||||
|
||||
private void OnCollectionChanged(NotifyCollectionChangedAction action, object? item, int index)
|
||||
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
|
||||
|
||||
private void OnCollectionChanged(NotifyCollectionChangedAction action, object? item, int index, int oldIndex)
|
||||
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex));
|
||||
|
||||
private void OnCollectionChanged(NotifyCollectionChangedAction action, object? oldItem, object? newItem, int index)
|
||||
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -83,9 +83,9 @@ public partial class MainWindow
|
|||
).ConfigureAwait(false);
|
||||
|
||||
#if DEBUG
|
||||
// await _threadWorkerView.Begin(cancellationToken =>
|
||||
// _applicationView.CUE4Parse.Extract(cancellationToken,
|
||||
// "FortniteGame/Content/Athena/Apollo/Maps/UI/Apollo_Terrain_Minimap.uasset"));
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
_applicationView.CUE4Parse.Extract(cancellationToken,
|
||||
"FortniteGame/Plugins/GameFeatures/BRCosmetics/Content/Athena/Items/Cosmetics/Characters/Character_Amour.uasset"));
|
||||
// await _threadWorkerView.Begin(cancellationToken =>
|
||||
// _applicationView.CUE4Parse.Extract(cancellationToken,
|
||||
// "FortniteGame/Content/Environments/Helios/Props/GlacierHotel/GlacierHotel_Globe_A/Meshes/SM_GlacierHotel_Globe_A.uasset"));
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ using System.Text.RegularExpressions;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
using AdonisUI.Controls;
|
||||
|
||||
using CUE4Parse.Compression;
|
||||
using CUE4Parse.Encryption.Aes;
|
||||
using CUE4Parse.FileProvider;
|
||||
|
|
@ -53,7 +51,6 @@ using CUE4Parse.UE4.Wwise;
|
|||
|
||||
using CUE4Parse_Conversion;
|
||||
using CUE4Parse_Conversion.Sounds;
|
||||
using CUE4Parse.UE4.Assets;
|
||||
using EpicManifestParser;
|
||||
|
||||
using FModel.Creator;
|
||||
|
|
@ -61,6 +58,7 @@ using FModel.Extensions;
|
|||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels.Nodify;
|
||||
using FModel.Views;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using FModel.Views.Snooper;
|
||||
|
|
@ -612,15 +610,18 @@ public class CUE4ParseViewModel : ViewModel
|
|||
case "uasset":
|
||||
case "umap":
|
||||
{
|
||||
var exports = Provider.LoadAllObjects(fullPath);
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(exports, Formatting.Indented), saveProperties, updateUi);
|
||||
if (HasFlag(bulk, EBulkType.Properties)) break; // do not search for viewable exports if we are dealing with jsons
|
||||
var package = Provider.LoadPackage(fullPath);
|
||||
TabControl.SelectedTab.NodifyEditor = new NodifyEditorViewModel(package);
|
||||
|
||||
foreach (var e in exports)
|
||||
{
|
||||
if (CheckExport(cancellationToken, e, bulk))
|
||||
break;
|
||||
}
|
||||
// var exports = Provider.LoadAllObjects(fullPath);
|
||||
// TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(exports, Formatting.Indented), saveProperties, updateUi);
|
||||
// if (HasFlag(bulk, EBulkType.Properties)) break; // do not search for viewable exports if we are dealing with jsons
|
||||
//
|
||||
// foreach (var e in exports)
|
||||
// {
|
||||
// if (CheckExport(cancellationToken, e, bulk))
|
||||
// break;
|
||||
// }
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
32
FModel/ViewModels/Nodify/ConnectionViewModel.cs
Normal file
32
FModel/ViewModels/Nodify/ConnectionViewModel.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using FModel.Framework;
|
||||
|
||||
namespace FModel.ViewModels.Nodify;
|
||||
|
||||
public class ConnectionViewModel : ViewModel
|
||||
{
|
||||
private ConnectorViewModel _input;
|
||||
public ConnectorViewModel Input
|
||||
{
|
||||
get => _input;
|
||||
set => SetProperty(ref _input, value);
|
||||
}
|
||||
|
||||
private ConnectorViewModel _output;
|
||||
public ConnectorViewModel Output
|
||||
{
|
||||
get => _output;
|
||||
set => SetProperty(ref _output, value);
|
||||
}
|
||||
|
||||
private bool _isSelected;
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set => SetProperty(ref _isSelected, value);
|
||||
}
|
||||
|
||||
public ConnectionViewModel()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
69
FModel/ViewModels/Nodify/ConnectorViewModel.cs
Normal file
69
FModel/ViewModels/Nodify/ConnectorViewModel.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using System.Windows;
|
||||
using CUE4Parse.UE4.Assets.Objects.Properties;
|
||||
using FModel.Framework;
|
||||
|
||||
namespace FModel.ViewModels.Nodify;
|
||||
|
||||
public enum ConnectorFlow
|
||||
{
|
||||
Input,
|
||||
Output
|
||||
}
|
||||
|
||||
public enum ConnectorShape
|
||||
{
|
||||
Circle,
|
||||
Triangle,
|
||||
Square,
|
||||
}
|
||||
|
||||
public class ConnectorViewModel : ViewModel
|
||||
{
|
||||
private string? _title;
|
||||
public string? Title
|
||||
{
|
||||
get => _title;
|
||||
set => SetProperty(ref _title, value);
|
||||
}
|
||||
|
||||
private bool _isConnected;
|
||||
public bool IsConnected
|
||||
{
|
||||
get => _isConnected;
|
||||
set => SetProperty(ref _isConnected, value);
|
||||
}
|
||||
|
||||
private Point _anchor;
|
||||
public Point Anchor
|
||||
{
|
||||
get => _anchor;
|
||||
set => SetProperty(ref _anchor, value);
|
||||
}
|
||||
|
||||
private ConnectorShape _shape;
|
||||
public ConnectorShape Shape
|
||||
{
|
||||
get => _shape;
|
||||
set => SetProperty(ref _shape, value);
|
||||
}
|
||||
|
||||
public NodeViewModel Node { get; set; }
|
||||
public ConnectorFlow Flow { get; set; }
|
||||
|
||||
public NodifyObservableCollection<ConnectionViewModel> Connections { get; } = new();
|
||||
|
||||
public ConnectorViewModel()
|
||||
{
|
||||
Connections.WhenAdded(c => c.Input = this);
|
||||
}
|
||||
|
||||
public ConnectorViewModel(string title) : this()
|
||||
{
|
||||
Title = title;
|
||||
}
|
||||
|
||||
public ConnectorViewModel(FPropertyTagType? tag) : this(tag?.ToString())
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
60
FModel/ViewModels/Nodify/FlowNodeViewModel.cs
Normal file
60
FModel/ViewModels/Nodify/FlowNodeViewModel.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
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;
|
||||
|
||||
namespace FModel.ViewModels.Nodify;
|
||||
|
||||
public class FlowNodeViewModel : NodeViewModel
|
||||
{
|
||||
private string? _title;
|
||||
public string? Title
|
||||
{
|
||||
get => _title;
|
||||
set => SetProperty(ref _title, value);
|
||||
}
|
||||
|
||||
public NodifyObservableCollection<ConnectorViewModel> Input { get; } = new();
|
||||
public NodifyObservableCollection<ConnectorViewModel> Output { get; } = new();
|
||||
|
||||
public FlowNodeViewModel()
|
||||
{
|
||||
Input.WhenAdded(c =>
|
||||
{
|
||||
c.Flow = ConnectorFlow.Input;
|
||||
c.Node = this;
|
||||
});
|
||||
Output.WhenAdded(c =>
|
||||
{
|
||||
c.Flow = ConnectorFlow.Output;
|
||||
c.Node = this;
|
||||
});
|
||||
|
||||
Orientation = Orientation.Horizontal;
|
||||
}
|
||||
|
||||
protected void ConnectOutput(ConnectorViewModel output, FPropertyTagType? tag)
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case FPropertyTagType<FScriptStruct> { Value: not null } s:
|
||||
switch (s.Value.StructType)
|
||||
{
|
||||
case FStructFallback fallback:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case FPropertyTagType<UScriptArray> { Value.Properties.Count: > 0 } a:
|
||||
break;
|
||||
case FPropertyTagType<FPackageIndex> { Value: not null } p:
|
||||
break;
|
||||
case FPropertyTagType<FSoftObjectPath> s:
|
||||
break;
|
||||
case { } t:
|
||||
output.Title = t.GenericValue?.ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
FModel/ViewModels/Nodify/IndexedNodeViewModel.cs
Normal file
53
FModel/ViewModels/Nodify/IndexedNodeViewModel.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System.Collections.Generic;
|
||||
using CUE4Parse.UE4.Assets.Objects.Properties;
|
||||
using FModel.Framework;
|
||||
|
||||
namespace FModel.ViewModels.Nodify;
|
||||
|
||||
public class IndexedNodeViewModel : BaseIndexedNodeViewModel
|
||||
{
|
||||
public IndexedNodeViewModel(List<FPropertyTagType> properties) : base(properties.Count) { }
|
||||
|
||||
protected override void OnArrayIndexChanged()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BaseIndexedNodeViewModel : FlowNodeViewModel
|
||||
{
|
||||
private int _arrayIndex = -1;
|
||||
public int ArrayIndex
|
||||
{
|
||||
get => _arrayIndex;
|
||||
set
|
||||
{
|
||||
if (!SetProperty(ref _arrayIndex, value)) return;
|
||||
|
||||
RaisePropertyChanged(nameof(DisplayIndex));
|
||||
NextCommand.RaiseCanExecuteChanged();
|
||||
PreviousCommand.RaiseCanExecuteChanged();
|
||||
|
||||
Input.Clear();
|
||||
Output.Clear();
|
||||
OnArrayIndexChanged();
|
||||
}
|
||||
}
|
||||
public int ArrayMax { get; }
|
||||
|
||||
public DelegateCommand NextCommand { get; }
|
||||
public DelegateCommand PreviousCommand { get; }
|
||||
|
||||
public BaseIndexedNodeViewModel(int arrayMax)
|
||||
{
|
||||
NextCommand = new DelegateCommand(() => ArrayIndex++, () => ArrayIndex < ArrayMax - 1);
|
||||
PreviousCommand = new DelegateCommand(() => ArrayIndex--, () => ArrayIndex > 0);
|
||||
|
||||
ArrayMax = arrayMax;
|
||||
ArrayIndex = 0;
|
||||
}
|
||||
|
||||
protected abstract void OnArrayIndexChanged();
|
||||
|
||||
public int DisplayIndex => ArrayIndex + 1;
|
||||
}
|
||||
24
FModel/ViewModels/Nodify/NodeViewModel.cs
Normal file
24
FModel/ViewModels/Nodify/NodeViewModel.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using FModel.Framework;
|
||||
|
||||
namespace FModel.ViewModels.Nodify;
|
||||
|
||||
public abstract class NodeViewModel : ViewModel
|
||||
{
|
||||
public NodifyEditorViewModel Graph { get; set; }
|
||||
|
||||
private Point _location;
|
||||
public Point Location
|
||||
{
|
||||
get => _location;
|
||||
set => SetProperty(ref _location, value);
|
||||
}
|
||||
|
||||
public Orientation Orientation { get; protected set; }
|
||||
|
||||
protected NodeViewModel()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
201
FModel/ViewModels/Nodify/NodifyEditorViewModel.cs
Normal file
201
FModel/ViewModels/Nodify/NodifyEditorViewModel.cs
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using CUE4Parse.UE4.Assets;
|
||||
using FModel.Framework;
|
||||
|
||||
namespace FModel.ViewModels.Nodify;
|
||||
|
||||
public class NodifyEditorViewModel : ViewModel
|
||||
{
|
||||
public NodifyEditorViewModel()
|
||||
{
|
||||
Nodes
|
||||
.WhenAdded(n => n.Graph = this)
|
||||
.WhenCleared(_ => Connections.Clear());
|
||||
}
|
||||
|
||||
public NodifyEditorViewModel(IPackage package) : this()
|
||||
{
|
||||
var root = new PackagedNodeViewModel(package);
|
||||
Nodes.Add(root);
|
||||
}
|
||||
|
||||
// private void AddToCollection(NodeViewModel parent, NodeViewModel node)
|
||||
// {
|
||||
// parent.Children.Add(Nodes.Count);
|
||||
// Nodes.Add(node);
|
||||
// }
|
||||
//
|
||||
// private void AddPropertyToNode(NodeViewModel parent, IPropertyHolder holder) => AddPropertyToNode(parent, holder.Properties);
|
||||
// private void AddPropertyToNode(NodeViewModel parent, List<FPropertyTag> properties)
|
||||
// {
|
||||
// var node = new NodeViewModel { Title = "Properties", Depth = parent.Depth + 1 };
|
||||
// properties.ForEach(p => AddPropertyToNode(parent, node, p.Name.ToString(), p.Tag, p.TagData));
|
||||
// AddToCollection(parent, node);
|
||||
// }
|
||||
//
|
||||
// private void AddPropertyToNode(NodeViewModel parent, List<FPropertyTagType> properties, FPropertyTagData type)
|
||||
// {
|
||||
// var node = new ArrayNodeViewModel { Title = "Properties", Depth = parent.Depth + 1, ArraySize = properties.Count };
|
||||
// AddPropertyToNode(parent, node, "In", properties[0], type);
|
||||
// AddToCollection(parent, node);
|
||||
// }
|
||||
//
|
||||
// private void AddPropertyToNode(NodeViewModel parent, NodeViewModel node, string name, FPropertyTagType tag, FPropertyTagData type)
|
||||
// {
|
||||
// switch (tag)
|
||||
// {
|
||||
// case FPropertyTagType<FScriptStruct> { Value: not null } s:
|
||||
// switch (s.Value.StructType)
|
||||
// {
|
||||
// case FStructFallback fallback:
|
||||
// if (node.Input.Count == 0)
|
||||
// {
|
||||
// // node.Output.RemoveAt(node.Output.Count - 1);
|
||||
// fallback.Properties.ForEach(p => AddPropertyToNode(parent, node, p.Name.ToString(), p.Tag, p.TagData));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// AddPropertyToNode(node, fallback);
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// var fields = s.Value.StructType.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
|
||||
// AddPropertyToNode(node, fields.Select(field => new FPropertyTag(field, s.Value.StructType)).ToList());
|
||||
// break;
|
||||
// }
|
||||
// break;
|
||||
// case FPropertyTagType<UScriptArray> { Value.Properties.Count: > 0 } a:
|
||||
// AddPropertyToNode(node, a.Value.Properties, a.Value.InnerTagData);
|
||||
// break;
|
||||
// // case FPropertyTagType<FPackageIndex> { Value: not null } i:
|
||||
// // // special node that if clicked will open the asset
|
||||
// // break;
|
||||
// // case FPropertyTagType<FSoftObjectPath> s:
|
||||
// // // special node that if clicked will open the asset
|
||||
// // break;
|
||||
// case { } t:
|
||||
// AddPropertyToNode(node, name, t.GenericValue);
|
||||
// break;
|
||||
// default:
|
||||
// AddPropertyToNode(node, name, tag);
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// if (parent.Output.Count > 0 && node.Input.Count > 0)
|
||||
// {
|
||||
// Connections.Add(new ConnectionViewModel(parent.Output[^1], node.Input[^1]));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void AddPropertyToNode(NodeViewModel node, string name, object? property)
|
||||
// {
|
||||
// node.AddProperty(name, property?.ToString() ?? "null");
|
||||
// }
|
||||
//
|
||||
// private const int MarginX = 64;
|
||||
// private const int MarginY = 16;
|
||||
//
|
||||
// public void OrganizeNodes() => OrganizeNodes(Nodes[^1]);
|
||||
// private void OrganizeNodes(NodeViewModel root)
|
||||
// {
|
||||
// FirstPass(root);
|
||||
// AssignFinalPositions(root);
|
||||
// }
|
||||
//
|
||||
// private double currentLeafY = 0;
|
||||
//
|
||||
// private void FirstPass(NodeViewModel node, double x = 0)
|
||||
// {
|
||||
// node.X = x;
|
||||
//
|
||||
// if (node.Children.Count == 0)
|
||||
// {
|
||||
// node.Y = currentLeafY;
|
||||
// currentLeafY += node.ActualSize.Height + MarginY;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// foreach (var child in node.Children)
|
||||
// {
|
||||
// FirstPass(Nodes[child], node.X + node.ActualSize.Width + MarginX);
|
||||
// }
|
||||
//
|
||||
// var leftmost = Nodes[node.Children[0]];
|
||||
// var rightmost = Nodes[node.Children[^1]];
|
||||
// node.Y = (leftmost.Y + rightmost.Y) / 2;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void AssignFinalPositions(NodeViewModel node)
|
||||
// {
|
||||
// node.Location = new Point((int)node.X, (int)node.Y);
|
||||
// foreach (var child in node.Children)
|
||||
// {
|
||||
// AssignFinalPositions(Nodes[child]);
|
||||
// }
|
||||
// }
|
||||
|
||||
private NodifyObservableCollection<NodeViewModel> _nodes = new();
|
||||
public NodifyObservableCollection<NodeViewModel> Nodes
|
||||
{
|
||||
get => _nodes;
|
||||
set => SetProperty(ref _nodes, value);
|
||||
}
|
||||
|
||||
private ObservableCollection<NodeViewModel> _selectedNodes = new();
|
||||
public ObservableCollection<NodeViewModel> SelectedNodes
|
||||
{
|
||||
get => _selectedNodes;
|
||||
set => SetProperty(ref _selectedNodes, value);
|
||||
}
|
||||
|
||||
private NodifyObservableCollection<ConnectionViewModel> _connections = new();
|
||||
public NodifyObservableCollection<ConnectionViewModel> Connections
|
||||
{
|
||||
get => _connections;
|
||||
set => SetProperty(ref _connections, value);
|
||||
}
|
||||
|
||||
private ObservableCollection<ConnectionViewModel> _selectedConnections = new();
|
||||
public ObservableCollection<ConnectionViewModel> SelectedConnections
|
||||
{
|
||||
get => _selectedConnections;
|
||||
set => SetProperty(ref _selectedConnections, value);
|
||||
}
|
||||
|
||||
private ConnectionViewModel? _selectedConnection;
|
||||
public ConnectionViewModel? SelectedConnection
|
||||
{
|
||||
get => _selectedConnection;
|
||||
set => SetProperty(ref _selectedConnection, value);
|
||||
}
|
||||
|
||||
private NodeViewModel? _selectedNode;
|
||||
public NodeViewModel? SelectedNode
|
||||
{
|
||||
get => _selectedNode;
|
||||
set => SetProperty(ref _selectedNode, value);
|
||||
}
|
||||
|
||||
private Size _viewportSize;
|
||||
public Size ViewportSize
|
||||
{
|
||||
get => _viewportSize;
|
||||
set => SetProperty(ref _viewportSize, value);
|
||||
}
|
||||
|
||||
private Point _viewportLocation;
|
||||
public Point ViewportLocation
|
||||
{
|
||||
get => _viewportLocation;
|
||||
set => SetProperty(ref _viewportLocation, value);
|
||||
}
|
||||
|
||||
private double _viewportZoom;
|
||||
public double ViewportZoom
|
||||
{
|
||||
get => _viewportZoom;
|
||||
set => SetProperty(ref _viewportZoom, value);
|
||||
}
|
||||
}
|
||||
19
FModel/ViewModels/Nodify/PackagedNodeViewModel.cs
Normal file
19
FModel/ViewModels/Nodify/PackagedNodeViewModel.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using CUE4Parse.UE4.Assets;
|
||||
|
||||
namespace FModel.ViewModels.Nodify;
|
||||
|
||||
public class PackagedNodeViewModel(IPackage package) : BaseIndexedNodeViewModel(package.ExportsLazy.Length)
|
||||
{
|
||||
protected override void OnArrayIndexChanged()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ using System.Windows;
|
|||
using System.Windows.Media.Imaging;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using FModel.ViewModels.Nodify;
|
||||
|
||||
namespace FModel.ViewModels;
|
||||
|
||||
|
|
@ -108,6 +109,13 @@ public class TabItem : ViewModel
|
|||
|
||||
public string FullPath => this.Directory + "/" + this.Header.SubstringBeforeLast(" (");
|
||||
|
||||
private NodifyEditorViewModel _nodifyEditor;
|
||||
public NodifyEditorViewModel NodifyEditor
|
||||
{
|
||||
get => _nodifyEditor;
|
||||
set => SetProperty(ref _nodifyEditor, value);
|
||||
}
|
||||
|
||||
private bool _hasSearchOpen;
|
||||
public bool HasSearchOpen
|
||||
{
|
||||
|
|
|
|||
24
FModel/Views/Resources/Controls/NodeTemplateSelector.cs
Normal file
24
FModel/Views/Resources/Controls/NodeTemplateSelector.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using FModel.ViewModels.Nodify;
|
||||
|
||||
namespace FModel.Views.Resources.Controls;
|
||||
|
||||
public class NodeTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate DefaultTemplate { get; set; }
|
||||
public DataTemplate FlowNodeTemplate { get; set; }
|
||||
public DataTemplate IndexedTemplate { get; set; }
|
||||
public DataTemplate PackagedTemplate { get; set; }
|
||||
|
||||
public override DataTemplate SelectTemplate(object item, DependencyObject container)
|
||||
{
|
||||
return item switch
|
||||
{
|
||||
PackagedNodeViewModel => PackagedTemplate,
|
||||
BaseIndexedNodeViewModel => IndexedTemplate,
|
||||
FlowNodeViewModel => FlowNodeTemplate,
|
||||
_ => DefaultTemplate
|
||||
};
|
||||
}
|
||||
}
|
||||
205
FModel/Views/Resources/Controls/NodifyEditor.xaml
Normal file
205
FModel/Views/Resources/Controls/NodifyEditor.xaml
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
<UserControl x:Class="FModel.Views.Resources.Controls.NodifyEditor"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:settings="clr-namespace:FModel.Settings"
|
||||
xmlns:local="clr-namespace:FModel.Views.Resources.Controls"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:viewModels="clr-namespace:FModel.ViewModels.Nodify"
|
||||
xmlns:nodify="https://miroiu.github.io/nodify"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300" d:DesignWidth="300">
|
||||
<UserControl.Resources>
|
||||
<GeometryDrawing x:Key="SmallGridGeometry"
|
||||
Geometry="M0,0 L0,1 0.03,1 0.03,0.03 1,0.03 1,0 Z"
|
||||
Brush="{DynamicResource {x:Static adonisUi:Brushes.Layer4BackgroundBrush}}" />
|
||||
|
||||
<GeometryDrawing x:Key="LargeGridGeometry"
|
||||
Geometry="M0,0 L0,1 0.015,1 0.015,0.015 1,0.015 1,0 Z"
|
||||
Brush="{DynamicResource {x:Static adonisUi:Brushes.Layer4BackgroundBrush}}" />
|
||||
|
||||
<DrawingBrush x:Key="SmallGridLinesDrawingBrush"
|
||||
TileMode="Tile"
|
||||
ViewportUnits="Absolute"
|
||||
Viewport="0 0 20 20"
|
||||
Transform="{Binding ViewportTransform, ElementName=Editor}"
|
||||
Drawing="{StaticResource SmallGridGeometry}" />
|
||||
|
||||
<DrawingBrush x:Key="LargeGridLinesDrawingBrush"
|
||||
TileMode="Tile"
|
||||
ViewportUnits="Absolute"
|
||||
Opacity="0.5"
|
||||
Viewport="0 0 100 100"
|
||||
Transform="{Binding ViewportTransform, ElementName=Editor}"
|
||||
Drawing="{StaticResource LargeGridGeometry}" />
|
||||
|
||||
<SolidColorBrush x:Key="SquareConnectorColor" Color="MediumSlateBlue" />
|
||||
<SolidColorBrush x:Key="TriangleConnectorColor" Color="MediumVioletRed" />
|
||||
<SolidColorBrush x:Key="SquareConnectorOutline" Color="MediumSlateBlue" Opacity="0.15" />
|
||||
<SolidColorBrush x:Key="TriangleConnectorOutline" Color="MediumVioletRed" Opacity="0.15" />
|
||||
|
||||
<ControlTemplate x:Key="SquareConnector" TargetType="Control">
|
||||
<Rectangle Width="14"
|
||||
Height="14"
|
||||
StrokeDashCap="Round"
|
||||
StrokeLineJoin="Round"
|
||||
StrokeStartLineCap="Round"
|
||||
StrokeEndLineCap="Round"
|
||||
Stroke="{TemplateBinding BorderBrush}"
|
||||
Fill="{TemplateBinding Background}"
|
||||
StrokeThickness="2" />
|
||||
</ControlTemplate>
|
||||
|
||||
<ControlTemplate x:Key="TriangleConnector" TargetType="Control">
|
||||
<Polygon Width="14"
|
||||
Height="14"
|
||||
Points="1,13 13,13 7,1"
|
||||
StrokeDashCap="Round"
|
||||
StrokeLineJoin="Round"
|
||||
StrokeStartLineCap="Round"
|
||||
StrokeEndLineCap="Round"
|
||||
Stroke="{TemplateBinding BorderBrush}"
|
||||
Fill="{TemplateBinding Background}"
|
||||
StrokeThickness="2" />
|
||||
</ControlTemplate>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<nodify:NodifyEditor x:Name="Editor"
|
||||
ItemsSource="{Binding Nodes}"
|
||||
SelectedItem="{Binding SelectedNode}"
|
||||
SelectedItems="{Binding SelectedNodes}"
|
||||
Connections="{Binding Connections}"
|
||||
SelectedConnection="{Binding SelectedConnection}"
|
||||
SelectedConnections="{Binding SelectedConnections}"
|
||||
ViewportLocation="{Binding ViewportLocation, Mode=OneWayToSource}"
|
||||
ViewportSize="{Binding ViewportSize, Mode=OneWayToSource}"
|
||||
ViewportZoom="{Binding ViewportZoom, Mode=OneWayToSource}"
|
||||
Background="{StaticResource SmallGridLinesDrawingBrush}"
|
||||
ItemTemplateSelector="{DynamicResource NodeTemplateSelector}">
|
||||
<nodify:NodifyEditor.Resources>
|
||||
<Style TargetType="{x:Type nodify:Connector}"
|
||||
BasedOn="{StaticResource {x:Type nodify:Connector}}">
|
||||
<Setter Property="Anchor" Value="{Binding Anchor, Mode=OneWayToSource}" />
|
||||
<Setter Property="IsConnected" Value="{Binding IsConnected}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type nodify:NodeInput}"
|
||||
BasedOn="{StaticResource {x:Type nodify:NodeInput}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Shape}" Value="{x:Static viewModels:ConnectorShape.Square}">
|
||||
<Setter Property="ConnectorTemplate" Value="{StaticResource SquareConnector}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource SquareConnectorColor}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Shape}" Value="{x:Static viewModels:ConnectorShape.Triangle}">
|
||||
<Setter Property="ConnectorTemplate" Value="{StaticResource TriangleConnector}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TriangleConnectorColor}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Header" Value="{Binding Title}" />
|
||||
<Setter Property="Anchor" Value="{Binding Anchor, Mode=OneWayToSource}" />
|
||||
<Setter Property="IsConnected" Value="{Binding IsConnected}" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type nodify:NodeOutput}"
|
||||
BasedOn="{StaticResource {x:Type nodify:NodeOutput}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Shape}" Value="{x:Static viewModels:ConnectorShape.Square}">
|
||||
<Setter Property="ConnectorTemplate" Value="{StaticResource SquareConnector}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource SquareConnectorColor}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Shape}" Value="{x:Static viewModels:ConnectorShape.Triangle}">
|
||||
<Setter Property="ConnectorTemplate" Value="{StaticResource TriangleConnector}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource TriangleConnectorColor}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Header" Value="{Binding Title}" />
|
||||
<Setter Property="Anchor" Value="{Binding Anchor, Mode=OneWayToSource}" />
|
||||
<Setter Property="IsConnected" Value="{Binding IsConnected}" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="DefaultNodeTemplate" DataType="{x:Type viewModels:NodeViewModel}">
|
||||
<nodify:Node Header="Dummy" />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="FlowNodeTemplate" DataType="{x:Type viewModels:FlowNodeViewModel}">
|
||||
<nodify:Node Header="{Binding Title}"
|
||||
Input="{Binding Input}"
|
||||
Output="{Binding Output}">
|
||||
</nodify:Node>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="IndexedNodeTemplate" DataType="{x:Type viewModels:BaseIndexedNodeViewModel}">
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="{Binding Orientation}" Margin="0 0 0 5">
|
||||
<Button Command="{Binding PreviousCommand}" Padding="2.5">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path StrokeThickness="2" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="M5 12l14 0" />
|
||||
<Path StrokeThickness="2" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="M5 12l6 6" />
|
||||
<Path StrokeThickness="2" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="M5 12l6 -6" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
<Button Command="{Binding NextCommand}" Padding="2.5">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path StrokeThickness="2" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="M5 12l14 0" />
|
||||
<Path StrokeThickness="2" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="M13 18l6 -6" />
|
||||
<Path StrokeThickness="2" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="M13 6l6 6" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<nodify:Node Header="{Binding Title}"
|
||||
Input="{Binding Input}"
|
||||
Output="{Binding Output}">
|
||||
<nodify:Node.Footer>
|
||||
<TextBlock HorizontalAlignment="Right">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="{}{0}/{1}">
|
||||
<Binding Path="DisplayIndex" />
|
||||
<Binding Path="ArrayMax" />
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</nodify:Node.Footer>
|
||||
</nodify:Node>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<local:NodeTemplateSelector x:Key="NodeTemplateSelector"
|
||||
DefaultTemplate="{StaticResource DefaultNodeTemplate}"
|
||||
FlowNodeTemplate="{StaticResource FlowNodeTemplate}"
|
||||
IndexedTemplate="{StaticResource IndexedNodeTemplate}"
|
||||
PackagedTemplate="{StaticResource IndexedNodeTemplate}"/>
|
||||
</nodify:NodifyEditor.Resources>
|
||||
|
||||
<nodify:NodifyEditor.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type nodify:ItemContainer}"
|
||||
BasedOn="{StaticResource {x:Type nodify:ItemContainer}}">
|
||||
<Setter Property="Location" Value="{Binding Location}" />
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter Property="Panel.ZIndex" Value="1" />
|
||||
</Trigger>
|
||||
</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" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
12
FModel/Views/Resources/Controls/NodifyEditor.xaml.cs
Normal file
12
FModel/Views/Resources/Controls/NodifyEditor.xaml.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Windows.Controls;
|
||||
|
||||
namespace FModel.Views.Resources.Controls;
|
||||
|
||||
public partial class NodifyEditor : UserControl
|
||||
{
|
||||
public NodifyEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -675,7 +675,7 @@
|
|||
<ColumnDefinition Width="{Binding AvalonImageSize, Source={x:Static settings:UserSettings.Default}, Mode=TwoWay}" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:AvalonEditor x:Name="DynamicArea" Grid.Column="0" DataContext="{Binding SelectedItem, ElementName=TabControlName}" />
|
||||
<controls:NodifyEditor x:Name="DynamicArea" Grid.Column="0" DataContext="{Binding SelectedItem.NodifyEditor, ElementName=TabControlName}" />
|
||||
<GridSplitter Grid.Column="1" Width="4" VerticalAlignment="Stretch" ResizeDirection="Columns" KeyboardIncrement="0"
|
||||
Visibility="{Binding SelectedItem.HasImage, ElementName=TabControlName, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user