Browse Source

Merge pull request #636 from PixiEditor/toolsets

Toolsets logic
Krzysztof Krysiński 11 months ago
parent
commit
b19b3d12fa

+ 33 - 0
src/PixiEditor/Data/Configs/ToolSetsConfig.json

@@ -0,0 +1,33 @@
+[
+  {
+    "Name": "PIXEL_ART_TOOLSET",
+    "Tools": [
+      "MoveViewport",
+      "RotateViewport",
+      "Move",
+      "Pen",
+      "Select",
+      "MagicWand",
+      "Lasso",
+      "FloodFill",
+      "Line",
+      "Ellipse",
+      "Rectangle",
+      "Eraser",
+      "ColorPicker",
+      "Brightness",
+      "Zoom"
+    ]
+  },
+  {
+    "Name": "VECTOR_TOOLSET",
+    "Tools": [
+      "MoveViewport",
+      "RotateViewport",
+      "Move",
+      "Line",
+      "Ellipse",
+      "Rectangle"
+    ]
+  }
+]

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

@@ -745,5 +745,7 @@
   "DIVIDE": "Divide",
   "DIVIDE": "Divide",
   "SIN": "Sin",
   "SIN": "Sin",
   "COS": "Cos",
   "COS": "Cos",
-  "TAN": "Tan"
+  "TAN": "Tan",
+  "PIXEL_ART_TOOLSET": "Pixel Art",
+  "VECTOR_TOOLSET": "Vector"
 }
 }

+ 28 - 27
src/PixiEditor/Helpers/ServiceCollectionHelpers.cs

@@ -81,33 +81,21 @@ internal static class ServiceCollectionHelpers
             .AddSingleton<CommandController>()
             .AddSingleton<CommandController>()
             .AddSingleton<DocumentManagerViewModel>()
             .AddSingleton<DocumentManagerViewModel>()
             // Tools
             // Tools
-            .AddSingleton<IToolHandler, MoveViewportToolViewModel>()
-            .AddSingleton<IToolHandler, RotateViewportToolViewModel>()
-            .AddSingleton<IMoveToolHandler, MoveToolViewModel>()
-            .AddSingleton<IToolHandler, MoveToolViewModel>(x => (MoveToolViewModel)x.GetService<IMoveToolHandler>())
-            .AddSingleton<IPenToolHandler, PenToolViewModel>()
-            .AddSingleton<IToolHandler, PenToolViewModel>(x => (PenToolViewModel)x.GetService<IPenToolHandler>())
-            .AddSingleton<ISelectToolHandler, SelectToolViewModel>()
-            .AddSingleton<IToolHandler, SelectToolViewModel>(x => (SelectToolViewModel)x.GetService<ISelectToolHandler>())
-            .AddSingleton<IMagicWandToolHandler, MagicWandToolViewModel>()
-            .AddSingleton<IToolHandler, MagicWandToolViewModel>(x => (MagicWandToolViewModel)x.GetService<IMagicWandToolHandler>())
-            .AddSingleton<ILassoToolHandler, LassoToolViewModel>()
-            .AddSingleton<IToolHandler, LassoToolViewModel>(x => (LassoToolViewModel)x.GetService<ILassoToolHandler>())
-            .AddSingleton<IFloodFillToolHandler, FloodFillToolViewModel>()
-            .AddSingleton<IToolHandler, FloodFillToolViewModel>(x => (FloodFillToolViewModel)x.GetService<IFloodFillToolHandler>())
-            .AddSingleton<ILineToolHandler, LineToolViewModel>()
-            .AddSingleton<IToolHandler, LineToolViewModel>(x => (LineToolViewModel)x.GetService<ILineToolHandler>())
-            .AddSingleton<IEllipseToolHandler, EllipseToolViewModel>()
-            .AddSingleton<IToolHandler, EllipseToolViewModel>(x => (EllipseToolViewModel)x.GetService<IEllipseToolHandler>())
-            .AddSingleton<IRectangleToolHandler, RectangleToolViewModel>()
-            .AddSingleton<IToolHandler, RectangleToolViewModel>(x => (RectangleToolViewModel)x.GetService<IRectangleToolHandler>())
-            .AddSingleton<IEraserToolHandler, EraserToolViewModel>()
-            .AddSingleton<IToolHandler, EraserToolViewModel>(x => (EraserToolViewModel)x.GetService<IEraserToolHandler>())
-            .AddSingleton<IColorPickerHandler, ColorPickerToolViewModel>()
-            .AddSingleton<IToolHandler, ColorPickerToolViewModel>(x => (ColorPickerToolViewModel)x.GetService<IColorPickerHandler>())
-            .AddSingleton<IBrightnessToolHandler, BrightnessToolViewModel>()
-            .AddSingleton<IToolHandler, BrightnessToolViewModel>(x => (BrightnessToolViewModel)x.GetService<IBrightnessToolHandler>())
-            .AddSingleton<IToolHandler, ZoomToolViewModel>()
+            .AddTool<MoveViewportToolViewModel>()
+            .AddTool<RotateViewportToolViewModel>()
+            .AddTool<IMoveToolHandler, MoveToolViewModel>()
+            .AddTool<IPenToolHandler, PenToolViewModel>()
+            .AddTool<ISelectToolHandler, SelectToolViewModel>()
+            .AddTool<IMagicWandToolHandler, MagicWandToolViewModel>()
+            .AddTool<ILassoToolHandler, LassoToolViewModel>()
+            .AddTool<IFloodFillToolHandler, FloodFillToolViewModel>()
+            .AddTool<ILineToolHandler, LineToolViewModel>()
+            .AddTool<IEllipseToolHandler, EllipseToolViewModel>()
+            .AddTool<IRectangleToolHandler, RectangleToolViewModel>()
+            .AddTool<IEraserToolHandler, EraserToolViewModel>()
+            .AddTool<IColorPickerHandler, ColorPickerToolViewModel>()
+            .AddTool<IBrightnessToolHandler, BrightnessToolViewModel>()
+            .AddTool<ZoomToolViewModel>()
             // File types
             // File types
             .AddSingleton<IoFileType, PixiFileType>()
             .AddSingleton<IoFileType, PixiFileType>()
             .AddSingleton<IoFileType, PngFileType>()
             .AddSingleton<IoFileType, PngFileType>()
@@ -167,6 +155,19 @@ internal static class ServiceCollectionHelpers
             url = Environment.GetEnvironmentVariable("PixiEditorAnalytics");
             url = Environment.GetEnvironmentVariable("PixiEditorAnalytics");
         }
         }
     }
     }
+    
+    private static IServiceCollection AddTool<T, T1>(this IServiceCollection collection)
+        where T : class, IToolHandler where T1 : class, T
+    {
+        return collection.AddSingleton<T, T1>()
+            .AddSingleton<IToolHandler, T1>(x => (T1)x.GetRequiredService<T>());
+    }
+    
+    private static IServiceCollection AddTool<T>(this IServiceCollection collection)
+        where T : class, IToolHandler
+    {
+        return collection.AddSingleton<IToolHandler, T>();
+    }
 
 
     private static IServiceCollection AddMenuBuilders(this IServiceCollection collection)
     private static IServiceCollection AddMenuBuilders(this IServiceCollection collection)
     {
     {

+ 20 - 0
src/PixiEditor/Models/Config/ConfigManager.cs

@@ -0,0 +1,20 @@
+using System.Reflection;
+using Avalonia.Platform;
+using Newtonsoft.Json;
+using PixiEditor.Views;
+
+namespace PixiEditor.Models.Config;
+
+public class ConfigManager
+{
+    public T GetConfig<T>(string configName)
+    {
+        string path = $"avares://{Assembly.GetExecutingAssembly().GetName().Name}/Data/Configs/{configName}.json";
+
+        using Stream config = AssetLoader.Open(new Uri(path));
+        using StreamReader reader = new(config);
+        
+        string json = reader.ReadToEnd();
+        return JsonConvert.DeserializeObject<T>(json);
+    }
+}

+ 13 - 0
src/PixiEditor/Models/Config/ToolSetConfig.cs

@@ -0,0 +1,13 @@
+using Newtonsoft.Json;
+
+namespace PixiEditor.Models.Config;
+
+public class ToolSetsConfig : List<ToolSetConfig>
+{
+}
+
+public class ToolSetConfig
+{
+    public string Name { get; set; }
+    public List<string> Tools { get; set; }
+}

+ 7 - 0
src/PixiEditor/Models/Handlers/IToolSetHandler.cs

@@ -0,0 +1,7 @@
+namespace PixiEditor.Models.Handlers;
+
+internal interface IToolSetHandler : IHandler
+{
+    public string Name { get; }
+    public ICollection<IToolHandler> Tools { get; }
+}

+ 4 - 2
src/PixiEditor/Models/Handlers/IToolsHandler.cs

@@ -3,6 +3,7 @@ using Avalonia.Input;
 using PixiEditor.Models.Preferences;
 using PixiEditor.Models.Preferences;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
+using PixiEditor.Models.Config;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Events;
 using PixiEditor.Numerics;
 using PixiEditor.Numerics;
 using PixiEditor.ViewModels.Tools;
 using PixiEditor.ViewModels.Tools;
@@ -14,11 +15,12 @@ internal interface IToolsHandler : IHandler
     public void SetTool(object parameter);
     public void SetTool(object parameter);
     public void RestorePreviousTool();
     public void RestorePreviousTool();
     public IToolHandler ActiveTool { get; }
     public IToolHandler ActiveTool { get; }
-    public ICollection<IToolHandler> ToolSet { get; }
+    public IToolSetHandler ActiveToolSet { get; } 
+    public ICollection<IToolSetHandler> AllToolSets { get; }
     public RightClickMode RightClickMode { get; set; }
     public RightClickMode RightClickMode { get; set; }
     public bool EnableSharedToolbar { get; set; }
     public bool EnableSharedToolbar { get; set; }
     public event EventHandler<SelectedToolEventArgs> SelectedToolChanged;
     public event EventHandler<SelectedToolEventArgs> SelectedToolChanged;
-    public void SetupTools(IServiceProvider services);
+    public void SetupTools(IServiceProvider services, ToolSetsConfig toolSetConfig);
     public void SetupToolsTooltipShortcuts(IServiceProvider services);
     public void SetupToolsTooltipShortcuts(IServiceProvider services);
     public void SetActiveTool<T>(bool transient) where T : IToolHandler;
     public void SetActiveTool<T>(bool transient) where T : IToolHandler;
     public void SetActiveTool(Type toolType, bool transient);
     public void SetActiveTool(Type toolType, bool transient);

+ 1 - 37
src/PixiEditor/PixiEditor.csproj

@@ -79,6 +79,7 @@
     <AvaloniaResource Include="Data\Languages\**"/>
     <AvaloniaResource Include="Data\Languages\**"/>
     <AvaloniaResource Include="Data\ShortcutActionMaps\**"/>
     <AvaloniaResource Include="Data\ShortcutActionMaps\**"/>
     <AvaloniaResource Include="Data\BetaExampleFiles\**"/>
     <AvaloniaResource Include="Data\BetaExampleFiles\**"/>
+    <AvaloniaResource Include="Data\Configs\**"/>
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
@@ -133,41 +134,4 @@
     </None>
     </None>
   </ItemGroup>
   </ItemGroup>
 
 
-  <ItemGroup>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Clipboard\Copy.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Clipboard\Cut.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Clipboard\Paste.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Clipboard\PasteAsNewLayer.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Clipboard\PasteReferenceLayer.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Document\CenterContent.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Document\ResizeCanvas.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Document\ResizeDocument.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Document\Rotate180Deg.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Document\Rotate180DegLayers.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Document\Rotate270Deg.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Document\Rotate270DegLayers.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Document\Rotate90Deg.png"/>
-    <UpToDateCheckInput Remove="Images\Commands\PixiEditor\Document\Rotate90DegLayers.png"/>
-    <UpToDateCheckInput Remove="Images\News\Article.png"/>
-    <UpToDateCheckInput Remove="Images\News\Misc.png"/>
-    <UpToDateCheckInput Remove="Images\News\NewVersion.png"/>
-    <UpToDateCheckInput Remove="Images\News\OfficialAnnouncement.png"/>
-    <UpToDateCheckInput Remove="Images\News\YouTube.png"/>
-    <UpToDateCheckInput Remove="Styles\Templates\NodeProperties\ImageNodePropertyView.axaml"/>
-  </ItemGroup>
-
-  <ItemGroup>
-    <Compile Update="Views\Dock\TimelineDockView.axaml.cs">
-      <DependentUpon>TimelineDockView.axaml</DependentUpon>
-      <SubType>Code</SubType>
-    </Compile>
-    <Compile Update="Views\Nodes\Properties\NodeSocket.cs">
-      <DependentUpon>NodeSocket.axaml</DependentUpon>
-      <SubType>Code</SubType>
-    </Compile>
-    <Compile Update="Views\Nodes\ConnectionView.cs">
-      <SubType>Code</SubType>
-    </Compile>
-  </ItemGroup>
-
 </Project>
 </Project>

+ 22 - 0
src/PixiEditor/ViewModels/SubViewModels/ToolSetViewModel.cs

@@ -0,0 +1,22 @@
+using System.Collections.ObjectModel;
+using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Models.Handlers;
+using PixiEditor.ViewModels.Tools;
+
+namespace PixiEditor.ViewModels.SubViewModels;
+
+internal class ToolSetViewModel : PixiObservableObject, IToolSetHandler
+{
+    public string Name { get; }
+    ICollection<IToolHandler> IToolSetHandler.Tools => Tools;
+    public ObservableCollection<IToolHandler> Tools { get; } = new();
+    
+    public ToolSetViewModel(string setName, List<IToolHandler> tools)
+    {
+        Name = setName;
+        foreach (var tool in tools)
+        {
+            Tools.Add(tool);
+        }    
+    }
+}

+ 87 - 8
src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs

@@ -10,6 +10,7 @@ using PixiEditor.Models.AnalyticsAPI;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Commands.Attributes.Evaluators;
 using PixiEditor.Models.Commands.Attributes.Evaluators;
 using PixiEditor.Models.Commands.CommandContext;
 using PixiEditor.Models.Commands.CommandContext;
+using PixiEditor.Models.Config;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
@@ -79,8 +80,15 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         }
         }
     }
     }
 
 
-    ICollection<IToolHandler> IToolsHandler.ToolSet => ToolSet;
-    public ObservableCollection<IToolHandler> ToolSet { get; private set; }
+    public IToolSetHandler ActiveToolSet
+    {
+        get => _activeToolSet!;
+        private set => SetProperty(ref _activeToolSet, value);
+    }
+
+    ICollection<IToolSetHandler> IToolsHandler.AllToolSets => AllToolSets;
+    
+    public ObservableCollection<IToolSetHandler> AllToolSets { get; } = new();
 
 
     public event EventHandler<SelectedToolEventArgs>? SelectedToolChanged;
     public event EventHandler<SelectedToolEventArgs>? SelectedToolChanged;
 
 
@@ -89,21 +97,39 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     private bool altIsDown;
     private bool altIsDown;
 
 
     private ToolViewModel _preTransientTool;
     private ToolViewModel _preTransientTool;
-
+    
+    private List<IToolHandler> allTools = new();
+    private IToolSetHandler? _activeToolSet;
 
 
     public ToolsViewModel(ViewModelMain owner)
     public ToolsViewModel(ViewModelMain owner)
         : base(owner)
         : base(owner)
     {
     {
     }
     }
 
 
-    public void SetupTools(IServiceProvider services)
+    public void SetupTools(IServiceProvider services, ToolSetsConfig toolSetConfig)
     {
     {
-        ToolSet = new ObservableCollection<IToolHandler>(services.GetServices<IToolHandler>());
+        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());
+    }
+
+    public void SetActiveToolSet(IToolSetHandler toolSetHandler)
+    {
+        ActiveToolSet = toolSetHandler;
     }
     }
 
 
     public void SetupToolsTooltipShortcuts(IServiceProvider services)
     public void SetupToolsTooltipShortcuts(IServiceProvider services)
     {
     {
-        foreach (ToolViewModel tool in ToolSet!)
+        foreach (ToolViewModel tool in ActiveToolSet.Tools!)
         {
         {
             tool.Shortcut = Owner.ShortcutController.GetToolShortcut(tool.GetType());
             tool.Shortcut = Owner.ShortcutController.GetToolShortcut(tool.GetType());
         }
         }
@@ -112,7 +138,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     public T? GetTool<T>()
     public T? GetTool<T>()
         where T : IToolHandler
         where T : IToolHandler
     {
     {
-        return (T?)ToolSet?.Where(static tool => tool is T).FirstOrDefault();
+        return (T?)ActiveToolSet?.Tools.Where(static tool => tool is T).FirstOrDefault();
     }
     }
 
 
     public void SetActiveTool<T>(bool transient)
     public void SetActiveTool<T>(bool transient)
@@ -131,6 +157,36 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
             return;
             return;
         doc.EventInlet.OnApplyTransform();
         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, 
+        Key = Key.E, AnalyticsTrack = true)]
+    [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)
+        {
+            nextIndex = 0;
+        }
+
+        SetActiveToolSet(AllToolSets.ElementAt(nextIndex));
+    }
+
+    [Evaluator.CanExecute("PixiEditor.HasNextToolSet")]
+    public bool HasNextToolSet(bool next)
+    {
+        int currentIndex = AllToolSets.IndexOf(ActiveToolSet);
+        int nextIndex = currentIndex + (next ? 1 : -1);
+        if (nextIndex < 0 || nextIndex >= AllToolSets.Count)
+        {
+            return false;
+        }
+
+        return AllToolSets.ElementAt(nextIndex) != ActiveToolSet;
+    } 
 
 
     [Command.Internal("PixiEditor.Tools.SelectTool", CanExecute = "PixiEditor.HasDocument")]
     [Command.Internal("PixiEditor.Tools.SelectTool", CanExecute = "PixiEditor.HasDocument")]
     public void SetActiveTool(ToolViewModel tool)
     public void SetActiveTool(ToolViewModel tool)
@@ -235,7 +291,9 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     {
     {
         if (!typeof(ToolViewModel).IsAssignableFrom(toolType))
         if (!typeof(ToolViewModel).IsAssignableFrom(toolType))
             throw new ArgumentException($"'{toolType}' does not inherit from {typeof(ToolViewModel)}");
             throw new ArgumentException($"'{toolType}' does not inherit from {typeof(ToolViewModel)}");
-        IToolHandler foundTool = ToolSet!.First(x => x.GetType().IsAssignableFrom(toolType));
+        IToolHandler foundTool = ActiveToolSet!.Tools.FirstOrDefault(x => x.GetType().IsAssignableFrom(toolType));
+        if (foundTool == null) return;
+        
         SetActiveTool(foundTool, transient, sourceInfo);
         SetActiveTool(foundTool, transient, sourceInfo);
     }
     }
     
     
@@ -303,4 +361,25 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     {
     {
         ActiveTool?.ModifierKeyChanged(args.IsCtrlDown, args.IsShiftDown, args.IsAltDown);
         ActiveTool?.ModifierKeyChanged(args.IsCtrlDown, args.IsShiftDown, args.IsAltDown);
     }
     }
+    
+    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)
+                {
+                    throw new InvalidOperationException($"Tool '{toolName}' not found.");
+                }
+                
+                tools.Add(tool);
+            }
+            
+            AllToolSets.Add(new ToolSetViewModel(toolSet.Name, tools));
+        }
+    }
 }
 }

+ 1 - 1
src/PixiEditor/ViewModels/ToolVM.cs

@@ -11,6 +11,6 @@ internal class ToolVM : MarkupExtension
 
 
     public override object ProvideValue(IServiceProvider serviceProvider)
     public override object ProvideValue(IServiceProvider serviceProvider)
     {
     {
-        return ViewModelMain.Current?.ToolsSubViewModel.ToolSet?.Where(tool => tool.GetType().Name == TypeName).FirstOrDefault();
+        return (ViewModelMain.Current?.ToolsSubViewModel.ActiveToolSet?.Tools).FirstOrDefault(tool => tool.GetType().Name == TypeName);
     }
     }
 }
 }

+ 7 - 25
src/PixiEditor/ViewModels/ViewModelMain.cs

@@ -10,6 +10,7 @@ using PixiEditor.Helpers;
 using PixiEditor.Helpers.Collections;
 using PixiEditor.Helpers.Collections;
 using PixiEditor.Models.AnalyticsAPI;
 using PixiEditor.Models.AnalyticsAPI;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.Commands;
+using PixiEditor.Models.Config;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.DocumentModels;
@@ -32,59 +33,37 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
     public Action CloseAction { get; set; }
     public Action CloseAction { get; set; }
     public event EventHandler OnStartupEvent;
     public event EventHandler OnStartupEvent;
     public FileViewModel FileSubViewModel { get; set; }
     public FileViewModel FileSubViewModel { get; set; }
-
     public UpdateViewModel UpdateSubViewModel { get; set; }
     public UpdateViewModel UpdateSubViewModel { get; set; }
-
     public IToolsHandler ToolsSubViewModel { get; set; }
     public IToolsHandler ToolsSubViewModel { get; set; }
-
     public IoViewModel IoSubViewModel { get; set; }
     public IoViewModel IoSubViewModel { get; set; }
-
     public LayersViewModel LayersSubViewModel { get; set; }
     public LayersViewModel LayersSubViewModel { get; set; }
-
     public ClipboardViewModel ClipboardSubViewModel { get; set; }
     public ClipboardViewModel ClipboardSubViewModel { get; set; }
-
     public UndoViewModel UndoSubViewModel { get; set; }
     public UndoViewModel UndoSubViewModel { get; set; }
-
     public SelectionViewModel SelectionSubViewModel { get; set; }
     public SelectionViewModel SelectionSubViewModel { get; set; }
-
     public ViewOptionsViewModel ViewportSubViewModel { get; set; }
     public ViewOptionsViewModel ViewportSubViewModel { get; set; }
-
     public ColorsViewModel ColorsSubViewModel { get; set; }
     public ColorsViewModel ColorsSubViewModel { get; set; }
-
     public MiscViewModel MiscSubViewModel { get; set; }
     public MiscViewModel MiscSubViewModel { get; set; }
-
     public DiscordViewModel DiscordViewModel { get; set; }
     public DiscordViewModel DiscordViewModel { get; set; }
-
     public DebugViewModel DebugSubViewModel { get; set; }
     public DebugViewModel DebugSubViewModel { get; set; }
-
     public DocumentManagerViewModel DocumentManagerSubViewModel { get; set; }
     public DocumentManagerViewModel DocumentManagerSubViewModel { get; set; }
-
     public CommandController CommandController { get; set; }
     public CommandController CommandController { get; set; }
-
     public ShortcutController ShortcutController { get; set; }
     public ShortcutController ShortcutController { get; set; }
-
     public StylusViewModel StylusSubViewModel { get; set; }
     public StylusViewModel StylusSubViewModel { get; set; }
-
     public WindowViewModel WindowSubViewModel { get; set; }
     public WindowViewModel WindowSubViewModel { get; set; }
-
     public SearchViewModel SearchSubViewModel { get; set; }
     public SearchViewModel SearchSubViewModel { get; set; }
-
     public RegistryViewModel RegistrySubViewModel { get; set; }
     public RegistryViewModel RegistrySubViewModel { get; set; }
-
     public AdditionalContentViewModel AdditionalContentSubViewModel { get; set; }
     public AdditionalContentViewModel AdditionalContentSubViewModel { get; set; }
-
     public ExtensionsViewModel ExtensionsSubViewModel { get; set; }
     public ExtensionsViewModel ExtensionsSubViewModel { get; set; }
-
     public LayoutViewModel LayoutSubViewModel { get; set; }
     public LayoutViewModel LayoutSubViewModel { get; set; }
-
     public MenuBarViewModel MenuBarViewModel { get; set; }
     public MenuBarViewModel MenuBarViewModel { get; set; }
     public AnimationsViewModel AnimationsSubViewModel { get; set; }
     public AnimationsViewModel AnimationsSubViewModel { get; set; }
-    
     public NodeGraphManagerViewModel NodeGraphManager { get; set; }
     public NodeGraphManagerViewModel NodeGraphManager { get; set; }
 
 
     public IPreferences Preferences { get; set; }
     public IPreferences Preferences { get; set; }
     public ILocalizationProvider LocalizationProvider { get; set; }
     public ILocalizationProvider LocalizationProvider { get; set; }
 
 
+    public ConfigManager Config { get; set; }    
+    
     public LocalizedString ActiveActionDisplay
     public LocalizedString ActiveActionDisplay
     {
     {
         get
         get
@@ -117,6 +96,8 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
     {
     {
         Services = services;
         Services = services;
 
 
+        Config = new ConfigManager(); 
+
         Preferences = services.GetRequiredService<IPreferences>();
         Preferences = services.GetRequiredService<IPreferences>();
         Preferences.Init();
         Preferences.Init();
         
         
@@ -146,7 +127,8 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
         ColorsSubViewModel = services.GetService<ColorsViewModel>();
         ColorsSubViewModel = services.GetService<ColorsViewModel>();
         ColorsSubViewModel?.SetupPaletteProviders(services);
         ColorsSubViewModel?.SetupPaletteProviders(services);
 
 
-        ToolsSubViewModel?.SetupTools(services);
+        ToolSetsConfig toolSetConfig = Config.GetConfig<ToolSetsConfig>("ToolSetsConfig");
+        ToolsSubViewModel?.SetupTools(services, toolSetConfig);
 
 
         DiscordViewModel = services.GetService<DiscordViewModel>();
         DiscordViewModel = services.GetService<DiscordViewModel>();
         UpdateSubViewModel = services.GetService<UpdateViewModel>();
         UpdateSubViewModel = services.GetService<UpdateViewModel>();

+ 41 - 15
src/PixiEditor/Views/Main/Tools/ToolsPicker.axaml

@@ -4,26 +4,52 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:tools="clr-namespace:PixiEditor.ViewModels.Tools"
              xmlns:tools="clr-namespace:PixiEditor.ViewModels.Tools"
              xmlns:tools1="clr-namespace:PixiEditor.Views.Main.Tools"
              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"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             ClipToBounds="False"
              x:Class="PixiEditor.Views.Main.Tools.ToolsPicker" Name="picker">
              x:Class="PixiEditor.Views.Main.Tools.ToolsPicker" Name="picker">
     <Border CornerRadius="{DynamicResource ControlCornerRadius}"
     <Border CornerRadius="{DynamicResource ControlCornerRadius}"
             BorderBrush="{DynamicResource ThemeBorderMidBrush}"
             BorderBrush="{DynamicResource ThemeBorderMidBrush}"
             BorderThickness="{DynamicResource ThemeBorderThickness}"
             BorderThickness="{DynamicResource ThemeBorderThickness}"
             Cursor="Arrow"
             Cursor="Arrow"
+            Width="48"
             Background="{DynamicResource ThemeBackgroundBrush1}">
             Background="{DynamicResource ThemeBackgroundBrush1}">
-        <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
-        <ItemsControl ItemsSource="{Binding ElementName=picker, Path=Tools}" Padding="0 2">
-            <ItemsControl.ItemTemplate>
-                <DataTemplate DataType="tools:ToolViewModel">
-                    <tools1:ToolPickerButton DataContext="{Binding}" IsSelected="{Binding IsActive}"/>
-                </DataTemplate>
-            </ItemsControl.ItemTemplate>
-            <ItemsControl.ItemsPanel>
-                <ItemsPanelTemplate>
-                    <StackPanel Orientation="Vertical"/>
-                </ItemsPanelTemplate>
-            </ItemsControl.ItemsPanel>
-        </ItemsControl>
-        </ScrollViewer>
+        <StackPanel>
+            <Border Background="{DynamicResource ThemeBackgroundBrush2}">
+                <StackPanel Orientation="Horizontal">
+                    <Button Classes="pixi-icon" Content="{DynamicResource icon-chevron-left}"
+                            Command="{Binding SwitchToolSetCommand, ElementName=picker}">
+                        <Button.CommandParameter>
+                            <system:Boolean>False</system:Boolean>
+                        </Button.CommandParameter>
+                    </Button>
+                    <Button Classes="pixi-icon" Content="{DynamicResource icon-chevron-right}"
+                            Command="{Binding SwitchToolSetCommand, ElementName=picker}">
+                        <Button.CommandParameter>
+                            <system:Boolean>True</system:Boolean>
+                        </Button.CommandParameter>
+                    </Button>
+                    <Border Padding="5" Margin="5 0" ClipToBounds="False" Background="{DynamicResource ThemeBackgroundBrush2}"
+                            CornerRadius="{DynamicResource ControlCornerRadius}">
+                       <TextBlock ui:Translator.Key="{Binding ElementName=picker, Path=ToolSet.Name}" VerticalAlignment="Center"/> 
+                    </Border>
+                </StackPanel>
+            </Border>
+            <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
+                <ItemsControl ItemsSource="{Binding ElementName=picker, Path=ToolSet.Tools}" Padding="0 2">
+                    <ItemsControl.ItemTemplate>
+                        <DataTemplate DataType="tools:ToolViewModel">
+                            <tools1:ToolPickerButton DataContext="{Binding}" IsSelected="{Binding IsActive}" />
+                        </DataTemplate>
+                    </ItemsControl.ItemTemplate>
+                    <ItemsControl.ItemsPanel>
+                        <ItemsPanelTemplate>
+                            <StackPanel Orientation="Vertical" />
+                        </ItemsPanelTemplate>
+                    </ItemsControl.ItemsPanel>
+                </ItemsControl>
+            </ScrollViewer>
+        </StackPanel>
     </Border>
     </Border>
-</UserControl>
+</UserControl>

+ 15 - 6
src/PixiEditor/Views/Main/Tools/ToolsPicker.axaml.cs

@@ -1,4 +1,4 @@
-using System.Collections.ObjectModel;
+using System.Windows.Input;
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
@@ -7,13 +7,22 @@ namespace PixiEditor.Views.Main.Tools;
 
 
 internal partial class ToolsPicker : UserControl
 internal partial class ToolsPicker : UserControl
 {
 {
-    public static readonly StyledProperty<ObservableCollection<IToolHandler>> ToolsProperty =
-        AvaloniaProperty.Register<ToolsPicker, ObservableCollection<IToolHandler>>(nameof(Tools));
+    public static readonly StyledProperty<IToolSetHandler> ToolSetProperty =
+        AvaloniaProperty.Register<ToolsPicker, IToolSetHandler>(
+            nameof(ToolSet));
+    public IToolSetHandler ToolSet
+    {
+        get => GetValue(ToolSetProperty);
+        set => SetValue(ToolSetProperty, value);
+    }
+
+    public static readonly StyledProperty<ICommand> SwitchToolSetCommandProperty = AvaloniaProperty.Register<ToolsPicker, ICommand>(
+        "SwitchToolSetCommand");
 
 
-    public ObservableCollection<IToolHandler> Tools
+    public ICommand SwitchToolSetCommand
     {
     {
-        get => GetValue(ToolsProperty);
-        set => SetValue(ToolsProperty, value);
+        get => GetValue(SwitchToolSetCommandProperty);
+        set => SetValue(SwitchToolSetCommandProperty, value);
     }
     }
 
 
     public ToolsPicker()
     public ToolsPicker()

+ 2 - 1
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -117,7 +117,8 @@
                            Margin="10 55 0 0"
                            Margin="10 55 0 0"
                            HorizontalAlignment="Left"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Top"
                            VerticalAlignment="Top"
-                           Tools="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ToolSet}" />
+                           ToolSet="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ActiveToolSet}" 
+                           SwitchToolSetCommand="{xaml:Command Name=PixiEditor.Tools.SwitchToolSet, UseProvided=True}"/>
         <rendering:Scene
         <rendering:Scene
             Focusable="False" Name="scene"
             Focusable="False" Name="scene"
             ZIndex="1"
             ZIndex="1"