Procházet zdrojové kódy

Added Command Groups

CPKreuz před 3 roky
rodič
revize
dbbc8b6d0f
24 změnil soubory, kde provedl 260 přidání a 72 odebrání
  1. 22 0
      PixiEditor/Models/Commands/Attributes/Commands/GroupAttribute.cs
  2. 27 0
      PixiEditor/Models/Commands/Attributes/Commands/InternalAttribute.cs
  3. 11 3
      PixiEditor/Models/Commands/Attributes/Evaluators/CanExecuteAttribute.cs
  4. 2 0
      PixiEditor/Models/Commands/CommandCollection.cs
  5. 66 17
      PixiEditor/Models/Commands/CommandController.cs
  6. 27 0
      PixiEditor/Models/Commands/CommandGroup.cs
  7. 37 0
      PixiEditor/Models/Services/CommandProvider.cs
  8. 8 0
      PixiEditor/Models/Services/DocumentProvider.cs
  9. 0 3
      PixiEditor/Styles/ThemeStyle.xaml
  10. 2 2
      PixiEditor/ViewModels/SettingsWindowViewModel.cs
  11. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs
  12. 1 1
      PixiEditor/ViewModels/SubViewModels/Main/DebugViewModel.cs
  13. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/DocumentViewModel.cs
  14. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs
  15. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs
  16. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/MiscViewModel.cs
  17. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/SearchViewModel.cs
  18. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/SelectionViewModel.cs
  19. 8 15
      PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs
  20. 2 3
      PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs
  21. 3 2
      PixiEditor/ViewModels/SubViewModels/Main/WindowViewModel.cs
  22. 0 12
      PixiEditor/ViewModels/ViewModelMain.cs
  23. 31 7
      PixiEditor/Views/Dialogs/SettingsWindow.xaml
  24. 6 7
      PixiEditor/Views/MainWindow.xaml

+ 22 - 0
PixiEditor/Models/Commands/Attributes/Commands/GroupAttribute.cs

@@ -0,0 +1,22 @@
+namespace PixiEditor.Models.Commands.Attributes
+{
+    public partial class Command
+    {
+        [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+        public class GroupAttribute : Attribute
+        {
+            public string Name { get; }
+
+            public string Display { get; }
+
+            /// <summary>
+            /// Group's all commands that start with the name <paramref name="name"/>
+            /// </summary>
+            public GroupAttribute(string name, string display)
+            {
+                Name = name;
+                Display = display;
+            }
+        }
+    }
+}

+ 27 - 0
PixiEditor/Models/Commands/Attributes/Commands/InternalAttribute.cs

@@ -0,0 +1,27 @@
+namespace PixiEditor.Models.Commands.Attributes
+{
+    public partial class Command
+    {
+        /// <summary>
+        /// A command that is not shown in the UI
+        /// </summary>
+        public class InternalAttribute : BasicAttribute
+        {
+            /// <summary>
+            /// A command that is not shown in the UI
+            /// </summary>
+            public InternalAttribute(string name)
+                : base(name, null, null)
+            {
+            }
+
+            /// <summary>
+            /// A command that is not shown in the UI
+            /// </summary>
+            public InternalAttribute(string name, object parameter)
+                : base(name, parameter, null, null)
+            {
+            }
+        }
+    }
+}

+ 11 - 3
PixiEditor/Models/Commands/Attributes/Evaluators/CanExecuteAttribute.cs

@@ -5,8 +5,16 @@ public partial class Evaluator
     [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)]
     public class CanExecuteAttribute : EvaluatorAttribute
     {
-        public CanExecuteAttribute(string name)
-            : base(name)
-        { }
+        public string[] Requires { get; }
+
+        public CanExecuteAttribute(string name) : base(name)
+        {
+            Requires = Array.Empty<string>();
+        }
+
+        public CanExecuteAttribute(string name, params string[] requires) : base(name)
+        {
+            Requires = requires;
+        }
     }
 }

+ 2 - 0
PixiEditor/Models/Commands/CommandCollection.cs

@@ -36,6 +36,8 @@ namespace PixiEditor.Models.Commands
             _commandShortcuts.Clear();
         }
 
+        public void ClearShortcuts() => _commandShortcuts.Clear();
+
         public bool Contains(Command item) => _commandNames.ContainsKey(item.Name);
 
         public void CopyTo(Command[] array, int arrayIndex) => _commandNames.Values.CopyTo(array, arrayIndex);

+ 66 - 17
PixiEditor/Models/Commands/CommandController.cs

@@ -16,13 +16,15 @@ namespace PixiEditor.Models.Commands
 
         public static CommandController Current { get; private set; }
 
-        public CommandCollection Commands { get; set; }
+        public CommandCollection Commands { get; }
 
-        public Dictionary<string, FactoryEvaluator> FactoryEvaluators { get; set; }
+        public List<CommandGroup> CommandGroups { get; }
 
-        public Dictionary<string, CanExecuteEvaluator> CanExecuteEvaluators { get; set; }
+        public Dictionary<string, FactoryEvaluator> FactoryEvaluators { get; }
 
-        public Dictionary<string, IconEvaluator> IconEvaluators { get; set; }
+        public Dictionary<string, CanExecuteEvaluator> CanExecuteEvaluators { get; }
+
+        public Dictionary<string, IconEvaluator> IconEvaluators { get; }
 
         public CommandController(IServiceProvider services)
         {
@@ -36,6 +38,7 @@ namespace PixiEditor.Models.Commands
                     this);
 
             Commands = new();
+            CommandGroups = new();
             FactoryEvaluators = new();
             CanExecuteEvaluators = new();
             IconEvaluators = new();
@@ -50,9 +53,18 @@ namespace PixiEditor.Models.Commands
 
             var types = typeof(CommandController).Assembly.GetTypes();
 
+            EnumerableDictionary<string, string> groups = new();
+            EnumerableDictionary<string, Command> commandGroups = new();
+
             foreach (var type in types)
             {
                 object instanceType = null;
+
+                foreach (var group in type.GetCustomAttributes<CommandAttribute.GroupAttribute>())
+                {
+                    groups.Add(group.Display, group.Name);
+                }
+
                 var methods = type.GetMethods();
 
                 foreach (var method in methods)
@@ -68,7 +80,17 @@ namespace PixiEditor.Models.Commands
                     {
                         if (attribute is Evaluator.CanExecuteAttribute canExecute)
                         {
-                            AddEvaluator<Evaluator.CanExecuteAttribute, CanExecuteEvaluator, bool>(method, instanceType, canExecute, CanExecuteEvaluators);
+                            var required = (CommandController controller) => canExecute.Requires.Select(x => controller.CanExecuteEvaluators[x]);
+                            AddEvaluatorFactory<Evaluator.CanExecuteAttribute, CanExecuteEvaluator, bool>(
+                                method,
+                                instanceType,
+                                canExecute,
+                                CanExecuteEvaluators,
+                                x => new CanExecuteEvaluator()
+                                {
+                                    Name = attribute.Name, 
+                                    Evaluate = y => x.Invoke(y) && required.Invoke(this).All(z => z.EvaluateEvaluator(null, y))
+                                });
                         }
                         else if (attribute is Evaluator.FactoryAttribute factory)
                         {
@@ -80,7 +102,6 @@ namespace PixiEditor.Models.Commands
                         }
                     }
                 }
-
             }
 
             foreach (var type in types)
@@ -126,7 +147,7 @@ namespace PixiEditor.Models.Commands
                         var tool = services.GetServices<Tool>().First(x => x.GetType() == type);
                         string name = $"PixiEditor.Tools.Select.{type.Name}";
 
-                        Commands.Add(new Command.ToolCommand()
+                        var command = new Command.ToolCommand()
                         {
                             Name = name,
                             Display = $"Select {tool.DisplayName} Tool",
@@ -137,16 +158,39 @@ namespace PixiEditor.Models.Commands
                             DefaultShortcut = toolAttr.GetShortcut(),
                             Shortcut = GetShortcut(name, toolAttr.GetShortcut()),
                             ToolType = type,
-                        });
+                        };
+
+                        Commands.Add(command);
+                        AddCommandToGroup(command);
                     }
                 }
             }
 
+            foreach (var commands in commandGroups)
+            {
+                CommandGroups.Add(new(commands.Key, commands.Value));
+            }
+
             KeyCombination GetShortcut(string name, KeyCombination defaultShortcut) => shortcuts.FirstOrDefault(x => x.Value.Contains(name), new(defaultShortcut, null)).Key;
 
+            void AddCommandToGroup(Command command)
+            {
+                var display = groups.FirstOrDefault(x => x.Value.Any(x => command.Name.StartsWith(x))).Key;
+                if (display == null)
+                {
+                    display = "Misc";
+                }
+                commandGroups.Add(display, command);
+            }
+
             void AddEvaluator<TAttr, T, TParameter>(MethodInfo method, object instance, TAttr attribute, IDictionary<string, T> evaluators)
                 where T : Evaluator<TParameter>, new()
                 where TAttr : Evaluator.EvaluatorAttribute
+                => AddEvaluatorFactory<TAttr, T, TParameter>(method, instance, attribute, evaluators, x => new T() { Name = attribute.Name, Evaluate = x });
+
+            void AddEvaluatorFactory<TAttr, T, TParameter>(MethodInfo method, object instance, TAttr attribute, IDictionary<string, T> evaluators, Func<Func<object, TParameter>, T> factory)
+                where T : Evaluator<TParameter>, new()
+                where TAttr : Evaluator.EvaluatorAttribute
             {
                 if (method.ReturnType != typeof(TParameter))
                 {
@@ -174,16 +218,12 @@ namespace PixiEditor.Models.Commands
                     func = x => (TParameter)method.Invoke(instance, null);
                 }
 
-                T evaluator = new()
-                {
-                    Name = attribute.Name,
-                    Evaluate = func
-                };
+                T evaluator = factory(func);
 
                 evaluators.Add(evaluator.Name, evaluator);
             }
 
-            void AddCommand<TAttr, TCommand>(MethodInfo method, object instance, TAttr attribute, Func<bool, string, Action<object>, CanExecuteEvaluator, IconEvaluator, TCommand> commandFactory)
+            TCommand AddCommand<TAttr, TCommand>(MethodInfo method, object instance, TAttr attribute, Func<bool, string, Action<object>, CanExecuteEvaluator, IconEvaluator, TCommand> commandFactory)
                 where TAttr : CommandAttribute.CommandAttribute
                 where TCommand : Command
             {
@@ -220,13 +260,17 @@ namespace PixiEditor.Models.Commands
                     name = name["#DEBUG#".Length..];
                 }
 
-                Commands.Add(
-                    commandFactory(
+                var command = commandFactory(
                         isDebug,
                         name,
                         action,
                         attribute.CanExecute != null ? CanExecuteEvaluators[attribute.CanExecute] : CanExecuteEvaluator.AlwaysTrue,
-                        attribute.IconEvaluator != null ? IconEvaluators[attribute.IconEvaluator] : IconEvaluator.Default));
+                        attribute.IconEvaluator != null ? IconEvaluators[attribute.IconEvaluator] : IconEvaluator.Default);
+
+                Commands.Add(command);
+                AddCommandToGroup(command);
+
+                return command;
             }
         }
 
@@ -257,5 +301,10 @@ namespace PixiEditor.Models.Commands
             command.Shortcut = newShortcut;
             shortcutFile.SaveShortcuts();
         }
+
+        public void ResetShortcuts()
+        {
+            Commands.ClearShortcuts();
+        }
     }
 }

+ 27 - 0
PixiEditor/Models/Commands/CommandGroup.cs

@@ -0,0 +1,27 @@
+using System.Collections;
+
+namespace PixiEditor.Models.Commands
+{
+    public class CommandGroup : IEnumerable<Command>
+    {
+        private readonly Command[] commands;
+        private readonly Command[] visibleCommands;
+
+        public string Display { get; set; }
+
+        public IEnumerable<Command> Commands => commands;
+
+        public IEnumerable<Command> VisibleCommands => visibleCommands;
+
+        public CommandGroup(string display, IEnumerable<Command> commands)
+        {
+            Display = display;
+            this.commands = commands.ToArray();
+            visibleCommands = commands.Where(x => !string.IsNullOrEmpty(x.Display)).ToArray();
+        }
+
+        public IEnumerator<Command> GetEnumerator() => Commands.GetEnumerator();
+
+        IEnumerator IEnumerable.GetEnumerator() => Commands.GetEnumerator();
+    }
+}

+ 37 - 0
PixiEditor/Models/Services/CommandProvider.cs

@@ -0,0 +1,37 @@
+using PixiEditor.Models.Commands;
+using PixiEditor.Models.Commands.Evaluators;
+using System.Windows.Input;
+using System.Windows.Media;
+using XAMLCommand = PixiEditor.Models.Commands.XAML.Command;
+
+namespace PixiEditor.Models.Services
+{
+    public class CommandProvider
+    {
+        private readonly CommandController _controller;
+
+        public CommandProvider(CommandController controller)
+        {
+            _controller = controller;
+        }
+
+        public Command GetCommand(string name) => _controller.Commands[name];
+
+        public CanExecuteEvaluator GetCanExecute(string name) => _controller.CanExecuteEvaluators[name];
+
+        public bool CanExecute(string name, Command command, object argument) =>
+            _controller.CanExecuteEvaluators[name].EvaluateEvaluator(command, argument);
+
+        public FactoryEvaluator GetFactoryEvaluator(string name) => _controller.FactoryEvaluators[name];
+
+        public object GetFromFactory(string name, Command command, object argument) =>
+            _controller.FactoryEvaluators[name].EvaluateEvaluator(command, argument);
+
+        public IconEvaluator GetIconEvaluator(string name) => _controller.IconEvaluators[name];
+
+        public ImageSource GetIcon(string name, Command command, object argument) =>
+            _controller.IconEvaluators[name].EvaluateEvaluator(command, argument);
+
+        public ICommand GetICommand(string name, bool useProvidedArgument = false) => XAMLCommand.GetICommand(_controller.Commands[name], useProvidedArgument);
+    }
+}

+ 8 - 0
PixiEditor/Models/Services/DocumentProvider.cs

@@ -32,6 +32,11 @@ namespace PixiEditor.Models.Services
         /// </summary>
         public IEnumerable<Layer> GetLayers() => _bitmapManager.ActiveDocument?.Layers;
 
+        /// <summary>
+        /// Gets the layer structure of the opened document
+        /// </summary>
+        public LayerStructure GetStructure() => _bitmapManager.ActiveDocument?.LayerStructure;
+
         /// <summary>
         /// Gets the active layer
         /// </summary>
@@ -47,6 +52,9 @@ namespace PixiEditor.Models.Services
         /// </summary>
         public Layer GetReferenceLayer() => _bitmapManager.ActiveDocument?.ReferenceLayer;
 
+        /// <summary>
+        /// Gets the reference layer surface of the active document
+        /// </summary>
         public Surface GetReferenceSurface() => _bitmapManager.ActiveDocument?.ReferenceLayer?.LayerBitmap;
 
         /// <summary>

+ 0 - 3
PixiEditor/Styles/ThemeStyle.xaml

@@ -54,9 +54,6 @@
     <Style TargetType="Button" x:Key="DarkRoundButton" BasedOn="{StaticResource BaseDarkButton}">
         <Setter Property="OverridesDefaultStyle" Value="True" />
         <Setter Property="Background" Value="#303030" />
-        <Setter Property="Focusable" Value="False" />
-        <Setter Property="Height" Value="28"/>
-        <Setter Property="Width" Value="70"/>
         <Setter Property="Template">
             <Setter.Value>
                 <ControlTemplate TargetType="Button">

+ 2 - 2
PixiEditor/ViewModels/SettingsWindowViewModel.cs

@@ -20,11 +20,11 @@ namespace PixiEditor.ViewModels
 
         public SettingsViewModel SettingsSubViewModel { get; set; }
 
-        public ObservableCollection<Command> Commands { get; }
+        public ObservableCollection<CommandGroup> Commands { get; }
 
         public SettingsWindowViewModel()
         {
-            Commands = new(CommandController.Current.Commands.Where(x => !string.IsNullOrWhiteSpace(x.Display)));
+            Commands = new(CommandController.Current.CommandGroups);
             SettingsSubViewModel = new SettingsViewModel(this);
         }
     }

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs

@@ -10,6 +10,7 @@ using System.Windows.Media;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Clipboard", "Clipboard")]
     public class ClipboardViewModel : SubViewModel<ViewModelMain>
     {
         public ClipboardViewModel(ViewModelMain owner)

+ 1 - 1
PixiEditor/ViewModels/SubViewModels/Main/DebugViewModel.cs

@@ -2,13 +2,13 @@
 using PixiEditor.Models.Commands.Attributes;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.UserPreferences;
-using System;
 using System.Diagnostics;
 using System.IO;
 using System.Reflection;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Debug", "Debug")]
     public class DebugViewModel : SubViewModel<ViewModelMain>
     {
         public bool IsDebugBuild { get; set; }

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/DocumentViewModel.cs

@@ -6,6 +6,7 @@ using System.Windows.Input;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Document", "Image")]
     public class DocumentViewModel : SubViewModel<ViewModelMain>
     {
         public const string ConfirmationDialogTitle = "Unsaved changes";

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -16,6 +16,7 @@ using System.Windows.Media.Imaging;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.File", "File")]
     public class FileViewModel : SubViewModel<ViewModelMain>
     {
         private bool hasRecent;

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -9,6 +9,7 @@ using System.Windows.Input;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Layer", "Image")]
     public class LayersViewModel : SubViewModel<ViewModelMain>
     {
         public RelayCommand SetActiveLayerCommand { get; set; }

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/MiscViewModel.cs

@@ -3,6 +3,7 @@ using PixiEditor.Models.Commands.Attributes;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Links", "Misc")]
     public class MiscViewModel : SubViewModel<ViewModelMain>
     {
         public MiscViewModel(ViewModelMain owner)

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/SearchViewModel.cs

@@ -3,6 +3,7 @@ using System.Windows.Input;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Search", "Search")]
     public class SearchViewModel : SubViewModel<ViewModelMain>
     {
         private bool searchWindowOpen;

+ 1 - 0
PixiEditor/ViewModels/SubViewModels/Main/SelectionViewModel.cs

@@ -7,6 +7,7 @@ using System.Windows.Input;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Selection", "Selection")]
     public class SelectionViewModel : SubViewModel<ViewModelMain>
     {
         private readonly SelectTool selectTool;

+ 8 - 15
PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -1,5 +1,6 @@
 using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.Helpers;
+using PixiEditor.Models.Commands.Attributes;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Tools;
@@ -13,15 +14,12 @@ using System.Windows.Input;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Tools", "Tools")]
     public class ToolsViewModel : SubViewModel<ViewModelMain>
     {
         private Cursor toolCursor;
         private Tool activeTool;
 
-        public RelayCommand SelectToolCommand { get; set; } // Command that handles tool switching.
-
-        public RelayCommand ChangeToolSizeCommand { get; set; }
-
         public Tool LastActionTool { get; private set; }
 
         public bool ActiveToolIsTransient { get; set; }
@@ -29,11 +27,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         public Cursor ToolCursor
         {
             get => toolCursor;
-            set
-            {
-                toolCursor = value;
-                RaisePropertyChanged("ToolCursor");
-            }
+            set => SetProperty(ref toolCursor, value);
         }
 
         public Tool ActiveTool
@@ -63,10 +57,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
         public ToolsViewModel(ViewModelMain owner)
             : base(owner)
-        {
-            SelectToolCommand = new RelayCommand(SetTool, Owner.DocumentIsNotNull);
-            ChangeToolSizeCommand = new RelayCommand(ChangeToolSize);
-        }
+        { }
 
         public void SetupTools(IServiceProvider services)
         {
@@ -91,6 +82,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             SetActiveTool(typeof(T));
         }
 
+        [Command.Internal("PixiEditor.Tools.SelectTool", CanExecute = "PixiEditor.HasDocument")]
         public void SetActiveTool(Tool tool)
         {
             if (ActiveTool == tool) return;
@@ -186,9 +178,10 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
         }
 
-        private void ChangeToolSize(object parameter)
+        [Command.Basic("PixiEditor.Tools.IncreaseSize", 1, "Increase Tool Size", "Increase Tool Size")]
+        [Command.Basic("PixiEditor.Tools.DecreaseSize", -1, "Decrease Tool Size", "Increase Tool Size")]
+        public void ChangeToolSize(int increment)
         {
-            int increment = (int)parameter;
             int newSize = ToolSize + increment;
             if (newSize > 0)
             {

+ 2 - 3
PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs

@@ -1,12 +1,11 @@
-using PixiEditor.Helpers;
+using PixiEditor.Models.Commands.Attributes;
 using PixiEditor.Models.Undo;
-using System;
 using System.IO;
-using PixiEditor.Models.Commands.Attributes;
 using System.Windows.Input;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Undo", "Undo")]
     public class UndoViewModel : SubViewModel<ViewModelMain>
     {
         public event EventHandler UndoRedoCalled;

+ 3 - 2
PixiEditor/ViewModels/SubViewModels/Main/WindowViewModel.cs

@@ -5,6 +5,7 @@ using PixiEditor.Views.Dialogs;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
+    [Command.Group("PixiEditor.Window", "Windows")]
     public class WindowViewModel : SubViewModel<ViewModelMain>
     {
         public RelayCommand<string> ShowAvalonDockWindowCommand { get; set; }
@@ -20,14 +21,14 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             ShowAvalonDockWindowCommand = new(ShowAvalonDockWindow);
         }
 
-        [Command.Basic("PixiEditor.Settings.Open", "Open Settings", "Open Settings Window")]
+        [Command.Basic("PixiEditor.Window.OpenSettingsWindow", "Open Settings", "Open Settings Window")]
         public static void OpenSettingsWindow()
         {
             SettingsWindow settings = new SettingsWindow();
             settings.Show();
         }
 
-        [Command.Basic("PixiEditor.Window.OpenStartupWindow", "Open Settings", "Open Settings Window")]
+        [Command.Basic("PixiEditor.Window.OpenStartupWindow", "Open Startup Window", "Open Startup Window")]
         public void OpenHelloThereWindow()
         {
             new HelloTherePopup(Owner.FileSubViewModel).Show();

+ 0 - 12
PixiEditor/ViewModels/ViewModelMain.cs

@@ -213,18 +213,6 @@ namespace PixiEditor.ViewModels
             if (!OverrideActionDisplay) RaisePropertyChanged(nameof(ActionDisplay));
         }
 
-        private Shortcut CreateToolShortcut<T>(Key key, ModifierKeys modifier = ModifierKeys.None)
-            where T : Tool
-        {
-            return new Shortcut(key, ToolsSubViewModel.SelectToolCommand, typeof(T), modifier);
-        }
-
-        private Shortcut CreateToolShortcut<T>(Key key, string description, ModifierKeys modifier = ModifierKeys.None)
-            where T : Tool
-        {
-            return new Shortcut(key, ToolsSubViewModel.SelectToolCommand, description, typeof(T), modifier);
-        }
-
         /// <summary>
         /// Removes documents with unsaved changes confirmation dialog.
         /// </summary>

+ 31 - 7
PixiEditor/Views/Dialogs/SettingsWindow.xaml

@@ -12,13 +12,15 @@
         xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
         xmlns:usercontrols="clr-namespace:PixiEditor.Views.UserControls"
         xmlns:dial="clr-namespace:PixiEditor.Views.Dialogs"
+        xmlns:cmds="clr-namespace:PixiEditor.Models.Commands"
         mc:Ignorable="d"
         Title="Settings" Name="window" 
         Height="500" Width="640"
         MinHeight="500" MinWidth="640"
         WindowStyle="None" DataContext="{DynamicResource SettingsWindowViewModel}"
         WindowStartupLocation="CenterScreen"
-        BorderBrush="Black" BorderThickness="1">
+        BorderBrush="Black" BorderThickness="1"
+        Background="{StaticResource AccentColor}">
     <Window.Resources>
         <viewmodels:SettingsWindowViewModel x:Key="SettingsWindowViewModel"/>
         <BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
@@ -165,14 +167,36 @@
 
             <Grid Visibility="{Binding SelectedItem, ElementName=pages, Converter={converters:EqualityBoolToVisibilityConverter}, ConverterParameter='Keybinds'}"
                         Margin="10">
-                <ScrollViewer>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="Auto"/>
+                    <RowDefinition/>
+                </Grid.RowDefinitions>
+                <Grid>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition/>
+                        <ColumnDefinition Width="100"/>
+                    </Grid.ColumnDefinitions>
+                    <TextBox Style="{StaticResource DarkTextBoxStyle}"/>
+                    <Button Grid.Column="1" Style="{StaticResource DarkRoundButton}"
+                            Content="Reset all" Margin="5,0"/>
+                </Grid>
+                <ScrollViewer Grid.Row="1">
                     <ItemsControl ItemsSource="{Binding Commands}" Foreground="White">
                         <ItemsControl.ItemTemplate>
-                            <DataTemplate>
-                                <Grid Margin="0,5,5,0">
-                                    <TextBlock Text="{Binding Display}" ToolTip="{Binding Description}"/>
-                                    <usercontrols:ShortcutBox Width="120" Command="{Binding}" HorizontalAlignment="Right"/>
-                                </Grid>
+                            <DataTemplate DataType="cmds:CommandGroup">
+                                <StackPanel Margin="0,0,0,20">
+                                    <TextBlock Text="{Binding Display}" FontSize="22" FontWeight="SemiBold"/>
+                                    <ItemsControl ItemsSource="{Binding VisibleCommands}">
+                                        <ItemsControl.ItemTemplate>
+                                            <DataTemplate DataType="cmds:Command">
+                                                <Grid Margin="0,5,5,0">
+                                                    <TextBlock Text="{Binding Display}" ToolTip="{Binding Description}"/>
+                                                    <usercontrols:ShortcutBox Width="120" Command="{Binding}" HorizontalAlignment="Right"/>
+                                                </Grid>
+                                            </DataTemplate>
+                                        </ItemsControl.ItemTemplate>
+                                    </ItemsControl>
+                                </StackPanel>
                             </DataTemplate>
                         </ItemsControl.ItemTemplate>
                     </ItemsControl>

+ 6 - 7
PixiEditor/Views/MainWindow.xaml

@@ -117,7 +117,7 @@
                         <Separator />
                         <MenuItem Header="_Delete Selected" cmds:Menu.Command="PixiEditor.Document.DeletePixels" />
                         <Separator />
-                        <MenuItem Header="_Settings" cmds:Menu.Command="PixiEditor.Settings.Open" />
+                        <MenuItem Header="_Settings" cmds:Menu.Command="PixiEditor.Window.OpenSettingsWindow" />
                     </MenuItem>
                     <MenuItem Header="_Select">
                         <MenuItem Header="_Select All" cmds:Menu.Command="PixiEditor.Selection.SelectAll" />
@@ -387,12 +387,11 @@
                     <ItemsControl ItemsSource="{Binding ToolsSubViewModel.ToolSet}">
                         <ItemsControl.ItemTemplate>
                             <DataTemplate>
-                                <Button BorderBrush="White"                                
-                                BorderThickness="{Binding IsActive, Converter={StaticResource BoolToIntConverter}}"
-                                Style="{StaticResource ToolButtonStyle}"
-                                Command="{Binding Path=DataContext.ToolsSubViewModel.SelectToolCommand,
-                                                  RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
-                                CommandParameter="{Binding}" ToolTip="{Binding Tooltip}">
+                                <Button BorderBrush="White"
+                                        BorderThickness="{Binding IsActive, Converter={StaticResource BoolToIntConverter}}"
+                                        Style="{StaticResource ToolButtonStyle}"
+                                        Command="{cmds:Command PixiEditor.Tools.SelectTool, UseProvided=True}"
+                                        CommandParameter="{Binding}" ToolTip="{Binding Tooltip}">
                                     <Button.Background>
                                         <ImageBrush ImageSource="{Binding ImagePath}" Stretch="Uniform" />
                                     </Button.Background>