Răsfoiți Sursa

Fixed brush overlay pixel perfect bug and disable tool size input when pixel perfect enabled

CPKreuz 2 ani în urmă
părinte
comite
9aaf48eec1

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

@@ -106,6 +106,8 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
             return;
         }
 
+        ActiveTool?.OnDeselecting();
+
         if (!tool.Toolbar.SettingsGenerated)
             tool.Toolbar.GenerateSettings();
 
@@ -157,8 +159,8 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
         SetActiveTool(tool.GetType(), false);
     }
 
-    [Command.Basic("PixiEditor.Tools.IncreaseSize", 1, "Increase Tool Size", "Increase Tool Size", Key = Key.OemCloseBrackets)]
-    [Command.Basic("PixiEditor.Tools.DecreaseSize", -1, "Decrease Tool Size", "Decrease Tool Size", Key = Key.OemOpenBrackets)]
+    [Command.Basic("PixiEditor.Tools.IncreaseSize", 1, "Increase Tool Size", "Increase Tool Size", CanExecute = "PixiEditor.Tools.CanChangeToolSize", Key = Key.OemCloseBrackets)]
+    [Command.Basic("PixiEditor.Tools.DecreaseSize", -1, "Decrease Tool Size", "Decrease Tool Size", CanExecute = "PixiEditor.Tools.CanChangeToolSize", Key = Key.OemOpenBrackets)]
     public void ChangeToolSize(int increment)
     {
         if (ActiveTool?.Toolbar is not BasicToolbar toolbar)
@@ -168,6 +170,9 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
             toolbar.ToolSize = newSize;
     }
 
+    [Evaluator.CanExecute("PixiEditor.Tools.CanChangeToolSize")]
+    public bool CanChangeToolSize() => Owner.ToolsSubViewModel.ActiveTool is PenToolViewModel { PixelPerfectEnabled: false };
+    
     public void SetActiveTool(Type toolType, bool transient)
     {
         if (!typeof(ToolViewModel).IsAssignableFrom(toolType))

+ 17 - 2
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Settings/Setting.cs

@@ -26,7 +26,7 @@ internal abstract class Setting<T> : Setting
     {
     }
 
-    public event EventHandler<SettingValueChangedEventArgs<T>> ValueChanged;
+    public new event EventHandler<SettingValueChangedEventArgs<T>> ValueChanged;
 
     public new virtual T Value
     {
@@ -50,12 +50,27 @@ internal abstract class Setting<T> : Setting
 
 internal abstract class Setting : NotifyableObject
 {
+    private object _value;
+    
     protected Setting(string name)
     {
         Name = name;
     }
 
-    public object Value { get; set; }
+    public event EventHandler<SettingValueChangedEventArgs<object>> ValueChanged;
+
+    public object Value
+    {
+        get => _value;
+        set
+        {
+            var old = _value;
+            if (SetProperty(ref _value, value))
+            {
+                ValueChanged?.Invoke(this, new SettingValueChangedEventArgs<object>(old, value));
+            }
+        }
+    }
 
     public string Name { get; }
 

+ 2 - 0
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/SettingAttributes.cs

@@ -64,6 +64,8 @@ public static class Settings
     public abstract class SettingsAttribute : Attribute
     {
         public string Name { get; set; }
+        
+        public string Notify { get; set; }
 
         public SettingsAttribute() { }
         

+ 29 - 4
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/ToolbarFactory.cs

@@ -6,14 +6,14 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
 internal static class ToolbarFactory
 {
-    public static Toolbar Create<T>() where T : ToolViewModel => Create<T, EmptyToolbar>();
+    public static Toolbar Create<T>(T tool) where T : ToolViewModel => Create<T, EmptyToolbar>(tool);
 
-    public static TToolbar Create<T, TToolbar>() where T : ToolViewModel where TToolbar : Toolbar, new()
+    public static TToolbar Create<T, TToolbar>(T tool) where T : ToolViewModel where TToolbar : Toolbar, new()
     {
-        var tool = typeof(T);
+        var toolType = typeof(T);
         var toolbar = new TToolbar();
 
-        foreach (var property in tool.GetProperties())
+        foreach (var property in toolType.GetProperties())
         {
             var attribute = property.GetCustomAttribute<Settings.SettingsAttribute>();
 
@@ -36,6 +36,11 @@ internal static class ToolbarFactory
                     throw new InvalidCastException($"Inherited setting '{name}' does not match property type '{property.PropertyType}' (Tool: {typeof(T).FullName})");
                 }
 
+                if (attribute.Notify != null)
+                {
+                    AddValueChangedHandler(toolType, tool, inherited, attribute);
+                }
+
                 continue;
             }
             
@@ -56,12 +61,32 @@ internal static class ToolbarFactory
                 throw new InvalidCastException($"Setting '{name}' does not match property type '{property.PropertyType}' (Tool: {typeof(T).FullName})");
             }
 
+            if (attribute.Notify != null)
+            {
+                AddValueChangedHandler(toolType, tool, setting, attribute);
+            }
+
             toolbar.Settings.Add(setting);
         }
 
         return toolbar;
     }
 
+    private static void AddValueChangedHandler<T>(Type toolType, T tool, Setting setting, Settings.SettingsAttribute attribute) where T : ToolViewModel
+    {
+        if (attribute.Notify != null)
+        {
+            var method = toolType.GetMethod(attribute.Notify, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, Array.Empty<Type>());
+
+            if (method is null)
+            {
+                throw new NullReferenceException($"No method found with the name '{attribute.Notify}' that does not have any parameters");
+            }
+
+            setting.ValueChanged += (_, _) => method.Invoke(tool, null);
+        }
+    }
+
     private static Setting GetEnumSetting(Type enumType, string name, Settings.SettingsAttribute attribute)
     {
         return (Setting)typeof(EnumSetting<>)

+ 3 - 0
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolViewModel.cs

@@ -60,6 +60,9 @@ internal abstract class ToolViewModel : NotifyableObject
         ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument?.Operations.TryStopToolLinkedExecutor();
     }
 
+    public virtual void OnDeselecting()
+    { }
+
     protected T GetValue<T>([CallerMemberName] string name = null)
     {
         var setting = Toolbar.GetSetting(name);

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/BrightnessToolViewModel.cs

@@ -17,7 +17,7 @@ internal class BrightnessToolViewModel : ToolViewModel
     public BrightnessToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<BrightnessToolViewModel, BasicToolbar>();
+        Toolbar = ToolbarFactory.Create<BrightnessToolViewModel, BasicToolbar>(this);
     }
 
     public override string Tooltip => $"Makes pixels brighter or darker ({Shortcut}). Hold Ctrl to make pixels darker.";

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/ColorPickerToolViewModel.cs

@@ -39,7 +39,7 @@ internal class ColorPickerToolViewModel : ToolViewModel
     public ColorPickerToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<ColorPickerToolViewModel, EmptyToolbar>();
+        Toolbar = ToolbarFactory.Create<ColorPickerToolViewModel, EmptyToolbar>(this);
     }
 
     public override void OnLeftMouseButtonDown(VecD pos)

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/EraserToolViewModel.cs

@@ -13,7 +13,7 @@ internal class EraserToolViewModel : ToolViewModel
     public EraserToolViewModel()
     {
         ActionDisplay = "Draw to remove color from a pixel.";
-        Toolbar = ToolbarFactory.Create<EraserToolViewModel, BasicToolbar>();
+        Toolbar = ToolbarFactory.Create<EraserToolViewModel, BasicToolbar>(this);
     }
 
     [Settings.Inherited]

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/LassoToolViewModel.cs

@@ -14,7 +14,7 @@ internal class LassoToolViewModel : ToolViewModel
 
     public LassoToolViewModel()
     {
-        Toolbar = ToolbarFactory.Create<LassoToolViewModel>();
+        Toolbar = ToolbarFactory.Create(this);
         ActionDisplay = defaultActionDisplay;
     }
 

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/LineToolViewModel.cs

@@ -14,7 +14,7 @@ internal class LineToolViewModel : ShapeTool
     public LineToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<LineToolViewModel, BasicToolbar>();
+        Toolbar = ToolbarFactory.Create<LineToolViewModel, BasicToolbar>(this);
     }
 
     public override string Tooltip => $"Draws line on canvas ({Shortcut}). Hold Shift to enable snapping.";

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/MagicWandToolViewModel.cs

@@ -23,7 +23,7 @@ internal class MagicWandToolViewModel : ToolViewModel
     
     public MagicWandToolViewModel()
     {
-        Toolbar = ToolbarFactory.Create<MagicWandToolViewModel>();
+        Toolbar = ToolbarFactory.Create(this);
         ActionDisplay = "Click to flood the selection.";
     }
     

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/MoveToolViewModel.cs

@@ -19,7 +19,7 @@ internal class MoveToolViewModel : ToolViewModel
     public MoveToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<MoveToolViewModel>();
+        Toolbar = ToolbarFactory.Create(this);
         Cursor = Cursors.Arrow;
     }
 

+ 63 - 2
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/PenToolViewModel.cs

@@ -3,8 +3,11 @@ using System.Windows.Media;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Events;
+using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Settings;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
+using PixiEditor.Views.UserControls;
 using PixiEditor.Views.UserControls.Overlays.BrushShapeOverlay;
 
 namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools
@@ -12,12 +15,17 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools
     [Command.Tool(Key = Key.B)]
     internal class PenToolViewModel : ShapeTool
     {
+        private int actualToolSize;
+
         public override BrushShape BrushShape => BrushShape.Circle;
+
         public PenToolViewModel()
         {
             Cursor = Cursors.Pen;
             ActionDisplay = "Click and move to draw.";
-            Toolbar = ToolbarFactory.Create<PenToolViewModel, BasicToolbar>();
+            Toolbar = ToolbarFactory.Create<PenToolViewModel, BasicToolbar>(this);
+            
+            ViewModelMain.Current.ToolsSubViewModel.SelectedToolChanged += SelectedToolChanged;
         }
 
         public override string Tooltip => $"Pen. ({Shortcut})";
@@ -25,12 +33,65 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools
         [Settings.Inherited]
         public int ToolSize => GetValue<int>();
 
-        [Settings.Bool("Pixel perfect")]
+        [Settings.Bool("Pixel perfect", Notify = nameof(PixelPerfectChanged))]
         public bool PixelPerfectEnabled => GetValue<bool>();
 
         public override void OnLeftMouseButtonDown(VecD pos)
         {
             ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UsePenTool();
         }
+
+        private void SelectedToolChanged(object sender, SelectedToolEventArgs e)
+        {
+            if (e.NewTool == this && PixelPerfectEnabled)
+            {
+                var toolbar = (BasicToolbar)Toolbar;
+                var setting = (SizeSetting)toolbar.Settings[0];
+                setting.Value = 1;
+            }
+            
+            if (!IPreferences.Current.GetPreference<bool>("EnableSharedToolbar"))
+            {
+                return;
+            }
+
+            if (e.OldTool is not { Toolbar: BasicToolbar oldToolbar })
+            {
+                return;
+            }
+            
+            var oldSetting = (SizeSetting)oldToolbar.Settings[0];
+            actualToolSize = oldSetting.Value;
+        }
+
+        public override void OnDeselecting()
+        {
+            if (!PixelPerfectEnabled)
+            {
+                return;
+            }
+
+            var toolbar = (BasicToolbar)Toolbar;
+            var setting = (SizeSetting)toolbar.Settings[0];
+            setting.Value = actualToolSize;
+        }
+
+        private void PixelPerfectChanged()
+        {
+            var toolbar = (BasicToolbar)Toolbar;
+            var setting = (SizeSetting)toolbar.Settings[0];
+
+            setting.SettingControl.IsEnabled = !PixelPerfectEnabled;
+
+            if (PixelPerfectEnabled)
+            {
+                actualToolSize = ToolSize;
+                setting.Value = 1;
+            }
+            else
+            {
+                setting.Value = actualToolSize;
+            }
+        }
     }
 }

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/SelectToolViewModel.cs

@@ -17,7 +17,7 @@ internal class SelectToolViewModel : ToolViewModel
     public SelectToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<SelectToolViewModel>();
+        Toolbar = ToolbarFactory.Create(this);
         Cursor = Cursors.Cross;
     }