Browse Source

Supported layer types

flabbet 11 months ago
parent
commit
9d20a4447c
25 changed files with 204 additions and 50 deletions
  1. 1 0
      src/PixiEditor/Models/Handlers/IToolHandler.cs
  2. 6 1
      src/PixiEditor/ViewModels/Document/DocumentManagerViewModel.cs
  3. 90 32
      src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs
  4. 36 0
      src/PixiEditor/ViewModels/Tools/ToolViewModel.cs
  5. 6 0
      src/PixiEditor/ViewModels/Tools/Tools/BrightnessToolViewModel.cs
  6. 16 9
      src/PixiEditor/ViewModels/Tools/Tools/ColorPickerToolViewModel.cs
  7. 5 0
      src/PixiEditor/ViewModels/Tools/Tools/EraserToolViewModel.cs
  8. 2 0
      src/PixiEditor/ViewModels/Tools/Tools/FloodFillToolViewModel.cs
  9. 2 0
      src/PixiEditor/ViewModels/Tools/Tools/LassoToolViewModel.cs
  10. 2 0
      src/PixiEditor/ViewModels/Tools/Tools/MagicWandToolViewModel.cs
  11. 1 0
      src/PixiEditor/ViewModels/Tools/Tools/MoveToolViewModel.cs
  12. 1 0
      src/PixiEditor/ViewModels/Tools/Tools/MoveViewportToolViewModel.cs
  13. 3 0
      src/PixiEditor/ViewModels/Tools/Tools/PenToolViewModel.cs
  14. 2 0
      src/PixiEditor/ViewModels/Tools/Tools/RasterEllipseToolViewModel.cs
  15. 3 2
      src/PixiEditor/ViewModels/Tools/Tools/RasterLineToolViewModel.cs
  16. 2 0
      src/PixiEditor/ViewModels/Tools/Tools/RasterRectangleToolViewModel.cs
  17. 1 0
      src/PixiEditor/ViewModels/Tools/Tools/RotateViewportToolViewModel.cs
  18. 1 0
      src/PixiEditor/ViewModels/Tools/Tools/SelectToolViewModel.cs
  19. 6 1
      src/PixiEditor/ViewModels/Tools/Tools/VectorEllipseToolViewModel.cs
  20. 2 0
      src/PixiEditor/ViewModels/Tools/Tools/VectorLineToolViewModel.cs
  21. 6 1
      src/PixiEditor/ViewModels/Tools/Tools/VectorRectangleToolViewModel.cs
  22. 1 0
      src/PixiEditor/ViewModels/Tools/Tools/ZoomToolViewModel.cs
  23. 2 1
      src/PixiEditor/Views/Main/Tools/ToolPickerButton.axaml
  24. 4 2
      src/PixiEditor/Views/Main/Tools/ToolPickerButton.axaml.cs
  25. 3 1
      src/PixiEditor/Views/Main/Tools/ToolsPicker.axaml

+ 1 - 0
src/PixiEditor/Models/Handlers/IToolHandler.cs

@@ -47,6 +47,7 @@ internal interface IToolHandler : IHandler
     public LocalizedString ActionDisplay { get; set; }
     public bool IsActive { get; set; }
     public Cursor Cursor { get; set; }
+    public bool CanBeUsed { get; }
     //public Toolbar Toolbar { get; set; }
 
     public void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown);

+ 6 - 1
src/PixiEditor/ViewModels/Document/DocumentManagerViewModel.cs

@@ -36,7 +36,12 @@ internal class DocumentManagerViewModel : SubViewModel<ViewModelMain>, IDocument
             
             if (ViewModelMain.Current.ToolsSubViewModel.ActiveTool == null)
             {
-                ViewModelMain.Current.ToolsSubViewModel.SetActiveTool<PenToolViewModel>(false);
+                var firstTool =
+                    ViewModelMain.Current.ToolsSubViewModel.ActiveToolSet.Tools.FirstOrDefault(x => x.CanBeUsed);
+                if (firstTool != null)
+                {
+                    ViewModelMain.Current.ToolsSubViewModel.SetActiveTool(firstTool.GetType(), false);
+                }
             }
         }
     }

+ 90 - 32
src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.ComponentModel;
 using System.Linq;
 using Avalonia.Input;
 using Microsoft.Extensions.DependencyInjection;
@@ -58,6 +59,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     }
 
     private Cursor? toolCursor;
+
     public Cursor? ToolCursor
     {
         get => toolCursor;
@@ -70,6 +72,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     }
 
     private IToolHandler? activeTool;
+
     public IToolHandler? ActiveTool
     {
         get => activeTool;
@@ -87,7 +90,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     }
 
     ICollection<IToolSetHandler> IToolsHandler.AllToolSets => AllToolSets;
-    
+
     public ObservableCollection<IToolSetHandler> AllToolSets { get; } = new();
 
     public event EventHandler<SelectedToolEventArgs>? SelectedToolChanged;
@@ -97,26 +100,27 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     private bool altIsDown;
 
     private ToolViewModel _preTransientTool;
-    
+
     private List<IToolHandler> allTools = new();
     private IToolSetHandler? _activeToolSet;
 
     public ToolsViewModel(ViewModelMain owner)
         : base(owner)
     {
+        owner.DocumentManagerSubViewModel.ActiveDocumentChanged += ActiveDocumentChanged;
     }
 
     public void SetupTools(IServiceProvider services, ToolSetsConfig toolSetConfig)
     {
         allTools = services.GetServices<IToolHandler>().ToList();
-        
+
         ToolSetConfig activeToolSetConfig = toolSetConfig.FirstOrDefault();
-        
+
         if (activeToolSetConfig is null)
         {
             throw new InvalidOperationException("No tool set configuration found.");
         }
-        
+
         AllToolSets.Clear();
         AddToolSets(toolSetConfig);
         SetActiveToolSet(AllToolSets.First());
@@ -125,6 +129,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     public void SetActiveToolSet(IToolSetHandler toolSetHandler)
     {
         ActiveToolSet = toolSetHandler;
+        UpdateEnabledState();
     }
 
     public void SetupToolsTooltipShortcuts(IServiceProvider services)
@@ -157,20 +162,28 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
             return;
         doc.EventInlet.OnApplyTransform();
     }
-    
-    [Command.Internal("PixiEditor.Tools.SwitchToolSet", AnalyticsTrack = true, CanExecute = "PixiEditor.HasNextToolSet")]
-    [Command.Basic("PixiEditor.Tools.NextToolSet", true, "NEXT_TOOL_SET", "NEXT_TOOL_SET", Modifiers = KeyModifiers.Shift, 
+
+    [Command.Internal("PixiEditor.Tools.SwitchToolSet", AnalyticsTrack = true,
+        CanExecute = "PixiEditor.HasNextToolSet")]
+    [Command.Basic("PixiEditor.Tools.NextToolSet", true, "NEXT_TOOL_SET", "NEXT_TOOL_SET",
+        Modifiers = KeyModifiers.Shift,
         Key = Key.E, AnalyticsTrack = true)]
-    [Command.Basic("PixiEditor.Tools.PreviousToolSet", false, "PREVIOUS_TOOL_SET", "PREVIOUS_TOOL_SET", Modifiers = KeyModifiers.Shift,
+    [Command.Basic("PixiEditor.Tools.PreviousToolSet", false, "PREVIOUS_TOOL_SET", "PREVIOUS_TOOL_SET",
+        Modifiers = KeyModifiers.Shift,
         Key = Key.Q, AnalyticsTrack = true)]
     public void SwitchToolSet(bool forward)
     {
         int currentIndex = AllToolSets.IndexOf(ActiveToolSet);
         int nextIndex = currentIndex + (forward ? 1 : -1);
-        if (nextIndex >= AllToolSets.Count || nextIndex < 0)
+        if (nextIndex >= AllToolSets.Count)
         {
             nextIndex = 0;
         }
+        
+        if (nextIndex < 0)
+        {
+            nextIndex = AllToolSets.Count - 1;
+        }
 
         SetActiveToolSet(AllToolSets.ElementAt(nextIndex));
     }
@@ -186,7 +199,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         }
 
         return AllToolSets.ElementAt(nextIndex) != ActiveToolSet;
-    } 
+    }
 
     [Command.Internal("PixiEditor.Tools.SelectTool", CanExecute = "PixiEditor.HasDocument")]
     public void SetActiveTool(ToolViewModel tool)
@@ -198,7 +211,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
 
     public void SetActiveTool(IToolHandler tool, bool transient, ICommandExecutionSourceInfo? sourceInfo)
     {
-        if(Owner.DocumentManagerSubViewModel.ActiveDocument is { PointerDragChangeInProgress: true }) return;
+        if (Owner.DocumentManagerSubViewModel.ActiveDocument is { PointerDragChangeInProgress: true }) return;
 
         if (ActiveTool == tool)
         {
@@ -223,7 +236,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
 
         LastActionTool = ActiveTool;
         ActiveTool = tool;
-        
+
         ActiveTool.Toolbar.SettingChanged += ToolbarSettingChanged;
 
         if (shareToolbar)
@@ -264,7 +277,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
             source = context.SourceInfo;
             parameter = context.Parameter;
         }
-        
+
         if (parameter is Type type)
         {
             SetActiveTool(type, false, source);
@@ -275,8 +288,10 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         SetActiveTool(tool.GetType(), false, source);
     }
 
-    [Command.Basic("PixiEditor.Tools.IncreaseSize", 1, "INCREASE_TOOL_SIZE", "INCREASE_TOOL_SIZE", CanExecute = "PixiEditor.Tools.CanChangeToolSize", Key = Key.OemCloseBrackets, AnalyticsTrack = true)]
-    [Command.Basic("PixiEditor.Tools.DecreaseSize", -1, "DECREASE_TOOL_SIZE", "DECREASE_TOOL_SIZE", CanExecute = "PixiEditor.Tools.CanChangeToolSize", Key = Key.OemOpenBrackets, AnalyticsTrack = true)]
+    [Command.Basic("PixiEditor.Tools.IncreaseSize", 1, "INCREASE_TOOL_SIZE", "INCREASE_TOOL_SIZE",
+        CanExecute = "PixiEditor.Tools.CanChangeToolSize", Key = Key.OemCloseBrackets, AnalyticsTrack = true)]
+    [Command.Basic("PixiEditor.Tools.DecreaseSize", -1, "DECREASE_TOOL_SIZE", "DECREASE_TOOL_SIZE",
+        CanExecute = "PixiEditor.Tools.CanChangeToolSize", Key = Key.OemOpenBrackets, AnalyticsTrack = true)]
     public void ChangeToolSize(int increment)
     {
         if (ActiveTool?.Toolbar is not BasicToolbar toolbar)
@@ -298,11 +313,11 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         if (!typeof(ToolViewModel).IsAssignableFrom(toolType))
             throw new ArgumentException($"'{toolType}' does not inherit from {typeof(ToolViewModel)}");
         IToolHandler foundTool = ActiveToolSet!.Tools.FirstOrDefault(x => x.GetType().IsAssignableFrom(toolType));
-        if (foundTool == null) return;
-        
+        if (foundTool == null || !foundTool.CanBeUsed) return;
+
         SetActiveTool(foundTool, transient, sourceInfo);
     }
-    
+
     public void RestorePreviousTool()
     {
         if (LastActionTool != null)
@@ -329,17 +344,17 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
 
     public void HandleToolRepeatShortcutDown()
     {
-        if(ActiveTool == null) return;
+        if (ActiveTool == null) return;
         if (ActiveTool is null or { IsTransient: false })
         {
             ShortcutController.BlockShortcutExecution("ShortcutDown");
             ActiveTool.IsTransient = true;
         }
     }
-    
+
     public void HandleToolShortcutUp()
     {
-        if(ActiveTool == null) return;
+        if (ActiveTool == null) return;
         if (ActiveTool.IsTransient && LastActionTool is { } tool)
             SetActiveTool(tool, false);
         ShortcutController.UnblockShortcutExecution("ShortcutDown");
@@ -347,7 +362,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
 
     public void UseToolEventInlet(VecD canvasPos, MouseButton button)
     {
-        if (ActiveTool == null) return;
+        if (ActiveTool is not { CanBeUsed: true }) return;
 
         ActiveTool.UsedWith = button;
         if (ActiveTool.StopsLinkedToolOnUse)
@@ -367,38 +382,81 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     {
         ActiveTool?.ModifierKeyChanged(args.IsCtrlDown, args.IsShiftDown, args.IsAltDown);
     }
-    
+
     private void ToolbarSettingChanged(string settingName, object value)
     {
         var document = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (document is null)
             return;
-        
+
         document.EventInlet.SettingsChanged(settingName, value);
     }
-    
+
     private void AddToolSets(ToolSetsConfig toolSetConfig)
     {
         foreach (ToolSetConfig toolSet in toolSetConfig)
         {
             List<IToolHandler> tools = new List<IToolHandler>();
-            
+
             foreach (string toolName in toolSet.Tools)
             {
                 IToolHandler? tool = allTools.FirstOrDefault(tool => tool.ToolName == toolName);
                 if (tool is null)
                 {
-                    #if DEBUG
+#if DEBUG
                     throw new InvalidOperationException($"Tool '{toolName}' not found.");
-                    #endif
-                    
+#endif
+
                     continue;
                 }
-                
+
                 tools.Add(tool);
             }
-            
+
             AllToolSets.Add(new ToolSetViewModel(toolSet.Name, tools));
         }
     }
+
+    private void ActiveDocumentChanged(object? sender, DocumentChangedEventArgs e)
+    {
+        if (e.OldDocument is not null)
+        {
+            e.OldDocument.PropertyChanged -= DocumentOnPropertyChanged;
+        }
+
+        if (e.NewDocument is not null)
+        {
+            e.NewDocument.PropertyChanged += DocumentOnPropertyChanged;
+            UpdateEnabledState();
+        }
+    }
+
+    private void DocumentOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+    {
+        if (e.PropertyName == nameof(DocumentViewModel.SelectedStructureMember))
+        {
+            UpdateEnabledState();
+        }
+    }
+
+    private void UpdateEnabledState()
+    {
+        var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        if (doc is null)
+            return;
+        
+        foreach (var toolHandler in ActiveToolSet.Tools)
+        {
+            if (toolHandler is ToolViewModel tool)
+            {
+                List<IStructureMemberHandler> selectedLayers = new List<IStructureMemberHandler>
+                {
+                    doc.SelectedStructureMember
+                };
+
+                selectedLayers.AddRange(doc.SoftSelectedStructureMembers);
+                tool.SelectedLayersChanged(selectedLayers.ToArray());
+            }
+        }
+    }
 }

+ 36 - 0
src/PixiEditor/ViewModels/Tools/ToolViewModel.cs

@@ -15,6 +15,7 @@ namespace PixiEditor.ViewModels.Tools;
 
 internal abstract class ToolViewModel : ObservableObject, IToolHandler
 {
+    private bool canBeSelected = true;
     public bool IsTransient { get; set; } = false;
     public KeyCombination Shortcut { get; set; }
 
@@ -26,6 +27,18 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
     public virtual string Icon => $"\u25a1";
 
     public virtual BrushShape BrushShape => BrushShape.Square;
+    
+    public abstract Type[] SupportedLayerTypes { get; }
+
+    public bool CanBeUsed
+    {
+        get => canBeSelected;
+        private set
+        {
+            canBeSelected = value;
+            OnPropertyChanged(nameof(CanBeUsed));
+        }
+    }
 
     public virtual bool HideHighlight { get; }
 
@@ -79,6 +92,29 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
     {
         ILocalizationProvider.Current.OnLanguageChanged += OnLanguageChanged;
     }
+    
+    internal void SelectedLayersChanged(IStructureMemberHandler[] layers)
+    {
+        foreach (var layer in layers)
+        {
+            if(SupportedLayerTypes == null || SupportedLayerTypes.Length == 0)
+            {
+                CanBeUsed = true;
+                return;
+            }
+            
+            foreach (var type in SupportedLayerTypes)
+            {
+                if (type.IsInstanceOfType(layer))
+                {
+                    CanBeUsed = true;
+                    return;
+                }
+            }
+        }
+        
+        CanBeUsed = false;
+    }
 
     private void OnLanguageChanged(Language obj)
     {

+ 6 - 0
src/PixiEditor/ViewModels/Tools/Tools/BrightnessToolViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Numerics;
@@ -31,6 +32,11 @@ internal class BrightnessToolViewModel : ToolViewModel, IBrightnessToolHandler
 
     public override string Icon => PixiPerfectIcons.Sun;
 
+    public override Type[] SupportedLayerTypes { get; } =
+    {
+        typeof(IRasterLayerHandler)
+    };
+
     BrightnessMode IBrightnessToolHandler.BrightnessMode
     {
         get => BrightnessMode;

+ 16 - 9
src/PixiEditor/ViewModels/Tools/Tools/ColorPickerToolViewModel.cs

@@ -3,6 +3,7 @@ using Avalonia.Input;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Numerics;
@@ -18,9 +19,9 @@ namespace PixiEditor.ViewModels.Tools.Tools;
 internal class ColorPickerToolViewModel : ToolViewModel, IColorPickerHandler
 {
     private readonly string defaultReferenceActionDisplay = "COLOR_PICKER_ACTION_DISPLAY_DEFAULT";
-    
+
     private readonly string defaultActionDisplay = "COLOR_PICKER_ACTION_DISPLAY_CANVAS_ONLY";
-    
+
     public override bool HideHighlight => true;
 
     public override bool UsesColor => true;
@@ -30,9 +31,12 @@ internal class ColorPickerToolViewModel : ToolViewModel, IColorPickerHandler
 
     public override string Icon => PixiPerfectIcons.Picker;
 
+    public override Type[] SupportedLayerTypes { get; } = [];  // all layer types are supported
+
     public override LocalizedString Tooltip => new("COLOR_PICKER_TOOLTIP", Shortcut);
 
     private bool pickFromCanvas = true;
+
     public bool PickFromCanvas
     {
         get => pickFromCanvas;
@@ -44,8 +48,9 @@ internal class ColorPickerToolViewModel : ToolViewModel, IColorPickerHandler
             }
         }
     }
-    
+
     private bool pickFromReferenceLayer = true;
+
     public bool PickFromReferenceLayer
     {
         get => pickFromReferenceLayer;
@@ -97,7 +102,8 @@ internal class ColorPickerToolViewModel : ToolViewModel, IColorPickerHandler
 
     private void ReferenceLayerChanged(object sender, PropertyChangedEventArgs e)
     {
-        if (e.PropertyName is nameof(ReferenceLayerViewModel.ReferenceBitmap) or nameof(ReferenceLayerViewModel.ReferenceShapeBindable))
+        if (e.PropertyName is nameof(ReferenceLayerViewModel.ReferenceBitmap)
+            or nameof(ReferenceLayerViewModel.ReferenceShapeBindable))
         {
             UpdateActionDisplay();
         }
@@ -108,10 +114,10 @@ internal class ColorPickerToolViewModel : ToolViewModel, IColorPickerHandler
         // TODO: We probably need to create keyboard service to handle this
         /*bool ctrlDown = (Keyboard.Modifiers & KeyModifiers.Control) != 0;
         bool shiftDown = (Keyboard.Modifiers & KeyModifiers.Shift) != 0;*/
-        
+
         UpdateActionDisplay(false, false);
     }
-    
+
     private void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown)
     {
         var document = ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument;
@@ -123,15 +129,16 @@ internal class ColorPickerToolViewModel : ToolViewModel, IColorPickerHandler
 
         var documentBounds = new RectD(default, document.SizeBindable);
         var referenceLayer = document.ReferenceLayerViewModel;
-        
-        if (referenceLayer.ReferenceBitmap == null || document.TransformViewModel.TransformActive || !referenceLayer.ReferenceShapeBindable.Intersects(documentBounds))
+
+        if (referenceLayer.ReferenceBitmap == null || document.TransformViewModel.TransformActive ||
+            !referenceLayer.ReferenceShapeBindable.Intersects(documentBounds))
         {
             PickFromCanvas = true;
             PickFromReferenceLayer = true;
             ActionDisplay = defaultActionDisplay;
             return;
         }
-    
+
         if (ctrlIsDown)
         {
             PickFromCanvas = false;

+ 5 - 0
src/PixiEditor/ViewModels/Tools/Tools/EraserToolViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.UI.Common.Fonts;
@@ -26,6 +27,10 @@ internal class EraserToolViewModel : ToolViewModel, IEraserToolHandler
 
     public override string ToolNameLocalizationKey => "ERASER_TOOL";
     public override BrushShape BrushShape => BrushShape.Circle;
+    public override Type[] SupportedLayerTypes { get; } =
+    {
+        typeof(IRasterLayerHandler)
+    };
 
     public override string Icon => PixiPerfectIcons.Eraser;
 

+ 2 - 0
src/PixiEditor/ViewModels/Tools/Tools/FloodFillToolViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.UI.Common.Fonts;
@@ -16,6 +17,7 @@ internal class FloodFillToolViewModel : ToolViewModel, IFloodFillToolHandler
 
     public override string ToolNameLocalizationKey => "FLOOD_FILL_TOOL";
     public override BrushShape BrushShape => BrushShape.Pixel;
+    public override Type[] SupportedLayerTypes { get; } = { typeof(IRasterLayerHandler) };
 
     public override LocalizedString Tooltip => new("FLOOD_FILL_TOOL_TOOLTIP", Shortcut);
 

+ 2 - 0
src/PixiEditor/ViewModels/Tools/Tools/LassoToolViewModel.cs

@@ -49,6 +49,8 @@ internal class LassoToolViewModel : ToolViewModel, ILassoToolHandler
     public override string ToolNameLocalizationKey => "LASSO_TOOL";
     public string Icon => PixiPerfectIcons.Lasso;
     public override BrushShape BrushShape => BrushShape.Pixel;
+    
+    public override Type[] SupportedLayerTypes { get; } = []; // all layer types are supported
 
     [Settings.Enum("MODE_LABEL")]
     public SelectionMode SelectMode => GetValue<SelectionMode>();

+ 2 - 0
src/PixiEditor/ViewModels/Tools/Tools/MagicWandToolViewModel.cs

@@ -3,6 +3,7 @@ using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Numerics;
@@ -19,6 +20,7 @@ internal class MagicWandToolViewModel : ToolViewModel, IMagicWandToolHandler
 
     public override string ToolNameLocalizationKey => "MAGIC_WAND_TOOL";
     public override BrushShape BrushShape => BrushShape.Pixel;
+    public override Type[] SupportedLayerTypes { get; } = { typeof(IRasterLayerHandler) }; 
 
     [Settings.Enum("MODE_LABEL")]
     public SelectionMode SelectMode => GetValue<SelectionMode>();

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

@@ -39,6 +39,7 @@ internal class MoveToolViewModel : ToolViewModel, IMoveToolHandler
     public bool KeepOriginalImage => GetValue<bool>();
 
     public override BrushShape BrushShape => BrushShape.Hidden;
+    public override Type[] SupportedLayerTypes { get; } = [];
     public override bool HideHighlight => true;
 
     public bool TransformingSelectedArea

+ 1 - 0
src/PixiEditor/ViewModels/Tools/Tools/MoveViewportToolViewModel.cs

@@ -11,6 +11,7 @@ internal class MoveViewportToolViewModel : ToolViewModel
 {
     public override string ToolNameLocalizationKey => "MOVE_VIEWPORT_TOOL";
     public override BrushShape BrushShape => BrushShape.Hidden;
+    public override Type[] SupportedLayerTypes { get; } = [];
     public override bool HideHighlight => true;
     public override LocalizedString Tooltip => new LocalizedString("MOVE_VIEWPORT_TOOLTIP", Shortcut);
 

+ 3 - 0
src/PixiEditor/ViewModels/Tools/Tools/PenToolViewModel.cs

@@ -4,6 +4,7 @@ using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Input;
 using PixiEditor.Numerics;
@@ -21,6 +22,8 @@ namespace PixiEditor.ViewModels.Tools.Tools
 
         public override string ToolNameLocalizationKey => "PEN_TOOL";
         public override BrushShape BrushShape => BrushShape.Circle;
+        
+        public override Type[] SupportedLayerTypes { get; } = { typeof(IRasterLayerHandler) };
 
         public PenToolViewModel()
         {

+ 2 - 0
src/PixiEditor/ViewModels/Tools/Tools/RasterEllipseToolViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.UI.Common.Fonts;
@@ -19,6 +20,7 @@ internal class RasterEllipseToolViewModel : ShapeTool, IRasterEllipseToolHandler
         ActionDisplay = defaultActionDisplay;
     }
 
+    public override Type[] SupportedLayerTypes { get; } = { typeof(IRasterLayerHandler) };
     public override LocalizedString Tooltip => new LocalizedString("ELLIPSE_TOOL_TOOLTIP", Shortcut);
     public bool DrawCircle { get; private set; }
 

+ 3 - 2
src/PixiEditor/ViewModels/Tools/Tools/RasterLineToolViewModel.cs

@@ -3,6 +3,7 @@ using PixiEditor.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.UI.Common.Fonts;
@@ -24,10 +25,10 @@ internal class RasterLineToolViewModel : ShapeTool, ILineToolHandler
     public override string ToolNameLocalizationKey => "LINE_TOOL";
     public override LocalizedString Tooltip => new LocalizedString("LINE_TOOL_TOOLTIP", Shortcut);
 
+    public override Type[] SupportedLayerTypes { get; } = { typeof(IRasterLayerHandler) };
     public override string Icon => PixiPerfectIcons.Line;
 
-    [Settings.Inherited]
-    public int ToolSize => GetValue<int>();
+    [Settings.Inherited] public int ToolSize => GetValue<int>();
 
     public bool Snap { get; private set; }
 

+ 2 - 0
src/PixiEditor/ViewModels/Tools/Tools/RasterRectangleToolViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.UI.Common.Fonts;
@@ -18,6 +19,7 @@ internal class RasterRectangleToolViewModel : ShapeTool, IRasterRectangleToolHan
     }
 
     public override string ToolNameLocalizationKey => "RECTANGLE_TOOL";
+    public override Type[] SupportedLayerTypes { get; } = { typeof(IRasterLayerHandler) };
     public override LocalizedString Tooltip => new LocalizedString("RECTANGLE_TOOL_TOOLTIP", Shortcut);
 
     public bool Filled { get; set; } = false;

+ 1 - 0
src/PixiEditor/ViewModels/Tools/Tools/RotateViewportToolViewModel.cs

@@ -11,6 +11,7 @@ internal class RotateViewportToolViewModel : ToolViewModel
 {
     public override string ToolNameLocalizationKey => "ROTATE_VIEWPORT_TOOL";
     public override BrushShape BrushShape => BrushShape.Hidden;
+    public override Type[] SupportedLayerTypes { get; } = [];
     public override bool HideHighlight => true;
     public override bool StopsLinkedToolOnUse => false;
     public override LocalizedString Tooltip => new LocalizedString("ROTATE_VIEWPORT_TOOLTIP", Shortcut);

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

@@ -56,6 +56,7 @@ internal class SelectToolViewModel : ToolViewModel, ISelectToolHandler
     public SelectionShape SelectShape => GetValue<SelectionShape>();
 
     public override BrushShape BrushShape => BrushShape.Pixel;
+    public override Type[] SupportedLayerTypes { get; } = [];
 
     public override LocalizedString Tooltip => new LocalizedString("SELECT_TOOL_TOOLTIP", Shortcut);
 

+ 6 - 1
src/PixiEditor/ViewModels/Tools/Tools/VectorEllipseToolViewModel.cs

@@ -1,10 +1,14 @@
-using PixiEditor.Extensions.Common.Localization;
+using Avalonia.Input;
+using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.ViewModels.Tools.Tools;
 
+[Command.Tool(Key = Key.C)]
 internal class VectorEllipseToolViewModel : ShapeTool, IVectorEllipseToolHandler
 {
     private string defaultActionDisplay = "ELLIPSE_TOOL_ACTION_DISPLAY_DEFAULT";
@@ -15,6 +19,7 @@ internal class VectorEllipseToolViewModel : ShapeTool, IVectorEllipseToolHandler
         ActionDisplay = defaultActionDisplay;
     }
 
+    public override Type[] SupportedLayerTypes { get; } = { typeof(IVectorLayerHandler) };
     public override LocalizedString Tooltip => new LocalizedString("ELLIPSE_TOOL_TOOLTIP", Shortcut);
     public bool DrawCircle { get; private set; }
 

+ 2 - 0
src/PixiEditor/ViewModels/Tools/Tools/VectorLineToolViewModel.cs

@@ -3,6 +3,7 @@ using PixiEditor.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.UI.Common.Fonts;
@@ -25,6 +26,7 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
     public override LocalizedString Tooltip => new LocalizedString("LINE_TOOL_TOOLTIP", Shortcut);
 
     public override string Icon => PixiPerfectIcons.Line;
+    public override Type[] SupportedLayerTypes { get; } = { typeof(IVectorLayerHandler) };
 
     [Settings.Inherited]
     public int ToolSize => GetValue<int>();

+ 6 - 1
src/PixiEditor/ViewModels/Tools/Tools/VectorRectangleToolViewModel.cs

@@ -1,10 +1,14 @@
-using PixiEditor.Extensions.Common.Localization;
+using Avalonia.Input;
+using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.ViewModels.Tools.Tools;
 
+[Command.Tool(Key = Key.R)]
 internal class VectorRectangleToolViewModel : ShapeTool, IVectorRectangleToolHandler
 {
     private string defaultActionDisplay = "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT";
@@ -15,6 +19,7 @@ internal class VectorRectangleToolViewModel : ShapeTool, IVectorRectangleToolHan
         ActionDisplay = defaultActionDisplay;
     }
 
+    public override Type[] SupportedLayerTypes { get; } = { typeof(IVectorLayerHandler) };
     public override LocalizedString Tooltip => new LocalizedString("RECTANGLE_TOOL_TOOLTIP", Shortcut);
     public bool DrawSquare { get; private set; }
 

+ 1 - 0
src/PixiEditor/ViewModels/Tools/Tools/ZoomToolViewModel.cs

@@ -20,6 +20,7 @@ internal class ZoomToolViewModel : ToolViewModel
 
     public override string ToolNameLocalizationKey => "ZOOM_TOOL";
     public override BrushShape BrushShape => BrushShape.Hidden;
+    public override Type[] SupportedLayerTypes { get; } = [];
 
     public override bool StopsLinkedToolOnUse => false;
 

+ 2 - 1
src/PixiEditor/Views/Main/Tools/ToolPickerButton.axaml

@@ -9,13 +9,14 @@
              xmlns:markupExtensions="clr-namespace:PixiEditor.Helpers.MarkupExtensions"
              xmlns:ui1="clr-namespace:PixiEditor.Helpers.UI"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
-             d:DataContext="{tools:ToolViewModel}"
+             d:DataContext="{tools:ToolViewModel}" Name="Uc"
              x:Class="PixiEditor.Views.Main.Tools.ToolPickerButton">
     <Design.DataContext>
         <tools:ToolViewModel />
     </Design.DataContext>
     <Button Command="{xaml:Command PixiEditor.Tools.SelectTool, UseProvided=true}"
             CommandParameter="{Binding}"
+            IsEnabled="{Binding CanBeUsed}"
             Width="44" Height="34"
             ui:Translator.TooltipKey="{Binding DisplayName}"
             Background="{DynamicResource ThemeBackgroundBrush1}">

+ 4 - 2
src/PixiEditor/Views/Main/Tools/ToolPickerButton.axaml.cs

@@ -1,11 +1,13 @@
-using Avalonia;
+using System.Collections.ObjectModel;
+using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.Metadata;
+using PixiEditor.Models.Handlers;
 
 namespace PixiEditor.Views.Main.Tools;
 
 [PseudoClasses(":selected")]
-public partial class ToolPickerButton : UserControl
+internal partial class ToolPickerButton : UserControl
 {
     public static readonly StyledProperty<bool> IsSelectedProperty = AvaloniaProperty.Register<ToolPickerButton, bool>(
         nameof(IsSelected));

+ 3 - 1
src/PixiEditor/Views/Main/Tools/ToolsPicker.axaml

@@ -6,6 +6,7 @@
              xmlns:tools1="clr-namespace:PixiEditor.Views.Main.Tools"
              xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
              xmlns:system="clr-namespace:System;assembly=System.Runtime"
+             xmlns:viewModels="clr-namespace:PixiEditor.ViewModels"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              ClipToBounds="False"
              x:Class="PixiEditor.Views.Main.Tools.ToolsPicker" Name="picker">
@@ -40,7 +41,8 @@
                 <ItemsControl ItemsSource="{Binding ElementName=picker, Path=ToolSet.Tools}" Padding="0 2">
                     <ItemsControl.ItemTemplate>
                         <DataTemplate DataType="tools:ToolViewModel">
-                            <tools1:ToolPickerButton DataContext="{Binding}" IsSelected="{Binding IsActive}" />
+                            <tools1:ToolPickerButton DataContext="{Binding}" 
+                                                     IsSelected="{Binding IsActive}" />
                         </DataTemplate>
                     </ItemsControl.ItemTemplate>
                     <ItemsControl.ItemsPanel>