瀏覽代碼

Add rotation context menu and update rotation handling logic

Introduce a context menu for rotation control within `WinTitleBar` to allow direct selection of rotation angles (0°, 90°, 180°, 270°). Implement new `RotateToCommand` in `MainViewModel` and streamline logic for handling right-click events.
Ruben 3 月之前
父節點
當前提交
177ec8e623

+ 47 - 0
src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml

@@ -12,6 +12,53 @@
     xmlns:uc="clr-namespace:PicView.Avalonia.Views.UC;assembly=PicView.Avalonia"
     xmlns:vm="clr-namespace:PicView.Avalonia.ViewModels;assembly=PicView.Avalonia"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+    <UserControl.ContextMenu>
+        <ContextMenu x:Name="RotationContextMenu">
+            <MenuItem
+                Command="{CompiledBinding RotateToCommand}"
+                CommandParameter="0"
+                GroupName="RotationGroup"
+                Header="0°"
+                ToggleType="Radio"
+                x:Name="Rotation0Item" />
+            <MenuItem
+                Command="{CompiledBinding RotateToCommand}"
+                CommandParameter="90"
+                GroupName="RotationGroup"
+                Header="90°"
+                ToggleType="Radio"
+                x:Name="Rotation90Item" />
+            <MenuItem
+                Command="{CompiledBinding RotateToCommand}"
+                CommandParameter="180"
+                GroupName="RotationGroup"
+                Header="180°"
+                ToggleType="Radio"
+                x:Name="Rotation180Item" />
+            <MenuItem
+                Command="{CompiledBinding RotateToCommand}"
+                CommandParameter="270"
+                GroupName="RotationGroup"
+                Header="270°"
+                ToggleType="Radio"
+                x:Name="Rotation270Item" />
+            <Separator />
+            <MenuItem Command="{CompiledBinding FlipCommand}" Header="{CompiledBinding Translation.IsFlipped}">
+                <MenuItem.Icon>
+                    <Path
+                        Data="{StaticResource FlipGeometry}"
+                        Fill="{DynamicResource MainTextColor}"
+                        Height="12"
+                        Stretch="Fill"
+                        Width="12">
+                        <Path.RenderTransform>
+                            <ScaleTransform ScaleX="{CompiledBinding PicViewer.ScaleX}" />
+                        </Path.RenderTransform>
+                    </Path>
+                </MenuItem.Icon>
+            </MenuItem>
+        </ContextMenu>
+    </UserControl.ContextMenu>
 
     <Border
         Background="{DynamicResource WindowBackgroundColor}"

+ 51 - 0
src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml.cs

@@ -1,3 +1,4 @@
+using System.Reactive.Linq;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Input;
@@ -5,6 +6,7 @@ using Avalonia.Media;
 using PicView.Avalonia.DragAndDrop;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.WindowBehavior;
+using ReactiveUI;
 
 namespace PicView.Avalonia.Win32.Views;
 
@@ -85,6 +87,55 @@ public partial class WinTitleBar : UserControl
             {
                 DragAndDropHelper.RemoveDragDropView();
             };
+
+            if (DataContext is not MainViewModel vm)
+            {
+                return;
+            }
+
+            this.WhenAnyValue(x => x.RotationContextMenu.IsOpen).Skip(1).Subscribe(_ =>
+            {
+                Rotation0Item.IsChecked = false;
+                Rotation90Item.IsChecked = false;
+                Rotation180Item.IsChecked = false;
+                Rotation270Item.IsChecked = false;
+                switch (vm.RotationAngle)
+                {
+                    case 0:
+                        Rotation0Item.IsChecked = true;
+                        break;
+                    case 90:
+                        Rotation90Item.IsChecked = true;
+                        break;
+                    case 180:
+                        Rotation180Item.IsChecked = true;
+                        break;
+                    case 270:
+                        Rotation270Item.IsChecked = true;
+                        break;
+                    
+                }
+            });
+
+            RotateLeftButton.PointerPressed += (_, e) =>
+            {
+                if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
+                {
+                    // Context menu doesn't want to be opened normally
+                    RotationContextMenu.Open();
+                    return;
+                }
+            };
+            FlipButton.PointerPressed += (_, e) =>
+            {
+                if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
+                {
+                    // Context menu doesn't want to be opened normally
+                    RotationContextMenu.Open();
+                    return;
+                }
+            };
+            
         };
 
     }

+ 11 - 0
src/PicView.Avalonia/ImageTransformations/Rotation/RotationNavigation.cs

@@ -5,6 +5,7 @@ using PicView.Avalonia.Gallery;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.Views.UC.Menus;
+using PicView.Avalonia.WindowBehavior;
 using PicView.Core.DebugTools;
 using PicView.Core.Gallery;
 
@@ -39,6 +40,16 @@ public static class RotationNavigation
 
         await MoveCursorAfterRotation(vm, rotationButton);
     }
+    
+    public static async Task RotateTo(MainViewModel? vm, int angle)
+    {
+        await Dispatcher.UIThread.InvokeAsync(() =>
+        {
+            vm.ImageViewer.Rotate(angle);
+        });
+        vm.RotationAngle = angle;
+        await WindowResizing.SetSizeAsync(vm);
+    }
 
     private static async Task MoveCursorAfterRotation(MainViewModel? vm, RotationButton rotationButton)
     {

+ 0 - 2
src/PicView.Avalonia/ImageTransformations/Rotation/RotationTransformer.cs

@@ -72,8 +72,6 @@ public class RotationTransformer(
         }
 
         var prevScaleX = vm.PicViewer.ScaleX;
-        vm.PicViewer.ScaleX = vm.PicViewer.ScaleX == -1 ? 1 : -1;
-        vm.Translation.IsFlipped = vm.PicViewer.ScaleX == 1 ? vm.Translation.UnFlip : vm.Translation.Flip;
 
         if (animate)
         {

+ 11 - 0
src/PicView.Avalonia/ViewModels/MainViewModel.cs

@@ -147,6 +147,7 @@ public class MainViewModel : ReactiveObject
         {
             await RotationNavigation.RotateRight(this, RotationButton.RotateRightButton);
         });
+        RotateToCommand = FunctionsHelper.CreateReactiveCommand<string>(RotateToTask);
 
         RotateRightWindowBorderButtonCommand = FunctionsHelper.CreateReactiveCommand(async () =>
         {
@@ -605,6 +606,7 @@ public class MainViewModel : ReactiveObject
     public ReactiveCommand<Unit, Unit>? RotateLeftCommand { get; }
     public ReactiveCommand<Unit, Unit>? RotateLeftButtonCommand { get; }
     public ReactiveCommand<Unit, Unit>? RotateRightCommand { get; }
+    public ReactiveCommand<string, Unit>? RotateToCommand { get; }
     public ReactiveCommand<Unit, Unit>? RotateRightButtonCommand { get; }
     public ReactiveCommand<Unit, Unit>? RotateRightWindowBorderButtonCommand { get; }
     public ReactiveCommand<Unit, Unit>? FlipCommand { get; }
@@ -1160,6 +1162,15 @@ public class MainViewModel : ReactiveObject
 
     public async Task StartSlideShowTask(int milliseconds) =>
         await Slideshow.StartSlideshow(this, milliseconds);
+
+    public async Task RotateToTask(string angle)
+    {
+        if (int.TryParse(angle, out var result))
+        {
+            await RotationNavigation.RotateTo(this, result);
+        }
+    }
+        
     
     #endregion