Browse Source

Changing png icons to font icons wip

flabbet 1 year ago
parent
commit
28c0b3ddc8
45 changed files with 193 additions and 170 deletions
  1. 46 31
      src/PixiEditor.AvaloniaUI/Helpers/IconEvaluators.cs
  2. 2 2
      src/PixiEditor.AvaloniaUI/Models/Commands/Attributes/Commands/CommandAttribute.cs
  3. 2 2
      src/PixiEditor.AvaloniaUI/Models/Commands/CommandController.cs
  4. 1 1
      src/PixiEditor.AvaloniaUI/Models/Commands/Commands/Command.cs
  5. 9 49
      src/PixiEditor.AvaloniaUI/Models/Commands/Evaluators/IconEvaluator.cs
  6. 5 2
      src/PixiEditor.AvaloniaUI/Models/Commands/XAML/Menu.cs
  7. 1 1
      src/PixiEditor.AvaloniaUI/Models/Handlers/IToolHandler.cs
  8. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/Dock/ColorPickerDockViewModel.cs
  9. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/Dock/ColorSlidersDockViewModel.cs
  10. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/Dock/LayersDockViewModel.cs
  11. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/Dock/NavigationDockViewModel.cs
  12. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/Dock/PaletteViewerDockViewModel.cs
  13. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/Dock/SwatchesDockViewModel.cs
  14. 4 4
      src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentManagerViewModel.cs
  15. 8 5
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/ClipboardViewModel.cs
  16. 2 2
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/ColorsViewModel.cs
  17. 5 5
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/DebugViewModel.cs
  18. 3 2
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/FileViewModel.cs
  19. 11 11
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/LayersViewModel.cs
  20. 5 5
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/MiscViewModel.cs
  21. 4 4
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/SelectionViewModel.cs
  22. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/StylusViewModel.cs
  23. 2 2
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/UndoViewModel.cs
  24. 4 4
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/WindowViewModel.cs
  25. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/ToolViewModel.cs
  26. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/BrightnessToolViewModel.cs
  27. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/ColorPickerToolViewModel.cs
  28. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/EllipseToolViewModel.cs
  29. 3 0
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/EraserToolViewModel.cs
  30. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/FloodFillToolViewModel.cs
  31. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/LassoToolViewModel.cs
  32. 3 0
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/LineToolViewModel.cs
  33. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MagicWandToolViewModel.cs
  34. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MoveToolViewModel.cs
  35. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MoveViewportToolViewModel.cs
  36. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/PenToolViewModel.cs
  37. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/RectangleToolViewModel.cs
  38. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/RotateViewportToolViewModel.cs
  39. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/SelectToolViewModel.cs
  40. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/ZoomToolViewModel.cs
  41. 5 4
      src/PixiEditor.AvaloniaUI/Views/Dialogs/Debugging/CommandDebugPopup.axaml.cs
  42. 1 1
      src/PixiEditor.AvaloniaUI/Views/Main/Tools/ToolPickerButton.axaml
  43. 27 6
      src/PixiEditor.UI.Common/Fonts/PixiPerfectIcons.axaml.cs
  44. 4 4
      src/PixiEditor/ViewModels/SubViewModels/Main/SelectionViewModel.cs
  45. 2 2
      src/PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs

+ 46 - 31
src/PixiEditor.AvaloniaUI/Helpers/IconEvaluators.cs

@@ -1,7 +1,10 @@
 using System.Globalization;
+using System.Reflection;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
 using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Evaluators;
 using PixiEditor.AvaloniaUI.Models.Commands.Commands;
 using PixiEditor.AvaloniaUI.Models.Commands.Search;
@@ -10,44 +13,56 @@ namespace PixiEditor.AvaloniaUI.Helpers;
 
 internal static class IconEvaluators
 {
-    /*TODO: Segoe is Windows only*/
-    private static readonly FontFamily segeoMdl2 = new FontFamily("Segoe MDL2 Assets");
+    public static Dictionary<string, Bitmap> images = new();
 
-    [Evaluator.Icon("PixiEditor.FontIcon")]
-    public static IImage GetFontIcon(object parameter)
+    [Evaluator.Icon("PixiEditor.BitmapIcon")]
+    public static IImage GetBitmapIcon(object parameter)
     {
-        string symbolCode = GetIconName(parameter);
+        string path = GetDefaultPath(parameter as Command);
 
-        var textBlock = new TextBlock
-        {
-            FontFamily = segeoMdl2,
-            Foreground = Brushes.White,
-            Text = char.ConvertFromUtf32(int.Parse(symbolCode, NumberStyles.HexNumber)),
-        };
+        var image = images.GetValueOrDefault(path);
+        if (image is not null)
+            return image;
+            
+        Uri uri = new($"avares://{Assembly.GetExecutingAssembly().GetName().Name}/{path}");
+        if (!AssetLoader.Exists(uri))
+            return null;
+            
+        image = new Bitmap(AssetLoader.Open(uri));
+        images.Add(path, image);
 
-        var brush = new VisualBrush
-        {
-            Visual = textBlock,
-            Stretch = Stretch.Uniform
-        };
+        return image;
+    }
+    
+    public static string GetDefaultPath(Command command)
+    {
+        string path;
 
-        var drawing = new GeometryDrawing
+        if (command.Icon != null)
         {
-            Brush = brush,
-            Geometry = new RectangleGeometry(
-                new Rect(0, 0, 32, 32))
-        };
-
-        return new DrawingImage(drawing);
-    }
+            if (command.Icon.StartsWith('@'))
+            {
+                path = command.Icon[1..];
+            }
+            else if (command.Icon.StartsWith('$'))
+            {
+                path = $"Images/Commands/{command.Icon[1..].Replace('.', '/')}.png";
+            }
+            else
+            {
+                path = $"Images/{command.Icon}";
+            }
+        }
+        else
+        {
+            path = $"Images/Commands/{command.InternalName.Replace('.', '/')}.png";
+        }
 
-    private static string GetIconName(object parameter)
-    {
-        return parameter switch
+        if (path.StartsWith("/"))
         {
-            Command command => command.IconPath,
-            CommandSearchResult cmdResult => cmdResult.Command.IconPath,
-            _ => throw new NotImplementedException($"Parameter typeof {parameter.GetType()} has not been implemented yet.")
-        };
+            path = path[1..];
+        }
+
+        return path;
     }
 }

+ 2 - 2
src/PixiEditor.AvaloniaUI/Models/Commands/Attributes/Commands/CommandAttribute.cs

@@ -33,9 +33,9 @@ internal partial class Command
         public string IconEvaluator { get; set; }
 
         /// <summary>
-        /// Gets or sets path to the icon. Must be bitmap image
+        /// Gets or sets the icon.
         /// </summary>
-        public string IconPath { get; set; }
+        public string Icon { get; set; }
 
         /// <summary>
         ///     Gets or sets the path to the menu item. If null, command will not be added to menu.

+ 2 - 2
src/PixiEditor.AvaloniaUI/Models/Commands/CommandController.cs

@@ -200,7 +200,7 @@ internal class CommandController
                 InternalName = internalName,
                 DisplayName = displayName,
                 Description = displayName,
-                IconPath = $"@{toolInstance.IconKey}",
+                Icon = toolInstance.Icon,
                 IconEvaluator = IconEvaluator.Default,
                 TransientKey = toolAttr.Transient,
                 DefaultShortcut = toolAttr.GetShortcut(),
@@ -250,7 +250,7 @@ internal class CommandController
                                 IsDebug = isDebug,
                                 DisplayName = attribute.DisplayName,
                                 Description = attribute.Description,
-                                IconPath = attribute.IconPath,
+                                Icon = attribute.Icon,
                                 IconEvaluator = xIcon,
                                 DefaultShortcut = attribute.GetShortcut(),
                                 Shortcut = GetShortcut(name, attribute.GetShortcut(), template),

+ 1 - 1
src/PixiEditor.AvaloniaUI/Models/Commands/Commands/Command.cs

@@ -16,7 +16,7 @@ internal abstract partial class Command : PixiObservableObject
 
     public string InternalName { get; init; }
 
-    public string IconPath { get; init; }
+    public string Icon { get; init; }
 
     public IconEvaluator IconEvaluator { get; init; }
 

+ 9 - 49
src/PixiEditor.AvaloniaUI/Models/Commands/Evaluators/IconEvaluator.cs

@@ -1,73 +1,33 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Reflection;
+using Avalonia;
+using Avalonia.Controls;
 using Avalonia.Media;
 using Avalonia.Media.Imaging;
 using Avalonia.Platform;
 using PixiEditor.AvaloniaUI.Models.Commands.Commands;
+using PixiEditor.AvaloniaUI.Models.Commands.Search;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.Models.Commands.Evaluators;
 
 internal class IconEvaluator : Evaluator<IImage>
 {
-    public static IconEvaluator Default { get; } = new CommandNameEvaluator();
+    public static IconEvaluator Default { get; } = new FontIconEvaluator();
 
     public override IImage? CallEvaluate(Command command, object parameter) =>
-        base.CallEvaluate(command, parameter ?? command);
-
-    public static string GetDefaultPath(Command command)
-    {
-        string path;
-
-        if (command.IconPath != null)
-        {
-            if (command.IconPath.StartsWith('@'))
-            {
-                path = command.IconPath[1..];
-            }
-            else if (command.IconPath.StartsWith('$'))
-            {
-                path = $"Images/Commands/{command.IconPath[1..].Replace('.', '/')}.png";
-            }
-            else
-            {
-                path = $"Images/{command.IconPath}";
-            }
-        }
-        else
-        {
-            path = $"Images/Commands/{command.InternalName.Replace('.', '/')}.png";
-        }
-
-        if (path.StartsWith("/"))
-        {
-            path = path[1..];
-        }
-
-        return path;
-    }
+        base.CallEvaluate(command, parameter is CommandSearchResult or Command ? parameter : command);
 
     [DebuggerDisplay("IconEvaluator.Default")]
-    private class CommandNameEvaluator : IconEvaluator
+    private class FontIconEvaluator : IconEvaluator
     {
-        public static Dictionary<string, Bitmap> images = new();
 
         public override IImage? CallEvaluate(Command command, object parameter)
         {
-            string path = GetDefaultPath(command);
-
-            var image = images.GetValueOrDefault(path);
-            if (image is not null)
-                return image;
-            
-            Uri uri = new($"avares://{Assembly.GetExecutingAssembly().GetName().Name}/{path}");
-            if (!AssetLoader.Exists(uri))
-                return null;
-            
-            image = new Bitmap(AssetLoader.Open(uri));
-            images.Add(path, image);
+            string symbolCode = command.Icon;
 
-            return image;
+            return PixiPerfectIcons.ToIcon(symbolCode, 64);
         }
     }
 }

+ 5 - 2
src/PixiEditor.AvaloniaUI/Models/Commands/XAML/Menu.cs

@@ -1,5 +1,6 @@
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Layout;
 using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Models.Input;
 
@@ -7,7 +8,7 @@ namespace PixiEditor.AvaloniaUI.Models.Commands.XAML;
 
 internal class Menu : global::Avalonia.Controls.Menu
 {
-    public const double IconDimensions = 21;
+    public const double IconDimensions = 16;
     public const double IconFontSize = 16;
     
     public static readonly AttachedProperty<string> CommandProperty =
@@ -41,7 +42,9 @@ internal class Menu : global::Avalonia.Controls.Menu
         {
             Source = command.GetIcon(),
             Width = IconDimensions, Height = IconDimensions,
-            Opacity = canExecute ? 1 : 0.75
+            Opacity = canExecute ? 1 : 0.75,
+            VerticalAlignment = VerticalAlignment.Center,
+            HorizontalAlignment = HorizontalAlignment.Center
         };
 
         icon.PropertyChanged += async (sender, args) =>

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

@@ -12,7 +12,7 @@ internal interface IToolHandler : IHandler
     public LocalizedString DisplayName => new LocalizedString(ToolNameLocalizationKey);
     public string ToolName => GetType().Name.Replace("Tool", string.Empty).Replace("ViewModel", string.Empty);
     public string ToolNameLocalizationKey { get; }
-    public string IconKey => $"icon-{ToolName.ToLower()}";
+    public string Icon => $"icon-{ToolName.ToLower()}";
     //public virtual BrushShape BrushShape => BrushShape.Square;
 
     public bool HideHighlight { get; }

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Dock/ColorPickerDockViewModel.cs

@@ -28,6 +28,6 @@ internal class ColorPickerDockViewModel : DockableViewModel
     public ColorPickerDockViewModel(ColorsViewModel colorsSubVm)
     {
         ColorsSubViewModel = colorsSubVm;
-        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIconControl(PixiPerfectIcons.ColorPicker);
+        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIcon(PixiPerfectIcons.ColorPicker);
     }
 }

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Dock/ColorSlidersDockViewModel.cs

@@ -26,6 +26,6 @@ internal class ColorSlidersDockViewModel : DockableViewModel
     public ColorSlidersDockViewModel(ColorsViewModel colorsSubVm)
     {
         ColorsSubViewModel = colorsSubVm;
-        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIconControl(PixiPerfectIcons.ColorSliders);
+        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIcon(PixiPerfectIcons.ColorSliders);
     }
 }

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Dock/LayersDockViewModel.cs

@@ -34,7 +34,7 @@ internal class LayersDockViewModel : DockableViewModel
     {
         DocumentManager = documentManager;
         DocumentManager.ActiveDocumentChanged += DocumentManager_ActiveDocumentChanged;
-        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIconControl(PixiPerfectIcons.Layers);
+        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIcon(PixiPerfectIcons.Layers);
     }
 
     private void DocumentManager_ActiveDocumentChanged(object? sender, DocumentChangedEventArgs e)

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Dock/NavigationDockViewModel.cs

@@ -36,6 +36,6 @@ internal class NavigationDockViewModel : DockableViewModel
     {
         ColorsSubViewModel = colorsSubViewModel;
         DocumentManagerSubViewModel = documentManagerViewModel;
-        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIconControl(PixiPerfectIcons.Compass);
+        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIcon(PixiPerfectIcons.Compass);
     }
 }

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Dock/PaletteViewerDockViewModel.cs

@@ -33,7 +33,7 @@ internal class PaletteViewerDockViewModel : DockableViewModel
         ColorsSubViewModel = colorsSubViewModel;
         DocumentManagerSubViewModel = documentManagerViewModel;
 
-        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIconControl(PixiPerfectIcons.ColorPalette);
+        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIcon(PixiPerfectIcons.ColorPalette);
 
     }
 }

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Dock/SwatchesDockViewModel.cs

@@ -24,6 +24,6 @@ internal class SwatchesDockViewModel : DockableViewModel
     public SwatchesDockViewModel(DocumentManagerViewModel documentManagerViewModel)
     {
         DocumentManagerSubViewModel = documentManagerViewModel;
-        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIconControl(PixiPerfectIcons.Grid);
+        TabCustomizationSettings.Icon = PixiPerfectIcons.ToIcon(PixiPerfectIcons.Grid);
     }
 }

+ 4 - 4
src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentManagerViewModel.cs

@@ -61,7 +61,7 @@ internal class DocumentManagerViewModel : SubViewModel<ViewModelMain>, IDocument
     [Evaluator.CanExecute("PixiEditor.HasDocument", nameof(ActiveDocument))]
     public bool DocumentNotNull() => ActiveDocument != null;
 
-    [Command.Basic("PixiEditor.Document.ClipCanvas", "CLIP_CANVAS", "CLIP_CANVAS", CanExecute = "PixiEditor.HasDocument", IconPath = "Crop.png",
+    [Command.Basic("PixiEditor.Document.ClipCanvas", "CLIP_CANVAS", "CLIP_CANVAS", CanExecute = "PixiEditor.HasDocument", Icon = "Crop.png",
         MenuItemPath = "IMAGE/CLIP_CANVAS", MenuItemOrder = 2)]
     public void ClipCanvas() => ActiveDocument?.Operations.ClipCanvas();
 
@@ -111,7 +111,7 @@ internal class DocumentManagerViewModel : SubViewModel<ViewModelMain>, IDocument
         ActiveDocument?.Operations.RotateImage(angle, ActiveDocument.GetSelectedMembers());
     }
 
-    [Command.Basic("PixiEditor.Document.ToggleVerticalSymmetryAxis", "TOGGLE_VERT_SYMMETRY_AXIS", "TOGGLE_VERT_SYMMETRY_AXIS", CanExecute = "PixiEditor.HasDocument", IconPath = "SymmetryVertical.png")]
+    [Command.Basic("PixiEditor.Document.ToggleVerticalSymmetryAxis", "TOGGLE_VERT_SYMMETRY_AXIS", "TOGGLE_VERT_SYMMETRY_AXIS", CanExecute = "PixiEditor.HasDocument", Icon = "SymmetryVertical.png")]
     public void ToggleVerticalSymmetryAxis()
     {
         if (ActiveDocument is null)
@@ -119,7 +119,7 @@ internal class DocumentManagerViewModel : SubViewModel<ViewModelMain>, IDocument
         ActiveDocument.VerticalSymmetryAxisEnabledBindable ^= true;
     }
 
-    [Command.Basic("PixiEditor.Document.ToggleHorizontalSymmetryAxis", "TOGGLE_HOR_SYMMETRY_AXIS", "TOGGLE_HOR_SYMMETRY_AXIS", CanExecute = "PixiEditor.HasDocument", IconPath = "SymmetryHorizontal.png")]
+    [Command.Basic("PixiEditor.Document.ToggleHorizontalSymmetryAxis", "TOGGLE_HOR_SYMMETRY_AXIS", "TOGGLE_HOR_SYMMETRY_AXIS", CanExecute = "PixiEditor.HasDocument", Icon = "SymmetryHorizontal.png")]
     public void ToggleHorizontalSymmetryAxis()
     {
         if (ActiveDocument is null)
@@ -152,7 +152,7 @@ internal class DocumentManagerViewModel : SubViewModel<ViewModelMain>, IDocument
         ActiveDocument.EventInlet.OnSymmetryDragEnded(dir);
     }
 
-    [Command.Basic("PixiEditor.Document.DeletePixels", "DELETE_PIXELS", "DELETE_PIXELS_DESCRIPTIVE", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.Delete, IconPath = "Tools/EraserImage.png",
+    [Command.Basic("PixiEditor.Document.DeletePixels", "DELETE_PIXELS", "DELETE_PIXELS_DESCRIPTIVE", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.Delete, Icon = "Tools/EraserImage.png",
         MenuItemPath = "EDIT/DELETE_SELECTED_PIXELS", MenuItemOrder = 6)]
     public void DeletePixels()
     {

+ 8 - 5
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/ClipboardViewModel.cs

@@ -15,6 +15,7 @@ using PixiEditor.AvaloniaUI.Models.Controllers;
 using PixiEditor.AvaloniaUI.Models.IO;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.SubViewModels;
 #nullable enable
@@ -31,7 +32,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
     }
 
     [Command.Basic("PixiEditor.Clipboard.Cut", "CUT", "CUT_DESCRIPTIVE", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.X, Modifiers = KeyModifiers.Control,
-        MenuItemPath = "EDIT/CUT", MenuItemOrder = 2)]
+        MenuItemPath = "EDIT/CUT", MenuItemOrder = 2, Icon = PixiPerfectIcons.Cut)]
     public async Task Cut()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -42,8 +43,9 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
     }
 
     [Command.Basic("PixiEditor.Clipboard.Paste", false, "PASTE", "PASTE_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste", Key = Key.V, Modifiers = KeyModifiers.Shift,
-        MenuItemPath = "EDIT/PASTE", MenuItemOrder = 4)]
-    [Command.Basic("PixiEditor.Clipboard.PasteAsNewLayer", true, "PASTE_AS_NEW_LAYER", "PASTE_AS_NEW_LAYER_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste", Key = Key.V, Modifiers = KeyModifiers.Control)]
+        MenuItemPath = "EDIT/PASTE", MenuItemOrder = 4, Icon = PixiPerfectIcons.Paste)]
+    [Command.Basic("PixiEditor.Clipboard.PasteAsNewLayer", true, "PASTE_AS_NEW_LAYER", "PASTE_AS_NEW_LAYER_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste", Key = Key.V, Modifiers = KeyModifiers.Control,
+        Icon = PixiPerfectIcons.PasteAsNewLayer)]
     public void Paste(bool pasteAsNewLayer)
     {
         if (Owner.DocumentManagerSubViewModel.ActiveDocument is null) 
@@ -51,7 +53,8 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         ClipboardController.TryPasteFromClipboard(Owner.DocumentManagerSubViewModel.ActiveDocument, pasteAsNewLayer);
     }
     
-    [Command.Basic("PixiEditor.Clipboard.PasteReferenceLayer", "PASTE_REFERENCE_LAYER", "PASTE_REFERENCE_LAYER_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste")]
+    [Command.Basic("PixiEditor.Clipboard.PasteReferenceLayer", "PASTE_REFERENCE_LAYER", "PASTE_REFERENCE_LAYER_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste",
+        Icon = PixiPerfectIcons.PasteReferenceLayer)]
     public async Task PasteReferenceLayer(IDataObject data)
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -106,7 +109,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
     }
 
     [Command.Basic("PixiEditor.Clipboard.Copy", "COPY", "COPY_DESCRIPTIVE", CanExecute = "PixiEditor.Selection.IsNotEmpty", Key = Key.C, Modifiers = KeyModifiers.Control,
-        MenuItemPath = "EDIT/COPY", MenuItemOrder = 3)]
+        MenuItemPath = "EDIT/COPY", MenuItemOrder = 3, Icon = PixiPerfectIcons.Copy)]
     public async Task Copy()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;

+ 2 - 2
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/ColorsViewModel.cs

@@ -160,7 +160,7 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
         await ImportLospecPalette();
     }
 
-    [Command.Basic("PixiEditor.Colors.OpenPaletteBrowser", "OPEN_PALETTE_BROWSER", "OPEN_PALETTE_BROWSER", CanExecute = "PixiEditor.HasDocument", IconPath = "Globe.png")]
+    [Command.Basic("PixiEditor.Colors.OpenPaletteBrowser", "OPEN_PALETTE_BROWSER", "OPEN_PALETTE_BROWSER", CanExecute = "PixiEditor.HasDocument", Icon = "Globe.png")]
     public void OpenPalettesBrowser() 
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -350,7 +350,7 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
         PrimaryColor = color.ToColor();
     }
 
-    [Command.Basic("PixIEditor.Colors.AddPrimaryToPalettes", "ADD_PRIMARY_COLOR_TO_PALETTE", "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE", CanExecute = "PixiEditor.HasDocument", IconPath = "CopyAdd.png")]
+    [Command.Basic("PixIEditor.Colors.AddPrimaryToPalettes", "ADD_PRIMARY_COLOR_TO_PALETTE", "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE", CanExecute = "PixiEditor.HasDocument", Icon = "CopyAdd.png")]
     public void AddPrimaryColorToPalette()
     {
         var palette = Owner.DocumentManagerSubViewModel.ActiveDocument.Palette;

+ 5 - 5
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/DebugViewModel.cs

@@ -87,9 +87,9 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
     }
     
 
-    [Command.Debug("PixiEditor.Debug.IO.OpenLocalAppDataDirectory", @"PixiEditor", "OPEN_LOCAL_APPDATA_DIR", "OPEN_LOCAL_APPDATA_DIR", IconPath = "Folder.png",
+    [Command.Debug("PixiEditor.Debug.IO.OpenLocalAppDataDirectory", @"PixiEditor", "OPEN_LOCAL_APPDATA_DIR", "OPEN_LOCAL_APPDATA_DIR", Icon = "Folder.png",
         MenuItemPath = "DEBUG/OPEN_LOCAL_APPDATA_DIR", MenuItemOrder = 3)]
-    [Command.Debug("PixiEditor.Debug.IO.OpenCrashReportsDirectory", @"PixiEditor\crash_logs", "OPEN_CRASH_REPORTS_DIR", "OPEN_CRASH_REPORTS_DIR", IconPath = "Folder.png",
+    [Command.Debug("PixiEditor.Debug.IO.OpenCrashReportsDirectory", @"PixiEditor\crash_logs", "OPEN_CRASH_REPORTS_DIR", "OPEN_CRASH_REPORTS_DIR", Icon = "Folder.png",
         MenuItemPath = "DEBUG/OPEN_CRASH_REPORTS_DIR", MenuItemOrder = 4)]
     public static void OpenLocalAppDataFolder(string subDirectory)
     {
@@ -97,7 +97,7 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         OpenFolder(path);
     }
 
-    [Command.Debug("PixiEditor.Debug.IO.OpenRoamingAppDataDirectory", @"PixiEditor", "OPEN_ROAMING_APPDATA_DIR", "OPEN_ROAMING_APPDATA_DIR", IconPath = "Folder.png",
+    [Command.Debug("PixiEditor.Debug.IO.OpenRoamingAppDataDirectory", @"PixiEditor", "OPEN_ROAMING_APPDATA_DIR", "OPEN_ROAMING_APPDATA_DIR", Icon = "Folder.png",
         MenuItemPath = "DEBUG/OPEN_ROAMING_APPDATA_DIR", MenuItemOrder = 5)]
     public static void OpenAppDataFolder(string subDirectory)
     {
@@ -105,7 +105,7 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         OpenFolder(path);
     }
 
-    [Command.Debug("PixiEditor.Debug.IO.OpenTempDirectory", @"PixiEditor", "OPEN_TEMP_DIR", "OPEN_TEMP_DIR", IconPath = "Folder.png",
+    [Command.Debug("PixiEditor.Debug.IO.OpenTempDirectory", @"PixiEditor", "OPEN_TEMP_DIR", "OPEN_TEMP_DIR", Icon = "Folder.png",
         MenuItemPath = "DEBUG/OPEN_TEMP_DIR", MenuItemOrder = 6)]
     public static void OpenTempFolder(string subDirectory)
     {
@@ -273,7 +273,7 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         });
     }
 
-    [Command.Debug("PixiEditor.Debug.IO.OpenInstallDirectory", "OPEN_INSTALLATION_DIR", "OPEN_INSTALLATION_DIR", IconPath = "Folder.png",
+    [Command.Debug("PixiEditor.Debug.IO.OpenInstallDirectory", "OPEN_INSTALLATION_DIR", "OPEN_INSTALLATION_DIR", Icon = "Folder.png",
         MenuItemPath = "DEBUG/OPEN_INSTALLATION_DIR", MenuItemOrder = 8)]
     public static void OpenInstallLocation()
     {

+ 3 - 2
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/FileViewModel.cs

@@ -26,6 +26,7 @@ using PixiEditor.Extensions.Exceptions;
 using PixiEditor.Numerics;
 using PixiEditor.OperatingSystem;
 using PixiEditor.Parser;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.SubViewModels;
 
@@ -302,9 +303,9 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         Owner.WindowSubViewModel.MakeDocumentViewportActive(doc);
     }
 
-    [Command.Basic("PixiEditor.File.Save", false, "SAVE", "SAVE_IMAGE", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = KeyModifiers.Control, IconPath = "Save.png",
+    [Command.Basic("PixiEditor.File.Save", false, "SAVE", "SAVE_IMAGE", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = KeyModifiers.Control, Icon = PixiPerfectIcons.Save,
         MenuItemPath = "FILE/SAVE_PIXI", MenuItemOrder = 3)]
-    [Command.Basic("PixiEditor.File.SaveAsNew", true, "SAVE_AS", "SAVE_IMAGE_AS", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = KeyModifiers.Control | KeyModifiers.Shift, IconPath = "Save.png",
+    [Command.Basic("PixiEditor.File.SaveAsNew", true, "SAVE_AS", "SAVE_IMAGE_AS", CanExecute = "PixiEditor.HasDocument", Key = Key.S, Modifiers = KeyModifiers.Control | KeyModifiers.Shift, Icon = PixiPerfectIcons.Save,
         MenuItemPath = "FILE/SAVE_AS_PIXI", MenuItemOrder = 4)]
     public async Task<bool> SaveActiveDocument(bool asNew)
     {

+ 11 - 11
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/LayersViewModel.cs

@@ -53,7 +53,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         return true;
     }
 
-    [Command.Basic("PixiEditor.Layer.DeleteSelected", "LAYER_DELETE_SELECTED", "LAYER_DELETE_SELECTED_DESCRIPTIVE", CanExecute = "PixiEditor.Layer.CanDeleteSelected", IconPath = "Trash.png")]
+    [Command.Basic("PixiEditor.Layer.DeleteSelected", "LAYER_DELETE_SELECTED", "LAYER_DELETE_SELECTED_DESCRIPTIVE", CanExecute = "PixiEditor.Layer.CanDeleteSelected", Icon = "Trash.png")]
     public void DeleteSelected()
     {
         var member = Owner.DocumentManagerSubViewModel.ActiveDocument?.SelectedStructureMember;
@@ -96,7 +96,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         return members;
     }
 
-    [Command.Basic("PixiEditor.Layer.DeleteAllSelected", "LAYER_DELETE_ALL_SELECTED", "LAYER_DELETE_ALL_SELECTED_DESCRIPTIVE", CanExecute = "PixiEditor.Layer.HasSelectedMembers", IconPath = "Trash.png")]
+    [Command.Basic("PixiEditor.Layer.DeleteAllSelected", "LAYER_DELETE_ALL_SELECTED", "LAYER_DELETE_ALL_SELECTED_DESCRIPTIVE", CanExecute = "PixiEditor.Layer.HasSelectedMembers", Icon = "Trash.png")]
     public void DeleteAllSelected()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -107,7 +107,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
             doc.Operations.DeleteStructureMembers(selected);
     }
 
-    [Command.Basic("PixiEditor.Layer.NewFolder", "NEW_FOLDER", "CREATE_NEW_FOLDER", CanExecute = "PixiEditor.Layer.CanCreateNewMember", IconPath = "Folder-add.png")]
+    [Command.Basic("PixiEditor.Layer.NewFolder", "NEW_FOLDER", "CREATE_NEW_FOLDER", CanExecute = "PixiEditor.Layer.CanCreateNewMember", Icon = "Folder-add.png")]
     public void NewFolder()
     {
         if (Owner.DocumentManagerSubViewModel.ActiveDocument is not { } doc)
@@ -115,7 +115,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         doc.Operations.CreateStructureMember(StructureMemberType.Folder);
     }
 
-    [Command.Basic("PixiEditor.Layer.NewLayer", "NEW_LAYER", "CREATE_NEW_LAYER", CanExecute = "PixiEditor.Layer.CanCreateNewMember", Key = Key.N, Modifiers = KeyModifiers.Control | KeyModifiers.Shift, IconPath = "Layer-add.png")]
+    [Command.Basic("PixiEditor.Layer.NewLayer", "NEW_LAYER", "CREATE_NEW_LAYER", CanExecute = "PixiEditor.Layer.CanCreateNewMember", Key = Key.N, Modifiers = KeyModifiers.Control | KeyModifiers.Shift, Icon = "Layer-add.png")]
     public void NewLayer()
     {
         if (Owner.DocumentManagerSubViewModel.ActiveDocument is not { } doc)
@@ -234,7 +234,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
     [Evaluator.CanExecute("PixiEditor.Layer.ActiveLayerHasNoMask")]
     public bool ActiveLayerHasNoMask() => !Owner.DocumentManagerSubViewModel.ActiveDocument?.SelectedStructureMember?.HasMaskBindable ?? false;
 
-    [Command.Basic("PixiEditor.Layer.CreateMask", "CREATE_MASK", "CREATE_MASK", CanExecute = "PixiEditor.Layer.ActiveLayerHasNoMask", IconPath = "Create-mask.png")]
+    [Command.Basic("PixiEditor.Layer.CreateMask", "CREATE_MASK", "CREATE_MASK", CanExecute = "PixiEditor.Layer.ActiveLayerHasNoMask", Icon = "Create-mask.png")]
     public void CreateMask()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -244,7 +244,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         doc!.Operations.CreateMask(member);
     }
 
-    [Command.Basic("PixiEditor.Layer.DeleteMask", "DELETE_MASK", "DELETE_MASK", CanExecute = "PixiEditor.Layer.ActiveLayerHasMask", IconPath = "Trash.png")]
+    [Command.Basic("PixiEditor.Layer.DeleteMask", "DELETE_MASK", "DELETE_MASK", CanExecute = "PixiEditor.Layer.ActiveLayerHasMask", Icon = "Trash.png")]
     public void DeleteMask()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -329,7 +329,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
     [Command.Basic("PixiEditor.Layer.MergeWithAbove", "MERGE_WITH_ABOVE", "MERGE_WITH_ABOVE_DESCRIPTIVE", CanExecute = "PixiEditor.Layer.HasMemberAbove")]
     public void MergeWithAbove() => MergeSelectedWith(true);
 
-    [Command.Basic("PixiEditor.Layer.MergeWithBelow", "MERGE_WITH_BELOW", "MERGE_WITH_BELOW_DESCRIPTIVE", CanExecute = "PixiEditor.Layer.HasMemberBelow", IconPath = "Merge-downwards.png")]
+    [Command.Basic("PixiEditor.Layer.MergeWithBelow", "MERGE_WITH_BELOW", "MERGE_WITH_BELOW_DESCRIPTIVE", CanExecute = "PixiEditor.Layer.HasMemberBelow", Icon = "Merge-downwards.png")]
     public void MergeWithBelow() => MergeSelectedWith(false);
 
     [Evaluator.CanExecute("PixiEditor.Layer.ReferenceLayerExists")]
@@ -338,7 +338,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
     public bool ReferenceLayerDoesntExist() => 
         Owner.DocumentManagerSubViewModel.ActiveDocument is not null && Owner.DocumentManagerSubViewModel.ActiveDocument.ReferenceLayerViewModel.ReferenceBitmap is null;
 
-    [Command.Basic("PixiEditor.Layer.ImportReferenceLayer", "ADD_REFERENCE_LAYER", "ADD_REFERENCE_LAYER", CanExecute = "PixiEditor.Layer.ReferenceLayerDoesntExist", IconPath = "Add-reference.png")]
+    [Command.Basic("PixiEditor.Layer.ImportReferenceLayer", "ADD_REFERENCE_LAYER", "ADD_REFERENCE_LAYER", CanExecute = "PixiEditor.Layer.ReferenceLayerDoesntExist", Icon = "Add-reference.png")]
     public async Task ImportReferenceLayer()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -389,7 +389,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         return null;
     }
 
-    [Command.Basic("PixiEditor.Layer.DeleteReferenceLayer", "DELETE_REFERENCE_LAYER", "DELETE_REFERENCE_LAYER", CanExecute = "PixiEditor.Layer.ReferenceLayerExists", IconPath = "Trash.png")]
+    [Command.Basic("PixiEditor.Layer.DeleteReferenceLayer", "DELETE_REFERENCE_LAYER", "DELETE_REFERENCE_LAYER", CanExecute = "PixiEditor.Layer.ReferenceLayerExists", Icon = "Trash.png")]
     public void DeleteReferenceLayer()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -399,7 +399,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         doc.Operations.DeleteReferenceLayer();
     }
 
-    [Command.Basic("PixiEditor.Layer.TransformReferenceLayer", "TRANSFORM_REFERENCE_LAYER", "TRANSFORM_REFERENCE_LAYER", CanExecute = "PixiEditor.Layer.ReferenceLayerExists", IconPath = "crop.png")]
+    [Command.Basic("PixiEditor.Layer.TransformReferenceLayer", "TRANSFORM_REFERENCE_LAYER", "TRANSFORM_REFERENCE_LAYER", CanExecute = "PixiEditor.Layer.ReferenceLayerExists", Icon = "crop.png")]
     public void TransformReferenceLayer()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -419,7 +419,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         doc.ReferenceLayerViewModel.IsTopMost = !doc.ReferenceLayerViewModel.IsTopMost;
     }
 
-    [Command.Basic("PixiEditor.Layer.ResetReferenceLayerPosition", "RESET_REFERENCE_LAYER_POS", "RESET_REFERENCE_LAYER_POS", CanExecute = "PixiEditor.Layer.ReferenceLayerExists", IconPath = "Layout.png")]
+    [Command.Basic("PixiEditor.Layer.ResetReferenceLayerPosition", "RESET_REFERENCE_LAYER_POS", "RESET_REFERENCE_LAYER_POS", CanExecute = "PixiEditor.Layer.ReferenceLayerExists", Icon = "Layout.png")]
     public void ResetReferenceLayerPosition()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;

+ 5 - 5
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/MiscViewModel.cs

@@ -14,15 +14,15 @@ internal class MiscViewModel : SubViewModel<ViewModelMain>
     }
 
     [Command.Internal("PixiEditor.Links.OpenHyperlink")]
-    [Command.Basic("PixiEditor.Links.OpenDocumentation", "https://pixieditor.net/docs/introduction", "DOCUMENTATION", "OPEN_DOCUMENTATION", IconPath = "Globe.png",
+    [Command.Basic("PixiEditor.Links.OpenDocumentation", "https://pixieditor.net/docs/introduction", "DOCUMENTATION", "OPEN_DOCUMENTATION", Icon = "Globe.png",
         MenuItemPath = "HELP/DOCUMENTATION", MenuItemOrder = 0)]
-    [Command.Basic("PixiEditor.Links.OpenWebsite", "https://pixieditor.net", "WEBSITE", "OPEN_WEBSITE", IconPath = "Globe.png",
+    [Command.Basic("PixiEditor.Links.OpenWebsite", "https://pixieditor.net", "WEBSITE", "OPEN_WEBSITE", Icon = "Globe.png",
         MenuItemPath = "HELP/WEBSITE", MenuItemOrder = 1)]
-    [Command.Basic("PixiEditor.Links.OpenRepository", "https://github.com/PixiEditor/PixiEditor", "REPOSITORY", "OPEN_REPOSITORY", IconPath = "Globe.png",
+    [Command.Basic("PixiEditor.Links.OpenRepository", "https://github.com/PixiEditor/PixiEditor", "REPOSITORY", "OPEN_REPOSITORY", Icon = "Globe.png",
         MenuItemPath = "HELP/REPOSITORY", MenuItemOrder = 2)]
-    [Command.Basic("PixiEditor.Links.OpenLicense", "LICENSE", "LICENSE", "OPEN_LICENSE", IconPath = "Globe.png",
+    [Command.Basic("PixiEditor.Links.OpenLicense", "LICENSE", "LICENSE", "OPEN_LICENSE", Icon = "Globe.png",
         MenuItemPath = "HELP/LICENSE", MenuItemOrder = 3)]
-    [Command.Basic("PixiEditor.Links.OpenOtherLicenses", "THIRD_PARTY_LICENSES.txt", "THIRD_PARTY_LICENSES", "OPEN_THIRD_PARTY_LICENSES", IconPath = "Globe.png",
+    [Command.Basic("PixiEditor.Links.OpenOtherLicenses", "THIRD_PARTY_LICENSES.txt", "THIRD_PARTY_LICENSES", "OPEN_THIRD_PARTY_LICENSES", Icon = "Globe.png",
         MenuItemPath = "HELP/THIRD_PARTY_LICENSES", MenuItemOrder = 4)]
     public static void OpenUri(string uri)
     {

+ 4 - 4
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/SelectionViewModel.cs

@@ -60,10 +60,10 @@ internal class SelectionViewModel : SubViewModel<ViewModelMain>
         Owner.DocumentManagerSubViewModel.ActiveDocument?.Operations.TransformSelectedArea(false);
     }
 
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectLeft", "NUDGE_SELECTED_LEFT", "NUDGE_SELECTED_LEFT", Key = Key.Left, Parameter = new int[] { -1, 0 }, IconPath = "E76B", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectRight", "NUDGE_SELECTED_RIGHT", "NUDGE_SELECTED_RIGHT", Key = Key.Right, Parameter = new int[] { 1, 0 }, IconPath = "E76C", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectUp", "NUDGE_SELECTED_UP", "NUDGE_SELECTED_UP", Key = Key.Up, Parameter = new int[] { 0, -1 }, IconPath = "E70E", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectDown", "NUDGE_SELECTED_DOWN", "NUDGE_SELECTED_DOWN", Key = Key.Down, Parameter = new int[] { 0, 1 }, IconPath = "E70D", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectLeft", "NUDGE_SELECTED_LEFT", "NUDGE_SELECTED_LEFT", Key = Key.Left, Parameter = new int[] { -1, 0 }, Icon = "E76B", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectRight", "NUDGE_SELECTED_RIGHT", "NUDGE_SELECTED_RIGHT", Key = Key.Right, Parameter = new int[] { 1, 0 }, Icon = "E76C", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectUp", "NUDGE_SELECTED_UP", "NUDGE_SELECTED_UP", Key = Key.Up, Parameter = new int[] { 0, -1 }, Icon = "E70E", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectDown", "NUDGE_SELECTED_DOWN", "NUDGE_SELECTED_DOWN", Key = Key.Down, Parameter = new int[] { 0, 1 }, Icon = "E70D", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
     public void NudgeSelectedObject(int[] dist)
     {
         VecI distance = new(dist[0], dist[1]);

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/StylusViewModel.cs

@@ -46,7 +46,7 @@ internal class StylusViewModel : SubViewModel<ViewModelMain>
         UpdateUseTouchGesture();
     }
 
-    [Command.Basic("PixiEditor.Stylus.TogglePenMode", "TOGGLE_PEN_MODE", "TOGGLE_PEN_MODE", IconPath = "penMode.png")]
+    [Command.Basic("PixiEditor.Stylus.TogglePenMode", "TOGGLE_PEN_MODE", "TOGGLE_PEN_MODE", Icon = "penMode.png")]
     public void TogglePenMode()
     {
         IsPenModeEnabled = !IsPenModeEnabled;

+ 2 - 2
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/UndoViewModel.cs

@@ -16,7 +16,7 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
     ///     Redo last action.
     /// </summary>
     [Command.Basic("PixiEditor.Undo.Redo", "REDO", "REDO_DESCRIPTIVE", CanExecute = "PixiEditor.Undo.CanRedo", Key = Key.Y, Modifiers = KeyModifiers.Control,
-        IconPath = "Redo.png", MenuItemPath = "EDIT/REDO", MenuItemOrder = 1)]
+        Icon = "Redo.png", MenuItemPath = "EDIT/REDO", MenuItemOrder = 1)]
     public void Redo()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -29,7 +29,7 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
     ///     Undo last action.
     /// </summary>
     [Command.Basic("PixiEditor.Undo.Undo", "UNDO", "UNDO_DESCRIPTIVE", CanExecute = "PixiEditor.Undo.CanUndo", Key = Key.Z, Modifiers = KeyModifiers.Control,
-        IconPath = "Undo.png", MenuItemPath = "EDIT/UNDO", MenuItemOrder = 0)]
+        Icon = "Undo.png", MenuItemPath = "EDIT/UNDO", MenuItemOrder = 0)]
     public void Undo()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;

+ 4 - 4
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/WindowViewModel.cs

@@ -49,7 +49,7 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
         this.commandController = commandController;
     }
 
-    [Command.Basic("PixiEditor.Window.CreateNewViewport", "NEW_WINDOW_FOR_IMG", "NEW_WINDOW_FOR_IMG", IconPath = "@Images/Plus-square.png", CanExecute = "PixiEditor.HasDocument",
+    [Command.Basic("PixiEditor.Window.CreateNewViewport", "NEW_WINDOW_FOR_IMG", "NEW_WINDOW_FOR_IMG", Icon = "@Images/Plus-square.png", CanExecute = "PixiEditor.HasDocument",
         MenuItemPath = "VIEW/NEW_WINDOW_FOR_IMG", MenuItemOrder = 0)]
     public void CreateNewViewport()
     {
@@ -66,7 +66,7 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
             viewport.CenterViewportTrigger.Execute(this, viewport.Document.SizeBindable);
     }
     
-    [Command.Basic("PixiEditor.Window.FlipHorizontally", "FLIP_VIEWPORT_HORIZONTALLY", "FLIP_VIEWPORT_HORIZONTALLY", CanExecute = "PixiEditor.HasDocument", IconPath = "FlipHorizontal.png")]
+    [Command.Basic("PixiEditor.Window.FlipHorizontally", "FLIP_VIEWPORT_HORIZONTALLY", "FLIP_VIEWPORT_HORIZONTALLY", CanExecute = "PixiEditor.HasDocument", Icon = "FlipHorizontal.png")]
     public void FlipViewportHorizontally()
     {
         if (ActiveWindow is ViewportWindowViewModel viewport)
@@ -75,7 +75,7 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
         }
     }
     
-    [Command.Basic("PixiEditor.Window.FlipVertically", "FLIP_VIEWPORT_VERTICALLY", "FLIP_VIEWPORT_VERTICALLY", CanExecute = "PixiEditor.HasDocument", IconPath = "FlipVertical.png")]
+    [Command.Basic("PixiEditor.Window.FlipVertically", "FLIP_VIEWPORT_VERTICALLY", "FLIP_VIEWPORT_VERTICALLY", CanExecute = "PixiEditor.HasDocument", Icon = "FlipVertical.png")]
     public void FlipViewportVertically()
     {
         if (ActiveWindow is ViewportWindowViewModel viewport)
@@ -175,7 +175,7 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
     }
 
     [Command.Basic("PixiEditor.Window.OpenPalettesBrowserWindow", "OPEN_PALETTE_BROWSER", "OPEN_PALETTE_BROWSER",
-        IconPath = "Database.png", MenuItemPath = "VIEW/OPEN_PALETTE_BROWSER", MenuItemOrder = 3)]
+        Icon = "Database.png", MenuItemPath = "VIEW/OPEN_PALETTE_BROWSER", MenuItemOrder = 3)]
     public void ShowPalettesBrowserWindow()
     {
         PalettesBrowser.Open();

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/ToolViewModel.cs

@@ -23,7 +23,7 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
     public abstract string ToolNameLocalizationKey { get; }
     public virtual LocalizedString DisplayName => new LocalizedString(ToolNameLocalizationKey);
 
-    public virtual string IconKey => $"icon-{ToolName.ToLower()}";
+    public virtual string Icon => $"\u25a1";
 
     public virtual BrushShape BrushShape => BrushShape.Square;
 

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/BrightnessToolViewModel.cs

@@ -7,6 +7,7 @@ using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -28,7 +29,7 @@ internal class BrightnessToolViewModel : ToolViewModel, IBrightnessToolHandler
 
     public override BrushShape BrushShape => BrushShape.Circle;
 
-    public override string IconKey => "icon-sun";
+    public override string Icon => PixiPerfectIcons.Sun;
 
     BrightnessMode IBrightnessToolHandler.BrightnessMode
     {

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/ColorPickerToolViewModel.cs

@@ -10,6 +10,7 @@ using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -27,7 +28,7 @@ internal class ColorPickerToolViewModel : ToolViewModel, IColorPickerHandler
     public override string ToolNameLocalizationKey => "COLOR_PICKER_TOOL";
     public override BrushShape BrushShape => BrushShape.Pixel;
 
-    public override string IconKey => "icon-picker";
+    public override string Icon => PixiPerfectIcons.Picker;
 
     public override LocalizedString Tooltip => new("COLOR_PICKER_TOOLTIP", Shortcut);
 

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/EllipseToolViewModel.cs

@@ -4,6 +4,7 @@ using PixiEditor.AvaloniaUI.Models.Handlers.Tools;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -21,7 +22,7 @@ internal class EllipseToolViewModel : ShapeTool, IEllipseToolHandler
     public override LocalizedString Tooltip => new LocalizedString("ELLIPSE_TOOL_TOOLTIP", Shortcut);
     public bool DrawCircle { get; private set; }
 
-    public override string IconKey => "icon-circle";
+    public override string Icon => PixiPerfectIcons.Circle;
 
     public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
     {

+ 3 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/EraserToolViewModel.cs

@@ -6,6 +6,7 @@ using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -26,6 +27,8 @@ internal class EraserToolViewModel : ToolViewModel, IEraserToolHandler
     public override string ToolNameLocalizationKey => "ERASER_TOOL";
     public override BrushShape BrushShape => BrushShape.Circle;
 
+    public override string Icon => PixiPerfectIcons.Eraser;
+
     public override LocalizedString Tooltip => new LocalizedString("ERASER_TOOL_TOOLTIP", Shortcut);
 
     public override void UseTool(VecD pos)

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/FloodFillToolViewModel.cs

@@ -5,6 +5,7 @@ using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -24,7 +25,7 @@ internal class FloodFillToolViewModel : ToolViewModel, IFloodFillToolHandler
 
     public bool ConsiderAllLayers { get; private set; }
 
-    public override string IconKey => "icon-bucket";
+    public override string Icon => PixiPerfectIcons.Bucket;
 
     public FloodFillToolViewModel()
     {

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/LassoToolViewModel.cs

@@ -7,6 +7,7 @@ using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -46,7 +47,7 @@ internal class LassoToolViewModel : ToolViewModel, ILassoToolHandler
     public override LocalizedString Tooltip => new LocalizedString("LASSO_TOOL_TOOLTIP", Shortcut);
 
     public override string ToolNameLocalizationKey => "LASSO_TOOL";
-    public string IconKey => "icon-lasso";
+    public string Icon => PixiPerfectIcons.Lasso;
     public override BrushShape BrushShape => BrushShape.Pixel;
 
     [Settings.Enum("MODE_LABEL")]

+ 3 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/LineToolViewModel.cs

@@ -6,6 +6,7 @@ using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -23,6 +24,8 @@ internal class LineToolViewModel : ShapeTool, ILineToolHandler
     public override string ToolNameLocalizationKey => "LINE_TOOL";
     public override LocalizedString Tooltip => new LocalizedString("LINE_TOOL_TOOLTIP", Shortcut);
 
+    public override string Icon => PixiPerfectIcons.Eraser;
+
     [Settings.Inherited]
     public int ToolSize => GetValue<int>();
 

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MagicWandToolViewModel.cs

@@ -8,6 +8,7 @@ using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -25,7 +26,7 @@ internal class MagicWandToolViewModel : ToolViewModel, IMagicWandToolHandler
     [Settings.Enum("SCOPE_LABEL")]
     public DocumentScope DocumentScope => GetValue<DocumentScope>();
 
-    public override string IconKey => "icon-magic-wand";
+    public override string Icon => PixiPerfectIcons.MagicWand;
 
     public MagicWandToolViewModel()
     {

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MoveToolViewModel.cs

@@ -10,6 +10,7 @@ using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surface.Vector;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -25,7 +26,7 @@ internal class MoveToolViewModel : ToolViewModel, IMoveToolHandler
 
     private bool removeSelection = false;
 
-    public override string IconKey => "icon-mouse-pointer";
+    public override string Icon => PixiPerfectIcons.MousePointer;
 
     public MoveToolViewModel()
     {

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/MoveViewportToolViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands;
 using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -13,7 +14,7 @@ internal class MoveViewportToolViewModel : ToolViewModel
     public override bool HideHighlight => true;
     public override LocalizedString Tooltip => new LocalizedString("MOVE_VIEWPORT_TOOLTIP", Shortcut);
 
-    public override string IconKey => "icon-move-view";
+    public override string Icon => PixiPerfectIcons.MoveView;
 
     public override bool StopsLinkedToolOnUse => false;
 

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/PenToolViewModel.cs

@@ -10,6 +10,7 @@ using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools
 {
@@ -37,7 +38,7 @@ namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools
         [Settings.Bool("PIXEL_PERFECT_SETTING", Notify = nameof(PixelPerfectChanged))]
         public bool PixelPerfectEnabled => GetValue<bool>();
 
-        public override string IconKey => "icon-edit";
+        public override string Icon => PixiPerfectIcons.Edit;
 
         public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
         {

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/RectangleToolViewModel.cs

@@ -4,6 +4,7 @@ using PixiEditor.AvaloniaUI.Models.Handlers.Tools;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -22,7 +23,7 @@ internal class RectangleToolViewModel : ShapeTool, IRectangleToolHandler
     public bool Filled { get; set; } = false;
     public bool DrawSquare { get; private set; } = false;
 
-    public override string IconKey => "icon-square";
+    public override string Icon => PixiPerfectIcons.Square;
 
     public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
     {

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/RotateViewportToolViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands;
 using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -14,7 +15,7 @@ internal class RotateViewportToolViewModel : ToolViewModel
     public override bool StopsLinkedToolOnUse => false;
     public override LocalizedString Tooltip => new LocalizedString("ROTATE_VIEWPORT_TOOLTIP", Shortcut);
 
-    public override string IconKey => "icon-rotate-view";
+    public override string Icon => PixiPerfectIcons.RotateView;
 
     public RotateViewportToolViewModel()
     {

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/SelectToolViewModel.cs

@@ -8,6 +8,7 @@ using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -17,7 +18,7 @@ internal class SelectToolViewModel : ToolViewModel, ISelectToolHandler
     private string defaultActionDisplay = "SELECT_TOOL_ACTION_DISPLAY_DEFAULT";
     public override string ToolNameLocalizationKey => "SELECT_TOOL_NAME";
 
-    public override string IconKey => "icon-rectangle-selection";
+    public override string Icon => PixiPerfectIcons.RectangleSelection;
 
     public SelectToolViewModel()
     {

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Tools/Tools/ZoomToolViewModel.cs

@@ -2,6 +2,7 @@
 using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands;
 using PixiEditor.AvaloniaUI.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Fonts;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 
@@ -22,7 +23,7 @@ internal class ZoomToolViewModel : ToolViewModel
 
     public override bool StopsLinkedToolOnUse => false;
 
-    public override string IconKey => "icon-zoom-in";
+    public override string Icon => PixiPerfectIcons.ZoomIn;
 
     public ZoomToolViewModel()
     {

+ 5 - 4
src/PixiEditor.AvaloniaUI/Views/Dialogs/Debugging/CommandDebugPopup.axaml.cs

@@ -4,6 +4,7 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.Documents;
 using Avalonia.Media;
+using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Models.Commands;
 using PixiEditor.AvaloniaUI.Models.Commands.Commands;
 using PixiEditor.AvaloniaUI.Models.Commands.Evaluators;
@@ -76,9 +77,9 @@ public partial class CommandDebugPopup : PixiEditorPopup
         {
             if (image == null && command.IconEvaluator == IconEvaluator.Default)
             {
-                var expected = IconEvaluator.GetDefaultPath(command);
+                var expected = IconEvaluators.GetDefaultPath(command);
 
-                if (string.IsNullOrWhiteSpace(command.IconPath))
+                if (string.IsNullOrWhiteSpace(command.Icon))
                 {
                     Info(
                         $"Default evaluator has not found a image (No icon path provided). Expected at '{expected}'\n");
@@ -100,9 +101,9 @@ public partial class CommandDebugPopup : PixiEditorPopup
             Info($"Uses custom icon evaluator ({command.IconEvaluator.Name})\n");
         }
 
-        if (!string.IsNullOrWhiteSpace(command.IconPath))
+        if (!string.IsNullOrWhiteSpace(command.Icon))
         {
-            Info($"Has custom icon path: '{command.IconPath}'\n");
+            Info($"Has custom icon path: '{command.Icon}'\n");
         }
 
         return inlines;

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

@@ -27,7 +27,7 @@
         </Button.Template>
         <Button.Content>
             <TextBlock Classes="pixi-icon" FontSize="30" HorizontalAlignment="Center" VerticalAlignment="Center"
-                       Text="{Binding Path=IconKey, Converter={converters:KeyToResourceConverter}}"/>
+                       Text="{Binding Path=Icon}"/>
         </Button.Content>
     </Button>
 </UserControl>

+ 27 - 6
src/PixiEditor.UI.Common/Fonts/PixiPerfectIcons.axaml.cs

@@ -7,6 +7,8 @@ namespace PixiEditor.UI.Common.Fonts;
 //TODO: Make autogenerated from PixiPerfectIcons.axaml
 public static class PixiPerfectIcons
 {
+    private static readonly FontFamily pixiPerfectFontFamily = new("avares://PixiEditor.UI.Common/Fonts/pixiperfect.ttf#pixiperfect");
+
     public const string AddReference = "\ue900";
     public const string AddToMask = "\ue901";
     public const string AlphaLock = "\ue902";
@@ -106,16 +108,35 @@ public static class PixiPerfectIcons
     public const string YSymmetry = "\uE960";
     public const string ZoomIn = "\uE961";
     public const string ZoomOut = "\uE962";
+    public const string Cut = "\u25a1"; // TODO: Create a cut icon
+    public const string PasteReferenceLayer = "\u25a1"; // TODO: Create a paste reference layer icon
+    public const string PasteAsNewLayer = "\u25a1"; // TODO: Create a paste as new layer icon
 
-    public static TextBlock ToIconControl(string unicode)
+    public static IImage ToIcon(string unicode, double size = 64)
     {
-        return new TextBlock()
+        if(string.IsNullOrEmpty(unicode)) return null;
+
+        var textBlock = new TextBlock
         {
+            FontFamily = pixiPerfectFontFamily,
+            Foreground = Brushes.White,
             Text = unicode,
-            FontFamily = Application.Current.Styles.TryGetResource("PixiPerfectIcons", null, out var font) ? font as FontFamily : null,
-            FontSize = 16,
-            VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
-            HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center
+            FontSize = size,
+        };
+
+        var brush = new VisualBrush
+        {
+            Visual = textBlock,
+            Stretch = Stretch.Uniform,
         };
+
+        var drawing = new GeometryDrawing
+        {
+            Brush = brush,
+            Geometry = new RectangleGeometry(
+                new Rect(0, 0, 64, 64)),
+        };
+
+        return new DrawingImage(drawing);
     }
 }

+ 4 - 4
src/PixiEditor/ViewModels/SubViewModels/Main/SelectionViewModel.cs

@@ -56,10 +56,10 @@ internal class SelectionViewModel : SubViewModel<ViewModelMain>
         Owner.DocumentManagerSubViewModel.ActiveDocument?.Operations.TransformSelectedArea(false);
     }
 
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectLeft", "NUDGE_SELECTED_LEFT", "NUDGE_SELECTED_LEFT", Key = Key.Left, Parameter = new int[] { -1, 0 }, IconPath = "E76B", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectRight", "NUDGE_SELECTED_RIGHT", "NUDGE_SELECTED_RIGHT", Key = Key.Right, Parameter = new int[] { 1, 0 }, IconPath = "E76C", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectUp", "NUDGE_SELECTED_UP", "NUDGE_SELECTED_UP", Key = Key.Up, Parameter = new int[] { 0, -1 }, IconPath = "E70E", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
-    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectDown", "NUDGE_SELECTED_DOWN", "NUDGE_SELECTED_DOWN", Key = Key.Down, Parameter = new int[] { 0, 1 }, IconPath = "E70D", IconEvaluator = "PixiEditor.FontIcon", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectLeft", "NUDGE_SELECTED_LEFT", "NUDGE_SELECTED_LEFT", Key = Key.Left, Parameter = new int[] { -1, 0 }, IconPath = "E76B", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectRight", "NUDGE_SELECTED_RIGHT", "NUDGE_SELECTED_RIGHT", Key = Key.Right, Parameter = new int[] { 1, 0 }, IconPath = "E76C", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectUp", "NUDGE_SELECTED_UP", "NUDGE_SELECTED_UP", Key = Key.Up, Parameter = new int[] { 0, -1 }, IconPath = "E70E", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
+    [Command.Basic("PixiEditor.Selection.NudgeSelectedObjectDown", "NUDGE_SELECTED_DOWN", "NUDGE_SELECTED_DOWN", Key = Key.Down, Parameter = new int[] { 0, 1 }, IconPath = "E70D", CanExecute = "PixiEditor.Selection.CanNudgeSelectedObject")]
     public void NudgeSelectedObject(int[] dist)
     {
         VecI distance = new(dist[0], dist[1]);

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

@@ -17,7 +17,7 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
     ///     Redo last action.
     /// </summary>
     [Command.Basic("PixiEditor.Undo.Redo", "REDO", "REDO_DESCRIPTIVE", CanExecute = "PixiEditor.Undo.CanRedo", Key = Key.Y, Modifiers = ModifierKeys.Control,
-        IconPath = "E7A6", IconEvaluator = "PixiEditor.FontIcon")]
+        IconPath = "E7A6")]
     public void Redo()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -30,7 +30,7 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
     ///     Undo last action.
     /// </summary>
     [Command.Basic("PixiEditor.Undo.Undo", "UNDO", "UNDO_DESCRIPTIVE", CanExecute = "PixiEditor.Undo.CanUndo", Key = Key.Z, Modifiers = ModifierKeys.Control,
-        IconPath = "E7A7", IconEvaluator = "PixiEditor.FontIcon")]
+        IconPath = "E7A7")]
     public void Undo()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;