Browse Source

Early wip of nodes graph ui

flabbet 1 year ago
parent
commit
b21a443d5b

+ 1 - 0
src/PixiEditor.AvaloniaUI/PixiEditor.AvaloniaUI.csproj

@@ -138,6 +138,7 @@
     <UpToDateCheckInput Remove="Images\News\NewVersion.png" />
     <UpToDateCheckInput Remove="Images\News\OfficialAnnouncement.png" />
     <UpToDateCheckInput Remove="Images\News\YouTube.png" />
+    <UpToDateCheckInput Remove="Styles\Templates\NodeProperties\ImageNodePropertyView.axaml" />
   </ItemGroup>
 
   <ItemGroup>

+ 7 - 4
src/PixiEditor.AvaloniaUI/Styles/Templates/NodeGraphView.axaml

@@ -1,13 +1,12 @@
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-                    xmlns:nodes="clr-namespace:PixiEditor.AvaloniaUI.Views.Nodes"
-                    xmlns:nodes1="clr-namespace:PixiEditor.AvaloniaUI.ViewModels.Document.Nodes">
+                    xmlns:nodes="clr-namespace:PixiEditor.AvaloniaUI.Views.Nodes">
     <ControlTheme TargetType="nodes:NodeGraphView" x:Key="{x:Type nodes:NodeGraphView}">
         <Setter Property="ZoomMode" Value="Move" />
         <Setter Property="Template">
             <ControlTemplate>
                 <Grid Background="Transparent">
-                    <ItemsControl
+                    <ItemsControl ClipToBounds="False"
                         ItemsSource="{TemplateBinding Nodes}">
                         <ItemsControl.ItemsPanel>
                             <ItemsPanelTemplate>
@@ -28,7 +27,11 @@
                         <ItemsControl.ItemTemplate>
                             <DataTemplate>
                                 <nodes:NodeView
-                                    DataContext="{Binding}" />
+                                    DisplayName="{Binding DisplayName}" 
+                                    Inputs="{Binding Inputs}"
+                                    Outputs="{Binding Outputs}"
+                                    ResultPreview="{Binding ResultPreview}"
+                                    />
                             </DataTemplate>
                         </ItemsControl.ItemTemplate>
                         <ItemsControl.ItemContainerTheme>

+ 22 - 2
src/PixiEditor.AvaloniaUI/Styles/Templates/NodeView.axaml

@@ -26,10 +26,30 @@
                         <TextBlock Grid.ColumnSpan="3" Grid.Row="0" Text="{TemplateBinding DisplayName}"
                                    FontWeight="Bold" />
                         <Grid Grid.Row="1">
-
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="0.5*" />
+                                <ColumnDefinition Width="0.5*" />
+                            </Grid.ColumnDefinitions>
+                            
+                            <ItemsControl ItemsSource="{TemplateBinding Inputs}" ClipToBounds="False">
+                                <ItemsControl.ItemContainerTheme>
+                                    <ControlTheme TargetType="ContentPresenter">
+                                        <Setter Property="DataContext" Value="."/>
+                                    </ControlTheme>
+                                </ItemsControl.ItemContainerTheme>
+                            </ItemsControl>
+                            <ItemsControl Grid.Column="1" ItemsSource="{TemplateBinding Outputs}" ClipToBounds="False">
+                                <ItemsControl.ItemContainerTheme>
+                                    <ControlTheme TargetType="ContentPresenter">
+                                        <Setter Property="DataContext" Value="."/>
+                                    </ControlTheme>
+                                </ItemsControl.ItemContainerTheme>
+                            </ItemsControl>
                         </Grid>
                         <Border CornerRadius="{DynamicResource ControlCornerRadius}" Grid.Row="2">
-                            <visuals:SurfaceControl Width="50" Height="50" RenderOptions.BitmapInterpolationMode="None">
+                            <visuals:SurfaceControl Width="50" Height="50"
+                                                    Surface="{TemplateBinding ResultPreview}"
+                                                    RenderOptions.BitmapInterpolationMode="None">
                                 <visuals:SurfaceControl.Background>
                                     <ImageBrush Source="/Images/CheckerTile.png"
                                                 TileMode="Tile" DestinationRect="0, 0, 25, 25" />

+ 0 - 26
src/PixiEditor.AvaloniaUI/ViewModels/Document/Nodes/NodeViewModel.cs

@@ -1,26 +0,0 @@
-namespace PixiEditor.AvaloniaUI.ViewModels.Document.Nodes;
-
-public class NodeViewModel : ViewModelBase
-{
-    private string name;
-    private double x;
-    private double y;
-    
-    public string Name
-    {
-        get => name;
-        set => SetProperty(ref name, value);
-    }
-    
-    public double X
-    {
-        get => x;
-        set => SetProperty(ref x, value);
-    }
-    
-    public double Y
-    {
-        get => y;
-        set => SetProperty(ref y, value);
-    }
-}

+ 46 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Nodes/NodePropertyViewModel.cs

@@ -0,0 +1,46 @@
+namespace PixiEditor.AvaloniaUI.ViewModels.Nodes;
+
+public abstract class NodePropertyViewModel : ViewModelBase
+{
+    private string name;
+    private object value;
+    private NodeViewModel node;
+    
+    public string Name
+    {
+        get => name;
+        set => SetProperty(ref name, value);
+    }
+    
+    public object Value
+    {
+        get => value;
+        set => SetProperty(ref value, value);
+    }
+    
+    public NodeViewModel Node
+    {
+        get => node;
+        set => SetProperty(ref node, value);
+    }
+    
+    public NodePropertyViewModel(NodeViewModel node)
+    {
+        Node = node;
+    }
+}
+
+public abstract class NodePropertyViewModel<T> : NodePropertyViewModel
+{
+    private T nodeValue;
+    
+    public new T Value
+    {
+        get => nodeValue;
+        set => SetProperty(ref nodeValue, value);
+    }
+    
+    public NodePropertyViewModel(NodeViewModel node) : base(node)
+    {
+    }
+}

+ 50 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Nodes/NodeViewModel.cs

@@ -0,0 +1,50 @@
+using System.Collections.ObjectModel;
+using ChunkyImageLib;
+
+namespace PixiEditor.AvaloniaUI.ViewModels.Nodes;
+
+public class NodeViewModel : ViewModelBase
+{
+    private string name;
+    private double x;
+    private double y;
+    private ObservableCollection<NodePropertyViewModel> inputs;
+    private ObservableCollection<NodePropertyViewModel> outputs;
+    private Surface resultPreview;
+    
+    public string Name
+    {
+        get => name;
+        set => SetProperty(ref name, value);
+    }
+    
+    public double X
+    {
+        get => x;
+        set => SetProperty(ref x, value);
+    }
+    
+    public double Y
+    {
+        get => y;
+        set => SetProperty(ref y, value);
+    }
+    
+    public ObservableCollection<NodePropertyViewModel> Inputs
+    {
+        get => inputs;
+        set => SetProperty(ref inputs, value);
+    }
+    
+    public ObservableCollection<NodePropertyViewModel> Outputs
+    {
+        get => outputs;
+        set => SetProperty(ref outputs, value);
+    }
+    
+    public Surface ResultPreview
+    {
+        get => resultPreview;
+        set => SetProperty(ref resultPreview, value);
+    }
+}

+ 17 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Nodes/Properties/ImageNodePropertyViewModel.cs

@@ -0,0 +1,17 @@
+using ChunkyImageLib;
+
+namespace PixiEditor.AvaloniaUI.ViewModels.Nodes.Properties;
+
+public class ImageNodePropertyViewModel : NodePropertyViewModel<Surface>
+{
+    public ImageNodePropertyViewModel(NodeViewModel node) : base(node)
+    {
+        base.PropertyChanged += (sender, args) =>
+        {
+            if (args.PropertyName == nameof(Value))
+            {
+                Node.ResultPreview = Value;
+            }
+        };
+    }
+}

+ 21 - 7
src/PixiEditor.AvaloniaUI/Views/Nodes/NodeGraphView.cs

@@ -1,18 +1,14 @@
 using System.Collections.ObjectModel;
 using Avalonia;
-using PixiEditor.AvaloniaUI.ViewModels.Document.Nodes;
+using PixiEditor.AvaloniaUI.ViewModels.Nodes;
+using PixiEditor.AvaloniaUI.ViewModels.Nodes.Properties;
 
 namespace PixiEditor.AvaloniaUI.Views.Nodes;
 
 public class NodeGraphView : Zoombox.Zoombox
 {
     public static readonly StyledProperty<ObservableCollection<NodeViewModel>> NodesProperty = AvaloniaProperty.Register<NodeGraphView, ObservableCollection<NodeViewModel>>(
-        nameof(Nodes), new ObservableCollection<NodeViewModel>()
-        {
-            new NodeViewModel() { Name = "Node 1", X = 100, Y = 100 },
-            new NodeViewModel() { Name = "Node 2", X = 200, Y = 200 },
-            new NodeViewModel() { Name = "Node 3", X = 300, Y = 300 }
-        });
+        nameof(Nodes));
 
     public ObservableCollection<NodeViewModel> Nodes
     {
@@ -21,5 +17,23 @@ public class NodeGraphView : Zoombox.Zoombox
     }
     
     protected override Type StyleKeyOverride => typeof(NodeGraphView);
+
+    public NodeGraphView()
+    {
+        NodeViewModel node = new NodeViewModel()
+        {
+            Name = "Node 1",
+            X = 100,
+            Y = 100,
+            Inputs = new ObservableCollection<NodePropertyViewModel>()
+        };
+        
+        node.Inputs.Add(new ImageNodePropertyViewModel(node) { Name = "Input 1" });
+
+        Nodes =
+        [
+            node,
+        ];
+    }
 }
 

+ 31 - 1
src/PixiEditor.AvaloniaUI/Views/Nodes/NodeView.cs

@@ -1,5 +1,8 @@
-using Avalonia;
+using System.Collections.ObjectModel;
+using Avalonia;
 using Avalonia.Controls.Primitives;
+using ChunkyImageLib;
+using PixiEditor.AvaloniaUI.ViewModels.Nodes;
 
 namespace PixiEditor.AvaloniaUI.Views.Nodes;
 
@@ -8,6 +11,33 @@ public class NodeView : TemplatedControl
     public static readonly StyledProperty<string> DisplayNameProperty = AvaloniaProperty.Register<NodeView, string>(
         nameof(DisplayName), "Node");
 
+    public static readonly StyledProperty<ObservableCollection<NodePropertyViewModel>> InputsProperty = AvaloniaProperty.Register<NodeView, ObservableCollection<NodePropertyViewModel>>(
+        nameof(Inputs));
+    
+    public static readonly StyledProperty<ObservableCollection<NodePropertyViewModel>> OutputsProperty = AvaloniaProperty.Register<NodeView, ObservableCollection<NodePropertyViewModel>>(
+        nameof(Outputs));
+
+    public static readonly StyledProperty<Surface> ResultPreviewProperty = AvaloniaProperty.Register<NodeView, Surface>(
+        nameof(ResultPreview));
+
+    public Surface ResultPreview
+    {
+        get => GetValue( ResultPreviewProperty);
+        set => SetValue( ResultPreviewProperty, value);
+    }
+
+    public ObservableCollection<NodePropertyViewModel> Outputs
+    {
+        get => GetValue(OutputsProperty);
+        set => SetValue(OutputsProperty, value);
+    }
+
+    public ObservableCollection<NodePropertyViewModel> Inputs
+    {
+        get => GetValue(InputsProperty);
+        set => SetValue(InputsProperty, value);
+    }
+
     public string DisplayName
     {
         get => GetValue(DisplayNameProperty);

+ 13 - 0
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/ImageNodePropertyView.axaml

@@ -0,0 +1,13 @@
+<properties:NodePropertyView x:TypeArguments="chunkyImageLib:Surface" xmlns="https://github.com/avaloniaui"
+                             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+                             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+                             xmlns:properties="clr-namespace:PixiEditor.AvaloniaUI.Views.Nodes.Properties"
+                             xmlns:chunkyImageLib="clr-namespace:ChunkyImageLib;assembly=ChunkyImageLib"
+                             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" ClipToBounds="False"
+                             x:Class="PixiEditor.AvaloniaUI.Views.Nodes.Properties.ImageNodePropertyView">
+    <StackPanel Orientation="Horizontal">
+        <Ellipse Margin="-10, 0, 0, 0" Width="10" Height="10" Fill="Red"/>
+        <Button Content="Open Image (temp)" Click="Button_OnClick"/>
+    </StackPanel>
+</properties:NodePropertyView>

+ 38 - 0
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/ImageNodePropertyView.axaml.cs

@@ -0,0 +1,38 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+using Avalonia.Platform.Storage;
+using ChunkyImageLib;
+using PixiEditor.AvaloniaUI.Helpers;
+using PixiEditor.AvaloniaUI.Models.IO;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.AvaloniaUI.Views.Nodes.Properties;
+
+public partial class ImageNodePropertyView : NodePropertyView<Surface> 
+{
+    public ImageNodePropertyView()
+    {
+        InitializeComponent();
+    }
+
+    private void Button_OnClick(object? sender, RoutedEventArgs e)
+    {
+        var filter = SupportedFilesHelper.BuildOpenFilter();
+
+        if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+        {
+            var dialog = desktop.MainWindow.StorageProvider.OpenFilePickerAsync(
+                new FilePickerOpenOptions { FileTypeFilter = filter });
+
+            var result = dialog.Result;
+
+            if (result.Count == 0 || !Importer.IsSupportedFile(result[0].Path.LocalPath))
+                return;
+
+            SetValue(Importer.ImportImage(result[0].Path.LocalPath, VecI.NegativeOne));
+        }
+    }
+}

+ 15 - 0
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/NodePropertyView.cs

@@ -0,0 +1,15 @@
+using Avalonia.Controls;
+using PixiEditor.AvaloniaUI.ViewModels.Nodes;
+
+namespace PixiEditor.AvaloniaUI.Views.Nodes.Properties;
+
+public abstract class NodePropertyView<T> : UserControl
+{
+    protected void SetValue(T value)
+    {
+        if (DataContext is NodePropertyViewModel<T> viewModel)
+        {
+            viewModel.Value = value;
+        }
+    }
+}