Browse Source

Added multi selection and passthrough selection

flabbet 9 months ago
parent
commit
a0f226e0bd

+ 3 - 3
src/PixiEditor/Models/Controllers/InputDevice/MouseInputFilter.cs

@@ -51,13 +51,13 @@ internal class MouseInputFilter
 
     public void DeactivatedInlet(object? sender, EventArgs e)
     {
-        MouseOnCanvasEventArgs argsLeft = new(MouseButton.Left, VecD.Zero);
+        MouseOnCanvasEventArgs argsLeft = new(MouseButton.Left, VecD.Zero, KeyModifiers.None);
         MouseUpInlet(argsLeft);
         
-        MouseOnCanvasEventArgs argsMiddle = new(MouseButton.Middle, VecD.Zero);
+        MouseOnCanvasEventArgs argsMiddle = new(MouseButton.Middle, VecD.Zero, KeyModifiers.None);
         MouseUpInlet(argsMiddle);
         
-        MouseOnCanvasEventArgs argsRight = new(MouseButton.Right, VecD.Zero);
+        MouseOnCanvasEventArgs argsRight = new(MouseButton.Right, VecD.Zero, KeyModifiers.None);
         MouseUpInlet(argsRight);
     }
 }

+ 3 - 1
src/PixiEditor/Models/Controllers/InputDevice/MouseOnCanvasEventArgs.cs

@@ -5,12 +5,14 @@ using Drawie.Numerics;
 namespace PixiEditor.Models.Controllers.InputDevice;
 internal class MouseOnCanvasEventArgs : EventArgs
 {
-    public MouseOnCanvasEventArgs(MouseButton button, VecD positionOnCanvas)
+    public MouseOnCanvasEventArgs(MouseButton button, VecD positionOnCanvas, KeyModifiers keyModifiers)
     {
         Button = button;
         PositionOnCanvas = positionOnCanvas;
+        KeyModifiers = keyModifiers;
     }
 
     public MouseButton Button { get; }
     public VecD PositionOnCanvas { get; }
+    public KeyModifiers KeyModifiers { get; }
 }

+ 3 - 2
src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs

@@ -8,6 +8,7 @@ using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
+using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Views.Overlays.SymmetryOverlay;
 
 namespace PixiEditor.Models.DocumentModels;
@@ -196,7 +197,7 @@ internal class ChangeExecutionController
 
     public void SymmetryDragEndedInlet(SymmetryAxisDirection dir) => currentSession?.OnSymmetryDragEnded(dir);
 
-    public void LeftMouseButtonDownInlet(VecD canvasPos)
+    public void LeftMouseButtonDownInlet(MouseOnCanvasEventArgs args)
     {
         //update internal state
         LeftMousePressed = true;
@@ -207,7 +208,7 @@ internal class ChangeExecutionController
         }
 
         //call session event
-        currentSession?.OnLeftMouseButtonDown(canvasPos);
+        currentSession?.OnLeftMouseButtonDown(args);
     }
 
     public void LeftMouseButtonUpInlet(VecD argsPositionOnCanvas)

+ 2 - 1
src/PixiEditor/Models/DocumentModels/Public/DocumentEventsModule.cs

@@ -5,6 +5,7 @@ using Drawie.Backend.Core.Numerics;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Handlers;
 using Drawie.Numerics;
+using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Views.Overlays.SymmetryOverlay;
 
 namespace PixiEditor.Models.DocumentModels.Public;
@@ -33,7 +34,7 @@ internal class DocumentEventsModule
         DocumentsHandler.TransformHandler.KeyModifiersInlet(args.IsShiftDown, args.IsCtrlDown, args.IsAltDown);
     }
 
-    public void OnCanvasLeftMouseButtonDown(VecD pos) => Internals.ChangeController.LeftMouseButtonDownInlet(pos);
+    public void OnCanvasLeftMouseButtonDown(MouseOnCanvasEventArgs args) => Internals.ChangeController.LeftMouseButtonDownInlet(args);
     public void OnCanvasMouseMove(VecD newPos)
     {
         DocumentsHandler.CoordinatesString = $"X: {(int)newPos.X} Y: {(int)newPos.Y}";

+ 2 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/SimpleShapeToolExecutor.cs

@@ -3,6 +3,7 @@ using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
+using PixiEditor.Models.Controllers.InputDevice;
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 
@@ -107,7 +108,7 @@ internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor,
         document!.LineToolOverlayHandler.Hide();
     }
 
-    public override void OnLeftMouseButtonDown(VecD pos)
+    public override void OnLeftMouseButtonDown(MouseOnCanvasEventArgs args)
     {
         if (ActiveMode == ShapeToolMode.Preview)
         {

+ 59 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformSelectedExecutor.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Linq;
+using Avalonia.Input;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using Drawie.Backend.Core.Numerics;
@@ -10,6 +11,7 @@ using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
+using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.ViewModels.Document.Nodes;
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
@@ -104,22 +106,76 @@ internal class TransformSelectedExecutor : UpdateableChangeExecutor, ITransforma
         return ExecutionState.Success;
     }
 
-    public override void OnLeftMouseButtonDown(VecD pos)
+    public override void OnLeftMouseButtonDown(MouseOnCanvasEventArgs args)
     {
         var allLayers = document.StructureHelper.GetAllLayers();
         var topMostWithinClick = allLayers.Where(x =>
-                x is { IsVisibleBindable: true, TightBounds: not null } && x.TightBounds.Value.ContainsInclusive(pos))
+                x is { IsVisibleBindable: true, TightBounds: not null } &&
+                x.TightBounds.Value.ContainsInclusive(args.PositionOnCanvas))
             .OrderByDescending(x => allLayers.IndexOf(x));
 
         var nonSelected = topMostWithinClick.Where(x => x != document.SelectedStructureMember
                                                         && !document.SoftSelectedStructureMembers.Contains(x))
             .ToArray();
+        
+        bool isHoldingShift = args.KeyModifiers.HasFlag(KeyModifiers.Shift);
 
         if (nonSelected.Any())
         {
             var topMost = nonSelected.First();
 
-            document.Operations.SetSelectedMember(topMost.Id);
+            if (!isHoldingShift)
+            {
+                document.Operations.ClearSoftSelectedMembers();
+                document.Operations.SetSelectedMember(topMost.Id);
+            }
+            else
+            {
+                document.Operations.AddSoftSelectedMember(topMost.Id);
+            }
+        }
+        else if (isHoldingShift)
+        {
+            var topMostList = topMostWithinClick.ToList();
+            if (document.SoftSelectedStructureMembers.Count > 0)
+            {
+                Deselect(topMostList);
+            }
+        }
+    }
+
+    private void Deselect(List<ILayerHandler> topMostWithinClick)
+    {
+        var topMost = topMostWithinClick.FirstOrDefault();
+        if (topMost is not null)
+        {
+            bool deselectingWasMain = document.SelectedStructureMember.Id == topMost.Id;
+            if (deselectingWasMain)
+            {
+                Guid? nextMain = document.SoftSelectedStructureMembers.FirstOrDefault().Id;
+                List<Guid> softSelected = document.SoftSelectedStructureMembers
+                    .Select(x => x.Id).Where(x => x != nextMain.Value).ToList();
+                    
+                document.Operations.ClearSoftSelectedMembers();
+                document.Operations.SetSelectedMember(nextMain.Value);
+                    
+                foreach (var guid in softSelected)
+                {
+                    document.Operations.AddSoftSelectedMember(guid);
+                }
+            }
+            else
+            {
+                List<Guid> softSelected = document.SoftSelectedStructureMembers
+                    .Select(x => x.Id).Where(x => x != topMost.Id).ToList();
+                    
+                document.Operations.ClearSoftSelectedMembers();
+                    
+                foreach (var guid in softSelected)
+                {
+                    document.Operations.AddSoftSelectedMember(guid);
+                }
+            }
         }
     }
 

+ 2 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/UpdateableChangeExecutor.cs

@@ -7,6 +7,7 @@ using Drawie.Backend.Core.Numerics;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
+using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Views.Overlays.SymmetryOverlay;
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
@@ -48,7 +49,7 @@ internal abstract class UpdateableChangeExecutor
     public abstract void ForceStop();
     public virtual void OnPixelPositionChange(VecI pos) { }
     public virtual void OnPrecisePositionChange(VecD pos) { }
-    public virtual void OnLeftMouseButtonDown(VecD pos) { }
+    public virtual void OnLeftMouseButtonDown(MouseOnCanvasEventArgs args) { }
     public virtual void OnLeftMouseButtonUp(VecD pos) { }
     public virtual void OnOpacitySliderDragStarted() { }
     public virtual void OnOpacitySliderDragged(float newValue) { }

+ 2 - 1
src/PixiEditor/Models/Handlers/ITransformHandler.cs

@@ -2,6 +2,7 @@
 using Drawie.Backend.Core.Numerics;
 using PixiEditor.Models.DocumentModels;
 using Drawie.Numerics;
+using PixiEditor.Models.Controllers.InputDevice;
 
 namespace PixiEditor.Models.Handlers;
 
@@ -16,5 +17,5 @@ internal interface ITransformHandler : IHandler
     public bool HasUndo { get; }
     public bool HasRedo { get; }
     public bool ShowTransformControls { get; set; }
-    public event Action<VecD> PassthroughPointerPressed;
+    public event Action<MouseOnCanvasEventArgs> PassthroughPointerPressed;
 }

+ 5 - 4
src/PixiEditor/ViewModels/Document/TransformOverlays/DocumentTransformViewModel.cs

@@ -10,6 +10,7 @@ using PixiEditor.Helpers.UI;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.Handlers;
 using Drawie.Numerics;
+using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Views.Overlays.TransformOverlay;
 
 namespace PixiEditor.ViewModels.Document.TransformOverlays;
@@ -85,7 +86,7 @@ internal class DocumentTransformViewModel : ObservableObject, ITransformHandler
         set => SetProperty(ref showTransformControls, value);
     }
 
-    public event Action<VecD>? PassthroughPointerPressed;
+    public event Action<MouseOnCanvasEventArgs>? PassthroughPointerPressed;
 
     private bool coverWholeScreen;
     public bool CoverWholeScreen
@@ -127,12 +128,12 @@ internal class DocumentTransformViewModel : ObservableObject, ITransformHandler
         set => SetProperty(ref actionCompletedCommand, value);
     }
 
-    private RelayCommand<VecD>? passThroughPointerPressedCommand; 
-    public RelayCommand<VecD> PassThroughPointerPressedCommand
+    private RelayCommand<MouseOnCanvasEventArgs>? passThroughPointerPressedCommand; 
+    public RelayCommand<MouseOnCanvasEventArgs> PassThroughPointerPressedCommand
     {
         get
         {
-            return passThroughPointerPressedCommand ??= new RelayCommand<VecD>(x => PassthroughPointerPressed?.Invoke(x));
+            return passThroughPointerPressedCommand ??= new RelayCommand<MouseOnCanvasEventArgs>(x => PassthroughPointerPressed?.Invoke(x));
         }
     }
 

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/IoViewModel.cs

@@ -193,7 +193,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
             return;
 
         drawingWithRight = args.Button == MouseButton.Right;
-        activeDocument.EventInlet.OnCanvasLeftMouseButtonDown(args.PositionOnCanvas);
+        activeDocument.EventInlet.OnCanvasLeftMouseButtonDown(args);
         Owner.ToolsSubViewModel.UseToolEventInlet(args.PositionOnCanvas, args.Button);
 
         Analytics.SendUseTool(Owner.ToolsSubViewModel.ActiveTool, args.PositionOnCanvas, activeDocument.SizeBindable);

+ 3 - 3
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml.cs

@@ -400,7 +400,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
         var pos = e.GetPosition(Scene);
         VecD scenePos = Scene.ToZoomboxSpace(new VecD(pos.X, pos.Y));
-        MouseOnCanvasEventArgs? parameter = new MouseOnCanvasEventArgs(mouseButton, scenePos);
+        MouseOnCanvasEventArgs? parameter = new MouseOnCanvasEventArgs(mouseButton, scenePos, e.KeyModifiers);
 
         if (MouseDownCommand.CanExecute(parameter))
             MouseDownCommand.Execute(parameter);
@@ -415,7 +415,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
         MouseButton mouseButton = e.GetMouseButton(this);
 
-        MouseOnCanvasEventArgs parameter = new(mouseButton, conv);
+        MouseOnCanvasEventArgs parameter = new(mouseButton, conv, e.KeyModifiers);
 
         if (MouseMoveCommand.CanExecute(parameter))
             MouseMoveCommand.Execute(parameter);
@@ -428,7 +428,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
         Point pos = e.GetPosition(Scene);
         VecD conv = Scene.ToZoomboxSpace(new VecD(pos.X, pos.Y));
-        MouseOnCanvasEventArgs parameter = new(e.InitialPressMouseButton, conv);
+        MouseOnCanvasEventArgs parameter = new(e.InitialPressMouseButton, conv, e.KeyModifiers);
         if (MouseUpCommand.CanExecute(parameter))
             MouseUpCommand.Execute(parameter);
     }

+ 2 - 1
src/PixiEditor/Views/Overlays/TransformOverlay/TransformOverlay.cs

@@ -524,7 +524,8 @@ internal class TransformOverlay : Overlay
 
         if (!isRotating && !actuallyMoved)
         {
-            PassthroughPointerPressedCommand?.Execute(e.Point);
+            MouseOnCanvasEventArgs args = new(MouseButton.Left, e.Point, e.Modifiers);
+            PassthroughPointerPressedCommand?.Execute(args);
         }
 
         if (isRotating)