Browse Source

Improved how folders are managed

flabbet 1 year ago
parent
commit
ab7e4ea9e9

+ 16 - 42
src/PixiEditor.AvaloniaUI/Models/DocumentModels/DocumentStructureHelper.cs

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using PixiEditor.AvaloniaUI.Models.Handlers;
 using PixiEditor.AvaloniaUI.Models.Layers;
+using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Enums;
@@ -94,41 +95,17 @@ internal class DocumentStructureHelper
     {
         if (memberToMoveInto == memberToMove)
             return;
-        
-        internals.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(memberToMove, memberToMoveInto));
+
+        internals.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(memberToMove, memberToMoveInto,
+            true));
     }
 
-    private void HandleMoveAboveBelow(Guid memberToMove, Guid referenceMember, bool above)
+    private void HandleMoveAboveBelow(Guid memberToMove, Guid referenceMemberId, bool above)
     {
-        /*IFolderHandler targetFolder = (IFolderHandler)memberToMoveRelativeToPath[1];
-        if (memberToMovePath[1].Id == memberToMoveRelativeToPath[1].Id)
-        {
-            // members are in the same folder
-            int indexOfMemberToMove = targetFolder.Children.IndexOf(memberToMovePath[0]);
-            int indexOfMemberToMoveAbove = targetFolder.Children.IndexOf(memberToMoveRelativeToPath[0]);
-            int index = indexOfMemberToMoveAbove;
-            if (above)
-                index++;
-            if (indexOfMemberToMove < indexOfMemberToMoveAbove)
-                index--;
-
-            Guid targetNodeId = targetFolder.Children[Math.Clamp(index, 0, targetFolder.Children.Count - 1)].Id;
-            internals.ActionAccumulator.AddFinishedActions(
-                new MoveStructureMember_Action(memberToMovePath[0].Id, targetNodeId));
-        }
-        else
-        {
-            // members are in different folders
-            if (memberToMoveRelativeToPath.Contains(memberToMovePath[0]))
-                return;
-            int index = targetFolder.Children.IndexOf(memberToMoveRelativeToPath[0]);
-            if (above)
-                index++;
-
-            Guid targetNodeId = targetFolder.Children[Math.Clamp(index, 0, targetFolder.Children.Count - 1)].Id;
-            internals.ActionAccumulator.AddFinishedActions(
-                new MoveStructureMember_Action(memberToMovePath[0].Id, targetNodeId));
-        }*/
+        var referenceMember = doc.StructureHelper.FindNode<INodeHandler>(referenceMemberId);
+        var memberToMoveInto = !above ? referenceMember : doc.StructureHelper.GetFirstForwardNode(referenceMember);
+        internals.ActionAccumulator.AddFinishedActions(
+            new MoveStructureMember_Action(memberToMove, memberToMoveInto.Id, above && memberToMoveInto is IFolderHandler));
     }
 
     public void TryMoveStructureMember(Guid memberToMove, Guid memberToMoveIntoOrNextTo,
@@ -140,22 +117,19 @@ internal class DocumentStructureHelper
                 HandleMoveAboveBelow(memberToMove, memberToMoveIntoOrNextTo, true);
                 break;
             case StructureMemberPlacement.Below:
-                //HandleMoveAboveBelow(memberPath, refPath, false);
+                HandleMoveAboveBelow(memberToMove, memberToMoveIntoOrNextTo, false);
                 break;
             case StructureMemberPlacement.Inside:
                 HandleMoveInside(memberToMove, memberToMoveIntoOrNextTo);
                 break;
             case StructureMemberPlacement.BelowOutsideFolder:
             {
-                /*IFolderHandler refFolder = (IFolderHandler)refPath[1];
-                int refIndexInParent = refFolder.Children.IndexOf(refPath[0]);
-                if (refIndexInParent > 0 || refPath.Count == 2)
-                {
-                    HandleMoveAboveBelow(memberPath, refPath, false);
-                    break;
-                }
-
-                HandleMoveAboveBelow(memberPath, doc.StructureHelper.FindPath(refPath[1].Id), false);*/
+                var path = doc.StructureHelper.FindPath(memberToMoveIntoOrNextTo);
+                var folder = path.FirstOrDefault(x => x is IFolderHandler) as IFolderHandler;
+                if (folder is null)
+                    HandleMoveAboveBelow(memberToMove, memberToMoveIntoOrNextTo, false);
+                else
+                    HandleMoveAboveBelow(memberToMove, folder.Id, false);
             }
                 break;
         }

+ 1 - 9
src/PixiEditor.AvaloniaUI/Models/DocumentModels/DocumentUpdater.cs

@@ -431,15 +431,7 @@ internal class DocumentUpdater
 
     private void ProcessMoveStructureMember(MoveStructureMember_ChangeInfo info)
     {
-        /*(IStructureMemberHandler memberVM, IFolderHandler curFolderVM) = doc.StructureHelper.FindChildAndParentOrThrow(info.Id);
-
-        IFolderHandler? targetFolderVM = (IFolderHandler)doc.StructureHelper.FindOrThrow(info.ParentToGuid);
-
-        curFolderVM.Children.Remove(memberVM);
-        targetFolderVM.Children.Insert(info.NewIndex, memberVM);*/
-
-        // TODO: Make sure property changed events are raised internally
-        //doc.InternalRaiseLayersChanged(new LayersChangedEventArgs(info.Id, LayerAction.Move));
+         
     }
     
     private void ProcessCreateRasterKeyFrame(CreateRasterKeyFrame_ChangeInfo info)

+ 54 - 20
src/PixiEditor.AvaloniaUI/Models/DocumentModels/Public/DocumentStructureModule.cs

@@ -72,7 +72,9 @@ internal class DocumentStructureModule
     public List<IStructureMemberHandler> FindPath(Guid guid)
     {
         List<INodeHandler>? list = new List<INodeHandler>();
-        FillPath(doc.NodeGraphHandler.OutputNode, guid, list);
+        var targetNode = FindNode<INodeHandler>(guid);
+        if (targetNode == null) return [];
+        FillPath(targetNode, list);
         return list.Cast<IStructureMemberHandler>().ToList();
     }
 
@@ -94,33 +96,17 @@ internal class DocumentStructureModule
         return layers;
     }
 
-    private bool FillPath(INodeHandler node, Guid guid, List<INodeHandler> toFill)
+    private void FillPath(INodeHandler node, List<INodeHandler> toFill)
     {
-        if (node.Id == guid)
+        node.TraverseForwards(newNode =>
         {
-            return true;
-        }
-
-        if (node is IStructureMemberHandler structureNode)
-        {
-            toFill.Add(structureNode);
-        }
-
-        bool found = false;
-
-        node.TraverseBackwards(newNode =>
-        {
-            if (newNode is IStructureMemberHandler strNode && newNode.Id == guid)
+            if (newNode is IStructureMemberHandler strNode)
             {
                 toFill.Add(strNode);
-                found = true;
-                return false;
             }
 
             return true;
         });
-
-        return found;
     }
 
     public INodeHandler? GetFirstForwardNode(INodeHandler startNode)
@@ -137,4 +123,52 @@ internal class DocumentStructureModule
 
         return result;
     }
+
+    public IStructureMemberHandler? GetAboveMember(Guid memberId, bool includeFolders)
+    {
+        INodeHandler member = FindNode<INodeHandler>(memberId);
+        if (member == null)
+            return null;
+        
+        IStructureMemberHandler? result = null;
+        member.TraverseForwards(node =>
+        {
+            if (node != member && node is IStructureMemberHandler structureMemberNode)
+            {
+                if(node is IFolderHandler && !includeFolders)
+                    return true;
+                
+                result = structureMemberNode;
+                return false;
+            }
+
+            return true;
+        });
+        
+        return result;
+    }
+    
+    public IStructureMemberHandler? GetBelowMember(Guid memberId, bool includeFolders)
+    {
+        INodeHandler member = FindNode<INodeHandler>(memberId);
+        if (member == null)
+            return null;
+        
+        IStructureMemberHandler? result = null;
+        member.TraverseBackwards(node =>
+        {
+            if (node != member && node is IStructureMemberHandler structureMemberNode)
+            {
+                if(node is IFolderHandler && !includeFolders)
+                    return true;
+                
+                result = structureMemberNode;
+                return false;
+            }
+
+            return true;
+        });
+        
+        return result;
+    }
 }

+ 2 - 0
src/PixiEditor.AvaloniaUI/Models/Handlers/INodeGraphHandler.cs

@@ -1,4 +1,5 @@
 using System.Collections.ObjectModel;
+using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.AvaloniaUI.ViewModels.Nodes;
 
 namespace PixiEditor.AvaloniaUI.Models.Handlers;
@@ -8,6 +9,7 @@ internal interface INodeGraphHandler
    public ObservableCollection<INodeHandler> AllNodes { get; }
    public ObservableCollection<NodeConnectionViewModel> Connections { get; }
    public INodeHandler OutputNode { get; }
+   public StructureTree StructureTree { get; }
    public bool TryTraverse(Func<INodeHandler, bool> func);
    public void AddNode(INodeHandler node);
    public void RemoveNode(Guid nodeId);

+ 7 - 4
src/PixiEditor.AvaloniaUI/Models/Handlers/INodeHandler.cs

@@ -1,6 +1,7 @@
 using System.Collections.ObjectModel;
 using ChunkyImageLib;
 using PixiEditor.AvaloniaUI.Models.Structures;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.AvaloniaUI.Models.Handlers;
@@ -14,8 +15,10 @@ public interface INodeHandler
     public Surface ResultPreview { get; set; }
     public VecD PositionBindable { get; set; }
     public bool IsSelected { get; set; }
-    void TraverseBackwards(Func<INodeHandler, bool> func);
-    void TraverseBackwards(Func<INodeHandler, INodeHandler, bool> func);
-    void TraverseForwards(Func<INodeHandler, bool> func);
-    void TraverseForwards(Func<INodeHandler, INodeHandler, bool> func);
+    public void TraverseBackwards(Func<INodeHandler, bool> func);
+    public void TraverseBackwards(Func<INodeHandler, INodeHandler, bool> func);
+    public void TraverseBackwards(Func<INodeHandler, INodeHandler, INodePropertyHandler, bool> func);
+    public void TraverseForwards(Func<INodeHandler, bool> func);
+    public void TraverseForwards(Func<INodeHandler, INodeHandler, bool> func);
+    public void TraverseForwards(Func<INodeHandler, INodeHandler, INodePropertyHandler, bool> func);
 }

+ 6 - 3
src/PixiEditor.AvaloniaUI/ViewModels/Document/StructureTree.cs

@@ -21,7 +21,7 @@ internal class StructureTree
         int relativeFolderIndex = 0;
         List<IStructureMemberHandler> membersMet = new();
         ObservableCollection<IStructureMemberHandler> lastRoot = Members;
-        nodeGraphViewModel.OutputNode.TraverseBackwards((node, previous) =>
+        nodeGraphViewModel.OutputNode.TraverseBackwards((node, previous, property) =>
         {
             if (previous != null && _memberMap.TryGetValue(previous, out var value))
             {
@@ -39,8 +39,11 @@ internal class StructureTree
             
             if (previous is IFolderHandler folder)
             {
-                lastRoot = folder.Children;
-                relativeFolderIndex = 0;
+                if (property.PropertyName == "Content")
+                {
+                    lastRoot = folder.Children;
+                    relativeFolderIndex = 0;
+                }
             }
             
             if (node is IFolderHandler handler)

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

@@ -7,6 +7,7 @@ using PixiEditor.AvaloniaUI.Models.Handlers;
 using PixiEditor.AvaloniaUI.Models.Structures;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.ChangeableDocument.Actions.Generated;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.Numerics;
 
@@ -156,6 +157,36 @@ internal class NodeViewModel : ObservableObject, INodeHandler
         }
     }
 
+    public void TraverseBackwards(Func<INodeHandler, INodeHandler, INodePropertyHandler, bool> func)
+    {
+        var visited = new HashSet<INodeHandler>();
+        var queueNodes = new Queue<(INodeHandler, INodeHandler, INodePropertyHandler)>();
+        queueNodes.Enqueue((this, null, null));
+
+        while (queueNodes.Count > 0)
+        {
+            var node = queueNodes.Dequeue();
+
+            if (!visited.Add(node.Item1))
+            {
+                continue;
+            }
+            
+            if (!func(node.Item1, node.Item2, node.Item3))
+            {
+                return;
+            }
+
+            foreach (var inputProperty in node.Item1.Inputs)
+            {
+                if (inputProperty.ConnectedOutput != null)
+                {
+                    queueNodes.Enqueue((inputProperty.ConnectedOutput.Node, node.Item1, inputProperty));
+                } 
+            }
+        }
+    }
+
     public void TraverseForwards(Func<INodeHandler, bool> func)
     {
         var visited = new HashSet<INodeHandler>();
@@ -215,6 +246,36 @@ internal class NodeViewModel : ObservableObject, INodeHandler
             }
         }
     }
+    
+    public void TraverseForwards(Func<INodeHandler, INodeHandler, INodePropertyHandler, bool> func)
+    {
+        var visited = new HashSet<INodeHandler>();
+        var queueNodes = new Queue<(INodeHandler, INodeHandler, INodePropertyHandler)>();
+        queueNodes.Enqueue((this, null, null));
+
+        while (queueNodes.Count > 0)
+        {
+            var node = queueNodes.Dequeue();
+
+            if (!visited.Add(node.Item1))
+            {
+                continue;
+            }
+            
+            if (!func(node.Item1, node.Item2, node.Item3))
+            {
+                return;
+            }
+
+            foreach (var outputProperty in node.Item1.Outputs)
+            {
+                foreach (var connection in outputProperty.ConnectedInputs)
+                {
+                    queueNodes.Enqueue((connection.Node, node.Item1, outputProperty));
+                }
+            }
+        }
+    }
 
     public NodePropertyViewModel FindInputProperty(string propName)
     {

+ 6 - 10
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/LayersViewModel.cs

@@ -196,16 +196,12 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         var member = doc?.SelectedStructureMember;
         if (member is null)
             return false;
-        var path = doc!.StructureHelper.FindPath(member.Id);
-        if (path.Count < 2)
-            return false;
-        var parent = (FolderViewModel)path[1];
-        int index = parent.Children.IndexOf(path[0]);
-        if (above && index == parent.Children.Count - 1)
-            return false;
-        if (!above && index == 0)
-            return false;
-        return true;
+        if (above)
+        {
+            return doc.StructureHelper.GetAboveMember(member.Id, false) is not null;
+        }
+        
+        return doc.StructureHelper.GetBelowMember(member.Id, false) is not null;
     }
 
     private void MoveSelectedMember(bool upwards)

+ 2 - 3
src/PixiEditor.AvaloniaUI/Views/Layers/LayersManager.axaml.cs

@@ -132,9 +132,8 @@ internal partial class LayersManager : UserControl
 
         if (droppedGuid is not null && ActiveDocument is not null)
         {
-            // TODO: Implement this
-            /*ActiveDocument.Operations.MoveStructureMember((Guid)droppedGuid,
-                ActiveDocument.NodeGraph.Children[0].GuidValue, StructureMemberPlacement.Below);*/
+            ActiveDocument.Operations.MoveStructureMember((Guid)droppedGuid,
+                ActiveDocument.NodeGraph.StructureTree.Members[^1].Id, StructureMemberPlacement.Below);
             e.Handled = true;
         }
 

+ 28 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs

@@ -4,8 +4,15 @@ using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
-public class FolderNode : StructureNode, IReadOnlyFolderNode 
+public class FolderNode : StructureNode, IReadOnlyFolderNode
 {
+    public InputProperty<ChunkyImage?> Content { get; }
+
+    public FolderNode()
+    {
+        Content = CreateInput<ChunkyImage?>("Content", "CONTENT", null);
+    }
+
     public override bool Validate()
     {
         return true;
@@ -15,14 +22,29 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
     public override ChunkyImage? OnExecute(KeyFrameTime frame)
     {
-        if (!IsVisible.Value)
+        if (!IsVisible.Value || Content.Value == null)
         {
-            Output.Value = null;
-            return null;
+            Output.Value = Background.Value;
+            return Output.Value;
         }
+
+        VecI size = Content.Value?.CommittedSize ?? Background.Value.CommittedSize;
         
-        Output.Value = Background.Value;
-        return Background.Value;
+        Output.Value = new ChunkyImage(size);
+
+        if (Background.Value != null)
+        {
+            Output.Value.EnqueueDrawUpToDateChunkyImage(VecI.Zero, Background.Value);
+        }
+
+        if (Content.Value != null)
+        {
+            Output.Value.EnqueueDrawUpToDateChunkyImage(VecI.Zero, Content.Value);
+        }
+
+        Output.Value.CommitChanges();
+
+        return Output.Value;
     }
 
     public override RectI? GetTightBounds(KeyFrameTime frameTime)

+ 11 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -39,19 +39,24 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         var imageFrame = frames.FirstOrDefault(x => x.IsInFrame(frame.Frame));
         var frameImage = imageFrame?.Image ?? frames[0].Image;
 
+        ChunkyImage result;
+
         if (Background.Value != null)
         {
             VecI targetSize = GetBiggerSize(frameImage.LatestSize, Background.Value.LatestSize);
-            ChunkyImage combined = new(targetSize);
-            combined.EnqueueDrawUpToDateChunkyImage(VecI.Zero, Background.Value);
-            combined.EnqueueDrawUpToDateChunkyImage(VecI.Zero, frameImage);
-            combined.CommitChanges();
+            result = new ChunkyImage(targetSize);
+            result.EnqueueDrawUpToDateChunkyImage(VecI.Zero, Background.Value);
+            result.EnqueueDrawUpToDateChunkyImage(VecI.Zero, frameImage);
+            result.CommitChanges();
             
-            Output.Value = combined;
+            Output.Value = result;
         }
         else
         {
-            Output.Value = frameImage;
+            result = new ChunkyImage(frameImage.LatestSize);
+            result.EnqueueDrawUpToDateChunkyImage(VecI.Zero, frameImage);
+            result.CommitChanges();
+            Output.Value = result;
         }
         
         return Output.Value;

+ 3 - 10
src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs

@@ -49,12 +49,8 @@ internal class CreateStructureMember_Change : Change
         
         if (member is FolderNode folder)
         {
-            MergeNode mergeNode = new() { Id = Guid.NewGuid() };
-            document.NodeGraph.AddNode(mergeNode);
             document.NodeGraph.AddNode(member);
-            
-            changes.Add(CreateNode_ChangeInfo.CreateFromNode(mergeNode));
-            AppendFolder(backgroundInput, folder, mergeNode, changes);
+            AppendFolder(backgroundInput, folder, changes);
         }
         else
         {
@@ -110,12 +106,9 @@ internal class CreateStructureMember_Change : Change
         return changes;
     }
 
-    private static void AppendFolder(IBackgroundInput backgroundInput, FolderNode folder, MergeNode mergeNode, List<IChangeInfo> changes)
+    private static void AppendFolder(IBackgroundInput backgroundInput, FolderNode folder, List<IChangeInfo> changes)
     {
-        var appened = NodeOperations.AppendMember(backgroundInput.Background, mergeNode.Output, mergeNode.Bottom, mergeNode.Id);
-        changes.AddRange(appened);
-        
-        appened = NodeOperations.AppendMember(mergeNode.Top, folder.Output, folder.Background, folder.Id);
+        var appened = NodeOperations.AppendMember(backgroundInput.Background, folder.Output, folder.Background, folder.Id);
         changes.AddRange(appened);
     }
 

+ 16 - 5
src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs

@@ -1,4 +1,5 @@
-using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.ChangeInfos.Structure;
 using PixiEditor.ChangeableDocument.Changes.NodeGraph;
@@ -15,13 +16,16 @@ internal class MoveStructureMember_Change : Change
 
     private List<IInputProperty> originalOutputConnections = new();
     private List<(IInputProperty, IOutputProperty?)> originalInputConnections = new();
+    
+    private bool putInsideFolder;
 
 
     [GenerateMakeChangeAction]
-    public MoveStructureMember_Change(Guid memberGuid, Guid targetNode)
+    public MoveStructureMember_Change(Guid memberGuid, Guid targetNode, bool putInsideFolder)
     {
         this.memberGuid = memberGuid;
         this.targetNodeGuid = targetNode;
+        this.putInsideFolder = putInsideFolder;
     }
 
     public override bool InitializeAndValidate(Document document)
@@ -36,7 +40,7 @@ internal class MoveStructureMember_Change : Change
         return true;
     }
 
-    private static List<IChangeInfo> Move(Document document, Guid sourceNodeGuid, Guid targetNodeGuid)
+    private static List<IChangeInfo> Move(Document document, Guid sourceNodeGuid, Guid targetNodeGuid, bool putInsideFolder)
     {
         var sourceNode = document.FindMember(sourceNodeGuid);
         var targetNode = document.FindNode(targetNodeGuid);
@@ -45,8 +49,15 @@ internal class MoveStructureMember_Change : Change
 
         List<IChangeInfo> changes = new();
 
+        InputProperty<ChunkyImage?> inputProperty = backgroundInput.Background;
+
+        if (targetNode is FolderNode folder && putInsideFolder)
+        {
+            inputProperty = folder.Content;
+        }
+
         changes.AddRange(NodeOperations.DetachStructureNode(sourceNode));
-        changes.AddRange(NodeOperations.AppendMember(backgroundInput.Background, sourceNode.Output,
+        changes.AddRange(NodeOperations.AppendMember(inputProperty, sourceNode.Output,
             sourceNode.Background,
             sourceNode.Id));
 
@@ -56,7 +67,7 @@ internal class MoveStructureMember_Change : Change
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
         out bool ignoreInUndo)
     {
-        var changes = Move(target, memberGuid, targetNodeGuid);
+        var changes = Move(target, memberGuid, targetNodeGuid, putInsideFolder);
         ignoreInUndo = false;
         return changes;
     }