Browse Source

Added custom right click mode when using ShapeTool

CPKreuz 2 years ago
parent
commit
8c999f4ae6

+ 5 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -564,5 +564,9 @@
   "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE": "Drag handles to scale transform. Hold Shift to scale proportionally. Drag outside handles to rotate.",
   "TRANSFORM_ACTION_DISPLAY_SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE": "Drag handles to scale transform. Hold Shift to scale proportionally.",
   "CROP_TO_SELECTION": "Crop to selection",
-  "CROP_TO_SELECTION_DESCRIPTIVE": "Crop image to selection"
+  "CROP_TO_SELECTION_DESCRIPTIVE": "Crop image to selection",
+  "SHOW_CONTEXT_MENU": "Show context menu",
+  "ERASE": "Erase",
+  "USE_SECONDARY_COLOR": "Use secondary color",
+  "RIGHT_CLICK_MODE": "Right click mode"
 }

+ 22 - 0
src/PixiEditor/Helpers/Extensions/EnumHelpers.cs

@@ -27,4 +27,26 @@ internal static class EnumHelpers
 
         return description;
     }
+    
+    public static string GetDescription(object enumValue)
+    {
+        if (!enumValue.GetType().IsEnum)
+        {
+            throw new ArgumentException("enumValue must be a enum", nameof(enumValue));
+        }
+        
+        var description = enumValue.ToString();
+        var fieldInfo = enumValue.GetType().GetField(enumValue.ToString());
+
+        if (fieldInfo != null)
+        {
+            var attrs = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
+            if (attrs != null && attrs.Length > 0)
+            {
+                description = ((DescriptionAttribute)attrs[0]).Description;
+            }
+        }
+
+        return description;
+    }
 }

+ 13 - 0
src/PixiEditor/Models/Enums/RightClickMode.cs

@@ -0,0 +1,13 @@
+using System.ComponentModel;
+
+namespace PixiEditor.Models.Enums;
+
+public enum RightClickMode
+{
+    [Description("SHOW_CONTEXT_MENU")]
+    ContextMenu,
+    [Description("ERASE")]
+    Erase,
+    [Description("USE_SECONDARY_COLOR")]
+    SecondaryColor
+}

+ 6 - 0
src/PixiEditor/Models/UserPreferences/PreferencesSettings.cs

@@ -176,6 +176,12 @@ internal class PreferencesSettings : IPreferences
         if (!dict.ContainsKey(name)) return fallbackValue;
         var preference = dict[name];
         if (typeof(T) == preference.GetType()) return (T)preference;
+
+        if (typeof(T).IsEnum)
+        {
+            return (T)Enum.Parse(typeof(T), preference.ToString());
+        }
+        
         if (preference.GetType() == typeof(JArray))
         {
             return ((JArray)preference).ToObject<T>();

+ 27 - 1
src/PixiEditor/Styles/ComboBoxDarkStyle.xaml

@@ -2,7 +2,8 @@
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2"
                     xmlns:controls="clr-namespace:PixiEditor.Views.UserControls"
-                    xmlns:sys="clr-namespace:System;assembly=mscorlib">
+                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
+                    xmlns:views="clr-namespace:PixiEditor.Views">
 
     <SolidColorBrush x:Key="ComboBox.Static.Background" Color="#252525" />
     <SolidColorBrush x:Key="ComboBox.Static.Border" Color="#2F2F37" />
@@ -235,4 +236,29 @@
             </Setter.Value>
         </Setter>
     </Style>
+    
+    <Style TargetType="ComboBox" x:Key="TranslatedEnum">
+        <Setter Property="ItemTemplate">
+            <Setter.Value>
+                <DataTemplate>
+                    <TextBlock views:Translator.Enum="{Binding}"/>
+                </DataTemplate>
+            </Setter.Value>
+        </Setter>
+        <Setter Property="ItemContainerStyle">
+            <Setter.Value>
+                <Style TargetType="{x:Type ComboBoxItem}">
+                    <Setter Property="Template">
+                        <Setter.Value>
+                            <ControlTemplate TargetType="{x:Type ComboBoxItem}">
+                                <TextBlock views:Translator.Enum="{Binding}" />
+                            </ControlTemplate>
+                        </Setter.Value>
+                    </Setter>
+                </Style>
+            </Setter.Value>
+        </Setter>
+        <Setter Property="Foreground" Value="White" />
+        <Setter Property="Template" Value="{DynamicResource DarkComboBox}" />
+    </Style>
 </ResourceDictionary>

+ 80 - 11
src/PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs

@@ -1,12 +1,12 @@
 using System.Windows;
 using System.Windows.Input;
-using ChunkyImageLib.DataHolders;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Enums;
 using PixiEditor.Models.Events;
 using PixiEditor.ViewModels.SubViewModels.Document;
 using PixiEditor.ViewModels.SubViewModels.Tools;
@@ -17,6 +17,11 @@ namespace PixiEditor.ViewModels.SubViewModels.Main;
 #nullable enable
 internal class IoViewModel : SubViewModel<ViewModelMain>
 {
+    private bool hadSwapped;
+    private int? previousEraseSize;
+    private bool hadSharedToolbar;
+    private bool? drawingWithRight;
+    
     public RelayCommand MouseMoveCommand { get; set; }
     public RelayCommand MouseDownCommand { get; set; }
     public RelayCommand PreviewMouseMiddleButtonCommand { get; set; }
@@ -150,16 +155,56 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
 
     private void OnMouseDown(object? sender, MouseOnCanvasEventArgs args)
     {
-        if (args.Button == MouseButton.Left)
+        if (drawingWithRight != null || args.Button is not (MouseButton.Left or MouseButton.Right))
+            return;
+
+        if (args.Button == MouseButton.Right && !HandleRightMouseDown())
+            return;
+
+        var docManager = Owner.DocumentManagerSubViewModel;
+        var activeDocument = docManager.ActiveDocument;
+        if (activeDocument == null)
+            return;
+
+        drawingWithRight = args.Button == MouseButton.Right;
+        Owner.ToolsSubViewModel.LeftMouseButtonDownInlet(args.PositionOnCanvas);
+        activeDocument.EventInlet.OnCanvasLeftMouseButtonDown(args.PositionOnCanvas);
+    }
+
+    private bool HandleRightMouseDown()
+    {
+        var tools = Owner.ToolsSubViewModel;
+
+        if (tools.ActiveTool is not ShapeTool || tools.RightClickMode == RightClickMode.ContextMenu)
         {
-            DocumentManagerViewModel docManager = Owner.DocumentManagerSubViewModel;
-            DocumentViewModel? activeDocument = docManager.ActiveDocument;
-            if (activeDocument == null)
-                return;
+            return false;
+        }
 
-            Owner.ToolsSubViewModel.LeftMouseButtonDownInlet(args.PositionOnCanvas);
-            activeDocument.EventInlet.OnCanvasLeftMouseButtonDown(args.PositionOnCanvas);
+        if (tools.RightClickMode == RightClickMode.Erase)
+        {
+            var currentToolSize = tools.ActiveTool.Toolbar.Settings.FirstOrDefault(x => x.Name == "ToolSize");
+            hadSharedToolbar = tools.EnableSharedToolbar;
+            if (currentToolSize != null)
+            {
+                tools.EnableSharedToolbar = false;
+                var toolSize = tools.GetTool<EraserToolViewModel>().Toolbar.Settings.First(x => x.Name == "ToolSize");
+                previousEraseSize = (int)toolSize.Value;
+                toolSize.Value = tools.ActiveTool is PenToolViewModel { PixelPerfectEnabled: true } ? 1 : currentToolSize.Value;
+            }
+            else
+            {
+                previousEraseSize = null;
+            }
+
+            tools.SetActiveTool<EraserToolViewModel>(true);
         }
+        else if (tools.RightClickMode == RightClickMode.SecondaryColor)
+        {
+            Owner.ColorsSubViewModel.SwapColors(null);
+            hadSwapped = true;
+        }
+
+        return true;
     }
 
     private void OnPreviewMiddleMouseButton(object sender)
@@ -177,15 +222,39 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
 
     private void OnMouseUp(object? sender, MouseButton button)
     {
+        if (drawingWithRight == null || (button == MouseButton.Left && drawingWithRight.Value) ||
+            (button == MouseButton.Right && !drawingWithRight.Value))
+            return;
+
         if (Owner.DocumentManagerSubViewModel.ActiveDocument is null)
             return;
-        if (button == MouseButton.Left)
+        var tools = Owner.ToolsSubViewModel;
+
+        var rightCanUp = (button == MouseButton.Right && tools.RightClickMode == RightClickMode.Erase || tools.RightClickMode == RightClickMode.SecondaryColor);
+        
+        if (button == MouseButton.Left || rightCanUp)
         {
             Owner.DocumentManagerSubViewModel.ActiveDocument.EventInlet.OnCanvasLeftMouseButtonUp();
         }
-        else if (button == MouseButton.Middle)
+        
+        drawingWithRight = null;
+
+        switch (button)
         {
-            Owner.ToolsSubViewModel.RestorePreviousTool();
+            case MouseButton.Middle:
+            case MouseButton.Right when tools.RightClickMode == RightClickMode.Erase:
+                tools.EnableSharedToolbar = hadSharedToolbar;
+                if (previousEraseSize != null)
+                {
+                    tools.GetTool<EraserToolViewModel>().Toolbar.Settings.First(x => x.Name == "ToolSize").Value = previousEraseSize.Value;
+                }
+                tools.RestorePreviousTool();
+                break;
+            case MouseButton.Right when hadSwapped && tools.RightClickMode == RightClickMode.SecondaryColor:
+                Owner.ColorsSubViewModel.SwapColors(null);
+                break;
         }
+
+        hadSwapped = false;
     }
 }

+ 34 - 2
src/PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Enums;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels.SubViewModels.Document;
@@ -16,10 +17,39 @@ namespace PixiEditor.ViewModels.SubViewModels.Main;
 [Command.Group("PixiEditor.Tools", "TOOLS")]
 internal class ToolsViewModel : SubViewModel<ViewModelMain>
 {
+    private RightClickMode rightClickMode;
+    
     public ZoomToolViewModel? ZoomTool => GetTool<ZoomToolViewModel>();
 
     public ToolViewModel? LastActionTool { get; private set; }
 
+    public RightClickMode RightClickMode
+    {
+        get => rightClickMode;
+        set
+        {
+            if (SetProperty(ref rightClickMode, value))
+            {
+                IPreferences.Current.UpdatePreference(nameof(RightClickMode), value);
+            }
+        }
+    }
+
+    public bool EnableSharedToolbar
+    {
+        get => IPreferences.Current.GetPreference<bool>(nameof(EnableSharedToolbar));
+        set
+        {
+            if (EnableSharedToolbar == value)
+            {
+                return;
+            }
+
+            IPreferences.Current.UpdatePreference(nameof(EnableSharedToolbar), value);
+            RaisePropertyChanged(nameof(EnableSharedToolbar));
+        }
+    }
+
     private Cursor? toolCursor;
     public Cursor? ToolCursor
     {
@@ -56,7 +86,9 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
 
     public ToolsViewModel(ViewModelMain owner)
         : base(owner)
-    { }
+    {
+        rightClickMode = IPreferences.Current.GetPreference<RightClickMode>(nameof(RightClickMode));
+    }
 
     public void SetupTools(IServiceProvider services)
     {
@@ -112,7 +144,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
             tool.Toolbar.GenerateSettings();
 
         if (ActiveTool != null) ActiveTool.IsTransient = false;
-        bool shareToolbar = IPreferences.Current.GetPreference<bool>("EnableSharedToolbar");
+        bool shareToolbar = EnableSharedToolbar;
         if (ActiveTool is not null)
         {
             ActiveTool.IsActive = false;

+ 11 - 2
src/PixiEditor/Views/Dialogs/SettingsWindow.xaml

@@ -6,7 +6,7 @@
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:PixiEditor.Views.Dialogs" 
         xmlns:sys="clr-namespace:System;assembly=System.Runtime"
-        xmlns:viewmodels="clr-namespace:PixiEditor.ViewModels" 
+        xmlns:vm="clr-namespace:PixiEditor.ViewModels" 
         xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" 
         xmlns:views="clr-namespace:PixiEditor.Views" 
         xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 
@@ -18,6 +18,7 @@
         xmlns:settingGroups="clr-namespace:PixiEditor.Views.Dialogs.SettingGroups"
         xmlns:localization="clr-namespace:PixiEditor.Localization"
         xmlns:helpers="clr-namespace:PixiEditor.Helpers"
+        xmlns:enums="clr-namespace:PixiEditor.Models.Enums"
         mc:Ignorable="d"
         Name="window" 
         Height="688" Width="780"
@@ -29,7 +30,7 @@
         FlowDirection="{helpers:Localization FlowDirection}"
         views:Translator.Key="SETTINGS">
     <Window.Resources>
-        <viewmodels:SettingsWindowViewModel x:Key="SettingsWindowViewModel"/>
+        <vm:SettingsWindowViewModel x:Key="SettingsWindowViewModel"/>
         <BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
     </Window.Resources>
 
@@ -144,6 +145,14 @@
                 </StackPanel>
 
                 <Label Style="{StaticResource SettingsHeader}" d:Content="Tools" views:Translator.Key="TOOLS"/>
+                
+                <StackPanel Margin="27 0" Orientation="Horizontal">
+                    <Label Style="{StaticResource SettingsText}"
+                           views:Translator.Key="RIGHT_CLICK_MODE"/>
+                    <ComboBox SelectedItem="{Binding RightClickMode, Source={vm:MainVM ToolsSubViewModel}, Mode=TwoWay}"
+                              ItemsSource="{helpers:Enum enums:RightClickMode}"
+                              Width="160" Style="{StaticResource TranslatedEnum}"/>
+                </StackPanel>
 
                 <CheckBox VerticalAlignment="Center" Margin="27 5"
                     IsChecked="{Binding SettingsSubViewModel.Tools.EnableSharedToolbar}" d:Content="Enable shared toolbar" views:Translator.Key="ENABLE_SHARED_TOOLBAR"/>

+ 1 - 0
src/PixiEditor/Views/MainWindow.xaml

@@ -623,6 +623,7 @@
                                             StylusOutOfRangeCommand="{cmds:Command PixiEditor.Stylus.StylusOutOfRange, UseProvided=True}"
                                             FlipX="{Binding FlipX, Mode=TwoWay}"
                                             FlipY="{Binding FlipY, Mode=TwoWay}"
+                                            ContextMenuOpening="Viewport_OnContextMenuOpening"
                                             Stylus.IsTapFeedbackEnabled="False" 
                                             Stylus.IsTouchFeedbackEnabled="False"
                                             Document="{Binding Document}">

+ 11 - 0
src/PixiEditor/Views/MainWindow.xaml.cs

@@ -9,9 +9,11 @@ using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Skia;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Enums;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels.SubViewModels.Document;
+using PixiEditor.ViewModels.SubViewModels.Tools;
 
 namespace PixiEditor.Views;
 
@@ -247,4 +249,13 @@ internal partial class MainWindow : Window
             e.Handled = true;
         }
     }
+
+    private void Viewport_OnContextMenuOpening(object sender, ContextMenuEventArgs e)
+    {
+        if (DataContext.ToolsSubViewModel.RightClickMode != RightClickMode.ContextMenu &&
+            DataContext.ToolsSubViewModel.ActiveTool is ShapeTool)
+        {
+            e.Handled = true;
+        }
+    }
 }

+ 23 - 2
src/PixiEditor/Views/Translator.cs

@@ -22,6 +22,12 @@ public class Translator : UIElement
         typeof(Translator),
         new FrameworkPropertyMetadata(default(LocalizedString), FrameworkPropertyMetadataOptions.AffectsRender, LocalizedStringPropertyChangedCallback));
 
+    public static readonly DependencyProperty EnumProperty = DependencyProperty.RegisterAttached(
+        "Enum",
+        typeof(object),
+        typeof(Translator),
+        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, EnumPropertyChangedCallback));
+
     public static readonly DependencyProperty TooltipKeyProperty = DependencyProperty.RegisterAttached(
         "TooltipKey", typeof(string), typeof(Translator), 
         new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.AffectsRender, TooltipKeyPropertyChangedCallback));
@@ -51,8 +57,7 @@ public class Translator : UIElement
 
         dependencyObject.SetValue(FrameworkElement.ToolTipProperty, newLocalizedString.Value);
     }
-
-
+    
     private static void TooltipKeyPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
         d.SetValue(FrameworkElement.ToolTipProperty, new LocalizedString(GetTooltipKey(d)).Value);
@@ -113,6 +118,17 @@ public class Translator : UIElement
         d.SetValue(KeyProperty, ((LocalizedString)e.NewValue).Key);
     }
 
+    private static void EnumPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
+    {
+        if (e.NewValue == null)
+        {
+            d.SetValue(KeyProperty, null);
+            return;
+        }
+        
+        d.SetValue(KeyProperty, EnumHelpers.GetDescription(e.NewValue));
+    }
+
     private static void UpdateKey(DependencyObject d, string key)
     {
         var parameters = GetLocalizedString(d).Parameters;
@@ -182,6 +198,11 @@ public class Translator : UIElement
         element.SetValue(LocalizedStringProperty, value);
     }
 
+    public static void SetEnum(DependencyObject element, object value)
+    {
+        element.SetValue(EnumProperty, value);
+    }
+
     public static LocalizedString GetLocalizedString(DependencyObject element)
     {
         return (LocalizedString)element.GetValue(LocalizedStringProperty);