Ver código fonte

Apply transform button and shortcut, fix zoombox movement with middle mouse click

Equbuxu 3 anos atrás
pai
commit
29eea8856f

+ 2 - 2
src/ChunkyImageLibTest/ClearRegionOperationTests.cs

@@ -14,7 +14,7 @@ public class ClearRegionOperationTests
     {
         ClearRegionOperation operation = new(new(new(chunkSize, chunkSize), new(chunkSize, chunkSize)));
         var expected = new HashSet<VecI>() { new(1, 1) };
-        var actual = operation.FindAffectedChunks();
+        var actual = operation.FindAffectedChunks(new(chunkSize));
         Assert.Equal(expected, actual);
     }
 
@@ -33,7 +33,7 @@ public class ClearRegionOperationTests
             new(-2, -0), new(-1, -0), new(0, -0), new(1, -0),
             new(-2,  1), new(-1,  1), new(0,  1), new(1,  1),
         };
-        var actual = operation.FindAffectedChunks();
+        var actual = operation.FindAffectedChunks(new(chunkSize));
         Assert.Equal(expected, actual);
     }
 #pragma warning restore format

+ 1 - 1
src/ChunkyImageLibTest/ImageOperationTests.cs

@@ -12,7 +12,7 @@ public class ImageOperationTests
     {
         using Surface testImage = new Surface((ChunkyImage.FullChunkSize, ChunkyImage.FullChunkSize));
         using ImageOperation operation = new((ChunkyImage.FullChunkSize, ChunkyImage.FullChunkSize), testImage);
-        var chunks = operation.FindAffectedChunks();
+        var chunks = operation.FindAffectedChunks(new(ChunkyImage.FullChunkSize));
         Assert.Equal(new HashSet<VecI>() { new(1, 1) }, chunks);
     }
 }

+ 7 - 7
src/ChunkyImageLibTest/RectangleOperationTests.cs

@@ -19,7 +19,7 @@ public class RectangleOperationTests
         RectangleOperation operation = new(new(new(x, y), new(w, h), 0, 1, SKColors.Black, SKColors.Transparent));
 
         HashSet<VecI> expected = new() { new(0, 0) };
-        var actual = operation.FindAffectedChunks();
+        var actual = operation.FindAffectedChunks(new(chunkSize));
 
         Assert.Equal(expected, actual);
     }
@@ -31,7 +31,7 @@ public class RectangleOperationTests
         RectangleOperation operation = new(new(new(x, y), new(w, h), 0, 1, SKColors.Black, SKColors.Transparent));
 
         HashSet<VecI> expected = new() { new(-1, -1), new(0, -1), new(-1, 0), new(0, 0) };
-        var actual = operation.FindAffectedChunks();
+        var actual = operation.FindAffectedChunks(new(chunkSize));
 
         Assert.Equal(expected, actual);
     }
@@ -48,7 +48,7 @@ public class RectangleOperationTests
             new(1, 2),            new(3, 2),
             new(1, 3), new(2, 3), new(3, 3),
         };
-        var actual = operation.FindAffectedChunks();
+        var actual = operation.FindAffectedChunks(new(chunkSize));
 
         Assert.Equal(expected, actual);
     }
@@ -65,7 +65,7 @@ public class RectangleOperationTests
             new(-4, -3),              new(-2, -3),
             new(-4, -2), new(-3, -2), new(-2, -2),
         };
-        var actual = operation.FindAffectedChunks();
+        var actual = operation.FindAffectedChunks(new(chunkSize));
 
         Assert.Equal(expected, actual);
     }
@@ -82,7 +82,7 @@ public class RectangleOperationTests
             new(1, 2), new(2, 2), new(3, 2),
             new(1, 3), new(2, 3), new(3, 3),
         };
-        var actual = operation.FindAffectedChunks();
+        var actual = operation.FindAffectedChunks(new(chunkSize));
 
         Assert.Equal(expected, actual);
     }
@@ -101,7 +101,7 @@ public class RectangleOperationTests
             new(0, 3), new(1, 3), new(2, 3), new(3, 3), new(4, 3),
             new(0, 4), new(1, 4), new(2, 4), new(3, 4), new(4, 4),
         };
-        var actual = operation.FindAffectedChunks();
+        var actual = operation.FindAffectedChunks(new(chunkSize));
 
         Assert.Equal(expected, actual);
     }
@@ -113,7 +113,7 @@ public class RectangleOperationTests
         RectangleOperation operation = new(new(new(x, y), new(w, h), 0, chunkSize, SKColors.Black, SKColors.White));
 
         HashSet<VecI> expected = new() { new(0, 0) };
-        var actual = operation.FindAffectedChunks();
+        var actual = operation.FindAffectedChunks(new(chunkSize));
 
         Assert.Equal(expected, actual);
     }

+ 2 - 0
src/PixiEditor.Zoombox/Operations/MoveDragOperation.cs

@@ -16,6 +16,7 @@ internal class MoveDragOperation : IDragOperation
     public void Start(MouseButtonEventArgs e)
     {
         prevMousePos = Zoombox.ToVecD(e.GetPosition(parent.mainCanvas));
+        parent.mainGrid.CaptureMouse();
     }
 
     public void Update(MouseEventArgs e)
@@ -27,5 +28,6 @@ internal class MoveDragOperation : IDragOperation
 
     public void Terminate()
     {
+        parent.mainGrid.ReleaseMouseCapture();
     }
 }

+ 2 - 0
src/PixiEditor.Zoombox/Operations/RotateDragOperation.cs

@@ -22,6 +22,7 @@ internal class RotateDragOperation : IDragOperation
         initialClickAngle = GetAngle(new(pointCur.X, pointCur.Y));
         initialZoomboxAngle = owner.Angle;
         rotationProcess = new LockingRotationProcess(initialZoomboxAngle);
+        owner.mainGrid.CaptureMouse();
     }
 
     private double GetAngle(VecD point)
@@ -47,5 +48,6 @@ internal class RotateDragOperation : IDragOperation
 
     public void Terminate()
     {
+        owner.mainGrid.ReleaseMouseCapture();
     }
 }

+ 2 - 0
src/PixiEditor.Zoombox/Operations/ZoomDragOperation.cs

@@ -23,6 +23,7 @@ internal class ZoomDragOperation : IDragOperation
         screenScaleOrigin = Zoombox.ToVecD(e.GetPosition(parent.mainCanvas));
         scaleOrigin = parent.ToZoomboxSpace(screenScaleOrigin);
         originalScale = parent.Scale;
+        parent.mainGrid.CaptureMouse();
     }
 
     public void Update(MouseEventArgs e)
@@ -39,5 +40,6 @@ internal class ZoomDragOperation : IDragOperation
 
     public void Terminate()
     {
+        parent.mainGrid.ReleaseMouseCapture();
     }
 }

+ 15 - 0
src/PixiEditor/Helpers/Converters/ZoomModeToHitTestVisibleConverter.cs

@@ -0,0 +1,15 @@
+using System.Globalization;
+using System.Windows;
+using PixiEditor.Zoombox;
+
+namespace PixiEditor.Helpers.Converters;
+
+internal class ZoomModeToHitTestVisibleConverter : SingleInstanceConverter<ZoomModeToHitTestVisibleConverter>
+{
+    public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        if (value is not ZoomboxMode zoomboxMode)
+            return DependencyProperty.UnsetValue;
+        return zoomboxMode == ZoomboxMode.Normal;
+    }
+}

+ 1 - 8
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/EllipseToolExecutor.cs

@@ -90,14 +90,7 @@ internal class EllipseToolExecutor : UpdateableChangeExecutor
         transforming = true;
         document!.TransformViewModel.ShowFixedAngleShapeTransform(new ShapeCorners(lastRect));
     }
-
-    public override void OnKeyDown(Key key)
-    {
-        if (key is not Key.Enter || !transforming)
-            return;
-        OnTransformApplied();
-    }
-
+    
     public override void ForceStop()
     {
         if (transforming)

+ 32 - 0
src/PixiEditor/Styles/ThemeStyle.xaml

@@ -62,6 +62,38 @@
         </Style.Triggers>
     </Style>
 
+    <Style TargetType="Button" x:Key="GrayRoundButton" BasedOn="{StaticResource BaseDarkButton}">
+        <Setter Property="Background" Value="#404040" />
+        <Setter Property="Foreground" Value="White" />
+        <Setter Property="OverridesDefaultStyle" Value="True" />
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Border CornerRadius="4" Background="{TemplateBinding Background}">
+                        <ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center"
+                                          VerticalAlignment="Center" Margin="{TemplateBinding Padding}"/>
+                    </Border>
+                    <ControlTemplate.Triggers>
+                        <Trigger Property="IsEnabled" Value="False">
+                            <Setter Property="Background" Value="Transparent" />
+                            <Setter Property="Foreground" Value="Gray" />
+                            <Setter Property="Cursor" Value="Arrow" />
+                        </Trigger>
+                        <Trigger Property="IsMouseOver" Value="True">
+                            <Setter Property="Background" Value="#FF515151" />
+                            <Setter Property="Foreground" Value="White" />
+                            <Setter Property="Cursor" Value="Hand" />
+                        </Trigger>
+                        <Trigger Property="IsPressed" Value="True">
+                            <Setter Property="Background" Value="#505050" />
+                            <Setter Property="Foreground" Value="White" />
+                        </Trigger>
+                    </ControlTemplate.Triggers>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+    
     <Style TargetType="Button" x:Key="DarkRoundButton" BasedOn="{StaticResource BaseDarkButton}">
         <Setter Property="OverridesDefaultStyle" Value="True" />
         <Setter Property="Background" Value="#303030" />

+ 1 - 0
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs

@@ -394,5 +394,6 @@ internal class DocumentViewModel : NotifyableObject
     public void OnOpacitySliderDragStarted() => Helpers.ChangeController.OnOpacitySliderDragStarted();
     public void OnOpacitySliderDragged(float newValue) => Helpers.ChangeController.OnOpacitySliderDragged(newValue);
     public void OnOpacitySliderDragEnded() => Helpers.ChangeController.OnOpacitySliderDragEnded();
+    public void OnApplyTransform() => Helpers.ChangeController.OnTransformApplied();
     #endregion
 }

+ 10 - 0
src/PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.UserPreferences;
+using PixiEditor.ViewModels.SubViewModels.Document;
 using PixiEditor.ViewModels.SubViewModels.Tools;
 using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
@@ -71,6 +72,15 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
         SetActiveTool(typeof(T));
     }
 
+    [Command.Basic("PixiEditor.Tools.ApplyTransform", "Apply transform", "", Key = Key.Enter)]
+    public void ApplyTransform()
+    {
+        DocumentViewModel doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        if (doc is null)
+            return;
+        doc.OnApplyTransform();
+    }
+
     [Command.Internal("PixiEditor.Tools.SelectTool", CanExecute = "PixiEditor.HasDocument")]
     public void SetActiveTool(ToolViewModel tool)
     {

+ 5 - 1
src/PixiEditor/Views/UserControls/SymmetryOverlay/SymmetryOverlay.cs

@@ -148,11 +148,13 @@ internal class SymmetryOverlay : Control
 
     private VecD ToVecD(Point pos) => new VecD(pos.X, pos.Y);
 
-    public SymmetryAxisDirection? capturedDirection;
+    private SymmetryAxisDirection? capturedDirection;
 
     protected override void OnMouseDown(MouseButtonEventArgs e)
     {
         base.OnMouseDown(e);
+        if (e.ChangedButton != MouseButton.Left)
+            return;
 
         var pos = ToVecD(e.GetPosition(this));
         var dir = IsTouchingHandle(pos);
@@ -178,6 +180,8 @@ internal class SymmetryOverlay : Control
     protected override void OnMouseUp(MouseButtonEventArgs e)
     {
         base.OnMouseUp(e);
+        if (e.ChangedButton != MouseButton.Left)
+            return;
 
         if (capturedDirection is null)
             return;

+ 7 - 2
src/PixiEditor/Views/UserControls/TransformOverlay/TransformOverlay.cs

@@ -1,12 +1,13 @@
 using System.Windows;
 using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
 using System.Windows.Input;
 using System.Windows.Media;
 using ChunkyImageLib.DataHolders;
 
 namespace PixiEditor.Views.UserControls.TransformOverlay;
 #nullable enable
-internal class TransformOverlay : Control
+internal class TransformOverlay : Decorator
 {
     public static readonly DependencyProperty RequestedCornersProperty =
         DependencyProperty.Register(nameof(RequestedCorners), typeof(ShapeCorners), typeof(TransformOverlay),
@@ -181,7 +182,9 @@ internal class TransformOverlay : Control
     protected override void OnMouseDown(MouseButtonEventArgs e)
     {
         base.OnMouseDown(e);
-
+        if (e.ChangedButton != MouseButton.Left)
+            return;
+        
         e.Handled = true;
         VecD pos = TransformHelper.ToVecD(e.GetPosition(this));
         Anchor? anchor = TransformHelper.GetAnchorInPosition(pos, Corners, InternalState.Origin, ZoomboxScale);
@@ -297,6 +300,8 @@ internal class TransformOverlay : Control
     protected override void OnMouseUp(MouseButtonEventArgs e)
     {
         base.OnMouseUp(e);
+        if (e.ChangedButton != MouseButton.Left)
+            return;
         if (ReleaseAnchor())
             e.Handled = true;
         else if (isMoving)

+ 17 - 1
src/PixiEditor/Views/UserControls/Viewport.xaml

@@ -13,6 +13,7 @@
     xmlns:sym="clr-namespace:PixiEditor.Views.UserControls.SymmetryOverlay"
     xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
     xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+    xmlns:cmds="clr-namespace:PixiEditor.Models.Commands.XAML"
     mc:Ignorable="d"
     x:Name="vpUc"
     d:DesignHeight="450"
@@ -93,6 +94,7 @@
                         Source="{Binding TargetBitmap}"
                         RenderOptions.BitmapScalingMode="{Binding Zoombox.Scale, Converter={converters:ScaleToBitmapScalingModeConverter}}"/>
                     <sym:SymmetryOverlay
+                        IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
                         ZoomboxScale="{Binding Zoombox.Scale}"
                         HorizontalAxisVisible="{Binding Document.HorizontalSymmetryAxisEnabledBindable}"
                         VerticalAxisVisible="{Binding Document.VerticalSymmetryAxisEnabledBindable}"
@@ -104,6 +106,8 @@
                         Path="{Binding Document.SelectionPathBindable}"
                         ZoomboxScale="{Binding Zoombox.Scale}" />
                     <to:TransformOverlay
+                        Cursor="Arrow"
+                        IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
                         HorizontalAlignment="Stretch"
                         VerticalAlignment="Stretch"
                         Visibility="{Binding Document.TransformViewModel.TransformActive, Converter={converters:BoolToVisibilityConverter}}"
@@ -113,7 +117,7 @@
                         SideFreedom="{Binding Document.TransformViewModel.SideFreedom}"
                         LockRotation="{Binding Document.TransformViewModel.LockRotation}"
                         InternalState="{Binding Document.TransformViewModel.InternalState, Mode=TwoWay}"
-                        ZoomboxScale="{Binding Zoombox.Scale}" />
+                        ZoomboxScale="{Binding Zoombox.Scale}"/>
                     <Grid IsHitTestVisible="False" SnapsToDevicePixels="True"
                         ShowGridLines="True" Width="{Binding Document.Width}" Height="{Binding Document.Height}" Panel.ZIndex="10" 
                         Visibility="{Binding GridLinesVisible, Converter={converters:BoolToVisibilityConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}}">
@@ -142,5 +146,17 @@
                 </Grid>
             </Border>
         </zoombox:Zoombox>
+        <Button 
+            Panel.ZIndex="99999"
+            DockPanel.Dock="Bottom" 
+            Width="140" 
+            Height="28" 
+            Margin="5" 
+            VerticalAlignment="Bottom" 
+            Style="{StaticResource GrayRoundButton}"
+            Visibility="{Binding Document.TransformViewModel.TransformActive, Converter={converters:BoolToVisibilityConverter}, ElementName=vpUc}"
+            Command="{cmds:Command PixiEditor.Tools.ApplyTransform}">
+            Apply transform
+        </Button>
     </Grid>
 </UserControl>