浏览代码

Light theme updates and fixes

Simplify code formatting, remove unused code, and refactor color resource values
Ruben 3 月之前
父节点
当前提交
eda00dbc1f
共有 31 个文件被更改,包括 1845 次插入893 次删除
  1. 1 1
      src/PicView.Avalonia.MacOS/Views/ExifWindow.axaml
  2. 1 1
      src/PicView.Avalonia.Win32/Views/ExifWindow.axaml
  3. 27 11
      src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml.cs
  4. 0 1
      src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml
  5. 7 10
      src/PicView.Avalonia/Animations/AnimationsHelper.cs
  6. 73 68
      src/PicView.Avalonia/ColorManagement/ColorManager.cs
  7. 479 0
      src/PicView.Avalonia/CustomControls/TextIconButton.cs
  8. 10 13
      src/PicView.Avalonia/PicViewTheme/Controls/AutoCompleteBox.axaml
  9. 27 0
      src/PicView.Avalonia/PicViewTheme/Controls/Button.axaml
  10. 67 1
      src/PicView.Avalonia/PicViewTheme/Controls/SplitButton.axaml
  11. 23 0
      src/PicView.Avalonia/PicViewTheme/Controls/ToggleButton.axaml
  12. 23 0
      src/PicView.Avalonia/PicViewTheme/Icons.axaml
  13. 3 3
      src/PicView.Avalonia/PicViewTheme/ResourceDictionaries/MainColors.axaml
  14. 42 36
      src/PicView.Avalonia/StartUp/StartUpHelper.cs
  15. 62 15
      src/PicView.Avalonia/UI/UIHelper.cs
  16. 104 115
      src/PicView.Avalonia/ViewModels/ToolsViewModel.cs
  17. 14 8
      src/PicView.Avalonia/Views/BottomBar.axaml
  18. 67 37
      src/PicView.Avalonia/Views/BottomBar.axaml.cs
  19. 20 13
      src/PicView.Avalonia/Views/MainView.axaml
  20. 0 82
      src/PicView.Avalonia/Views/SettingsView.axaml
  21. 41 40
      src/PicView.Avalonia/Views/SettingsView.axaml.cs
  22. 2 2
      src/PicView.Avalonia/Views/UC/Buttons/SettingsMenuButton.axaml
  23. 116 12
      src/PicView.Avalonia/Views/UC/GalleryItem.axaml
  24. 61 87
      src/PicView.Avalonia/Views/UC/Menus/FileMenu.axaml
  25. 18 0
      src/PicView.Avalonia/Views/UC/Menus/FileMenu.axaml.cs
  26. 146 119
      src/PicView.Avalonia/Views/UC/Menus/ImageMenu.axaml
  27. 64 12
      src/PicView.Avalonia/Views/UC/Menus/ImageMenu.axaml.cs
  28. 87 64
      src/PicView.Avalonia/Views/UC/Menus/SettingsMenu.axaml
  29. 66 7
      src/PicView.Avalonia/Views/UC/Menus/SettingsMenu.axaml.cs
  30. 100 130
      src/PicView.Avalonia/Views/UC/Menus/ToolsMenu.axaml
  31. 94 5
      src/PicView.Avalonia/Views/UC/Menus/ToolsMenu.axaml.cs

+ 1 - 1
src/PicView.Avalonia.MacOS/Views/ExifWindow.axaml

@@ -75,10 +75,10 @@
                 Command="{CompiledBinding Tools.OpenWithCommand}"
                 CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
-                Data="{StaticResource OpenWithGeometry}"
                 DockPanel.Dock="Right"
                 Foreground="{StaticResource SecondaryTextColor}"
                 Height="28"
+                Icon="{StaticResource OpenExternalImage}"
                 IconHeight="17"
                 IconWidth="17"
                 ToolTip.Tip="{CompiledBinding Translation.OpenWith.Value,

+ 1 - 1
src/PicView.Avalonia.Win32/Views/ExifWindow.axaml

@@ -135,10 +135,10 @@
                     Command="{CompiledBinding Tools.OpenWithCommand}"
                     CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                        FallbackValue=''}"
-                    Data="{StaticResource OpenWithGeometry}"
                     DockPanel.Dock="Right"
                     Foreground="{DynamicResource MainTextColor}"
                     Height="28"
+                    Icon="{StaticResource OpenExternalImage}"
                     IconHeight="17"
                     IconWidth="17"
                     ToolTip.Tip="{CompiledBinding Translation.OpenWith.Value,

+ 27 - 11
src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml.cs

@@ -22,28 +22,28 @@ public partial class SettingsWindow : Window
         {
             LogoBorder.Background = Brushes.Transparent;
             LogoBorder.BorderThickness = new Thickness(0);
-            
+
             SettingsButton.Background = Brushes.Transparent;
             SettingsButton.BorderThickness = new Thickness(0);
-            
+
             CloseButton.Background = Brushes.Transparent;
             CloseButton.BorderThickness = new Thickness(0);
-            
+
             MinimizeButton.Background = Brushes.Transparent;
             MinimizeButton.BorderThickness = new Thickness(0);
-            
+
             GoBackBorder.Background = Brushes.Transparent;
             GoBackBorder.BorderThickness = new Thickness(0);
             GoBackButton.Background = Brushes.Transparent;
             GoBackButton.BorderThickness = new Thickness(0);
-            
+
             GoForwardBorder.Background = Brushes.Transparent;
             GoForwardBorder.BorderThickness = new Thickness(0);
             GoForwardButton.Background = Brushes.Transparent;
             GoForwardButton.BorderThickness = new Thickness(0);
-            
+
             TitleText.Background = Brushes.Transparent;
-            
+
             SettingsButton.Background = Brushes.Transparent;
             SettingsButton.BorderThickness = new Thickness(0);
 
@@ -52,7 +52,7 @@ public partial class SettingsWindow : Window
                 settingsIcon.Background = Brushes.Transparent;
                 settingsIcon.BorderThickness = new Thickness(0);
             }
-            
+
             if (!Application.Current.TryGetResource("SecondaryTextColor",
                     Application.Current.RequestedThemeVariant, out var textColor))
             {
@@ -63,15 +63,28 @@ public partial class SettingsWindow : Window
             {
                 return;
             }
-            
+
             TitleText.Foreground = new SolidColorBrush(color);
             MinimizeButton.Foreground = new SolidColorBrush(color);
             CloseButton.Foreground = new SolidColorBrush(color);
         }
         else if (!Settings.Theme.Dark)
         {
-            ParentBorder.Background = new SolidColorBrush(Color.FromArgb(114,132, 132, 132));
+            ParentBorder.Background = new SolidColorBrush(Color.FromArgb(114, 132, 132, 132));
+            SettingsButton.Background = Brushes.White;
+            if (!Application.Current.TryGetResource("MainBorderColor",
+                    Application.Current.RequestedThemeVariant, out var mbColor))
+            {
+                return;
+            }
+
+            if (mbColor is Color color)
+            {
+                SettingsButton.BorderThickness = new Thickness(1, 0, 0, 0);
+                SettingsButton.BorderBrush = new SolidColorBrush(color);
+            }
         }
+
         Loaded += delegate
         {
             MinWidth = Width;
@@ -121,7 +134,10 @@ public partial class SettingsWindow : Window
 
     private void MoveWindow(object? sender, PointerPressedEventArgs e)
     {
-        if (VisualRoot is null) { return; }
+        if (VisualRoot is null)
+        {
+            return;
+        }
 
         var hostWindow = (Window)VisualRoot;
         hostWindow?.BeginMoveDrag(e);

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

@@ -71,7 +71,6 @@
                 Background="{DynamicResource WindowButtonBackgroundColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
-                CornerRadius="8,0,0,0"
                 DockPanel.Dock="Left"
                 x:Name="LogoBorder">
                 <Image

+ 7 - 10
src/PicView.Avalonia/Animations/AnimationsHelper.cs

@@ -54,7 +54,7 @@ public static class AnimationsHelper
                         }
                     },
                     Cue = new Cue(1d)
-                },
+                }
             }
         };
     }
@@ -108,11 +108,11 @@ public static class AnimationsHelper
                         }
                     },
                     Cue = new Cue(1d)
-                },
+                }
             }
         };
     }
-    
+
     /// <summary>
     /// Displays a brief animation to indicate a clipboard operation occurred.
     /// Fades a semi-transparent rectangle in and out to provide visual feedback.
@@ -142,12 +142,9 @@ public static class AnimationsHelper
         await endOpacityAnimation.RunAsync(rectangle);
         await Task.Delay(200);
 
-        await Dispatcher.UIThread.InvokeAsync(() =>
-        {
-            UIHelper.GetMainView.MainGrid.Children.Remove(rectangle);
-        });
+        await Dispatcher.UIThread.InvokeAsync(() => { UIHelper.GetMainView.MainGrid.Children.Remove(rectangle); });
     }
-    
+
     /// <summary>
     /// Creates an animation to move a <see cref="TranslateTransform"/> from a start position to an end position.
     /// </summary>
@@ -157,7 +154,7 @@ public static class AnimationsHelper
     /// <param name="toY">The ending Y position.</param>
     /// <param name="speed">The duration of the animation in seconds.</param>
     /// <returns>An <see cref="Animation"/> that animates the X and Y properties of a <see cref="TranslateTransform"/>.</returns>
-    public static Animation CenteringAnimation(double fromX, double fromY, double toX, double toY, double speed) => 
+    public static Animation CenteringAnimation(double fromX, double fromY, double toX, double toY, double speed) =>
         CenteringAnimation(fromX, fromY, toX, toY, TimeSpan.FromSeconds(speed));
 
     /// <summary>
@@ -211,7 +208,7 @@ public static class AnimationsHelper
                         }
                     },
                     Cue = new Cue(1d)
-                },
+                }
             }
         };
     }

+ 73 - 68
src/PicView.Avalonia/ColorManagement/ColorManager.cs

@@ -13,88 +13,88 @@ public static class ColorManager
     private static readonly Dictionary<int, ThemeColors> ThemeColorMap = new()
     {
         [(int)ColorOptions.Blue] = new ThemeColors(
-            logoLight: Color.FromRgb(225, 210, 80),
-            logoDark: Color.FromRgb(255, 240, 90),
-            primary: Color.FromRgb(26, 140, 240),
-            secondary: Color.FromArgb(242, 66, 163, 249),
-            buttonColor: Color.FromRgb(26, 140, 240)
+            Color.FromRgb(225, 210, 80),
+            Color.FromRgb(255, 240, 90),
+            Color.FromRgb(26, 140, 240),
+            Color.FromArgb(242, 66, 163, 249),
+            Color.FromRgb(26, 140, 240)
         ),
         [(int)ColorOptions.Pink] = new ThemeColors(
-            logoLight: Color.FromRgb(250, 180, 38),
-            logoDark: Color.FromRgb(255, 237, 38),
-            primary: Color.FromRgb(255, 53, 197),
-            secondary: Color.FromArgb(230, 255, 98, 210),
-            buttonColor: Color.FromRgb(255, 53, 197)
+            Color.FromRgb(250, 180, 38),
+            Color.FromRgb(255, 237, 38),
+            Color.FromRgb(255, 53, 197),
+            Color.FromArgb(230, 255, 98, 210),
+            Color.FromRgb(255, 53, 197)
         ),
         [(int)ColorOptions.Orange] = new ThemeColors(
-            logoLight: Color.FromRgb(248, 175, 60),
-            logoDark: Color.FromRgb(248, 175, 60),
-            primary: Color.FromRgb(219, 91, 61),
-            secondary: Color.FromArgb(242, 245, 121, 57),
-            buttonColor: Color.FromRgb(219, 91, 61)
+            Color.FromRgb(184, 172, 17),
+            Color.FromRgb(248, 175, 60),
+            Color.FromRgb(219, 91, 61),
+            Color.FromArgb(242, 245, 121, 57),
+            Color.FromRgb(219, 91, 61)
         ),
         [(int)ColorOptions.Ruby] = new ThemeColors(
-            logoLight: Color.FromRgb(175, 157, 38),
-            logoDark: Color.FromRgb(209, 237, 93),
-            primary: Color.FromRgb(255, 32, 110), 
-            secondary: Color.FromArgb(242, 255, 80, 140), 
-            buttonColor: Color.FromRgb(255, 32, 110)
+            Color.FromRgb(254, 172, 150),
+            Color.FromRgb(209, 237, 93),
+            Color.FromRgb(255, 32, 110),
+            Color.FromArgb(242, 255, 80, 140),
+            Color.FromRgb(255, 32, 110)
         ),
         [(int)ColorOptions.Red] = new ThemeColors(
-            logoLight: Color.FromRgb(250, 192, 92),
-            logoDark: Color.FromRgb(250, 192, 92),
-            primary: Color.FromRgb(249, 17, 16),
-            secondary: Color.FromArgb(242, 249, 61, 60),
-            buttonColor: Color.FromRgb(249, 17, 16)
+            Color.FromRgb(250, 192, 92),
+            Color.FromRgb(250, 192, 92),
+            Color.FromRgb(249, 17, 16),
+            Color.FromArgb(242, 249, 61, 60),
+            Color.FromRgb(249, 17, 16)
         ),
         [(int)ColorOptions.Teal] = new ThemeColors(
-            logoLight: Color.FromRgb(254, 172, 150),
-            logoDark: Color.FromRgb(254, 172, 150),
-            primary: Color.FromRgb(68, 161, 160),
-            secondary: Color.FromArgb(242, 77, 195, 194),
-            buttonColor: Color.FromRgb(68, 161, 160)
+            Color.FromRgb(254, 172, 150),
+            Color.FromRgb(254, 172, 150),
+            Color.FromRgb(68, 161, 160),
+            Color.FromArgb(242, 77, 195, 194),
+            Color.FromRgb(68, 161, 160)
         ),
         [(int)ColorOptions.Raspberry] = new ThemeColors(
-            logoLight: Color.FromRgb(228, 209, 17),
-            logoDark: Color.FromRgb(228, 209, 17),
-            primary: Color.FromRgb(181, 69, 126),
-            secondary: Color.FromArgb(242, 201, 100, 156),
-            buttonColor: Color.FromRgb(181, 69, 126)
+            Color.FromRgb(228, 209, 17),
+            Color.FromRgb(228, 209, 17),
+            Color.FromRgb(181, 69, 126),
+            Color.FromArgb(242, 201, 100, 156),
+            Color.FromRgb(181, 69, 126)
         ),
         [(int)ColorOptions.Golden] = new ThemeColors(
-            logoLight: Color.FromRgb(226, 180, 224),
-            logoDark: Color.FromRgb(255, 253, 42),
-            primary: Color.FromRgb(254, 169, 85),
-            secondary: Color.FromArgb(242, 249, 187, 125),
-            buttonColor: Color.FromRgb(254, 169, 85)
+            Color.FromRgb(226, 180, 224),
+            Color.FromRgb(255, 253, 42),
+            Color.FromRgb(254, 169, 85),
+            Color.FromArgb(242, 249, 187, 125),
+            Color.FromRgb(254, 169, 85)
         ),
         [(int)ColorOptions.Purple] = new ThemeColors(
-            logoLight: Color.FromRgb(226, 141, 223),
-            logoDark: Color.FromRgb(237, 184, 135),
-            primary: Color.FromRgb(151, 56, 235),
-            secondary: Color.FromArgb(242, 194, 95, 255),
-            buttonColor: Color.FromRgb(151, 56, 235)
+            Color.FromRgb(226, 141, 223),
+            Color.FromRgb(237, 184, 135),
+            Color.FromRgb(151, 56, 235),
+            Color.FromArgb(242, 194, 95, 255),
+            Color.FromRgb(151, 56, 235)
         ),
         [(int)ColorOptions.Cyan] = new ThemeColors(
-            logoLight: Color.FromRgb(215, 200, 70),
-            logoDark: Color.FromRgb(255, 253, 66),
-            primary: Color.FromRgb(27, 161, 226),
-            secondary: Color.FromArgb(242, 89, 186, 233),
-            buttonColor: Color.FromRgb(27, 161, 226)
+            Color.FromRgb(215, 200, 70),
+            Color.FromRgb(255, 253, 66),
+            Color.FromRgb(27, 161, 226),
+            Color.FromArgb(242, 89, 186, 233),
+            Color.FromRgb(27, 161, 226)
         ),
         [(int)ColorOptions.Magenta] = new ThemeColors(
-            logoLight: Color.FromRgb(226, 141, 223),
-            logoDark: Color.FromRgb(255, 237, 38),
-            primary: Color.FromRgb(230, 139, 238),
-            secondary: Color.FromArgb(242, 255, 108, 212),
-            buttonColor: Color.FromRgb(230, 139, 238)
+            Color.FromRgb(226, 141, 223),
+            Color.FromRgb(255, 237, 38),
+            Color.FromRgb(230, 139, 238),
+            Color.FromArgb(242, 255, 108, 212),
+            Color.FromRgb(230, 139, 238)
         ),
         [(int)ColorOptions.Emerald] = new ThemeColors(
-            logoLight: Color.FromRgb(255, 253, 42),
-            logoDark: Color.FromRgb(255, 253, 42),
-            primary: Color.FromRgb(0, 114, 0), 
-            secondary: Color.FromArgb(242, 50, 164, 50), 
-            buttonColor: Color.FromRgb(0, 114, 0) 
+            Color.FromRgb(255, 253, 42),
+            Color.FromRgb(255, 253, 42),
+            Color.FromRgb(0, 114, 0),
+            Color.FromArgb(242, 50, 164, 50),
+            Color.FromRgb(0, 114, 0)
         )
     };
 
@@ -113,7 +113,7 @@ public static class ColorManager
     /// Gets the primary accent color based on the current color theme.
     /// </summary>
     public static Color PrimaryAccentColor => GetThemeColors().Primary;
-    
+
     /// <summary>
     /// Gets the button color for a specific theme
     /// </summary>
@@ -126,7 +126,7 @@ public static class ColorManager
     private static ThemeColors GetThemeColors()
     {
         var themeIndex = Settings.Theme.ColorTheme;
-        
+
         if (ThemeColorMap.TryGetValue(themeIndex, out var colors))
         {
             return colors;
@@ -136,8 +136,8 @@ public static class ColorManager
         {
             return ThemeColorMap[0];
         }
-        
-        throw new ArgumentOutOfRangeException(nameof(Settings.Theme.ColorTheme), 
+
+        throw new ArgumentOutOfRangeException(nameof(Settings.Theme.ColorTheme),
             $"Color theme index {themeIndex} is not supported");
     }
 
@@ -152,7 +152,7 @@ public static class ColorManager
         var primaryBrush = new SolidColorBrush(PrimaryAccentColor);
         var secondaryBrush = new SolidColorBrush(SecondaryAccentColor);
         var logoAccentBrush = new SolidColorBrush(LogoAccentColor);
-        
+
         if (Settings.Theme.GlassTheme)
         {
             GlassThemeHelper.GlassThemeUpdates();
@@ -163,7 +163,7 @@ public static class ColorManager
         UpdateResourceIfExists("SecondaryAccentColor", secondaryBrush);
         UpdateResourceIfExists("LogoAccentColor", logoAccentBrush);
     }
-    
+
     /// <summary>
     /// Updates a resource if it exists in the application resources
     /// </summary>
@@ -174,11 +174,16 @@ public static class ColorManager
             Application.Current.Resources[resourceKey] = value;
         }
     }
-    
+
     /// <summary>
     /// Represents a set of colors for a theme
     /// </summary>
-    private readonly struct ThemeColors(Color logoLight, Color logoDark, Color primary, Color secondary, Color buttonColor)
+    private readonly struct ThemeColors(
+        Color logoLight,
+        Color logoDark,
+        Color primary,
+        Color secondary,
+        Color buttonColor)
     {
         private Color LogoLight { get; } = logoLight;
         private Color LogoDark { get; } = logoDark;

+ 479 - 0
src/PicView.Avalonia/CustomControls/TextIconButton.cs

@@ -0,0 +1,479 @@
+using System.Diagnostics;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Layout;
+using Avalonia.Media;
+using Avalonia.Metadata;
+using Avalonia.Threading;
+
+namespace PicView.Avalonia.CustomControls;
+
+/// <summary>
+/// A custom button control that displays text followed by an icon, which can be either a <see cref="DrawingImage"/>
+/// or a <see cref="StreamGeometry"/>. It also supports dynamic brush changes to reflect hover states.
+/// </summary>
+public class TextIconButton : Button
+{
+    /// <summary>
+    /// Defines the <see cref="Icon"/> property.
+    /// The icon is displayed as a <see cref="DrawingImage"/> with support for dynamic brush changes.
+    /// </summary>
+    public static readonly AvaloniaProperty<DrawingImage?> IconProperty =
+        AvaloniaProperty.Register<TextIconButton, DrawingImage?>(nameof(Icon));
+
+    /// <summary>
+    /// Defines the <see cref="Data"/> property.
+    /// The icon can also be displayed as a <see cref="StreamGeometry"/> for path-based rendering.
+    /// </summary>
+    public static readonly AvaloniaProperty<StreamGeometry> PathProperty =
+        AvaloniaProperty.Register<TextIconButton, StreamGeometry>(nameof(Data));
+
+    /// <summary>
+    /// Defines the <see cref="IconWidth"/> property.
+    /// The width of the icon, whether it is a <see cref="DrawingImage"/> or <see cref="StreamGeometry"/>.
+    /// </summary>
+    public static readonly AvaloniaProperty<double> IconWidthProperty =
+        AvaloniaProperty.Register<TextIconButton, double>(nameof(IconWidth));
+
+    /// <summary>
+    /// Defines the <see cref="IconHeight"/> property.
+    /// The height of the icon, whether it is a <see cref="DrawingImage"/> or <see cref="StreamGeometry"/>.
+    /// </summary>
+    public static readonly AvaloniaProperty<double> IconHeightProperty =
+        AvaloniaProperty.Register<TextIconButton, double>(nameof(IconHeight));
+
+    /// <summary>
+    /// Defines the maximum width for the text content of the button.
+    /// This property restricts the width that the text within the button can occupy, potentially affecting text wrapping or truncation.
+    /// </summary>
+    public static readonly AvaloniaProperty<double> TextMaxWidthProperty =
+        AvaloniaProperty.Register<TextIconButton, double>(nameof(TextMaxWidth));
+
+    /// <summary>
+    /// Defines the <see cref="Text"/> property.
+    /// </summary>
+    public static readonly StyledProperty<string?> TextProperty =
+        AvaloniaProperty.Register<TextBlock, string?>(nameof(Text));
+
+    public static readonly StyledProperty<Thickness> IconMarginProperty =
+        AvaloniaProperty.Register<Layoutable, Thickness>(nameof(IconMargin));
+
+    /// <summary>
+    /// Defines the <see cref="TextMargin"/> property.
+    /// Specifies the margin around the text content of the button.
+    /// </summary>
+    public static readonly StyledProperty<Thickness> TextMarginProperty =
+        AvaloniaProperty.Register<Layoutable, Thickness>(nameof(TextMargin));
+
+    /// <summary>
+    /// Overrides the default style key to <see cref="Button"/>.
+    /// </summary>
+    protected override Type StyleKeyOverride => typeof(Button);
+
+    /// <summary>
+    /// Gets or sets the <see cref="DrawingImage"/> displayed as the icon of the button.
+    /// </summary>
+    [Content]
+    public DrawingImage? Icon
+    {
+        get => (DrawingImage?)GetValue(IconProperty);
+        set => SetValue(IconProperty, value);
+    }
+
+    /// <summary>
+    /// Gets or sets the margin around the icon.
+    /// </summary>
+    public Thickness IconMargin
+    {
+        get { return GetValue(IconMarginProperty); }
+        set { SetValue(IconMarginProperty, value); }
+    }
+
+    /// <summary>
+    /// Defines the <see cref="TextMaxWidth"/> property.
+    /// Specifies the maximum width of the text content within the button.
+    /// </summary>
+    public double? TextMaxWidth
+    {
+        get => (double?)GetValue(TextMaxWidthProperty);
+        set => SetValue(TextMaxWidthProperty, value);
+    }
+
+    /// <summary>
+    /// Gets or sets the margin around the text.
+    /// </summary>
+    public Thickness TextMargin
+    {
+        get { return GetValue(TextMarginProperty); }
+        set { SetValue(TextMarginProperty, value); }
+    }
+
+    /// <summary>
+    /// Gets or sets the text.
+    /// </summary>
+    public string? Text
+    {
+        get => GetValue(TextProperty);
+        set => SetValue(TextProperty, value);
+    }
+
+    /// <summary>
+    /// Gets or sets the <see cref="StreamGeometry"/> used as the icon's path data.
+    /// </summary>
+    public StreamGeometry? Data
+    {
+        get => (StreamGeometry)GetValue(PathProperty)!;
+        set => SetValue(PathProperty, value);
+    }
+
+    /// <summary>
+    /// Gets or sets the width of the icon.
+    /// </summary>
+    public double IconWidth
+    {
+        get => (double)GetValue(IconWidthProperty)!;
+        set => SetValue(IconWidthProperty, value);
+    }
+
+    /// <summary>
+    /// Gets or sets the height of the icon.
+    /// </summary>
+    public double IconHeight
+    {
+        get => (double)GetValue(IconHeightProperty)!;
+        set => SetValue(IconHeightProperty, value);
+    }
+
+    /// <summary>
+    /// Called when the control is added to a visual tree. Initializes the content of the button with the icon.
+    /// </summary>
+    /// <param name="e">The event data associated with attaching the visual tree.</param>
+    protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+    {
+        base.OnAttachedToVisualTree(e);
+        Content = BuildControl();
+    }
+
+    protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+    {
+        base.OnPropertyChanged(change);
+
+        if (change.Property == IconProperty)
+        {
+            Content = BuildControl();
+        }
+
+        if (change.Property == IsPressedProperty && change.GetNewValue<bool>() == false)
+        {
+            StopTimer();
+        }
+    }
+
+    private StackPanel BuildControl()
+    {
+        var stackPanel = new StackPanel
+        {
+            Orientation = Orientation.Horizontal
+        };
+        var icon = BuildIcon();
+        icon.Margin = IconMargin;
+        var textBlock = BuildText();
+        stackPanel.Children.Add(icon);
+        stackPanel.Children.Add(textBlock);
+        return stackPanel;
+    }
+
+    private TextBlock BuildText()
+    {
+        var textBlock = new TextBlock
+        {
+            Margin = TextMargin,
+            Text = Text,
+            VerticalAlignment = VerticalAlignment.Center,
+            FontSize = FontSize,
+            Foreground = Foreground
+        };
+        PointerEntered += (_, _) =>
+        {
+            if (!Application.Current.TryGetResource("SecondaryTextColor",
+                    Application.Current.RequestedThemeVariant, out var secondaryAccentColor))
+            {
+                return;
+            }
+
+            if (secondaryAccentColor is Color color)
+            {
+                textBlock.Foreground = new SolidColorBrush(color);
+            }
+        };
+        PointerExited += (_, _) =>
+        {
+            if (!Application.Current.TryGetResource("MainTextColor", Application.Current.RequestedThemeVariant,
+                    out var mainTextColor))
+            {
+                return;
+            }
+
+            if (mainTextColor is Color color)
+            {
+                textBlock.Foreground = new SolidColorBrush(color);
+            }
+        };
+        textBlock.Classes.Add("txt");
+        return textBlock;
+    }
+
+    /// <summary>
+    /// Builds the icon for the button, either from a <see cref="DrawingImage"/> or a <see cref="StreamGeometry"/>.
+    /// It also sets up dynamic brush updates on mouse hover.
+    /// </summary>
+    /// <returns>A <see cref="Control"/> representing the icon, or <c>null</c> if no icon is set.</returns>
+    private Control? BuildIcon()
+    {
+        if (Icon is { Drawing: DrawingGroup drawingGroup })
+        {
+            // Set the initial pen brush to match the Foreground color
+            foreach (var drawing in drawingGroup.Children)
+            {
+                if (drawing is not GeometryDrawing { Pen: Pen pen })
+                {
+                    continue;
+                }
+
+                if (Settings.Theme.GlassTheme)
+                {
+                    if (!Application.Current.TryGetResource("SecondaryTextColor",
+                            Application.Current.RequestedThemeVariant, out var secondaryAccentColor))
+                    {
+                        continue;
+                    }
+
+                    if (secondaryAccentColor is Color color)
+                    {
+                        pen.Brush = new SolidColorBrush(color);
+                    }
+                }
+                else
+                {
+                    pen.Brush = Foreground;
+                }
+            }
+
+            var image = new Image
+            {
+                Source = Icon,
+                Width = IconWidth,
+                Height = IconHeight
+            };
+
+            // Change brush to secondary accent color on pointer enter
+            PointerEntered += delegate
+            {
+                Dispatcher.UIThread.InvokeAsync(() =>
+                {
+                    if (!Application.Current.TryGetResource("SecondaryTextColor",
+                            Application.Current.RequestedThemeVariant, out var secondaryAccentColor))
+                    {
+                        return;
+                    }
+#if DEBUG
+                    Debug.Assert(secondaryAccentColor != null, nameof(secondaryAccentColor) + " != null");
+#endif
+                    foreach (var drawing in drawingGroup.Children)
+                    {
+                        if (drawing is GeometryDrawing { Pen: Pen pen })
+                        {
+                            pen.Brush = new SolidColorBrush((Color)secondaryAccentColor);
+                        }
+                    }
+                });
+            };
+
+            // Revert brush to main text color on pointer exit
+            PointerExited += delegate
+            {
+                if (Settings.Theme.GlassTheme)
+                {
+                    return;
+                }
+
+                Dispatcher.UIThread.InvokeAsync(() =>
+                {
+                    foreach (var drawing in drawingGroup.Children)
+                    {
+                        if (drawing is GeometryDrawing { Pen: Pen pen })
+                        {
+                            pen.Brush = Foreground;
+                        }
+                    }
+                });
+            };
+
+            return image;
+        }
+
+        // If no DrawingImage, use PathIcon
+        // Make sure button has the hover class and the Foreground property is set
+        if (Data is null)
+        {
+            return null;
+        }
+
+        var pathIcon = new PathIcon
+        {
+            Data = Data,
+            Width = IconWidth,
+            Height = IconHeight
+        };
+
+        PointerEntered += delegate
+        {
+            Dispatcher.UIThread.InvokeAsync(() =>
+            {
+                if (!Application.Current.TryGetResource("SecondaryTextColor",
+                        Application.Current.RequestedThemeVariant, out var secondaryAccentColor))
+                {
+                    return;
+                }
+#if DEBUG
+                Debug.Assert(secondaryAccentColor != null, nameof(secondaryAccentColor) + " != null");
+#endif
+
+                if (secondaryAccentColor is Color color)
+                {
+                    pathIcon.Foreground = new SolidColorBrush(color);
+                }
+            });
+        };
+
+        PointerExited += (_, _) => { pathIcon.Foreground = Foreground; };
+
+        return pathIcon;
+    }
+
+    #region Repeat
+
+    /// <summary>
+    /// Defines the <see cref="Interval"/> property.
+    /// </summary>
+    public static readonly StyledProperty<int> IntervalProperty =
+        AvaloniaProperty.Register<RepeatButton, int>(nameof(Interval), 100);
+
+    /// <summary>
+    /// Defines the <see cref="Delay"/> property.
+    /// </summary>
+    public static readonly StyledProperty<int> DelayProperty =
+        AvaloniaProperty.Register<RepeatButton, int>(nameof(Delay), 300);
+
+    private DispatcherTimer? _repeatTimer;
+
+    /// <summary>
+    /// Gets or sets the amount of time, in milliseconds, of repeating clicks.
+    /// </summary>
+    public int Interval
+    {
+        get => GetValue(IntervalProperty);
+        set => SetValue(IntervalProperty, value);
+    }
+
+    /// <summary>
+    /// Gets or sets the amount of time, in milliseconds, to wait before repeating begins.
+    /// </summary>
+    public int Delay
+    {
+        get => GetValue(DelayProperty);
+        set => SetValue(DelayProperty, value);
+    }
+
+    public static readonly StyledProperty<bool> IsRepeatEnabledProperty =
+        AvaloniaProperty.Register<RepeatButton, bool>(nameof(IsRepeatEnabled), true);
+
+    public bool IsRepeatEnabled
+    {
+        get => GetValue(IsRepeatEnabledProperty);
+        set => SetValue(IsRepeatEnabledProperty, value);
+    }
+
+    private void StartTimer()
+    {
+        if (!IsRepeatEnabled)
+        {
+            return;
+        }
+
+        if (_repeatTimer == null)
+        {
+            _repeatTimer = new DispatcherTimer();
+            _repeatTimer.Tick += RepeatTimerOnTick;
+        }
+
+        if (_repeatTimer.IsEnabled)
+        {
+            return;
+        }
+
+        _repeatTimer.Interval = TimeSpan.FromMilliseconds(Delay);
+        _repeatTimer.Start();
+    }
+
+    private void RepeatTimerOnTick(object? sender, EventArgs e)
+    {
+        if (!IsRepeatEnabled)
+        {
+            return;
+        }
+
+        var interval = TimeSpan.FromMilliseconds(Interval);
+        if (_repeatTimer!.Interval != interval)
+        {
+            _repeatTimer.Interval = interval;
+        }
+
+        OnClick();
+    }
+
+    private void StopTimer()
+    {
+        _repeatTimer?.Stop();
+    }
+
+    protected override void OnKeyDown(KeyEventArgs e)
+    {
+        base.OnKeyDown(e);
+
+        if (e.Key == Key.Space)
+        {
+            StartTimer();
+        }
+    }
+
+    protected override void OnKeyUp(KeyEventArgs e)
+    {
+        base.OnKeyUp(e);
+
+        StopTimer();
+    }
+
+    protected override void OnPointerPressed(PointerPressedEventArgs e)
+    {
+        base.OnPointerPressed(e);
+
+        if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+        {
+            StartTimer();
+        }
+    }
+
+    protected override void OnPointerReleased(PointerReleasedEventArgs e)
+    {
+        base.OnPointerReleased(e);
+
+        if (e.InitialPressMouseButton == MouseButton.Left)
+        {
+            StopTimer();
+        }
+    }
+
+    #endregion
+}

+ 10 - 13
src/PicView.Avalonia/PicViewTheme/Controls/AutoCompleteBox.axaml

@@ -19,6 +19,8 @@
                         MaxLength="{TemplateBinding MaxLength}"
                         Name="PART_TextBox"
                         Padding="{TemplateBinding Padding}"
+                        SelectionBrush="{DynamicResource AccentColor}"
+                        SelectionForegroundBrush="{StaticResource SecondaryTextColor}"
                         Watermark="{TemplateBinding Watermark}" />
 
                     <Popup
@@ -27,19 +29,14 @@
                         MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
                         Name="PART_Popup"
                         PlacementTarget="{TemplateBinding}">
-                        <Border
-                            Background="{DynamicResource ThemeBackgroundBrush}"
-                            BorderBrush="{DynamicResource ThemeBorderMidBrush}"
-                            BorderThickness="1">
-                            <ListBox
-                                Background="{TemplateBinding Background}"
-                                BorderThickness="0"
-                                Foreground="{TemplateBinding Foreground}"
-                                ItemTemplate="{TemplateBinding ItemTemplate}"
-                                Name="PART_SelectingItemsControl"
-                                ScrollViewer.HorizontalScrollBarVisibility="Auto"
-                                ScrollViewer.VerticalScrollBarVisibility="Auto" />
-                        </Border>
+                        <ListBox
+                            Background="{TemplateBinding Background}"
+                            BorderThickness="0"
+                            Foreground="{TemplateBinding Foreground}"
+                            ItemTemplate="{TemplateBinding ItemTemplate}"
+                            Name="PART_SelectingItemsControl"
+                            ScrollViewer.HorizontalScrollBarVisibility="Auto"
+                            ScrollViewer.VerticalScrollBarVisibility="Auto" />
                     </Popup>
                 </Panel>
             </ControlTemplate>

+ 27 - 0
src/PicView.Avalonia/PicViewTheme/Controls/Button.axaml

@@ -135,6 +135,7 @@
             <Setter Property="Background" Value="{DynamicResource AltBackgroundHoverColor}" />
             <Setter Property="BorderBrush" Value="{DynamicResource SecondaryBorderColor}" />
         </Style>
+
         <Style Selector="^.noBorderHover:pointerover /template/ ContentPresenter#PART_ContentPresenter">
             <Style.Animations>
                 <Animation Duration="{StaticResource ButtonHoverAnimationDuration}" IterationCount="1">
@@ -152,6 +153,32 @@
             </Style.Animations>
             <Setter Property="Background" Value="{DynamicResource BackgroundHoverColor}" />
         </Style>
+
+        <Style Selector="^.noBorderHoverAlt:pointerover /template/ ContentPresenter#PART_ContentPresenter">
+            <Style.Animations>
+                <Animation Duration="{StaticResource ButtonHoverAnimationDuration}" IterationCount="1">
+                    <KeyFrame>
+                        <Setter Property="Background">
+                            <SolidColorBrush Color="{DynamicResource MainButtonBackgroundColor}" />
+                        </Setter>
+                        <Setter Property="Foreground">
+                            <SolidColorBrush Color="{DynamicResource MainTextColor}" />
+                        </Setter>
+                    </KeyFrame>
+                    <KeyFrame Cue="1">
+                        <Setter Property="Background">
+                            <SolidColorBrush Color="{DynamicResource SecondaryAccentColor}" />
+                        </Setter>
+                        <Setter Property="Foreground">
+                            <SolidColorBrush Color="{StaticResource SecondaryTextColor}" />
+                        </Setter>
+                    </KeyFrame>
+                </Animation>
+            </Style.Animations>
+            <Setter Property="Background" Value="{DynamicResource SecondaryAccentColor}" />
+            <Setter Property="Foreground" Value="{StaticResource SecondaryTextColor}" />
+        </Style>
+
         <Style Selector="^.foregroundHover:pointerover /template/ ContentPresenter#PART_ContentPresenter">
             <Style.Animations>
                 <Animation Duration="{StaticResource ButtonHoverAnimationDuration}" IterationCount="1">

+ 67 - 1
src/PicView.Avalonia/PicViewTheme/Controls/SplitButton.axaml

@@ -141,6 +141,69 @@
             </ControlTemplate>
         </Setter>
 
+        <Style Selector="^.hover:pointerover /template/ Button#PART_PrimaryButton">
+            <Style.Animations>
+                <Animation Duration="{StaticResource ButtonHoverAnimationDuration}" IterationCount="1">
+                    <KeyFrame>
+                        <Setter Property="Foreground">
+                            <SolidColorBrush Color="{DynamicResource MainTextColor}" />
+                        </Setter>
+                        <Setter Property="Background">
+                            <SolidColorBrush Color="{DynamicResource SecondaryBackgroundColor}" />
+                        </Setter>
+                        <Setter Property="BorderBrush">
+                            <SolidColorBrush Color="{DynamicResource MainBorderColor}" />
+                        </Setter>
+                    </KeyFrame>
+                    <KeyFrame Cue="1">
+                        <Setter Property="Foreground">
+                            <SolidColorBrush Color="{StaticResource SecondaryTextColor}" />
+                        </Setter>
+                        <Setter Property="Background">
+                            <SolidColorBrush Color="{DynamicResource AccentColor}" />
+                        </Setter>
+                        <Setter Property="BorderBrush">
+                            <SolidColorBrush Color="{DynamicResource AccentColor}" />
+                        </Setter>
+                    </KeyFrame>
+                </Animation>
+            </Style.Animations>
+            <Setter Property="Foreground" Value="{StaticResource SecondaryTextColor}" />
+            <Setter Property="Background" Value="{DynamicResource AccentColor}" />
+            <Setter Property="BorderBrush" Value="{DynamicResource AccentColor}" />
+        </Style>
+        <Style Selector="^.hover:pointerover /template/ Button#PART_SecondaryButton">
+            <Style.Animations>
+                <Animation Duration="{StaticResource ButtonHoverAnimationDuration}" IterationCount="1">
+                    <KeyFrame>
+                        <Setter Property="Foreground">
+                            <SolidColorBrush Color="{DynamicResource MainTextColor}" />
+                        </Setter>
+                        <Setter Property="Background">
+                            <SolidColorBrush Color="{DynamicResource SecondaryBackgroundColor}" />
+                        </Setter>
+                        <Setter Property="BorderBrush">
+                            <SolidColorBrush Color="{DynamicResource MainBorderColor}" />
+                        </Setter>
+                    </KeyFrame>
+                    <KeyFrame Cue="1">
+                        <Setter Property="Foreground">
+                            <SolidColorBrush Color="{StaticResource SecondaryTextColor}" />
+                        </Setter>
+                        <Setter Property="Background">
+                            <SolidColorBrush Color="{DynamicResource SecondaryAccentColor}" />
+                        </Setter>
+                        <Setter Property="BorderBrush">
+                            <SolidColorBrush Color="{DynamicResource SecondaryAccentColor}" />
+                        </Setter>
+                    </KeyFrame>
+                </Animation>
+            </Style.Animations>
+            <Setter Property="Foreground" Value="{StaticResource SecondaryTextColor}" />
+            <Setter Property="Background" Value="{DynamicResource SecondaryAccentColor}" />
+            <Setter Property="BorderBrush" Value="{DynamicResource SecondaryAccentColor}" />
+        </Style>
+
         <Style Selector="^.altHover:pointerover /template/ Button#PART_PrimaryButton">
             <Style.Animations>
                 <Animation Duration="{StaticResource ButtonHoverAnimationDuration}" IterationCount="1">
@@ -273,5 +336,8 @@
         <Style Selector="^.up /template/ PathIcon#PART_ChevronIcon">
             <Setter Property="Data" Value="{StaticResource ChevronUpGeometry}" />
         </Style>
+        <Style Selector="^.up:pointerover /template/ PathIcon#PART_ChevronIcon">
+            <Setter Property="Foreground" Value="{StaticResource SecondaryTextColor}" />
+        </Style>
     </ControlTheme>
-</ResourceDictionary>
+</ResourceDictionary>

+ 23 - 0
src/PicView.Avalonia/PicViewTheme/Controls/ToggleButton.axaml

@@ -89,6 +89,10 @@
             </Style.Animations>
             <Setter Property="Background" Value="{DynamicResource AccentColor}" />
         </Style>
+        <Style Selector="^.hover:pointerover /template/ Image#TickMark">
+            <Setter Property="Source" Value="{DynamicResource SquareCheckAltImage}" />
+        </Style>
+
         <Style Selector="^.altHover:pointerover /template/ Border#border">
             <Style.Animations>
                 <Animation Duration="{StaticResource ButtonHoverAnimationDuration}" IterationCount="1">
@@ -113,6 +117,25 @@
             <Setter Property="Background" Value="{DynamicResource AltBackgroundHoverColor}" />
             <Setter Property="BorderBrush" Value="{DynamicResource SecondaryBorderColor}" />
         </Style>
+
+        <Style Selector="^.foregroundHover:pointerover /template/ ContentPresenter#PART_ContentPresenter">
+            <Style.Animations>
+                <Animation Duration="{StaticResource ButtonHoverAnimationDuration}" IterationCount="1">
+                    <KeyFrame>
+                        <Setter Property="Foreground">
+                            <SolidColorBrush Color="{DynamicResource MainTextColor}" />
+                        </Setter>
+                    </KeyFrame>
+                    <KeyFrame Cue="1">
+                        <Setter Property="Foreground">
+                            <SolidColorBrush Color="{DynamicResource AccentColor}" />
+                        </Setter>
+                    </KeyFrame>
+                </Animation>
+            </Style.Animations>
+            <Setter Property="Foreground" Value="{DynamicResource AccentColor}" />
+        </Style>
+
         <Style Selector="^.noBorderHover:pointerover /template/ Border#border">
             <Style.Animations>
                 <Animation Duration="{StaticResource ButtonHoverAnimationDuration}" IterationCount="1">

文件差异内容过多而无法显示
+ 23 - 0
src/PicView.Avalonia/PicViewTheme/Icons.axaml


+ 3 - 3
src/PicView.Avalonia/PicViewTheme/ResourceDictionaries/MainColors.axaml

@@ -5,11 +5,11 @@
 
     <ResourceDictionary.ThemeDictionaries>
         <ResourceDictionary x:Key="Default">
-            <Color x:Key="MainTextColor">#FFf6f4f4</Color>
+            <Color x:Key="MainTextColor">#E2E0E0</Color>
             <Color x:Key="MainTextColorFaded">#d6d4d4</Color>
 
             <!--  Used for icons that should stay white in both themes  -->
-            <Color x:Key="SecondaryTextColor">#FFf6f4f4</Color>
+            <Color x:Key="SecondaryTextColor">#fff</Color>
 
             <Color x:Key="MainBackgroundColor">#CC2b2b2b</Color>
             <Color x:Key="SecondaryBackgroundColor">#CC252525</Color>
@@ -94,7 +94,7 @@
             <Color x:Key="MainButtonBackgroundColor">#fff</Color>
             <Color x:Key="SecondaryButtonBackgroundColor">#F1E1E1E1</Color>
 
-            <Color x:Key="MainBorderColor">#78B5B5B5</Color>
+            <Color x:Key="MainBorderColor">#DEDEDE</Color>
             <Color x:Key="SecondaryBorderColor">#95B5B5B5</Color>
             <Color x:Key="TertiaryBorderColor">#77D8D8D8</Color>
 

+ 42 - 36
src/PicView.Avalonia/StartUp/StartUpHelper.cs

@@ -22,7 +22,8 @@ namespace PicView.Avalonia.StartUp;
 
 public static class StartUpHelper
 {
-    public static void StartWithArguments(MainViewModel vm, bool settingsExists, IClassicDesktopStyleApplicationLifetime desktop,
+    public static void StartWithArguments(MainViewModel vm, bool settingsExists,
+        IClassicDesktopStyleApplicationLifetime desktop,
         Window window)
     {
         var args = Environment.GetCommandLineArgs();
@@ -59,27 +60,28 @@ public static class StartUpHelper
                 }
             }
         }
-        
+
         SettingsUpdater.InitializeSettings(vm);
-        
+
         HandleWindowScalingMode(vm, window);
-        
+
         HandleStartUpMenuOrImage(vm, args);
         window.Show();
-        
+
         HandlePostWindowUpdates(vm, settingsExists, desktop, window);
     }
-    
-    public static void StartWithoutArguments(MainViewModel vm, bool settingsExists, IClassicDesktopStyleApplicationLifetime desktop,
+
+    public static void StartWithoutArguments(MainViewModel vm, bool settingsExists,
+        IClassicDesktopStyleApplicationLifetime desktop,
         Window window, string? arg = null)
     {
         SettingsUpdater.InitializeSettings(vm);
-        
+
         HandleWindowScalingMode(vm, window);
-        
+
         HandleStartUpMenuOrImage(vm, arg);
         window.Show();
-        
+
         HandlePostWindowUpdates(vm, settingsExists, desktop, window);
     }
 
@@ -91,6 +93,7 @@ public static class StartUpHelper
         {
             Settings.WindowProperties.Margin = 45;
         }
+
         if (Settings.WindowProperties.AutoFit)
         {
             HandleAutoFit(vm, window);
@@ -101,10 +104,11 @@ public static class StartUpHelper
         }
     }
 
-    private static void HandlePostWindowUpdates(MainViewModel vm, bool settingsExists, IClassicDesktopStyleApplicationLifetime desktop, Window window)
+    private static void HandlePostWindowUpdates(MainViewModel vm, bool settingsExists,
+        IClassicDesktopStyleApplicationLifetime desktop, Window window)
     {
         ResourceLimits.LimitMemory(new Percentage(90));
-        
+
         Task.Run(() => LanguageUpdater.UpdateLanguageAsync(vm.Translation, vm.PicViewer, settingsExists));
         if (settingsExists)
         {
@@ -114,51 +118,45 @@ public static class StartUpHelper
         {
             Task.Run(() => KeybindingManager.SetDefaultKeybindings(vm.PlatformService));
         }
-        
+
         Task.Run(FileHistoryManager.InitializeAsync);
 
         HandleThemeUpdates(vm);
-        
+
         UIHelper.SetControls(desktop);
         Task.Run(() =>
         {
             HandleWindowControlSettings(vm, desktop);
             SettingsUpdater.ValidateGallerySettings(vm, settingsExists);
         });
-        
+
         vm.MainWindow.LayoutButtonSubscription();
-        
+
         SetWindowEventHandlers(window);
         MenuManager.AddMenus();
-        
+
         if (!Settings.WindowProperties.AutoFit)
         {
             // Need to update the screen size after the window is shown,
             // to avoid rendering error when switching between auto-fit
             ScreenHelper.UpdateScreenSize(window);
         }
-        
+
         // Need to delay setting fullscreen or maximized until after the window is shown to select the correct monitor
         if (Settings.WindowProperties.Maximized && !Settings.WindowProperties.Fullscreen)
         {
-            Dispatcher.UIThread.InvokeAsync(() =>
-            {
-                vm.PlatformWindowService.Maximize(false);
-            }, DispatcherPriority.Background).Wait();
+            Dispatcher.UIThread
+                .InvokeAsync(() => { vm.PlatformWindowService.Maximize(false); }, DispatcherPriority.Background).Wait();
         }
         else if (Settings.WindowProperties.Fullscreen)
         {
-            Dispatcher.UIThread.InvokeAsync(() =>
-            {
-                vm.PlatformWindowService.Fullscreen(false);
-            }, DispatcherPriority.Background).Wait();
+            Dispatcher.UIThread.InvokeAsync(() => { vm.PlatformWindowService.Fullscreen(false); },
+                DispatcherPriority.Background).Wait();
         }
         else if (Settings.WindowProperties.AutoFit && Settings.ImageScaling.ShowImageSideBySide)
         {
-            Dispatcher.UIThread.InvokeAsync(() =>
-            {
-                WindowFunctions.CenterWindowOnScreen();
-            }, DispatcherPriority.Background).Wait();
+            Dispatcher.UIThread
+                .InvokeAsync(() => { WindowFunctions.CenterWindowOnScreen(); }, DispatcherPriority.Background).Wait();
         }
 
         Application.Current.Name = "PicView";
@@ -205,27 +203,33 @@ public static class StartUpHelper
     private static void HandleStartUpMenuOrImage(MainViewModel vm, string[] args)
     {
         vm.ImageViewer = new ImageViewer();
-        
+
         if (args.Length > 1)
         {
             vm.MainWindow.CurrentView.Value = vm.ImageViewer;
             Task.Run(() => QuickLoad.QuickLoadAsync(vm, args[1]));
         }
-        else StartUpMenuOrLastFile(vm);
+        else
+        {
+            StartUpMenuOrLastFile(vm);
+        }
     }
 
     private static void HandleStartUpMenuOrImage(MainViewModel vm, string? arg = null)
     {
         vm.ImageViewer = new ImageViewer();
-        
+
         if (arg is not null)
         {
             vm.MainWindow.CurrentView.Value = vm.ImageViewer;
             Task.Run(() => QuickLoad.QuickLoadAsync(vm, arg));
         }
-        else StartUpMenuOrLastFile(vm);
+        else
+        {
+            StartUpMenuOrLastFile(vm);
+        }
     }
-    
+
     private static void StartUpMenuOrLastFile(MainViewModel vm)
     {
         if (Settings.StartUp.OpenLastFile)
@@ -270,6 +274,7 @@ public static class StartUpHelper
             vm.MainWindow.IsTopToolbarShown.Value = true;
             vm.MainWindow.IsBottomToolbarShown.Value = Settings.UIProperties.ShowBottomNavBar;
         }
+
         WindowFunctions.InitializeWindowSizeAndPosition(window);
     }
 
@@ -283,6 +288,7 @@ public static class StartUpHelper
             vm.MainWindow.IsTopToolbarShown.Value = true;
             vm.MainWindow.IsBottomToolbarShown.Value = Settings.UIProperties.ShowBottomNavBar;
         }
+
         window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
     }
 
@@ -291,7 +297,7 @@ public static class StartUpHelper
         w.KeyDown += async (_, e) => await MainKeyboardShortcuts.MainWindow_KeysDownAsync(e).ConfigureAwait(false);
         w.KeyUp += (_, e) => MainKeyboardShortcuts.MainWindow_KeysUp(e);
         w.PointerPressed += async (_, e) => await MouseShortcuts.MainWindow_PointerPressed(e).ConfigureAwait(false);
-        
+
         w.Deactivated += delegate
         {
             MainKeyboardShortcuts.Reset();

+ 62 - 15
src/PicView.Avalonia/UI/UIHelper.cs

@@ -1,6 +1,8 @@
 using System.Diagnostics.CodeAnalysis;
+using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Media;
 using Avalonia.Threading;
 using PicView.Avalonia.CustomControls;
 using PicView.Avalonia.Gallery;
@@ -25,9 +27,11 @@ public static class UIHelper
     public static GalleryAnimationControlView? GetGalleryView { get; private set; }
     public static BottomBar? GetBottomBar { get; private set; }
     public static ToolTipMessage? GetToolTipMessage { get; private set; }
-    
-    public static AvaloniaRenderingFrameProvider ? GetFrameProvider { get; private set; }
-    public static void SetFrameProvider(AvaloniaRenderingFrameProvider frameProvider) => GetFrameProvider = frameProvider;
+
+    public static AvaloniaRenderingFrameProvider? GetFrameProvider { get; private set; }
+
+    public static void SetFrameProvider(AvaloniaRenderingFrameProvider frameProvider) =>
+        GetFrameProvider = frameProvider;
 
     /// <summary>
     /// Sets up control references from the main desktop application
@@ -45,13 +49,13 @@ public static class UIHelper
     #endregion
 
     #region Helper functions
-    
+
     public static bool TryGetMainViewModel([NotNullWhen(true)] out MainViewModel? vm)
     {
         vm = GetMainView.DataContext as MainViewModel;
         return vm is not null;
     }
-    
+
     /// <summary>
     /// Centers the window or gallery based on current state
     /// </summary>
@@ -61,7 +65,7 @@ public static class UIHelper
         {
             return;
         }
-        
+
         if (GalleryFunctions.IsFullGalleryOpen)
         {
             GalleryFunctions.CenterGallery(vm);
@@ -71,7 +75,7 @@ public static class UIHelper
             WindowFunctions.CenterWindowOnScreen();
         }
     }
-    
+
     /// <inheritdoc cref="Center"/>
     public static async Task CenterAsync(MainViewModel? vm)
     {
@@ -80,10 +84,7 @@ public static class UIHelper
             return;
         }
 
-        await Dispatcher.UIThread.InvokeAsync(() =>
-        {
-            Center(vm);
-        });
+        await Dispatcher.UIThread.InvokeAsync(() => { Center(vm); });
     }
 
     /// <summary>
@@ -107,7 +108,7 @@ public static class UIHelper
             await Dispatcher.UIThread.InvokeAsync(() => { GetGalleryView.GalleryListBox.ScrollToHome(); });
         }
     }
-    
+
     public static void SetButtonInterval(RepeatButton? button)
     {
         if (button != null)
@@ -115,14 +116,60 @@ public static class UIHelper
             button.Interval = (int)TimeSpan.FromSeconds(Settings.UIProperties.NavSpeed).TotalMilliseconds;
         }
     }
-    
-    public static void SetButtonInterval(IconButton? button)   
+
+    public static void SetButtonInterval(IconButton? button)
     {
         if (button != null)
         {
             button.Interval = (int)TimeSpan.FromSeconds(Settings.UIProperties.NavSpeed).TotalMilliseconds;
         }
     }
-    
+
+    public static void SetButtonHover(Control button, SolidColorBrush brush)
+    {
+        button.PointerEntered += (_, _) =>
+        {
+            if (!Application.Current.TryGetResource("SecondaryTextColor",
+                    Application.Current.RequestedThemeVariant, out var textColor))
+            {
+                return;
+            }
+
+            if (textColor is not Color color)
+            {
+                return;
+            }
+
+            brush.Color = color;
+        };
+        button.PointerExited += (s, e) =>
+        {
+            if (!Application.Current.TryGetResource("MainTextColor",
+                    Application.Current.RequestedThemeVariant, out var textColor))
+            {
+                return;
+            }
+
+            if (textColor is not Color color)
+            {
+                return;
+            }
+
+            brush.Color = color;
+        };
+    }
+
+    public static void SwitchHoverClass(Control control)
+    {
+        control.Classes.Remove("altHover");
+        control.Classes.Add("hover");
+    }
+
+    public static void SwitchHoverBorderClass(Control control)
+    {
+        control.Classes.Remove("noBorderHover");
+        control.Classes.Add("hover");
+    }
+
     #endregion
 }

+ 104 - 115
src/PicView.Avalonia/ViewModels/ToolsViewModel.cs

@@ -16,23 +16,18 @@ public class ToolsViewModel : IDisposable
     {
         await FunctionsMapper.ShowSettingsFile();
     });
-        
+
     public ReactiveCommand ShowKeybindingsFileCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.ShowSettingsFile();
     });
-    
+
     // Open related
-    public ReactiveCommand OpenFileCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.Open();
-    });
-    
-    public ReactiveCommand OpenLastFileCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.OpenLastFile();
-    });
-    
+    public ReactiveCommand OpenFileCommand { get; } = new(async (_, _) => { await FunctionsMapper.Open(); });
+
+    public ReactiveCommand OpenLastFileCommand { get; } =
+        new(async (_, _) => { await FunctionsMapper.OpenLastFile(); });
+
     public ReactiveCommand<string> OpenWithCommand { get; } = new(async (path, _) =>
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
@@ -40,34 +35,32 @@ public class ToolsViewModel : IDisposable
             await FileManager.OpenWith(path, vm).ConfigureAwait(false);
         }
     });
+
     // Save related
-    public ReactiveCommand<string> SaveFileCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.Save();
-    });
-    
-    public ReactiveCommand<string> SaveFileAsCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.SaveAs();
-    });
-    
+    public ReactiveCommand<string> SaveFileCommand { get; } = new(async (_, _) => { await FunctionsMapper.Save(); });
+
+    public ReactiveCommand<string> SaveFileAsCommand { get; } =
+        new(async (_, _) => { await FunctionsMapper.SaveAs(); });
+
     // File Tasks
     public ReactiveCommand<string> RecycleFileCommand { get; } = new(async (path, _) =>
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
         {
-            await Task.Run(() => FileManager.DeleteFileWithOptionalDialog(true, path, vm.PlatformService)).ConfigureAwait(false);
+            await Task.Run(() => FileManager.DeleteFileWithOptionalDialog(true, path, vm.PlatformService))
+                .ConfigureAwait(false);
         }
     });
-    
+
     public ReactiveCommand<string> DeleteFilePermanentlyCommand { get; } = new(async (path, _) =>
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
         {
-            await Task.Run(() => FileManager.DeleteFileWithOptionalDialog(false, path, vm.PlatformService)).ConfigureAwait(false);
+            await Task.Run(() => FileManager.DeleteFileWithOptionalDialog(false, path, vm.PlatformService))
+                .ConfigureAwait(false);
         }
     });
-    
+
     public ReactiveCommand<string> LocateOnDiskCommand { get; } = new(async (path, _) =>
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
@@ -75,7 +68,7 @@ public class ToolsViewModel : IDisposable
             await FileManager.LocateOnDisk(path, vm).ConfigureAwait(false);
         }
     });
-    
+
     public ReactiveCommand<string> FilePropertiesCommand { get; } = new(async (path, _) =>
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
@@ -83,7 +76,7 @@ public class ToolsViewModel : IDisposable
             await FileManager.ShowFileProperties(path, vm).ConfigureAwait(false);
         }
     });
-    
+
     public ReactiveCommand<string> PrintCommand { get; } = new(async (path, _) =>
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
@@ -91,19 +84,19 @@ public class ToolsViewModel : IDisposable
             await FileManager.Print(path, vm).ConfigureAwait(false);
         }
     });
-    
+
     public ReactiveCommand<string> RenameCommand { get; } = new(async (_, _) =>
     {
         await Task.Run(FunctionsMapper.Rename);
     });
 
-    
+
     // Copy related
     public ReactiveCommand<string> PasteCommand { get; } = new(async (_, _) =>
     {
         await Task.Run(FunctionsMapper.Paste);
     });
-    
+
     public ReactiveCommand<string> DuplicateFileCommand { get; } = new(async (path, _) =>
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
@@ -111,12 +104,12 @@ public class ToolsViewModel : IDisposable
             await ClipboardFileOperations.Duplicate(path, vm).ConfigureAwait(false);
         }
     });
-    
+
     public ReactiveCommand<string> CopyImageCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.CopyImage().ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand<string> CopyFileCommand { get; } = new(async (path, _) =>
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
@@ -124,12 +117,12 @@ public class ToolsViewModel : IDisposable
             await ClipboardFileOperations.CopyFileToClipboard(path, vm).ConfigureAwait(false);
         }
     });
-    
+
     public ReactiveCommand<string> CopyFilePathCommand { get; } = new(async (path, _) =>
     {
         await ClipboardTextOperations.CopyTextToClipboard(path).ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand<string> CopyBase64Command { get; } = new(async (path, _) =>
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
@@ -137,18 +130,18 @@ public class ToolsViewModel : IDisposable
             await ClipboardImageOperations.CopyBase64ToClipboard(path, vm).ConfigureAwait(false);
         }
     });
-    
+
     // Settings
     public ReactiveCommand ChangeAutoFitCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.AutoFitWindow().ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand ChangeTopMostCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.SetTopMost().ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand ToggleSubdirectoriesCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.ToggleSubdirectories().ConfigureAwait(false);
@@ -158,160 +151,147 @@ public class ToolsViewModel : IDisposable
     {
         await FunctionsMapper.ToggleLooping().ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand ResetSettingsCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.ResetSettings().ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand RestartCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.Restart().ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand ToggleOpeningInSameWindowCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.ToggleOpeningInSameWindow().ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand ToggleUsingTouchPadCommand { get; } = new(async (_, _) =>
     {
         await SettingsUpdater.ToggleUsingTouchpad(UIHelper.GetMainView.DataContext as MainViewModel);
     });
-    
+
     // UI
-    public ReactiveCommand ToggleUICommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.ToggleInterface();
-    });
-    
+    public ReactiveCommand ToggleUICommand { get; } = new(async (_, _) => { await FunctionsMapper.ToggleInterface(); });
+
     public ReactiveCommand ToggleBottomNavBarCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.ToggleBottomToolbar();
     });
-    
+
     public ReactiveCommand ToggleBottomGalleryShownInHiddenUICommand { get; } = new(async (_, _) =>
     {
-        await HideInterfaceLogic.ToggleBottomGalleryShownInHiddenUI(UIHelper.GetMainView.DataContext as MainViewModel);
+        await HideInterfaceLogic.ToggleBottomGalleryShownInHiddenUI(
+            UIHelper.GetMainView.DataContext as MainViewModel);
     });
-    
+
     public ReactiveCommand ToggleFadeInButtonsOnHoverCommand { get; } = new(async (_, _) =>
     {
         await HideInterfaceLogic.ToggleFadeInButtonsOnHover(UIHelper.GetMainView.DataContext as MainViewModel);
     });
-    
+
     public ReactiveCommand ChangeCtrlZoomCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.ChangeCtrlZoom();
     });
-    
+
     public ReactiveCommand ToggleTaskbarProgressCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.ToggleTaskbarProgress();
     });
-    
+
     public ReactiveCommand ToggleConstrainBackgroundColorCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.ToggleConstrainBackgroundColor();
     });
-    
+
     // Image related
-    public ReactiveCommand RotateLeftCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.RotateLeft();
-    });
+    public ReactiveCommand RotateLeftCommand { get; } = new(async (_, _) => { await FunctionsMapper.RotateLeft(); });
+
     public ReactiveCommand RotateLeftButtonCommand { get; } = new(async (_, _) =>
     {
-        await RotationNavigation.RotateLeft(UIHelper.GetMainView.DataContext as MainViewModel, RotationButton.RotateLeftButton);
-    });
-    
-    public ReactiveCommand RotateRightCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.RotateRight();
+        await RotationNavigation.RotateLeft(UIHelper.GetMainView.DataContext as MainViewModel,
+            RotationButton.RotateLeftButton);
     });
+
+    public ReactiveCommand RotateRightCommand { get; } = new(async (_, _) => { await FunctionsMapper.RotateRight(); });
+
     public ReactiveCommand RotateRightButtonCommand { get; } = new(async (_, _) =>
     {
-        await RotationNavigation.RotateRight(UIHelper.GetMainView.DataContext as MainViewModel, RotationButton.RotateLeftButton);
+        await RotationNavigation.RotateRight(UIHelper.GetMainView.DataContext as MainViewModel,
+            RotationButton.RotateLeftButton);
     });
-    
+
     public ReactiveCommand RotateRightWindowBorderButtonCommand { get; } = new(async (_, _) =>
     {
-        await RotationNavigation.RotateRight(UIHelper.GetMainView.DataContext as MainViewModel, RotationButton.WindowBorderButton);
-    });
-    
-    public ReactiveCommand FlipCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.Flip();
+        await RotationNavigation.RotateRight(UIHelper.GetMainView.DataContext as MainViewModel,
+            RotationButton.WindowBorderButton);
     });
-    
-    public ReactiveCommand StretchCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.Stretch();
-    });
-    
-    public ReactiveCommand CropCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.Crop();
-    });
-    
-    public ReactiveCommand ToggleScrollCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.ToggleScroll();
-    });
-    
+
+    public ReactiveCommand FlipCommand { get; } = new(async (_, _) => { await FunctionsMapper.Flip(); });
+
+    public ReactiveCommand StretchCommand { get; } = new(async (_, _) => { await FunctionsMapper.Stretch(); });
+
+    public ReactiveCommand CropCommand { get; } = new(async (_, _) => { await FunctionsMapper.Crop(); });
+
+    public ReactiveCommand ToggleScrollCommand { get; } =
+        new(async (_, _) => { await FunctionsMapper.ToggleScroll(); });
+
     public ReactiveCommand OptimizeImageCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.OptimizeImage();
     });
-    
+
     public ReactiveCommand ChangeBackgroundCommand { get; } = new(async (_, _) =>
     {
         await FunctionsMapper.ChangeBackground();
     });
-    
-    public ReactiveCommand ShowSideBySideCommand { get; } = new(async (_, _) =>
-    {
-        await FunctionsMapper.SideBySide();
-    });
-    
-    public async Task StartSlideShowTask(int milliseconds) =>
-        await Slideshow.StartSlideshow(UIHelper.GetMainView.DataContext as MainViewModel, milliseconds);
-    
-    public async Task RotateTask(int angle) =>
-        await RotationNavigation.RotateTo(UIHelper.GetMainView.DataContext as MainViewModel, angle);
 
-    public async Task StretchedCommand() =>
-        await SettingsUpdater.ToggleStretch(UIHelper.GetMainView.DataContext as MainViewModel);
-    
+    public ReactiveCommand ShowSideBySideCommand { get; } =
+        new(async (_, _) => { await FunctionsMapper.SideBySide(); });
+
     // Wallpaper
     public ReactiveCommand<string> SetAsWallpaperCommand { get; } = new(async (path, _) =>
     {
-        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Fill, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+        await WallpaperManager
+            .SetAsWallpaper(path, WallpaperStyle.Fill, UIHelper.GetMainView.DataContext as MainViewModel)
+            .ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand<string> SetAsWallpaperTiledCommand { get; } = new(async (path, _) =>
     {
-        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Tile, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+        await WallpaperManager
+            .SetAsWallpaper(path, WallpaperStyle.Tile, UIHelper.GetMainView.DataContext as MainViewModel)
+            .ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand<string> SetAsWallpaperStretchedCommand { get; } = new(async (path, _) =>
     {
-        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Stretch, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+        await WallpaperManager
+            .SetAsWallpaper(path, WallpaperStyle.Stretch, UIHelper.GetMainView.DataContext as MainViewModel)
+            .ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand<string> SetAsWallpaperCenteredCommand { get; } = new(async (path, _) =>
     {
-        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Center, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+        await WallpaperManager
+            .SetAsWallpaper(path, WallpaperStyle.Center, UIHelper.GetMainView.DataContext as MainViewModel)
+            .ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand<string> SetAsWallpaperFilledCommand { get; } = new(async (path, _) =>
     {
-        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Fill, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+        await WallpaperManager
+            .SetAsWallpaper(path, WallpaperStyle.Fill, UIHelper.GetMainView.DataContext as MainViewModel)
+            .ConfigureAwait(false);
     });
-    
+
     public ReactiveCommand<string> SetAsWallpaperFittedCommand { get; } = new(async (path, _) =>
     {
-        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Fit, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+        await WallpaperManager
+            .SetAsWallpaper(path, WallpaperStyle.Fit, UIHelper.GetMainView.DataContext as MainViewModel)
+            .ConfigureAwait(false);
     });
 
     public void Dispose()
@@ -323,4 +303,13 @@ public class ToolsViewModel : IDisposable
             SetAsWallpaperFilledCommand,
             SetAsWallpaperFittedCommand);
     }
+
+    public async void StartSlideShowTask(Unit unit) =>
+        await Slideshow.StartSlideshow(UIHelper.GetMainView.DataContext as MainViewModel);
+
+    public async Task RotateTask(int angle) =>
+        await RotationNavigation.RotateTo(UIHelper.GetMainView.DataContext as MainViewModel, angle);
+
+    public async Task StretchedCommand() =>
+        await SettingsUpdater.ToggleStretch(UIHelper.GetMainView.DataContext as MainViewModel);
 }

文件差异内容过多而无法显示
+ 14 - 8
src/PicView.Avalonia/Views/BottomBar.axaml


+ 67 - 37
src/PicView.Avalonia/Views/BottomBar.axaml.cs

@@ -14,82 +14,112 @@ public partial class BottomBar : UserControl
     public BottomBar()
     {
         InitializeComponent();
-        
+
         Loaded += delegate
         {
             PointerPressed += (_, e) => MoveWindow(e);
-            PointerExited += (_, _) =>
+            PointerExited += (_, _) => { DragAndDropHelper.RemoveDragDropView(); };
+
+            if (DataContext is not MainViewModel vm)
             {
-                DragAndDropHelper.RemoveDragDropView();
+                return;
+            }
+
+            PreviousButton.Click += (_, _) =>
+            {
+                vm.MainWindow.IsNavigationButtonLeftClicked = true;
+                UIHelper.SetButtonInterval(PreviousButton);
+            };
+            NextButton.Click += (_, _) =>
+            {
+                vm.MainWindow.IsNavigationButtonRightClicked = true;
+                UIHelper.SetButtonInterval(NextButton);
             };
 
+            if (!Application.Current.TryGetResource("SecondaryTextColor",
+                    Application.Current.RequestedThemeVariant, out var textColor))
+            {
+                return;
+            }
+
+            if (textColor is not Color color)
+            {
+                return;
+            }
+
             if (Settings.Theme.GlassTheme)
             {
                 MainBottomBorder.Background = Brushes.Transparent;
                 MainBottomBorder.BorderThickness = new Thickness(0);
-                
+
                 FileMenuButton.Background = Brushes.Transparent;
                 FileMenuButton.Classes.Remove("noBorderHover");
                 FileMenuButton.Classes.Add("hover");
-                
+
                 ImageMenuButton.Background = Brushes.Transparent;
                 ImageMenuButton.Classes.Remove("noBorderHover");
                 ImageMenuButton.Classes.Add("hover");
-                
+
                 ToolsMenuButton.Background = Brushes.Transparent;
                 ToolsMenuButton.Classes.Remove("noBorderHover");
                 ToolsMenuButton.Classes.Add("hover");
-                
+
                 SettingsMenuButton.Background = Brushes.Transparent;
                 SettingsMenuButton.Classes.Remove("noBorderHover");
                 SettingsMenuButton.Classes.Add("hover");
-            
-                NextButton.Background = new SolidColorBrush(Color.FromArgb(15, 255, 255, 255));
-                
-                PreviousButton.Background = new SolidColorBrush(Color.FromArgb(15, 255, 255, 255));
 
-                if (!Application.Current.TryGetResource("SecondaryTextColor",
-                        Application.Current.RequestedThemeVariant, out var textColor))
-                {
-                    return;
-                }
+                NextButton.Background = new SolidColorBrush(Color.FromArgb(15, 255, 255, 255));
 
-                if (textColor is not Color color)
-                {
-                    return;
-                }
+                PreviousButton.Background = new SolidColorBrush(Color.FromArgb(15, 255, 255, 255));
 
                 FileMenuButton.Foreground = new SolidColorBrush(color);
                 ImageMenuButton.Foreground = new SolidColorBrush(color);
                 ToolsMenuButton.Foreground = new SolidColorBrush(color);
                 SettingsMenuButton.Foreground = new SolidColorBrush(color);
-            
+
                 NextButton.Foreground = new SolidColorBrush(color);
                 PreviousButton.Foreground = new SolidColorBrush(color);
             }
-
-            if (DataContext is not MainViewModel vm)
+            else if (!Settings.Theme.Dark)
             {
-                return;
-            }
+                FileMenuButton.Classes.Remove("noBorderHover");
+                FileMenuButton.Classes.Add("noBorderHoverAlt");
 
-            PreviousButton.Click += (_, _) =>
-            {
-                vm.MainWindow.IsNavigationButtonLeftClicked = true;
-                UIHelper.SetButtonInterval(PreviousButton);
-            };
-            NextButton.Click += (_, _) =>
-            {
-                vm.MainWindow.IsNavigationButtonRightClicked = true;
-                UIHelper.SetButtonInterval(NextButton);
-            };
+                ImageMenuButton.Classes.Remove("noBorderHover");
+                ImageMenuButton.Classes.Add("noBorderHoverAlt");
+                if (TryGetResource("ImageMenuBrush", Application.Current.RequestedThemeVariant,
+                        out var imageMenuBrush))
+                {
+                    if (imageMenuBrush is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(ImageMenuButton, brush);
+                    }
+                }
+
+                ToolsMenuButton.Classes.Remove("noBorderHover");
+                ToolsMenuButton.Classes.Add("noBorderHoverAlt");
+                if (TryGetResource("ToolsMenuBrush", Application.Current.RequestedThemeVariant,
+                        out var toolsMenuBrush))
+                {
+                    if (toolsMenuBrush is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(ToolsMenuButton, brush);
+                    }
+                }
+
+                SettingsMenuButton.Classes.Remove("noBorderHover");
+                SettingsMenuButton.Classes.Add("noBorderHoverAlt");
+            }
         };
     }
 
     private void MoveWindow(PointerPressedEventArgs e)
     {
-        if (VisualRoot is null) { return; }
-        
+        if (VisualRoot is null)
+        {
+            return;
+        }
+
         if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
         {
             // Context menu doesn't want to be opened normally

+ 20 - 13
src/PicView.Avalonia/Views/MainView.axaml

@@ -108,11 +108,9 @@
                                             Converter={x:Static ObjectConverters.IsNotNull}}"
                 x:Name="OpenWithMenuItem">
                 <MenuItem.Icon>
-                    <Path
-                        Data="{StaticResource OpenWithGeometry}"
-                        Fill="{StaticResource Brush0}"
+                    <Image
                         Height="12"
-                        Stretch="Fill"
+                        Source="{StaticResource OpenExternalImage}"
                         Width="12" />
                 </MenuItem.Icon>
             </MenuItem>
@@ -401,7 +399,8 @@
                 <Separator />
 
                 <!--  Toggle ctrl zoom  -->
-                <MenuItem Command="{CompiledBinding Tools.ChangeCtrlZoomCommand}" Header="{CompiledBinding Translation.IsCtrlToZoom.Value, Mode=OneWay}">
+                <MenuItem Command="{CompiledBinding Tools.ChangeCtrlZoomCommand}"
+                          Header="{CompiledBinding Translation.IsCtrlToZoom.Value, Mode=OneWay}">
                     <MenuItem.Icon>
                         <Image
                             Height="12"
@@ -466,8 +465,10 @@
                             <DrawingImage>
                                 <DrawingImage.Drawing>
                                     <DrawingGroup ClipGeometry="M0,0 V512 H512 V0 H0 Z">
-                                        <GeometryDrawing Brush="{StaticResource Brush0}" Geometry="F1 M512,512z M0,0z M80,132L80,460A20,20,0,0,0,100,480L492,480A20,20,0,0,0,512,460L512,132A20,20,0,0,0,492,112L100,112A20,20,0,0,0,80,132z M373.14,173.33A46,46,0,1,1,326.86,219.33A46.19,46.19,0,0,1,373.14,173.33z M111.73,449.33L111.73,353.85 234.49,243.65 328.27,337 215.27,449.33z M480,449.33L259,449.33 403.58,305.33 480,370.59z" />
-                                        <GeometryDrawing Brush="{StaticResource Brush0}" Geometry="F1 M512,512z M0,0z M20,32A20,20,0,0,0,0,52L0,396A20,20,0,0,0,20,416L48,416 48,100A20,20,0,0,1,68,80L448,80 448,52A20,20,0,0,0,428,32z" />
+                                        <GeometryDrawing Brush="{StaticResource Brush0}"
+                                                         Geometry="F1 M512,512z M0,0z M80,132L80,460A20,20,0,0,0,100,480L492,480A20,20,0,0,0,512,460L512,132A20,20,0,0,0,492,112L100,112A20,20,0,0,0,80,132z M373.14,173.33A46,46,0,1,1,326.86,219.33A46.19,46.19,0,0,1,373.14,173.33z M111.73,449.33L111.73,353.85 234.49,243.65 328.27,337 215.27,449.33z M480,449.33L259,449.33 403.58,305.33 480,370.59z" />
+                                        <GeometryDrawing Brush="{StaticResource Brush0}"
+                                                         Geometry="F1 M512,512z M0,0z M20,32A20,20,0,0,0,0,52L0,396A20,20,0,0,0,20,416L48,416 48,100A20,20,0,0,1,68,80L448,80 448,52A20,20,0,0,0,428,32z" />
                                     </DrawingGroup>
                                 </DrawingImage.Drawing>
                             </DrawingImage>
@@ -494,7 +495,8 @@
                 <Separator />
 
                 <!--  Show Settings window  -->
-                <MenuItem Command="{CompiledBinding Window.ShowSettingsWindow}" Header="{CompiledBinding Translation.ShowAllSettingsWindow.Value, Mode=OneWay}">
+                <MenuItem Command="{CompiledBinding Window.ShowSettingsWindow}"
+                          Header="{CompiledBinding Translation.ShowAllSettingsWindow.Value, Mode=OneWay}">
                     <MenuItem.Icon>
                         <Path
                             Data="M262.29 192.31a64 64 0 1057.4 57.4 64.13 64.13 0 00-57.4-57.4zM416.39 256a154.34 154.34 0 01-1.53 20.79l45.21 35.46a10.81 10.81 0 012.45 13.75l-42.77 74a10.81 10.81 0 01-13.14 4.59l-44.9-18.08a16.11 16.11 0 00-15.17 1.75A164.48 164.48 0 01325 400.8a15.94 15.94 0 00-8.82 12.14l-6.73 47.89a11.08 11.08 0 01-10.68 9.17h-85.54a11.11 11.11 0 01-10.69-8.87l-6.72-47.82a16.07 16.07 0 00-9-12.22 155.3 155.3 0 01-21.46-12.57 16 16 0 00-15.11-1.71l-44.89 18.07a10.81 10.81 0 01-13.14-4.58l-42.77-74a10.8 10.8 0 012.45-13.75l38.21-30a16.05 16.05 0 006-14.08c-.36-4.17-.58-8.33-.58-12.5s.21-8.27.58-12.35a16 16 0 00-6.07-13.94l-38.19-30A10.81 10.81 0 0149.48 186l42.77-74a10.81 10.81 0 0113.14-4.59l44.9 18.08a16.11 16.11 0 0015.17-1.75A164.48 164.48 0 01187 111.2a15.94 15.94 0 008.82-12.14l6.73-47.89A11.08 11.08 0 01213.23 42h85.54a11.11 11.11 0 0110.69 8.87l6.72 47.82a16.07 16.07 0 009 12.22 155.3 155.3 0 0121.46 12.57 16 16 0 0015.11 1.71l44.89-18.07a10.81 10.81 0 0113.14 4.58l42.77 74a10.8 10.8 0 01-2.45 13.75l-38.21 30a16.05 16.05 0 00-6.05 14.08c.33 4.14.55 8.3.55 12.47z"
@@ -713,7 +715,8 @@
                 </MenuItem.Icon>
 
                 <!--  Exif window  -->
-                <MenuItem Command="{CompiledBinding Window.ShowExifWindow}" Header="{CompiledBinding Translation.ImageInfo.Value, Mode=OneWay}">
+                <MenuItem Command="{CompiledBinding Window.ShowExifWindow}"
+                          Header="{CompiledBinding Translation.ImageInfo.Value, Mode=OneWay}">
                     <MenuItem.Icon>
                         <Path
                             Data="{StaticResource ImageInfoGeometry}"
@@ -742,7 +745,8 @@
                 </MenuItem>
 
                 <!--  Single image resize  -->
-                <MenuItem Command="{CompiledBinding Window.ShowSingleImageResizeWindow}" Header="{CompiledBinding Translation.ResizeImage.Value, Mode=OneWay}">
+                <MenuItem Command="{CompiledBinding Window.ShowSingleImageResizeWindow}"
+                          Header="{CompiledBinding Translation.ResizeImage.Value, Mode=OneWay}">
                     <MenuItem.Icon>
                         <Image
                             Height="12"
@@ -752,7 +756,8 @@
                 </MenuItem>
 
                 <!--  Batch resize  -->
-                <MenuItem Command="{CompiledBinding Window.ShowBatchResizeWindow}" Header="{CompiledBinding Translation.BatchResize.Value, Mode=OneWay}">
+                <MenuItem Command="{CompiledBinding Window.ShowBatchResizeWindow}"
+                          Header="{CompiledBinding Translation.BatchResize.Value, Mode=OneWay}">
                     <MenuItem.Icon>
                         <Image
                             Height="12"
@@ -883,7 +888,8 @@
             <Separator />
 
             <!--  Paste  -->
-            <MenuItem Command="{CompiledBinding Tools.PasteCommand}" Header="{CompiledBinding Translation.Paste.Value, Mode=OneWay}">
+            <MenuItem Command="{CompiledBinding Tools.PasteCommand}"
+                      Header="{CompiledBinding Translation.Paste.Value, Mode=OneWay}">
                 <MenuItem.Icon>
                     <Path
                         Data="{StaticResource PasteGeometry}"
@@ -1064,7 +1070,8 @@
             </MenuItem>
 
             <!--  Close  -->
-            <MenuItem Command="{CompiledBinding MainWindow.ExitCommand}" Header="{CompiledBinding Translation.Close.Value, Mode=OneWay}">
+            <MenuItem Command="{CompiledBinding MainWindow.ExitCommand}"
+                      Header="{CompiledBinding Translation.Close.Value, Mode=OneWay}">
                 <MenuItem.Icon>
                     <Path
                         Data="{StaticResource CloseGeometry}"

文件差异内容过多而无法显示
+ 0 - 82
src/PicView.Avalonia/Views/SettingsView.axaml


+ 41 - 40
src/PicView.Avalonia/Views/SettingsView.axaml.cs

@@ -20,15 +20,6 @@ namespace PicView.Avalonia.Views;
 
 public partial class SettingsView : UserControl
 {
-    #region Fields
-
-    private static CompositeDisposable? _marginSubscription;
-    private readonly Stack<TabItem?> _backStack = new();
-    private readonly Stack<TabItem?> _forwardStack = new();
-    private TabItem? _currentTab;
-
-    #endregion
-
     #region Constructor
 
     public SettingsView()
@@ -50,6 +41,45 @@ public partial class SettingsView : UserControl
 
     #endregion
 
+    #region Input Handlers
+
+    private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
+    {
+        if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
+        {
+            return;
+        }
+
+        var topLevel = TopLevel.GetTopLevel(desktop.MainWindow);
+        var properties = e.GetCurrentPoint(topLevel).Properties;
+
+        if (properties.IsXButton1Pressed)
+        {
+            GoBack();
+        }
+
+        if (properties.IsXButton2Pressed)
+        {
+            GoForward();
+        }
+
+        if (properties.IsRightButtonPressed)
+        {
+            ContextMenu.Open();
+        }
+    }
+
+    #endregion
+
+    #region Fields
+
+    private static CompositeDisposable? _marginSubscription;
+    private readonly Stack<TabItem?> _backStack = new();
+    private readonly Stack<TabItem?> _forwardStack = new();
+    private TabItem? _currentTab;
+
+    #endregion
+
     #region Initialization
 
     private void OnLoaded(object? sender, EventArgs e)
@@ -86,7 +116,7 @@ public partial class SettingsView : UserControl
         _marginSubscription = new CompositeDisposable();
         Observable.EveryValueChanged(settingsVm.WindowMargin, x => x.CurrentValue, UIHelper.GetFrameProvider)
             .Skip(1)
-            .Subscribe(x => 
+            .Subscribe(x =>
             {
                 Settings.WindowProperties.Margin = x;
                 WindowResizing.SetSize(vm.PicViewer.PixelWidth.CurrentValue, vm.PicViewer.PixelHeight.CurrentValue, 0,
@@ -126,6 +156,7 @@ public partial class SettingsView : UserControl
 
     private void AttachEventHandlers()
     {
+        CloseItem.Click += (_, _) => (VisualRoot as Window)?.Close();
         MainTabControl.SelectionChanged += OnTabSelectionChanged;
         PointerPressed += OnPointerPressed;
     }
@@ -205,34 +236,4 @@ public partial class SettingsView : UserControl
     }
 
     #endregion
-
-    #region Input Handlers
-
-    private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
-    {
-        if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
-        {
-            return;
-        }
-
-        var topLevel = TopLevel.GetTopLevel(desktop.MainWindow);
-        var properties = e.GetCurrentPoint(topLevel).Properties;
-
-        if (properties.IsXButton1Pressed)
-        {
-            GoBack();
-        }
-
-        if (properties.IsXButton2Pressed)
-        {
-            GoForward();
-        }
-
-        if (properties.IsRightButtonPressed)
-        {
-            ContextMenu.Open();
-        }
-    }
-
-    #endregion
 }

+ 2 - 2
src/PicView.Avalonia/Views/UC/Buttons/SettingsMenuButton.axaml

@@ -17,7 +17,7 @@
         BorderThickness="1,0,0,0"
         Classes="hover"
         ClickMode="Press"
-        Foreground="{StaticResource SecondaryTextColor}"
+        Foreground="{DynamicResource MainTextColor}"
         Icon="{StaticResource MenuImage}"
         IconHeight="12"
         IconWidth="12"
@@ -65,4 +65,4 @@
             </MenuFlyout>
         </customControls:IconButton.Flyout>
     </customControls:IconButton>
-</UserControl>
+</UserControl>

+ 116 - 12
src/PicView.Avalonia/Views/UC/GalleryItem.axaml

@@ -24,7 +24,8 @@
                                 Mode=OneWay}"
         x:DataType="viewModels:MainViewModel"
         x:Name="ImageBorder">
-        <customControls:ThumbImage Stretch="{CompiledBinding Gallery.GalleryStretch.Value, Mode=OneWay}" x:Name="GalleryImage" />
+        <customControls:ThumbImage Stretch="{CompiledBinding Gallery.GalleryStretch.Value, Mode=OneWay}"
+                                   x:Name="GalleryImage" />
         <ToolTip.Tip>
             <StackPanel MaxWidth="600">
                 <TextBlock
@@ -57,6 +58,8 @@
         </ToolTip.Tip>
         <Border.ContextMenu>
             <ContextMenu x:Name="GalleryContextMenu">
+
+                <!--  Print  -->
                 <MenuItem
                     Command="{CompiledBinding Tools.PrintCommand}"
                     CommandParameter="{CompiledBinding Path=Text,
@@ -72,22 +75,25 @@
                             Width="12" />
                     </MenuItem.Icon>
                 </MenuItem>
+
+                <!--  Open with  -->
                 <MenuItem
                     Command="{CompiledBinding Tools.OpenWithCommand}"
                     CommandParameter="{CompiledBinding Path=Text,
                                                        ElementName=FileLocation}"
+                    Foreground="{DynamicResource MainTextColor}"
                     Header="{CompiledBinding Translation.OpenWith.Value,
                                              Mode=OneWay}"
                     x:Name="OpenWith">
                     <MenuItem.Icon>
-                        <Path
-                            Data="{StaticResource OpenWithGeometry}"
-                            Fill="{DynamicResource MainTextColor}"
+                        <Image
                             Height="12"
-                            Stretch="Fill"
+                            Source="{StaticResource OpenExternalImage}"
                             Width="12" />
                     </MenuItem.Icon>
                 </MenuItem>
+
+                <!--  Locate on disk  -->
                 <MenuItem
                     Command="{CompiledBinding Tools.LocateOnDiskCommand}"
                     CommandParameter="{CompiledBinding Path=Text,
@@ -104,20 +110,118 @@
                     </MenuItem.Icon>
                 </MenuItem>
                 <Separator />
+
+                <!--  Set as wallpaper  -->
                 <MenuItem
-                    Command="{CompiledBinding Tools.SetAsWallpaperCommand}"
-                    CommandParameter="{CompiledBinding Path=Text,
-                                                       ElementName=FileLocation}"
                     Header="{CompiledBinding Translation.SetAsWallpaper.Value,
-                                             Mode=OneWay}">
+                                             Mode=OneWay}"
+                    IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
+                                                Converter={x:Static ObjectConverters.IsNotNull}}"
+                    x:Name="WallpaperMenuItem">
                     <MenuItem.Icon>
                         <Path
                             Data="{StaticResource PanoramaGeometry}"
                             Fill="{DynamicResource MainTextColor}"
-                            Height="12"
+                            Height="10.40"
                             Stretch="Fill"
-                            Width="12" />
+                            Width="13" />
                     </MenuItem.Icon>
+
+                    <!--  Set as wallpaper filled  -->
+                    <MenuItem
+                        Command="{CompiledBinding Tools.SetAsWallpaperFilledCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
+                                                           FallbackValue=''}"
+                        Header="{CompiledBinding Translation.Fill.Value,
+                                                 Mode=OneWay}"
+                        IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
+                                                    Converter={x:Static ObjectConverters.IsNotNull}}">
+                        <MenuItem.Icon>
+                            <Path
+                                Data="{StaticResource PanoramaGeometry}"
+                                Fill="{DynamicResource MainTextColor}"
+                                Height="10.40"
+                                Stretch="Fill"
+                                Width="13" />
+                        </MenuItem.Icon>
+                    </MenuItem>
+
+                    <!--  Set as wallpaper fit  -->
+                    <MenuItem
+                        Command="{CompiledBinding Tools.SetAsWallpaperFittedCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
+                                                           FallbackValue=''}"
+                        Header="{CompiledBinding Translation.Fit.Value,
+                                                 Mode=OneWay}"
+                        IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
+                                                    Converter={x:Static ObjectConverters.IsNotNull}}">
+                        <MenuItem.Icon>
+                            <Path
+                                Data="{StaticResource PanoramaGeometry}"
+                                Fill="{DynamicResource MainTextColor}"
+                                Height="10.40"
+                                Stretch="Fill"
+                                Width="13" />
+                        </MenuItem.Icon>
+                    </MenuItem>
+
+                    <!--  Set as wallpaper stretched  -->
+                    <MenuItem
+                        Command="{CompiledBinding Tools.SetAsWallpaperStretchedCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
+                                                           FallbackValue=''}"
+                        Header="{CompiledBinding Translation.Stretch.Value,
+                                                 Mode=OneWay}"
+                        IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
+                                                    Converter={x:Static ObjectConverters.IsNotNull}}">
+                        <MenuItem.Icon>
+                            <Path
+                                Data="{StaticResource PanoramaGeometry}"
+                                Fill="{DynamicResource MainTextColor}"
+                                Height="10.40"
+                                Stretch="Fill"
+                                Width="13" />
+                        </MenuItem.Icon>
+                    </MenuItem>
+
+
+                    <!--  Set as wallpaper centered  -->
+                    <MenuItem
+                        Command="{CompiledBinding Tools.SetAsWallpaperCenteredCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
+                                                           FallbackValue=''}"
+                        Header="{CompiledBinding Translation.Center.Value,
+                                                 Mode=OneWay}"
+                        IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
+                                                    Converter={x:Static ObjectConverters.IsNotNull}}">
+                        <MenuItem.Icon>
+                            <Path
+                                Data="{StaticResource PanoramaGeometry}"
+                                Fill="{DynamicResource MainTextColor}"
+                                Height="10.40"
+                                Stretch="Fill"
+                                Width="13" />
+                        </MenuItem.Icon>
+                    </MenuItem>
+
+                    <!--  Set as wallpaper tiled  -->
+                    <MenuItem
+                        Command="{CompiledBinding Tools.SetAsWallpaperTiledCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
+                                                           FallbackValue=''}"
+                        Header="{CompiledBinding Translation.Tile.Value,
+                                                 Mode=OneWay}"
+                        IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
+                                                    Converter={x:Static ObjectConverters.IsNotNull}}">
+                        <MenuItem.Icon>
+                            <Path
+                                Data="{StaticResource PanoramaGeometry}"
+                                Fill="{DynamicResource MainTextColor}"
+                                Height="10.40"
+                                Stretch="Fill"
+                                Width="13" />
+                        </MenuItem.Icon>
+                    </MenuItem>
                 </MenuItem>
                 <Separator />
                 <MenuItem
@@ -301,4 +405,4 @@
             </ContextMenu>
         </Border.ContextMenu>
     </Border>
-</UserControl>
+</UserControl>

文件差异内容过多而无法显示
+ 61 - 87
src/PicView.Avalonia/Views/UC/Menus/FileMenu.axaml


+ 18 - 0
src/PicView.Avalonia/Views/UC/Menus/FileMenu.axaml.cs

@@ -17,6 +17,24 @@ public partial class FileMenu : AnimatedMenu
             else if (!Settings.Theme.Dark)
             {
                 TopBorder.Background = Brushes.White;
+
+                NewWindowButton.Classes.Remove("altHover");
+                NewWindowButton.Classes.Add("hover");
+
+                PasteButton.Classes.Remove("altHover");
+                PasteButton.Classes.Add("hover");
+
+                SaveAsButton.Classes.Remove("altHover");
+                SaveAsButton.Classes.Add("hover");
+
+                ShowInFolderButton.Classes.Remove("altHover");
+                ShowInFolderButton.Classes.Add("hover");
+
+                OpenWithButton.Classes.Remove("altHover");
+                OpenWithButton.Classes.Add("hover");
+
+                OpenButton.Classes.Remove("altHover");
+                OpenButton.Classes.Add("hover");
             }
 
             if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))

+ 146 - 119
src/PicView.Avalonia/Views/UC/Menus/ImageMenu.axaml

@@ -9,10 +9,12 @@
     <Design.DataContext>
         <viewModels:MainViewModel />
     </Design.DataContext>
+    <customControls:AnimatedMenu.Resources>
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="ResizeBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="SideBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="SlideShowBrush" />
+    </customControls:AnimatedMenu.Resources>
     <StackPanel>
-        <StackPanel.Resources>
-            <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="Brush0" />
-        </StackPanel.Resources>
         <StackPanel.Styles>
             <Styles>
                 <Style Selector="TextBlock.btnTxt">
@@ -44,12 +46,14 @@
                     Background="Transparent"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
+                    Canvas.Left="-1"
+                    Canvas.Top="-1"
                     Classes="hover"
                     Command="{CompiledBinding Tools.RotateLeftButtonCommand}"
                     CornerRadius="8,0,0,0"
                     Data="{StaticResource RotateLeftGeometry}"
                     Foreground="{DynamicResource MainTextColor}"
-                    Height="45"
+                    Height="47"
                     IconHeight="15"
                     IconWidth="15"
                     IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
@@ -61,12 +65,13 @@
 
                 <customControls:IconButton
                     Background="Transparent"
-                    Canvas.Left="45"
+                    Canvas.Left="44"
+                    Canvas.Top="-1"
                     Classes="hover"
                     Command="{CompiledBinding Tools.RotateRightButtonCommand}"
                     Data="{StaticResource RotateRightGeometry}"
                     Foreground="{DynamicResource MainTextColor}"
-                    Height="45"
+                    Height="47"
                     IconHeight="15"
                     IconWidth="15"
                     IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
@@ -80,12 +85,13 @@
                     Background="Transparent"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="1,0,1,0"
-                    Canvas.Left="90"
+                    Canvas.Left="89"
+                    Canvas.Top="-1"
                     Classes="hover"
                     Command="{CompiledBinding Tools.FlipCommand}"
                     Data="{StaticResource FlipGeometry}"
                     Foreground="{DynamicResource MainTextColor}"
-                    Height="45"
+                    Height="47"
                     IconHeight="15"
                     IconWidth="15"
                     IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
@@ -100,9 +106,10 @@
 
                 <Button
                     Background="Transparent"
-                    Canvas.Left="135"
+                    Canvas.Left="134"
+                    Canvas.Top="-1"
                     Classes="hover"
-                    Height="45"
+                    Height="47"
                     IsEnabled="False"
                     ToolTip.Tip="Coming soon..."
                     Width="45">
@@ -119,14 +126,15 @@
                     Background="Transparent"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="1,0,1,0"
-                    Canvas.Left="180"
+                    Canvas.Left="179"
+                    Canvas.Top="-1"
                     Classes="noBorderHover"
-                    Height="45"
+                    Height="47"
                     IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                 Converter={x:Static ObjectConverters.IsNotNull}}"
                     ToolTip.Tip="{CompiledBinding Translation.GoToImageAtSpecifiedIndex.Value,
                                                   Mode=OneWay}"
-                    Width="90"
+                    Width="92"
                     x:Name="GoToPicButton">
                     <StackPanel Margin="5,0" Orientation="Horizontal">
                         <Image Height="17" Width="17">
@@ -172,14 +180,15 @@
 
                 <customControls:IconButton
                     Background="Transparent"
-                    Canvas.Left="270"
+                    Canvas.Left="271"
+                    Canvas.Top="-1"
                     Classes="hover"
                     ClickMode="Release"
                     Command="{CompiledBinding MainWindow.CloseMenuCommand}"
                     CornerRadius="0,8,0,0"
                     Data="{StaticResource CloseGeometry}"
                     Foreground="{DynamicResource MainTextColor}"
-                    Height="45"
+                    Height="47"
                     IconHeight="10"
                     IconWidth="10"
                     ToolTip.Tip="{CompiledBinding Translation.Close.Value,
@@ -196,7 +205,8 @@
                     IsEnabled="{CompiledBinding PicViewer.FileInfo,
                                                 Converter={x:Static ObjectConverters.IsNotNull}}"
                     ToolTip.Tip="{CompiledBinding Translation.ResizeImage.Value,
-                                                  Mode=OneWay}">
+                                                  Mode=OneWay}"
+                    x:Name="ResizeImageButton">
                     <StackPanel Orientation="Horizontal">
                         <Canvas Height="54" Width="45">
                             <Image
@@ -207,7 +217,7 @@
                                     <DrawingImage>
                                         <DrawingImage.Drawing>
                                             <DrawingGroup ClipGeometry="M0,0 V512 H512 V0 H0 Z">
-                                                <GeometryDrawing Brush="{StaticResource Brush0}" Geometry="F1 M512,512z M0,0z M464,448L48,448C21.49,448,0,426.51,0,400L0,112C0,85.49,21.49,64,48,64L464,64C490.51,64,512,85.49,512,112L512,400C512,426.51,490.51,448,464,448z M112,120C81.072,120 56,145.072 56,176 56,206.928 81.072,232 112,232 142.928,232 168,206.928 168,176 168,145.072 142.928,120 112,120z M64,384L448,384 448,272 360.485,184.485C355.799,179.799,348.201,179.799,343.514,184.485L208,320 152.485,264.485C147.799,259.799,140.201,259.799,135.514,264.485L64,336 64,384z" />
+                                                <GeometryDrawing Brush="{StaticResource ResizeBrush}" Geometry="F1 M512,512z M0,0z M464,448L48,448C21.49,448,0,426.51,0,400L0,112C0,85.49,21.49,64,48,64L464,64C490.51,64,512,85.49,512,112L512,400C512,426.51,490.51,448,464,448z M112,120C81.072,120 56,145.072 56,176 56,206.928 81.072,232 112,232 142.928,232 168,206.928 168,176 168,145.072 142.928,120 112,120z M64,384L448,384 448,272 360.485,184.485C355.799,179.799,348.201,179.799,343.514,184.485L208,320 152.485,264.485C147.799,259.799,140.201,259.799,135.514,264.485L64,336 64,384z" />
                                             </DrawingGroup>
                                         </DrawingImage.Drawing>
                                     </DrawingImage>
@@ -222,7 +232,7 @@
                                     <DrawingImage>
                                         <DrawingImage.Drawing>
                                             <DrawingGroup ClipGeometry="M0,0 V512 H512 V0 H0 Z">
-                                                <GeometryDrawing Brush="{StaticResource Brush0}" Geometry="F1 M512,512z M0,0z M464,448L48,448C21.49,448,0,426.51,0,400L0,112C0,85.49,21.49,64,48,64L464,64C490.51,64,512,85.49,512,112L512,400C512,426.51,490.51,448,464,448z M112,120C81.072,120 56,145.072 56,176 56,206.928 81.072,232 112,232 142.928,232 168,206.928 168,176 168,145.072 142.928,120 112,120z M64,384L448,384 448,272 360.485,184.485C355.799,179.799,348.201,179.799,343.514,184.485L208,320 152.485,264.485C147.799,259.799,140.201,259.799,135.514,264.485L64,336 64,384z" />
+                                                <GeometryDrawing Brush="{StaticResource ResizeBrush}" Geometry="F1 M512,512z M0,0z M464,448L48,448C21.49,448,0,426.51,0,400L0,112C0,85.49,21.49,64,48,64L464,64C490.51,64,512,85.49,512,112L512,400C512,426.51,490.51,448,464,448z M112,120C81.072,120 56,145.072 56,176 56,206.928 81.072,232 112,232 142.928,232 168,206.928 168,176 168,145.072 142.928,120 112,120z M64,384L448,384 448,272 360.485,184.485C355.799,179.799,348.201,179.799,343.514,184.485L208,320 152.485,264.485C147.799,259.799,140.201,259.799,135.514,264.485L64,336 64,384z" />
                                             </DrawingGroup>
                                         </DrawingImage.Drawing>
                                     </DrawingImage>
@@ -231,35 +241,32 @@
                         </Canvas>
                         <TextBlock
                             Classes="txt btnTxt"
+                            Foreground="{StaticResource ResizeBrush}"
                             Margin="-3,0,0,0"
                             Text="{CompiledBinding Translation.Resize.Value,
                                                    Mode=OneWay}" />
                     </StackPanel>
                 </Button>
 
-                <Button
+                <customControls:TextIconButton
                     Background="{DynamicResource MenuButtonColor}"
                     Canvas.Left="160"
                     Canvas.Top="53"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.CropCommand}"
+                    Data="{StaticResource CropGeometry}"
+                    Foreground="{DynamicResource MainTextColor}"
                     Height="46"
+                    IconHeight="17"
+                    IconMargin="9,0,9,0"
+                    IconWidth="17"
                     IsEnabled="{CompiledBinding PicViewer.ShouldCropBeEnabled.Value,
                                                 Mode=OneWay}"
+                    Text="{CompiledBinding Translation.Crop.Value,
+                                           Mode=OneWay}"
                     ToolTip.Tip="{CompiledBinding Translation.Crop,
-                                                  Mode=OneWay}">
-                    <StackPanel Orientation="Horizontal">
-                        <Path
-                            Data="{StaticResource CropGeometry}"
-                            Fill="{DynamicResource MainTextColor}"
-                            Height="17"
-                            Margin="9,0,9,0"
-                            Stretch="Fill"
-                            Width="17" />
-                        <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.Crop.Value, Mode=OneWay}" />
-                    </StackPanel>
-                </Button>
-
+                                                  Mode=OneWay}"
+                    x:Name="CropButton" />
 
                 <SplitButton
                     Background="{DynamicResource MenuButtonColor}"
@@ -278,105 +285,128 @@
                             Height="20"
                             Margin="5,0,10,0"
                             Source="{StaticResource SlideshowImage}"
-                            Width="20" />
+                            Width="20"
+                            x:Name="SlideShowImage" />
                         <TextBlock
                             Classes="txt btnTxt"
+                            Foreground="{StaticResource SlideShowBrush}"
                             Margin="0,2,0,0"
                             Text="{CompiledBinding Translation.Slideshow.Value,
                                                    Mode=OneWay}" />
                     </StackPanel>
                     <SplitButton.Flyout>
                         <MenuFlyout Placement="Top" ShowMode="Transient">
-                            <Button
+                            <MenuItem
                                 Background="Transparent"
                                 Command="{CompiledBinding Tools.StartSlideShowTask}"
                                 CommandParameter="2000"
-                                Width="86">
-                                <TextBlock Classes="txt" Foreground="{DynamicResource MainTextColor}">
-                                    <Run Text="2 " />
-                                    <Run Text="{CompiledBinding Translation.SecAbbreviation.Value, Mode=OneWay}" />
-                                </TextBlock>
-                            </Button>
-                            <Button
+                                Width="140"
+                                x:Name="Item2">
+                                <MenuItem.Icon>
+                                    <Image
+                                        Height="12"
+                                        Source="{StaticResource TimerImage}"
+                                        Width="12" />
+                                </MenuItem.Icon>
+                            </MenuItem>
+
+                            <MenuItem
                                 Background="Transparent"
                                 Command="{CompiledBinding Tools.StartSlideShowTask}"
                                 CommandParameter="5000"
-                                Width="86">
-                                <TextBlock Classes="txt" Foreground="{DynamicResource MainTextColor}">
-                                    <Run Text="5 " />
-                                    <Run Text="{CompiledBinding Translation.SecAbbreviation.Value, Mode=OneWay}" />
-                                </TextBlock>
-                            </Button>
-                            <Button
+                                Width="140"
+                                x:Name="Item5">
+                                <MenuItem.Icon>
+                                    <Image
+                                        Height="12"
+                                        Source="{StaticResource TimerImage}"
+                                        Width="12" />
+                                </MenuItem.Icon>
+                            </MenuItem>
+
+                            <MenuItem
                                 Background="Transparent"
                                 Command="{CompiledBinding Tools.StartSlideShowTask}"
                                 CommandParameter="10000"
-                                Width="86">
-                                <TextBlock Classes="txt" Foreground="{DynamicResource MainTextColor}">
-                                    <Run Text="10 " />
-                                    <Run Text="{CompiledBinding Translation.SecAbbreviation.Value, Mode=OneWay}" />
-                                </TextBlock>
-                            </Button>
-                            <Button
+                                Width="140"
+                                x:Name="Item10">
+                                <MenuItem.Icon>
+                                    <Image
+                                        Height="12"
+                                        Source="{StaticResource TimerImage}"
+                                        Width="12" />
+                                </MenuItem.Icon>
+                            </MenuItem>
+
+                            <MenuItem
                                 Background="Transparent"
                                 Command="{CompiledBinding Tools.StartSlideShowTask}"
                                 CommandParameter="20000"
-                                Width="86">
-                                <TextBlock Classes="txt" Foreground="{DynamicResource MainTextColor}">
-                                    <Run Text="20 " />
-                                    <Run Text="{CompiledBinding Translation.SecAbbreviation.Value, Mode=OneWay}" />
-                                </TextBlock>
-                            </Button>
-                            <Button
+                                Width="140"
+                                x:Name="Item20">
+                                <MenuItem.Icon>
+                                    <Image
+                                        Height="12"
+                                        Source="{StaticResource TimerImage}"
+                                        Width="12" />
+                                </MenuItem.Icon>
+                            </MenuItem>
+
+                            <MenuItem
                                 Background="Transparent"
                                 Command="{CompiledBinding Tools.StartSlideShowTask}"
                                 CommandParameter="30000"
-                                Width="86">
-                                <TextBlock Classes="txt" Foreground="{DynamicResource MainTextColor}">
-                                    <Run Text="30 " />
-                                    <Run Text="{CompiledBinding Translation.SecAbbreviation.Value, Mode=OneWay}" />
-                                </TextBlock>
-                            </Button>
-                            <Button
+                                Width="140"
+                                x:Name="Item30">
+                                <MenuItem.Icon>
+                                    <Image
+                                        Height="12"
+                                        Source="{StaticResource TimerImage}"
+                                        Width="12" />
+                                </MenuItem.Icon>
+                            </MenuItem>
+
+                            <MenuItem
                                 Background="Transparent"
                                 Command="{CompiledBinding Tools.StartSlideShowTask}"
                                 CommandParameter="60000"
-                                Width="86">
-                                <TextBlock Classes="txt" Foreground="{DynamicResource MainTextColor}">
-                                    <Run Text="60 " />
-                                    <Run Text="{CompiledBinding Translation.SecAbbreviation.Value, Mode=OneWay}" />
-                                </TextBlock>
-                            </Button>
-                            <Button
-                                Background="Transparent"
-                                Command="{CompiledBinding Tools.StartSlideShowTask}"
-                                CommandParameter="120000"
-                                Width="86">
-                                <TextBlock Classes="txt" Foreground="{DynamicResource MainTextColor}">
-                                    <Run Text="120 " />
-                                    <Run Text="{CompiledBinding Translation.SecAbbreviation.Value, Mode=OneWay}" />
-                                </TextBlock>
-                            </Button>
-                            <Button
+                                Width="140"
+                                x:Name="Item60">
+                                <MenuItem.Icon>
+                                    <Image
+                                        Height="12"
+                                        Source="{StaticResource TimerImage}"
+                                        Width="12" />
+                                </MenuItem.Icon>
+                            </MenuItem>
+
+                            <MenuItem
                                 Background="Transparent"
                                 Command="{CompiledBinding Tools.StartSlideShowTask}"
-                                CommandParameter="180000"
-                                Width="86">
-                                <TextBlock Classes="txt" Foreground="{DynamicResource MainTextColor}">
-                                    <Run Text="180 " />
-                                    <Run Text="{CompiledBinding Translation.SecAbbreviation.Value, Mode=OneWay}" />
-                                </TextBlock>
-                            </Button>
-                            <Button
+                                CommandParameter="90000"
+                                Width="140"
+                                x:Name="Item90">
+                                <MenuItem.Icon>
+                                    <Image
+                                        Height="12"
+                                        Source="{StaticResource TimerImage}"
+                                        Width="12" />
+                                </MenuItem.Icon>
+                            </MenuItem>
+
+                            <MenuItem
                                 Background="Transparent"
                                 Command="{CompiledBinding Tools.StartSlideShowTask}"
-                                CommandParameter="300000"
-                                Width="86">
-                                <TextBlock Classes="txt" Foreground="{DynamicResource MainTextColor}">
-                                    <Run Text="300 " />
-                                    <Run Text="{CompiledBinding Translation.SecAbbreviation.Value, Mode=OneWay}" />
-                                </TextBlock>
-                            </Button>
+                                CommandParameter="120000"
+                                Width="140"
+                                x:Name="Item120">
+                                <MenuItem.Icon>
+                                    <Image
+                                        Height="12"
+                                        Source="{StaticResource TimerImage}"
+                                        Width="12" />
+                                </MenuItem.Icon>
+                            </MenuItem>
                         </MenuFlyout>
                     </SplitButton.Flyout>
                 </SplitButton>
@@ -387,43 +417,40 @@
                     Canvas.Top="104"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.ShowSideBySideCommand}"
-                    Foreground="{DynamicResource MainTextColor}"
+                    Foreground="{StaticResource SideBrush}"
                     Height="46"
                     IsChecked="{CompiledBinding PicViewer.IsShowingSideBySide.Value}"
                     IsEnabled="{CompiledBinding !PicViewer.IsSingleImage.Value}"
-                    ToolTip.Tip="{CompiledBinding Translation.SideBySideTooltip.Value}">
+                    ToolTip.Tip="{CompiledBinding Translation.SideBySideTooltip.Value}"
+                    x:Name="SideBySideButton">
                     <TextBlock
                         Classes="txt btnTxt"
+                        Foreground="{StaticResource SideBrush}"
                         Margin="1,0,6,0"
                         Text="{CompiledBinding Translation.SideBySide.Value}" />
                 </ToggleButton>
 
-                <Button
+                <customControls:TextIconButton
                     Background="{DynamicResource MenuButtonColor}"
                     Canvas.Left="7"
                     Canvas.Top="155"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Gallery.ToggleBottomGalleryCommand}"
+                    Data="{StaticResource GalleryGeometry}"
                     Foreground="{DynamicResource MainTextColor}"
                     Height="46"
+                    IconHeight="20"
+                    IconMargin="9,0,9,0"
+                    IconWidth="20"
                     IsEnabled="{CompiledBinding !PicViewer.IsSingleImage.Value}"
+                    Text="{CompiledBinding Translation.IsShowingBottomGallery.Value,
+                                           Mode=OneWay}"
+                    TextMargin="0,0,6,0"
+                    TextMaxWidth="175"
                     ToolTip.Tip="{CompiledBinding Translation.IsShowingBottomGallery.Value,
                                                   Mode=OneWay}"
-                    Width="300">
-                    <StackPanel Orientation="Horizontal">
-                        <Image
-                            Height="20"
-                            Margin="9,0,9,0"
-                            Source="{StaticResource GalleryImage}"
-                            Width="20" />
-                        <TextBlock
-                            Classes="txt btnTxt"
-                            Margin="0,0,6,0"
-                            MaxWidth="175"
-                            Text="{CompiledBinding Translation.IsShowingBottomGallery.Value,
-                                                   Mode=OneWay}" />
-                    </StackPanel>
-                </Button>
+                    Width="300"
+                    x:Name="GalleryButton" />
             </Canvas>
         </Border>
         <Polygon

+ 64 - 12
src/PicView.Avalonia/Views/UC/Menus/ImageMenu.axaml.cs

@@ -1,3 +1,4 @@
+using Avalonia;
 using Avalonia.Input;
 using Avalonia.Media;
 using PicView.Avalonia.Crop;
@@ -5,11 +6,12 @@ using PicView.Avalonia.CustomControls;
 using PicView.Avalonia.Navigation;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
+using PicView.Core.Localization;
 using R3;
 
 namespace PicView.Avalonia.Views.UC.Menus;
 
-public partial class ImageMenu  : AnimatedMenu
+public partial class ImageMenu : AnimatedMenu
 {
     public ImageMenu()
     {
@@ -18,29 +20,79 @@ public partial class ImageMenu  : AnimatedMenu
         {
             if (Settings.Theme.GlassTheme)
             {
-               GoToPicButton.Classes.Remove("noBorderHover");
-               GoToPicButton.Classes.Add("hover");
-               GoToPicBox.Background = new SolidColorBrush(Color.FromArgb(90, 197, 197, 197));
+                GoToPicButton.Classes.Remove("noBorderHover");
+                GoToPicButton.Classes.Add("hover");
+                GoToPicBox.Background = new SolidColorBrush(Color.FromArgb(90, 197, 197, 197));
             }
             else if (!Settings.Theme.Dark)
             {
                 TopBorder.Background = Brushes.White;
+
+                CropButton.Classes.Remove("altHover");
+                CropButton.Classes.Add("hover");
+
+                GalleryButton.Classes.Remove("altHover");
+                GalleryButton.Classes.Add("hover");
+
+                SideBySideButton.Classes.Remove("altHover");
+                SideBySideButton.Classes.Add("hover");
+                if (TryGetResource("SideBrush", Application.Current.RequestedThemeVariant,
+                        out var sideBrush))
+                {
+                    if (sideBrush is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(SideBySideButton, brush);
+                    }
+                }
+
+                ResizeImageButton.Classes.Remove("altHover");
+                ResizeImageButton.Classes.Add("hover");
+                if (TryGetResource("ResizeBrush", Application.Current.RequestedThemeVariant,
+                        out var value))
+                {
+                    if (value is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(ResizeImageButton, brush);
+                    }
+                }
+
+                SlideShowButton.Classes.Remove("altHover");
+                SlideShowButton.Classes.Add("hover");
+                if (Application.Current.TryGetResource("SlideshowAltImage", Application.Current.RequestedThemeVariant,
+                        out var slideshowAltImage) && Application.Current.TryGetResource("SlideshowImage",
+                        Application.Current.RequestedThemeVariant,
+                        out var slideshowImage))
+                {
+                    if (slideshowAltImage is DrawingImage imgAlt && slideshowImage is DrawingImage img &&
+                        TryGetResource("SlideShowBrush",
+                            Application.Current.RequestedThemeVariant,
+                            out var slideshowBrush))
+                    {
+                        UIHelper.SetButtonHover(SlideShowButton, slideshowBrush as SolidColorBrush);
+                        SlideShowButton.PointerEntered += (_, _) => { SlideShowImage.Source = imgAlt; };
+                        SlideShowButton.PointerExited += (_, _) => { SlideShowImage.Source = img; };
+                    }
+                }
             }
+
             GoToPicBox.KeyDown += async (_, e) => await GoToPicBox_OnKeyDown(e);
             Observable.EveryValueChanged(this, x => x.IsVisible, UIHelper.GetFrameProvider)
                 .Skip(1)
                 .Where(isVisible => !isVisible)
-                .Subscribe(_ =>
-                {
-                    SlideShowButton.Flyout.Hide();
-                });
+                .Subscribe(_ => { SlideShowButton.Flyout.Hide(); });
             // Determine if crop should be enabled every time it opens
             Observable.EveryValueChanged(this, x => x.IsOpen, UIHelper.GetFrameProvider)
                 .Where(x => x)
-                .Subscribe(_ =>
-                {
-                    DetermineIfCropShouldBeEnabled();
-                });
+                .Subscribe(_ => { DetermineIfCropShouldBeEnabled(); });
+
+            Item2.Header = $"2 {TranslationManager.Translation.SecAbbreviation}";
+            Item5.Header = $"5 {TranslationManager.Translation.SecAbbreviation}";
+            Item10.Header = $"10 {TranslationManager.Translation.SecAbbreviation}";
+            Item20.Header = $"20 {TranslationManager.Translation.SecAbbreviation}";
+            Item30.Header = $"30 {TranslationManager.Translation.SecAbbreviation}";
+            Item60.Header = $"60 {TranslationManager.Translation.SecAbbreviation}";
+            Item90.Header = $"90 {TranslationManager.Translation.SecAbbreviation}";
+            Item120.Header = $"120 {TranslationManager.Translation.SecAbbreviation}";
         };
     }
 

+ 87 - 64
src/PicView.Avalonia/Views/UC/Menus/SettingsMenu.axaml

@@ -12,6 +12,16 @@
     <Design.DataContext>
         <viewModels:MainViewModel />
     </Design.DataContext>
+
+    <UserControl.Resources>
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="ScrollingBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="StretchBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="LoopingBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="AutofitBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="SubDirBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="TopMostBrush" />
+    </UserControl.Resources>
+
     <StackPanel>
         <StackPanel.Styles>
             <Styles>
@@ -40,69 +50,54 @@
                     Width="359"
                     x:Name="TopBorder" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="Transparent"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
+                    Canvas.Left="-1"
+                    Canvas.Top="-1"
                     Classes="noBorderHover"
                     Command="{CompiledBinding Window.ShowAboutWindow}"
                     CornerRadius="8,0,0,0"
+                    FontSize="14"
+                    Foreground="{DynamicResource MainTextColor}"
                     Height="46"
+                    Icon="{StaticResource AboutImage}"
+                    IconHeight="19"
+                    IconMargin="15,0, 0,0"
+                    IconWidth="19"
+                    Text="{CompiledBinding Translation.About.Value,
+                                           Mode=OneWay}"
+                    TextMargin="15,0,6,0"
+                    TextMaxWidth="150"
                     ToolTip.Tip="{CompiledBinding Translation.AboutWindow.Value,
                                                   Mode=OneWay}"
-                    Width="178"
-                    x:Name="AboutWindowButton">
-                    <StackPanel Orientation="Horizontal">
-                        <Image
-                            Height="17"
-                            Margin="15,0,0,0"
-                            Source="{StaticResource AboutImage}"
-                            Width="17" />
-                        <TextBlock
-                            Classes="txt"
-                            FontSize="14"
-                            Foreground="{DynamicResource MainTextColor}"
-                            Margin="15,0,6,0"
-                            MaxWidth="150"
-                            Text="{CompiledBinding Translation.About.Value,
-                                                   Mode=OneWay}"
-                            VerticalAlignment="Center" />
-                    </StackPanel>
-                </Button>
+                    Width="180"
+                    x:Name="AboutWindowButton" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="Transparent"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
-                    Canvas.Left="178"
+                    Canvas.Left="179"
+                    Canvas.Top="-1"
                     Classes="noBorderHover"
                     Command="{Binding Window.ShowSettingsWindow}"
+                    Data="{StaticResource HammerGeometry}"
+                    FontSize="14"
+                    Foreground="{DynamicResource MainTextColor}"
                     Height="46"
+                    IconHeight="17"
+                    IconMargin="14,0,3,0"
+                    IconWidth="17"
+                    Text="{CompiledBinding Translation.Settings.Value,
+                                           Mode=OneWay}"
+                    TextMargin="10,0,6,0"
+                    TextMaxWidth="150"
                     ToolTip.Tip="{CompiledBinding Translation.ShowAllSettingsWindow.Value,
                                                   Mode=OneWay}"
                     Width="137"
-                    x:Name="SettingsButton">
-                    <StackPanel Orientation="Horizontal">
-                        <Path
-                            Data="M 1.9205 50.6968 L 5.3687 54.1450 C 7.2673 56 9.4279 55.8472 11.3484 53.7303 L 34.1981 28.8511 C 35.1365 29.5058 36.0094 29.4840 37.0788 29.2658 L 39.4140 28.7856 L 40.9636 30.3351 L 40.8545 31.4918 C 40.7017 32.6921 41.0508 33.6087 42.1637 34.7217 L 43.9971 36.5331 C 45.1100 37.6679 46.5939 37.7334 47.6634 36.6640 L 54.9309 29.3967 C 56.0000 28.3273 55.9347 26.8651 54.8218 25.7303 L 52.9884 23.8971 C 51.8755 22.7840 50.9369 22.3912 49.7583 22.5658 L 48.5801 22.6967 L 47.0958 21.2127 L 47.7506 18.6593 C 48.0563 17.3936 47.7287 16.3678 46.3539 15.0147 L 40.9636 9.6461 C 33.0414 1.7677 22.8933 2.0077 15.9532 9.0132 C 14.9930 9.9734 14.9057 11.2829 15.5168 12.2431 C 16.0187 13.0724 17.0881 13.5744 18.5503 13.2034 C 21.9330 12.3522 25.3157 12.6141 28.6330 14.8620 L 27.2362 18.3975 C 26.7124 19.7069 26.7561 20.7762 27.2799 21.7583 L 2.3352 44.7171 C .2401 46.6594 0 48.7763 1.9205 50.6968 Z M 19.4233 9.8861 C 25.3812 5.4341 32.8013 6.1542 38.1700 11.5229 L 44.0404 17.3499 C 44.5643 17.8737 44.6300 18.2883 44.4771 18.9431 L 43.6480 22.4349 L 47.1615 25.9485 L 49.3002 25.7521 C 49.9331 25.6866 50.1293 25.7303 50.6531 26.2322 L 52.0284 27.6290 L 45.8957 33.7833 L 44.4990 32.3866 C 43.9971 31.8846 43.9533 31.6882 44.0190 31.0553 L 44.2152 28.8947 L 40.7236 25.4029 L 37.1006 26.1013 C 36.4677 26.2322 36.1404 26.2322 35.5948 25.6866 L 30.7499 20.8199 C 30.2261 20.2961 30.1606 19.9906 30.4443 19.2922 L 32.5831 14.1855 C 29.0040 10.7591 24.2682 8.8604 19.7070 10.3226 C 19.5106 10.3881 19.3796 10.3444 19.3142 10.2571 C 19.2487 10.1480 19.2487 10.0389 19.4233 9.8861 Z M 4.7576 49.1255 C 3.6446 48.0125 4.0374 47.3359 4.7794 46.6594 L 29.2877 24.0499 L 32.0156 26.7996 L 9.3406 51.2206 C 8.6641 51.9626 7.8130 52.1808 6.8964 51.2861 Z"
-                            Fill="{DynamicResource MainTextColor}"
-                            Height="17"
-                            Margin="14,0,3,0"
-                            Stretch="Fill"
-                            Width="17" />
-                        <TextBlock
-                            Classes="txt"
-                            FontSize="14"
-                            Foreground="{DynamicResource MainTextColor}"
-                            Margin="10,0,6,0"
-                            MaxWidth="150"
-                            Text="{CompiledBinding Translation.Settings.Value,
-                                                   Mode=OneWay}"
-                            VerticalAlignment="Center" />
-                    </StackPanel>
-                </Button>
-
-
+                    x:Name="SettingsButton" />
 
                 <customControls:IconButton
                     Background="Transparent"
@@ -127,13 +122,18 @@
                     Canvas.Top="53"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.ToggleScrollCommand}"
-                    Foreground="{DynamicResource MainTextColor}"
+                    Foreground="{StaticResource ScrollingBrush}"
                     Height="46"
                     IsChecked="{CompiledBinding GlobalSettings.IsScrollingEnabled.Value}"
                     ToolTip.Tip="{CompiledBinding Translation.ToggleScroll.Value,
                                                   Mode=OneWay}"
-                    Width="169">
-                    <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.IsScrolling.Value, Mode=OneWay}" />
+                    Width="169"
+                    x:Name="ScrollButton">
+                    <TextBlock
+                        Classes="txt btnTxt"
+                        Foreground="{StaticResource ScrollingBrush}"
+                        Text="{CompiledBinding Translation.IsScrolling.Value,
+                                               Mode=OneWay}" />
                 </ToggleButton>
 
                 <ToggleButton
@@ -142,14 +142,19 @@
                     Canvas.Top="53"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.StretchedCommand}"
-                    Foreground="{DynamicResource MainTextColor}"
+                    Foreground="{StaticResource StretchBrush}"
                     Height="46"
                     IsChecked="{CompiledBinding GlobalSettings.IsStretched.Value,
                                                 Mode=TwoWay}"
                     ToolTip.Tip="{CompiledBinding Translation.Stretch.Value,
                                                   Mode=OneWay}"
-                    Width="169">
-                    <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.Stretch.Value, Mode=OneWay}" />
+                    Width="169"
+                    x:Name="StretchButton">
+                    <TextBlock
+                        Classes="txt btnTxt"
+                        Foreground="{StaticResource StretchBrush}"
+                        Text="{CompiledBinding Translation.Stretch.Value,
+                                               Mode=OneWay}" />
                 </ToggleButton>
 
                 <ToggleButton
@@ -158,13 +163,18 @@
                     Canvas.Top="104"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.ToggleLoopingCommand}"
-                    Foreground="{DynamicResource MainTextColor}"
+                    Foreground="{StaticResource LoopingBrush}"
                     Height="46"
                     IsChecked="{CompiledBinding GlobalSettings.IsLooping.Value}"
                     ToolTip.Tip="{CompiledBinding Translation.ToggleLooping.Value,
                                                   Mode=OneWay}"
-                    Width="169">
-                    <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.IsLooping.Value, Mode=OneWay}" />
+                    Width="169"
+                    x:Name="LoopingButton">
+                    <TextBlock
+                        Classes="txt btnTxt"
+                        Foreground="{StaticResource LoopingBrush}"
+                        Text="{CompiledBinding Translation.IsLooping.Value,
+                                               Mode=OneWay}" />
                 </ToggleButton>
 
                 <ToggleButton
@@ -173,13 +183,18 @@
                     Canvas.Top="104"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.ChangeAutoFitCommand}"
-                    Foreground="{DynamicResource MainTextColor}"
+                    Foreground="{StaticResource AutofitBrush}"
                     Height="46"
                     IsChecked="{CompiledBinding GlobalSettings.IsAutoFit.Value}"
                     ToolTip.Tip="{CompiledBinding Translation.AutoFitWindow.Value,
                                                   Mode=OneWay}"
-                    Width="169">
-                    <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.AutoFitWindow.Value, Mode=OneWay}" />
+                    Width="169"
+                    x:Name="AutofitButton">
+                    <TextBlock
+                        Classes="txt btnTxt"
+                        Foreground="{StaticResource AutofitBrush}"
+                        Text="{CompiledBinding Translation.AutoFitWindow.Value,
+                                               Mode=OneWay}" />
                 </ToggleButton>
 
                 <ToggleButton
@@ -188,13 +203,18 @@
                     Canvas.Top="155"
                     Classes="ButtonBorder altHover"
                     Command="{Binding Tools.ChangeTopMostCommand}"
-                    Foreground="{DynamicResource MainTextColor}"
+                    Foreground="{StaticResource TopMostBrush}"
                     Height="46"
                     IsChecked="{CompiledBinding GlobalSettings.IsTopMost.Value}"
                     ToolTip.Tip="{CompiledBinding Translation.StayTopMost.Value,
                                                   Mode=OneWay}"
-                    Width="169">
-                    <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.StayTopMost.Value, Mode=OneWay}" />
+                    Width="169"
+                    x:Name="TopMostButton">
+                    <TextBlock
+                        Classes="txt btnTxt"
+                        Foreground="{StaticResource TopMostBrush}"
+                        Text="{CompiledBinding Translation.StayTopMost.Value,
+                                               Mode=OneWay}" />
                 </ToggleButton>
 
                 <ToggleButton
@@ -203,13 +223,16 @@
                     Canvas.Top="155"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.ToggleSubdirectoriesCommand}"
-                    Foreground="{DynamicResource MainTextColor}"
+                    Foreground="{StaticResource SubDirBrush}"
                     Height="46"
                     IsChecked="{CompiledBinding GlobalSettings.IsIncludingSubdirectories.Value}"
-                    ToolTip.Tip="{CompiledBinding Translation.SearchSubdirectory.Value,
-                                                  Mode=OneWay}"
-                    Width="169">
-                    <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.SearchSubdirectory.Value, Mode=OneWay}" />
+                    Width="169"
+                    x:Name="SubDirButton">
+                    <TextBlock
+                        Classes="txt btnTxt"
+                        Foreground="{StaticResource SubDirBrush}"
+                        Text="{CompiledBinding Translation.SearchSubdirectory.Value,
+                                               Mode=OneWay}" />
                 </ToggleButton>
             </Canvas>
         </Border>

+ 66 - 7
src/PicView.Avalonia/Views/UC/Menus/SettingsMenu.axaml.cs

@@ -1,5 +1,7 @@
+using Avalonia;
 using Avalonia.Media;
 using PicView.Avalonia.CustomControls;
+using PicView.Avalonia.UI;
 
 namespace PicView.Avalonia.Views.UC.Menus;
 
@@ -10,19 +12,76 @@ public partial class SettingsMenu : AnimatedMenu
         InitializeComponent();
         Loaded += (_, _) =>
         {
-            if (Settings.Theme.GlassTheme)
+            if (Settings.Theme.GlassTheme || !Settings.Theme.Dark)
             {
-                SettingsButton.Classes.Remove("noBorderHover");
-                SettingsButton.Classes.Add("hover");
-                
-                AboutWindowButton.Classes.Remove("noBorderHover");
-                AboutWindowButton.Classes.Add("hover");
+                UIHelper.SwitchHoverBorderClass(SettingsButton);
+                UIHelper.SwitchHoverBorderClass(AboutWindowButton);
             }
-            else if (!Settings.Theme.Dark)
+
+            if (!Settings.Theme.Dark && !Settings.Theme.GlassTheme)
             {
                 TopBorder.Background = Brushes.White;
+                UIHelper.SwitchHoverClass(StretchButton);
+                UIHelper.SwitchHoverClass(ScrollButton);
+                UIHelper.SwitchHoverClass(LoopingButton);
+                UIHelper.SwitchHoverClass(AutofitButton);
+                UIHelper.SwitchHoverClass(TopMostButton);
+                UIHelper.SwitchHoverClass(SubDirButton);
             }
         };
 
+        if (TryGetResource("ScrollingBrush", Application.Current.RequestedThemeVariant,
+                out var scrollingBrush))
+        {
+            if (scrollingBrush is SolidColorBrush brush)
+            {
+                UIHelper.SetButtonHover(ScrollButton, brush);
+            }
+        }
+
+        if (TryGetResource("StretchBrush", Application.Current.RequestedThemeVariant,
+                out var stretchBrush))
+        {
+            if (stretchBrush is SolidColorBrush brush)
+            {
+                UIHelper.SetButtonHover(StretchButton, brush);
+            }
+        }
+
+        if (TryGetResource("LoopingBrush", Application.Current.RequestedThemeVariant,
+                out var loopingBrush))
+        {
+            if (loopingBrush is SolidColorBrush brush)
+            {
+                UIHelper.SetButtonHover(LoopingButton, brush);
+            }
+        }
+
+        if (TryGetResource("AutofitBrush", Application.Current.RequestedThemeVariant,
+                out var autofitBrush))
+        {
+            if (autofitBrush is SolidColorBrush brush)
+            {
+                UIHelper.SetButtonHover(AutofitButton, brush);
+            }
+        }
+
+        if (TryGetResource("TopMostBrush", Application.Current.RequestedThemeVariant,
+                out var topMostBrush))
+        {
+            if (topMostBrush is SolidColorBrush brush)
+            {
+                UIHelper.SetButtonHover(TopMostButton, brush);
+            }
+        }
+
+        if (TryGetResource("SubDirBrush", Application.Current.RequestedThemeVariant,
+                out var subDirBrush))
+        {
+            if (subDirBrush is SolidColorBrush brush)
+            {
+                UIHelper.SetButtonHover(SubDirButton, brush);
+            }
+        }
     }
 }

+ 100 - 130
src/PicView.Avalonia/Views/UC/Menus/ToolsMenu.axaml

@@ -12,16 +12,18 @@
     <Design.DataContext>
         <viewModels:MainViewModel />
     </Design.DataContext>
+
+    <UserControl.Resources>
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="BatchBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="EffectsBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="ImageInfoBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="OptimizeBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="ChangeBgBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="KeybindingBrush" />
+        <SolidColorBrush Color="{DynamicResource MainTextColor}" x:Key="InterfaceBrush" />
+    </UserControl.Resources>
+
     <StackPanel>
-        <StackPanel.Styles>
-            <Styles>
-                <Style Selector="TextBlock.btnTxt">
-                    <Setter Property="MaxWidth" Value="132" />
-                    <Setter Property="VerticalAlignment" Value="Center" />
-                    <Setter Property="Foreground" Value="{DynamicResource MainTextColor}" />
-                </Style>
-            </Styles>
-        </StackPanel.Styles>
 
         <Border
             Background="{DynamicResource MenuBackgroundColor}"
@@ -40,72 +42,53 @@
                     Width="359"
                     x:Name="TopBorder" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="Transparent"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
+                    Canvas.Left="-1"
+                    Canvas.Top="-1"
                     Classes="noBorderHover"
                     Command="{CompiledBinding Window.ShowBatchResizeWindow}"
                     CornerRadius="8,0,0,0"
-                    Height="45"
+                    FontSize="14"
+                    Foreground="{StaticResource BatchBrush}"
+                    Height="46"
+                    Icon="{StaticResource BatchResizeImage}"
+                    IconHeight="22"
+                    IconMargin="17,0,0,0"
+                    IconWidth="22"
+                    Text="{CompiledBinding Translation.BatchResize.Value,
+                                           Mode=OneWay}"
+                    TextMargin="10,0,7,0"
+                    TextMaxWidth="130"
                     ToolTip.Tip="{CompiledBinding Translation.BatchResize.Value,
                                                   Mode=OneWay}"
-                    Width="179"
-                    x:Name="BatchResizeButton">
-                    <StackPanel Orientation="Horizontal">
-                        <Image
-                            Height="22"
-                            Margin="17,0,0,0"
-                            Source="{StaticResource BatchResizeImage}"
-                            Width="22" />
-                        <TextBlock
-                            Classes="txt"
-                            FontSize="14"
-                            Foreground="{DynamicResource MainTextColor}"
-                            Margin="10,0,7,0"
-                            MaxWidth="130"
-                            Text="{CompiledBinding Translation.BatchResize.Value,
-                                                   Mode=OneWay}"
-                            VerticalAlignment="Center" />
-                    </StackPanel>
-                </Button>
+                    Width="180"
+                    x:Name="BatchResizeButton" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="Transparent"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
                     Canvas.Left="179"
+                    Canvas.Top="-1"
                     Classes="noBorderHover"
                     Command="{CompiledBinding Window.ShowEffectsWindow}"
+                    FontSize="14"
+                    Foreground="{StaticResource EffectsBrush}"
                     Height="45"
+                    Icon="{StaticResource FlaskImage}"
+                    IconHeight="20"
+                    IconMargin="11,0,0,0"
+                    IconWidth="20"
+                    Text="{CompiledBinding Translation.Effects.Value,
+                                           Mode=OneWay}"
+                    TextMargin="10,0,6,0"
                     ToolTip.Tip="{CompiledBinding Translation.EffectsTooltip.Value,
                                                   Mode=OneWay}"
                     Width="136"
-                    x:Name="EffectsButton">
-                    <StackPanel Orientation="Horizontal">
-                        <Image
-                            Height="20"
-                            Margin="11,0,0,0"
-                            Width="20">
-                            <DrawingImage>
-                                <DrawingImage.Drawing>
-                                    <DrawingGroup ClipGeometry="M0,0 V512 H512 V0 H0 Z">
-                                        <GeometryDrawing Brush="{DynamicResource Brush0}" Geometry="F1 M512,512z M0,0z M469.11,382.76L325,153.92 325,74 357,74 357,32 155,32 155,74 187,74 187,153.92 42.89,382.76C29.89,403.4 28.11,426.49 39.89,447.86 51.67,469.23 71.59,480 96,480L416,480C440.41,480 460.32,469.24 472.1,447.86 483.88,426.48 482.14,403.4 469.11,382.76z M224.39,173.39A29.76,29.76,0,0,0,229.01,157.39L229.01,74 283.01,74 283.01,158.59A25.85,25.85,0,0,0,287.01,172.41L356.82,283 155.18,283z" />
-                                    </DrawingGroup>
-                                </DrawingImage.Drawing>
-                            </DrawingImage>
-                        </Image>
-                        <TextBlock
-                            Classes="txt"
-                            FontSize="14"
-                            Foreground="{DynamicResource MainTextColor}"
-                            Margin="10,0,6,0"
-                            MaxWidth="130"
-                            Text="{CompiledBinding Translation.Effects.Value,
-                                                   Mode=OneWay}"
-                            VerticalAlignment="Center" />
-                    </StackPanel>
-                </Button>
+                    x:Name="EffectsButton" />
 
                 <customControls:IconButton
                     Background="Transparent"
@@ -124,130 +107,117 @@
                                                   Mode=OneWay}"
                     Width="45" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="{DynamicResource MenuButtonColor}"
                     Canvas.Left="7"
                     Canvas.Top="53"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Window.ShowExifWindow}"
+                    Data="{StaticResource ImageInfoGeometry}"
+                    Foreground="{StaticResource ImageInfoBrush}"
                     Height="46"
-                    Width="169">
-                    <StackPanel Orientation="Horizontal">
-                        <Path
-                            Data="{StaticResource ImageInfoGeometry}"
-                            Fill="{DynamicResource Brush0}"
-                            Height="17"
-                            Margin="9,0,2,0"
-                            Stretch="Fill"
-                            Width="17" />
-                        <TextBlock
-                            Classes="txt btnTxt"
-                            Margin="10,0,6,0"
-                            Text="{CompiledBinding Translation.ImageInfo.Value,
-                                                   Mode=OneWay}" />
-                    </StackPanel>
-                </Button>
+                    IconHeight="17"
+                    IconMargin="9,0,2,0"
+                    IconWidth="17"
+                    Text="{CompiledBinding Translation.ImageInfo.Value,
+                                           Mode=OneWay}"
+                    TextMargin="10,0,6,0"
+                    TextMaxWidth="132"
+                    Width="169"
+                    x:Name="ImageInfoButton" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="{DynamicResource MenuButtonColor}"
                     Canvas.Left="182"
                     Canvas.Top="53"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.OptimizeImageCommand}"
+                    Foreground="{StaticResource OptimizeBrush}"
                     Height="46"
+                    Icon="{StaticResource PortalImage}"
+                    IconHeight="20"
+                    IconMargin="9,0,10,0"
+                    IconWidth="20"
                     IsEnabled="{CompiledBinding PicViewer.ShouldOptimizeImageBeEnabled.Value,
                                                 Mode=OneWay}"
+                    Text="{CompiledBinding Translation.OptimizeImage.Value,
+                                           Mode=OneWay}"
                     ToolTip.Tip="{CompiledBinding Translation.OptimizeImage.Value,
                                                   Mode=OneWay}"
                     Width="169"
-                    x:Name="OptimizeImageButton">
-                    <StackPanel Orientation="Horizontal">
-                        <Image
-                            Height="20"
-                            Margin="9,0,10,0"
-                            Source="{StaticResource PortalImage}"
-                            Width="20" />
-                        <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.OptimizeImage.Value, Mode=OneWay}" />
-                    </StackPanel>
-                </Button>
+                    x:Name="OptimizeImageButton" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="{DynamicResource MenuButtonColor}"
                     Canvas.Left="7"
                     Canvas.Top="104"
                     Classes="ButtonBorder altHover"
+                    Data="{StaticResource BrushGeometry}"
+                    Foreground="{DynamicResource DisabledTextColor}"
                     Height="46"
+                    IconHeight="17"
+                    IconMargin="9,0,12,0"
+                    IconWidth="17"
                     IsEnabled="False"
+                    Text="{CompiledBinding Translation.ColorPickerTool.Value,
+                                           Mode=OneWay}"
                     ToolTip.Tip="{CompiledBinding Translation.ColorPickerToolTooltip.Value,
                                                   Mode=OneWay}"
-                    Width="169">
-                    <StackPanel Orientation="Horizontal">
-                        <Path
-                            Data="{StaticResource BrushGeometry}"
-                            Fill="{DynamicResource Brush0}"
-                            Height="17"
-                            Margin="9,0,12,0"
-                            Stretch="Fill"
-                            Width="17" />
-                        <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.ColorPickerTool.Value, Mode=OneWay}" />
-                    </StackPanel>
-                </Button>
+                    Width="169" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="{DynamicResource MenuButtonColor}"
                     Canvas.Left="182"
                     Canvas.Top="104"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.ChangeBackgroundCommand}"
+                    Foreground="{StaticResource ChangeBgBrush}"
                     Height="46"
-                    Width="169">
-                    <StackPanel Orientation="Horizontal">
-                        <Image
-                            Height="20"
-                            Margin="10,0,10,0"
-                            Source="{StaticResource CanvasImage}"
-                            Width="20" />
-                        <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.ChangeBackground.Value, Mode=OneWay}" />
-                    </StackPanel>
-                </Button>
+                    Icon="{StaticResource PaletteImage}"
+                    IconHeight="17"
+                    IconMargin="10,0,10,0"
+                    IconWidth="17"
+                    Text="{CompiledBinding Translation.ChangeBackground.Value,
+                                           Mode=OneWay}"
+                    Width="169"
+                    x:Name="ChangeBgButton" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="{DynamicResource MenuButtonColor}"
                     Canvas.Left="7"
                     Canvas.Top="155"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Window.ShowKeybindingsWindow}"
+                    Foreground="{StaticResource KeybindingBrush}"
                     Height="46"
+                    Icon="{StaticResource ShortcutsImage}"
+                    IconHeight="18"
+                    IconMargin="9,0,12,0"
+                    IconWidth="18"
+                    Text="{CompiledBinding Translation.ApplicationShortcuts.Value,
+                                           Mode=OneWay}"
                     ToolTip.Tip="{CompiledBinding Translation.ApplicationShortcuts.Value,
                                                   Mode=OneWay}"
-                    Width="169">
-                    <StackPanel Orientation="Horizontal">
-                        <Image
-                            Height="17"
-                            Margin="9,0,12,0"
-                            Source="{StaticResource ShortcutsImage}"
-                            Width="17" />
-                        <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.ApplicationShortcuts.Value, Mode=OneWay}" />
-                    </StackPanel>
-                </Button>
+                    Width="169"
+                    x:Name="KeybindingsButton" />
 
-                <Button
+                <customControls:TextIconButton
                     Background="{DynamicResource MenuButtonColor}"
                     Canvas.Left="182"
                     Canvas.Top="155"
                     Classes="ButtonBorder altHover"
                     Command="{CompiledBinding Tools.ToggleUICommand}"
+                    Foreground="{StaticResource InterfaceBrush}"
                     Height="46"
-                    Width="169">
-                    <StackPanel Orientation="Horizontal">
-                        <Image
-                            Height="18"
-                            Margin="11,0,12,0"
-                            Source="{StaticResource EyeOffImage}"
-                            Width="18" />
-                        <TextBlock Classes="txt btnTxt" Text="{CompiledBinding Translation.IsShowingUI.Value, Mode=OneWay}" />
-                    </StackPanel>
-                </Button>
+                    Icon="{StaticResource EyeOffImage}"
+                    IconHeight="20"
+                    IconMargin="11,0,12,0"
+                    IconWidth="20"
+                    Text="{CompiledBinding Translation.IsShowingUI.Value,
+                                           Mode=OneWay}"
+                    Width="169"
+                    x:Name="InterfaceButton" />
+
             </Canvas>
         </Border>
         <Polygon

+ 94 - 5
src/PicView.Avalonia/Views/UC/Menus/ToolsMenu.axaml.cs

@@ -1,3 +1,4 @@
+using Avalonia;
 using Avalonia.Media;
 using PicView.Avalonia.Converters;
 using PicView.Avalonia.CustomControls;
@@ -18,20 +19,108 @@ public partial class ToolsMenu : AnimatedMenu
             {
                 BatchResizeButton.Classes.Remove("noBorderHover");
                 BatchResizeButton.Classes.Add("hover");
-                
+
                 EffectsButton.Classes.Remove("noBorderHover");
                 EffectsButton.Classes.Add("hover");
             }
             else if (!Settings.Theme.Dark)
             {
                 TopBorder.Background = Brushes.White;
+
+                // Batch
+                BatchResizeButton.Classes.Remove("noBorderHover");
+                BatchResizeButton.Classes.Add("noBorderHoverAlt");
+                if (TryGetResource("BatchBrush", Application.Current.RequestedThemeVariant,
+                        out var batchBrush))
+                {
+                    if (batchBrush is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(BatchResizeButton, brush);
+                    }
+                }
+
+                // Effects
+                EffectsButton.Classes.Remove("noBorderHover");
+                EffectsButton.Classes.Add("noBorderHoverAlt");
+                if (TryGetResource("EffectsBrush", Application.Current.RequestedThemeVariant,
+                        out var effectsBrush))
+                {
+                    if (effectsBrush is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(EffectsButton, brush);
+                    }
+                }
+
+                // Image info
+                ImageInfoButton.Classes.Remove("altHover");
+                ImageInfoButton.Classes.Add("hover");
+                if (TryGetResource("ImageInfoBrush", Application.Current.RequestedThemeVariant,
+                        out var imageInfoBrush))
+                {
+                    if (imageInfoBrush is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(ImageInfoButton, brush);
+                    }
+                }
+
+                // Optimize
+                OptimizeImageButton.Classes.Remove("altHover");
+                OptimizeImageButton.Classes.Add("hover");
+                if (TryGetResource("OptimizeBrush", Application.Current.RequestedThemeVariant,
+                        out var optimizeBrush))
+                {
+                    if (optimizeBrush is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(OptimizeImageButton, brush);
+                    }
+                }
+
+                // Change bg
+                ChangeBgButton.Classes.Remove("altHover");
+                ChangeBgButton.Classes.Add("hover");
+                if (TryGetResource("ChangeBgBrush", Application.Current.RequestedThemeVariant,
+                        out var changeBgBrush))
+                {
+                    if (changeBgBrush is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(ChangeBgButton, brush);
+                    }
+                }
+
+                // Keybindings
+                KeybindingsButton.Classes.Remove("altHover");
+                KeybindingsButton.Classes.Add("hover");
+                if (TryGetResource("KeybindingBrush", Application.Current.RequestedThemeVariant,
+                        out var keybindingBrush) && Application.Current.TryGetResource("ShortcutsImageAlt",
+                        Application.Current.RequestedThemeVariant,
+                        out var shortcutsImageAlt) && Application.Current.TryGetResource("ShortcutsImage",
+                        Application.Current.RequestedThemeVariant,
+                        out var shortcutsImage))
+                {
+                    if (keybindingBrush is SolidColorBrush brush && shortcutsImageAlt is DrawingImage imgAlt &&
+                        shortcutsImage is DrawingImage img)
+                    {
+                        UIHelper.SetButtonHover(KeybindingsButton, brush);
+                        KeybindingsButton.PointerEntered += (_, _) => { KeybindingsButton.Icon = imgAlt; };
+                        KeybindingsButton.PointerExited += (_, _) => { KeybindingsButton.Icon = img; };
+                    }
+                }
+
+                InterfaceButton.Classes.Remove("altHover");
+                InterfaceButton.Classes.Add("hover");
+                if (TryGetResource("InterfaceBrush", Application.Current.RequestedThemeVariant,
+                        out var interfaceBrush))
+                {
+                    if (interfaceBrush is SolidColorBrush brush)
+                    {
+                        UIHelper.SetButtonHover(InterfaceButton, brush);
+                    }
+                }
             }
+
             Observable.EveryValueChanged(this, x => x.IsOpen, UIHelper.GetFrameProvider)
                 .Skip(1)
-                .Subscribe(_ =>
-                {
-                    DetermineIfOptimizeImageShouldBeEnabled();
-                });
+                .Subscribe(_ => { DetermineIfOptimizeImageShouldBeEnabled(); });
         };
     }
 

部分文件因为文件数量过多而无法显示