Browse Source

Merge pull request #523 from PixiEditor/color-picker-improvement

Various Improvements
Krzysztof Krysiński 2 years ago
parent
commit
6b40282050
66 changed files with 776 additions and 208 deletions
  1. 17 3
      src/PixiEditor/App.xaml.cs
  2. 13 1
      src/PixiEditor/Data/Localization/Languages/en.json
  3. 7 6
      src/PixiEditor/Helpers/Collections/ActionDisplayList.cs
  4. 39 0
      src/PixiEditor/Helpers/ColorHelper.cs
  5. 44 1
      src/PixiEditor/Helpers/Extensions/PixiParserDocumentEx.cs
  6. 15 2
      src/PixiEditor/Helpers/ProcessHelper.cs
  7. BIN
      src/PixiEditor/Images/Commands/PixiEditor/Clipboard/PasteAsNewLayer.png
  8. BIN
      src/PixiEditor/Images/Commands/PixiEditor/Clipboard/PasteReferenceLayer.png
  9. BIN
      src/PixiEditor/Images/Commands/PixiEditor/File/OpenFileFromClipboard.png
  10. BIN
      src/PixiEditor/Images/Commands/PixiEditor/Layer/DuplicateSelectedLayer.png
  11. BIN
      src/PixiEditor/Images/Commands/PixiEditor/Selection/Clear.png
  12. BIN
      src/PixiEditor/Images/Commands/PixiEditor/Selection/InvertSelection.png
  13. BIN
      src/PixiEditor/Images/Commands/PixiEditor/Selection/SelectAll.png
  14. BIN
      src/PixiEditor/Images/Commands/PixiEditor/Window/OpenAboutWindow.png
  15. BIN
      src/PixiEditor/Images/Commands/PixiEditor/Window/OpenShortcutWindow.png
  16. 2 0
      src/PixiEditor/Localization/ILocalizationProvider.cs
  17. 2 2
      src/PixiEditor/Localization/LocalizationData.cs
  18. 16 14
      src/PixiEditor/Localization/LocalizationProvider.cs
  19. 14 2
      src/PixiEditor/Models/Commands/Search/ColorSearchResult.cs
  20. 3 1
      src/PixiEditor/Models/Commands/Search/FileSearchResult.cs
  21. 2 1
      src/PixiEditor/Models/Commands/Search/SearchResult.cs
  22. 18 0
      src/PixiEditor/Models/Commands/XAML/Menu.cs
  23. 36 3
      src/PixiEditor/Models/Controllers/ClipboardController.cs
  24. 4 0
      src/PixiEditor/Models/Dialogs/ImportFileDialog.cs
  25. 2 1
      src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs
  26. 3 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/ColorPickerToolExecutor.cs
  27. 13 3
      src/PixiEditor/Models/IO/Importer.cs
  28. 14 0
      src/PixiEditor/PixiEditor.csproj
  29. 2 2
      src/PixiEditor/Styles/MenuButtonStyle.xaml
  30. 1 2
      src/PixiEditor/Styles/Titlebar.xaml
  31. 15 4
      src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs
  32. 18 1
      src/PixiEditor/ViewModels/SubViewModels/Document/ReferenceLayerViewModel.cs
  33. 6 4
      src/PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs
  34. 77 17
      src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs
  35. 0 16
      src/PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs
  36. 7 2
      src/PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs
  37. 1 1
      src/PixiEditor/ViewModels/SubViewModels/Main/WindowViewModel.cs
  38. 17 2
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Settings/Setting.cs
  39. 2 0
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/SettingAttributes.cs
  40. 67 36
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/ToolbarFactory.cs
  41. 3 0
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolViewModel.cs
  42. 1 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/BrightnessToolViewModel.cs
  43. 21 5
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/ColorPickerToolViewModel.cs
  44. 1 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/EraserToolViewModel.cs
  45. 1 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/LassoToolViewModel.cs
  46. 1 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/LineToolViewModel.cs
  47. 1 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/MagicWandToolViewModel.cs
  48. 1 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/MoveToolViewModel.cs
  49. 63 2
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/PenToolViewModel.cs
  50. 1 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/SelectToolViewModel.cs
  51. 3 2
      src/PixiEditor/ViewModels/SubViewModels/UserPreferences/Settings/GeneralSettings.cs
  52. 11 8
      src/PixiEditor/Views/Dialogs/ConfirmationPopup.xaml
  53. 6 6
      src/PixiEditor/Views/Dialogs/ConfirmationPopup.xaml.cs
  54. 25 14
      src/PixiEditor/Views/Dialogs/HelloTherePopup.xaml
  55. 1 1
      src/PixiEditor/Views/Dialogs/ImportFilePopup.xaml.cs
  56. 36 7
      src/PixiEditor/Views/MainWindow.xaml
  57. 5 2
      src/PixiEditor/Views/MainWindow.xaml.cs
  58. 2 2
      src/PixiEditor/Views/UserControls/CommandSearch/CommandSearchControl.xaml
  59. 1 0
      src/PixiEditor/Views/UserControls/CommandSearch/CommandSearchControl.xaml.cs
  60. 8 8
      src/PixiEditor/Views/UserControls/DiscordRPPreview.xaml
  61. 1 1
      src/PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs
  62. 1 1
      src/PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml.cs
  63. 5 4
      src/PixiEditor/Views/UserControls/Overlays/SymmetryOverlay/SymmetryOverlay.cs
  64. 15 1
      src/PixiEditor/Views/UserControls/Palettes/PaletteViewer.xaml.cs
  65. 1 1
      src/PixiEditor/Views/UserControls/PreviewWindow.xaml.cs
  66. 84 6
      src/PixiEditor/Views/UserControls/Viewport.xaml

+ 17 - 3
src/PixiEditor/App.xaml.cs

@@ -1,6 +1,7 @@
 using System.IO;
 using System.IO;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using System.Windows;
 using System.Windows;
+using System.Windows.Media;
 using PixiEditor.Localization;
 using PixiEditor.Localization;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
@@ -40,11 +41,24 @@ internal partial class App : Application
             return;
             return;
         }
         }
 
 
-        if (HandleNewInstance())
+        if (!HandleNewInstance())
         {
         {
-            MainWindow = new MainWindow();
-            MainWindow.Show();
+            return;
         }
         }
+
+        AddNativeAssets();
+        
+        MainWindow = new MainWindow();
+        MainWindow.Show();
+    }
+
+    private void AddNativeAssets()
+    {
+        var iconFont = OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000)
+            ? "Segoe Fluent Icons"
+            : "Segoe MDL2 Assets";
+        
+        Resources.Add("NativeIconFont", new FontFamily(iconFont));
     }
     }
 
 
     private bool HandleNewInstance()
     private bool HandleNewInstance()

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

@@ -501,7 +501,19 @@
     "ALT_KEY": "Alt",
     "ALT_KEY": "Alt",
     "RENAME": "Rename",
     "RENAME": "Rename",
     "PIXEL_UNIT": "px",
     "PIXEL_UNIT": "px",
-    "OPEN_LOCALIZATION_DEBUG_WINDOW": "Open Localization Debug Window",
+    "IMPORT_AS_NEW_LAYER": "Import as new layer",
+  "PASTE_AS_PRIMARY_COLOR": "Paste as primary color",
+  "IMPORT_AS_NEW_FILE": "Import as new file",
+  "IMPORT_PALETTE_FILE": "Import palette file",
+  "IMPORT_MULTIPLE_PALETTE_COLORS": "Import colors into palette",
+  "IMPORT_SINGLE_PALETTE_COLOR": "Import color into palette",
+  "IMPORT_AS_REFERENCE_LAYER": "Import as reference layer",
+  "NAVIGATOR_PICK_ACTION_DISPLAY": "Right-click to pick color, Shift-right-click to copy color to clipboard",
+  
+  "OPEN_FILE_FROM_CLIPBOARD": "Open from clipboard",
+  "OPEN_FILE_FROM_CLIPBOARD_DESCRIPTIVE": "Open from clipboard",
+  
+  "OPEN_LOCALIZATION_DEBUG_WINDOW": "Open Localization Debug Window",
     "FORCE_OTHER_FLOW_DIRECTION": "Force other flow direction",
     "FORCE_OTHER_FLOW_DIRECTION": "Force other flow direction",
     "API_KEY": "API Key",
     "API_KEY": "API Key",
     "LOCALIZATION_VIEW_TYPE": "Localization View Type",
     "LOCALIZATION_VIEW_TYPE": "Localization View Type",

+ 7 - 6
src/PixiEditor/Helpers/Collections/ActionDisplayList.cs

@@ -1,10 +1,11 @@
 using System.Collections;
 using System.Collections;
+using PixiEditor.Localization;
 
 
 namespace PixiEditor.Helpers.Collections;
 namespace PixiEditor.Helpers.Collections;
 
 
-public class ActionDisplayList : IEnumerable<KeyValuePair<string, string>>
+public class ActionDisplayList : IEnumerable<KeyValuePair<string, LocalizedString>>
 {
 {
-    private Dictionary<string, string> _dictionary = new();
+    private Dictionary<string, LocalizedString> _dictionary = new();
     private Action notifyUpdate;
     private Action notifyUpdate;
 
 
     public ActionDisplayList(Action notifyUpdate)
     public ActionDisplayList(Action notifyUpdate)
@@ -12,7 +13,7 @@ public class ActionDisplayList : IEnumerable<KeyValuePair<string, string>>
         this.notifyUpdate = notifyUpdate;
         this.notifyUpdate = notifyUpdate;
     }
     }
 
 
-    public string this[string key]
+    public LocalizedString? this[string key]
     {
     {
         get => _dictionary[key];
         get => _dictionary[key];
         set
         set
@@ -24,16 +25,16 @@ public class ActionDisplayList : IEnumerable<KeyValuePair<string, string>>
                 return;
                 return;
             }
             }
             
             
-            _dictionary[key] = value;
+            _dictionary[key] = value.Value;
             notifyUpdate();
             notifyUpdate();
         }
         }
     }
     }
 
 
-    public string GetActive() => _dictionary.Last().Value;
+    public LocalizedString GetActive() => _dictionary.Last().Value;
 
 
     public bool HasActive() => _dictionary.Count != 0;
     public bool HasActive() => _dictionary.Count != 0;
 
 
-    public IEnumerator<KeyValuePair<string, string>> GetEnumerator() => _dictionary.GetEnumerator();
+    public IEnumerator<KeyValuePair<string, LocalizedString>> GetEnumerator() => _dictionary.GetEnumerator();
 
 
     IEnumerator IEnumerable.GetEnumerator() => _dictionary.GetEnumerator();
     IEnumerator IEnumerable.GetEnumerator() => _dictionary.GetEnumerator();
 }
 }

+ 39 - 0
src/PixiEditor/Helpers/ColorHelper.cs

@@ -9,6 +9,9 @@ public class ColorHelper
     public static bool ParseAnyFormat(IDataObject data, [NotNullWhen(true)] out DrawingApi.Core.ColorsImpl.Color? result) => 
     public static bool ParseAnyFormat(IDataObject data, [NotNullWhen(true)] out DrawingApi.Core.ColorsImpl.Color? result) => 
         ParseAnyFormat(((DataObject)data).GetText().Trim(), out result);
         ParseAnyFormat(((DataObject)data).GetText().Trim(), out result);
     
     
+    public static bool ParseAnyFormatList(IDataObject data, [NotNullWhen(true)] out List<DrawingApi.Core.ColorsImpl.Color> result) => 
+        ParseAnyFormatList(((DataObject)data).GetText().Trim(), out result);
+
     public static bool ParseAnyFormat(string value, [NotNullWhen(true)] out DrawingApi.Core.ColorsImpl.Color? result)
     public static bool ParseAnyFormat(string value, [NotNullWhen(true)] out DrawingApi.Core.ColorsImpl.Color? result)
     {
     {
         bool hex = Regex.IsMatch(value, "^#?([a-fA-F0-9]{8}|[a-fA-F0-9]{6}|[a-fA-F0-9]{3})$");
         bool hex = Regex.IsMatch(value, "^#?([a-fA-F0-9]{8}|[a-fA-F0-9]{6}|[a-fA-F0-9]{3})$");
@@ -34,6 +37,42 @@ public class ColorHelper
 
 
         result = new DrawingApi.Core.ColorsImpl.Color(r, g, b, a);
         result = new DrawingApi.Core.ColorsImpl.Color(r, g, b, a);
         return true;
         return true;
+    }
+    
+    public static bool ParseAnyFormatList(string value, [NotNullWhen(true)] out List<DrawingApi.Core.ColorsImpl.Color> result)
+    {
+        result = new List<DrawingApi.Core.ColorsImpl.Color>();
+
+        // Regex patterns for hex and RGB(A) formats
+        const string hexPattern = @"#?([a-fA-F0-9]{8}|[a-fA-F0-9]{6}|[a-fA-F0-9]{3})";
+        const string rgbaPattern = @"(?:rgba?\(?)? *(?<r>\d{1,3})(?:, *| +)(?<g>\d{1,3})(?:, *| +)(?<b>\d{1,3})(?:(?:, *| +)(?<a>\d{0,3}))?\)?";
+
+        // Combined pattern for both hex and RGB(A) formats
+        const string combinedPattern = $@"({hexPattern})|({rgbaPattern})";
+        var matches = Regex.Matches(value, combinedPattern);
+
+        if (matches.Count == 0)
+        {
+            return false;
+        }
+
+        foreach (Match match in matches)
+        {
+            if (Regex.IsMatch(match.Value, $"^{hexPattern}$"))
+            {
+                result.Add(DrawingApi.Core.ColorsImpl.Color.Parse(match.Value));
+            }
+            else if (match.Groups["r"].Success && match.Groups["g"].Success && match.Groups["b"].Success)
+            {
+                byte r = byte.Parse(match.Groups["r"].ValueSpan);
+                byte g = byte.Parse(match.Groups["g"].ValueSpan);
+                byte b = byte.Parse(match.Groups["b"].ValueSpan);
+                byte a = match.Groups["a"].Success ? byte.Parse(match.Groups["a"].ValueSpan) : (byte)255;
+
+                result.Add(new DrawingApi.Core.ColorsImpl.Color(r, g, b, a));
+            }
+        }
 
 
+        return result.Count > 0;
     }
     }
 }
 }

+ 44 - 1
src/PixiEditor/Helpers/Extensions/PixiParserDocumentEx.cs

@@ -1,8 +1,8 @@
 using ChunkyImageLib;
 using ChunkyImageLib;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Models.IO;
 using PixiEditor.Parser;
 using PixiEditor.Parser;
+using PixiEditor.Parser.Deprecated;
 using PixiEditor.ViewModels.SubViewModels.Document;
 using PixiEditor.ViewModels.SubViewModels.Document;
 
 
 namespace PixiEditor.Helpers.Extensions;
 namespace PixiEditor.Helpers.Extensions;
@@ -77,4 +77,47 @@ internal static class PixiParserDocumentEx
             }
             }
         }
         }
     }
     }
+    
+    public static SKBitmap RenderOldDocument(this SerializableDocument document)
+    {
+        SKImageInfo info = new(document.Width, document.Height, SKColorType.RgbaF32, SKAlphaType.Unpremul, SKColorSpace.CreateSrgb());
+        using SKSurface surface = SKSurface.Create(info);
+        SKCanvas canvas = surface.Canvas;
+        using SKPaint paint = new();
+
+        foreach (var layer in document)
+        {
+            if (layer.PngBytes == null || layer.PngBytes.Length == 0)
+            {
+                continue;
+            }
+
+            bool visible = document.Layers.GetFinalLayerVisibilty(layer);
+
+            if (!visible)
+            {
+                continue;
+            }
+
+            double opacity = document.Layers.GetFinalLayerOpacity(layer);
+
+            if (opacity == 0)
+            {
+                continue;
+            }
+
+            using SKColorFilter filter = SKColorFilter.CreateBlendMode(SKColors.White.WithAlpha((byte)(opacity * 255)), SKBlendMode.DstIn);
+            paint.ColorFilter = filter;
+
+            using var image = SKImage.FromEncodedData(layer.PngBytes);
+            
+            canvas.DrawImage(image, layer.OffsetX, layer.OffsetY, paint);
+        }
+
+        SKBitmap bitmap = new(info);
+
+        surface.ReadPixels(info, bitmap.GetPixels(), info.RowBytes, 0, 0);
+
+        return bitmap;
+    }
 }
 }

+ 15 - 2
src/PixiEditor/Helpers/ProcessHelper.cs

@@ -2,6 +2,7 @@
 using System.Diagnostics;
 using System.Diagnostics;
 using System.IO;
 using System.IO;
 using System.Security.Principal;
 using System.Security.Principal;
+using System.Windows.Input;
 
 
 namespace PixiEditor.Helpers;
 namespace PixiEditor.Helpers;
 
 
@@ -25,7 +26,19 @@ internal static class ProcessHelper
 
 
     public static void OpenInExplorer(string path)
     public static void OpenInExplorer(string path)
     {
     {
-        string fixedPath = Path.GetFullPath(path);
-        Process.Start("explorer.exe", $"/select,\"{fixedPath}\"");
+        Mouse.OverrideCursor = Cursors.Wait;
+
+        try
+        {
+            string fixedPath = Path.GetFullPath(path);
+            var process = Process.Start("explorer.exe", $"/select,\"{fixedPath}\"");
+
+            // Explorer might need a second to show up
+            process.WaitForExit(500);
+        }
+        finally
+        {
+            Mouse.OverrideCursor = null;
+        }
     }
     }
 }
 }

BIN
src/PixiEditor/Images/Commands/PixiEditor/Clipboard/PasteAsNewLayer.png


BIN
src/PixiEditor/Images/Commands/PixiEditor/Clipboard/PasteReferenceLayer.png


BIN
src/PixiEditor/Images/Commands/PixiEditor/File/OpenFileFromClipboard.png


BIN
src/PixiEditor/Images/Commands/PixiEditor/Layer/DuplicateSelectedLayer.png


BIN
src/PixiEditor/Images/Commands/PixiEditor/Selection/Clear.png


BIN
src/PixiEditor/Images/Commands/PixiEditor/Selection/InvertSelection.png


BIN
src/PixiEditor/Images/Commands/PixiEditor/Selection/SelectAll.png


BIN
src/PixiEditor/Images/Commands/PixiEditor/Window/OpenAboutWindow.png


BIN
src/PixiEditor/Images/Commands/PixiEditor/Window/OpenShortcutWindow.png


+ 2 - 0
src/PixiEditor/Localization/ILocalizationProvider.cs

@@ -9,6 +9,8 @@ public interface ILocalizationProvider
     public string LocalizationDataPath { get; }
     public string LocalizationDataPath { get; }
     public LocalizationData LocalizationData { get; }
     public LocalizationData LocalizationData { get; }
     public Language CurrentLanguage { get; set; }
     public Language CurrentLanguage { get; set; }
+    public LanguageData SelectedLanguage { get; }
+    public LanguageData FollowSystem { get; }
     public event Action<Language> OnLanguageChanged;
     public event Action<Language> OnLanguageChanged;
 
 
     /// <summary>
     /// <summary>

+ 2 - 2
src/PixiEditor/Localization/LocalizationData.cs

@@ -2,8 +2,8 @@
 
 
 namespace PixiEditor.Localization;
 namespace PixiEditor.Localization;
 
 
-[DebuggerDisplay("{Languages.Length} Language(s)")]
+[DebuggerDisplay("{Languages.Count} Language(s)")]
 public class LocalizationData
 public class LocalizationData
 {
 {
-    public LanguageData[] Languages { get; set; }
+    public List<LanguageData> Languages { get; set; }
 }
 }

+ 16 - 14
src/PixiEditor/Localization/LocalizationProvider.cs

@@ -1,4 +1,5 @@
-using System.IO;
+using System.Globalization;
+using System.IO;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Models.UserPreferences;
 
 
 namespace PixiEditor.Localization;
 namespace PixiEditor.Localization;
@@ -9,6 +10,8 @@ internal class LocalizationProvider : ILocalizationProvider
     public string LocalizationDataPath { get; } = Path.Combine("Data", "Localization", "LocalizationData.json");
     public string LocalizationDataPath { get; } = Path.Combine("Data", "Localization", "LocalizationData.json");
     public LocalizationData LocalizationData { get; private set; }
     public LocalizationData LocalizationData { get; private set; }
     public Language CurrentLanguage { get; set; }
     public Language CurrentLanguage { get; set; }
+    public LanguageData SelectedLanguage { get; private set; }
+    public LanguageData FollowSystem { get; } = new() { Name = "Follow system", Code = "system" };
     public event Action<Language> OnLanguageChanged;
     public event Action<Language> OnLanguageChanged;
     public void ReloadLanguage() => OnLanguageChanged?.Invoke(CurrentLanguage);
     public void ReloadLanguage() => OnLanguageChanged?.Invoke(CurrentLanguage);
 
 
@@ -31,27 +34,18 @@ internal class LocalizationProvider : ILocalizationProvider
             throw new InvalidDataException("Localization data is null.");
             throw new InvalidDataException("Localization data is null.");
         }
         }
         
         
-        if (LocalizationData.Languages is null || LocalizationData.Languages.Length == 0)
+        if (LocalizationData.Languages is null || LocalizationData.Languages.Count == 0)
         {
         {
             throw new InvalidDataException("Localization data does not contain any languages.");
             throw new InvalidDataException("Localization data does not contain any languages.");
         }
         }
 
 
+        LocalizationData.Languages.Add(FollowSystem);
+        
         DefaultLanguage = LoadLanguageInternal(LocalizationData.Languages[0]);
         DefaultLanguage = LoadLanguageInternal(LocalizationData.Languages[0]);
         
         
         string currentLanguageCode = IPreferences.Current.GetPreference<string>("LanguageCode");
         string currentLanguageCode = IPreferences.Current.GetPreference<string>("LanguageCode");
 
 
-        int languageIndex = 0;
-        
-        for (int i = 0; i < LocalizationData.Languages.Length; i++)
-        {
-            if (LocalizationData.Languages[i].Code == currentLanguageCode)
-            {
-                languageIndex = i;
-                break;
-            }
-        }
-        
-        LoadLanguage(LocalizationData.Languages[languageIndex]);
+        LoadLanguage(LocalizationData.Languages.FirstOrDefault(x => x.Code == currentLanguageCode, FollowSystem));
     }
     }
 
 
     public void LoadLanguage(LanguageData languageData)
     public void LoadLanguage(LanguageData languageData)
@@ -67,7 +61,15 @@ internal class LocalizationProvider : ILocalizationProvider
         }
         }
         
         
         bool firstLoad = CurrentLanguage is null;
         bool firstLoad = CurrentLanguage is null;
+        
+        SelectedLanguage = languageData;
 
 
+        if (languageData.Code == FollowSystem.Code)
+        {
+            string osLanguage = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
+            languageData = LocalizationData.Languages.FirstOrDefault(x => x.Code == osLanguage, LocalizationData.Languages[0]);
+        }
+        
         CurrentLanguage = LoadLanguageInternal(languageData);
         CurrentLanguage = LoadLanguageInternal(languageData);
 
 
         if (!firstLoad)
         if (!firstLoad)

+ 14 - 2
src/PixiEditor/Models/Commands/Search/ColorSearchResult.cs

@@ -1,4 +1,7 @@
-using System.Windows.Media;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Media;
 
 
 namespace PixiEditor.Models.Commands.Search;
 namespace PixiEditor.Models.Commands.Search;
 
 
@@ -13,7 +16,11 @@ internal class ColorSearchResult : SearchResult
 
 
     public override string Text => text;
     public override string Text => text;
 
 
-    public override string Description => $"{color} rgba({color.R}, {color.G}, {color.B}, {color.A})";
+    public override FrameworkElement Description => new TextBlock(new Run($"{color} rgba({color.R}, {color.G}, {color.B}, {color.A})"))
+    {
+        FontSize = 16,
+        TextDecorations = GetDecoration(new Pen(new SolidColorBrush(color.ToOpaqueMediaColor()), 1))
+    };
 
 
     //public override bool CanExecute => !requiresDocument || (requiresDocument && ViewModelMain.Current.BitmapManager.ActiveDocument != null);
     //public override bool CanExecute => !requiresDocument || (requiresDocument && ViewModelMain.Current.BitmapManager.ActiveDocument != null);
     public override bool CanExecute => !isPalettePaste || ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument != null;
     public override bool CanExecute => !isPalettePaste || ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument != null;
@@ -55,4 +62,9 @@ internal class ColorSearchResult : SearchResult
         drawing.Geometry = geometry;
         drawing.Geometry = geometry;
         return new DrawingImage(drawing);
         return new DrawingImage(drawing);
     }
     }
+    
+    private static TextDecorationCollection GetDecoration(Pen pen) => new()
+    {
+        new TextDecoration(TextDecorationLocation.Underline, pen, 0, TextDecorationUnit.Pixel, TextDecorationUnit.Pixel)
+    };
 }
 }

+ 3 - 1
src/PixiEditor/Models/Commands/Search/FileSearchResult.cs

@@ -1,4 +1,6 @@
 using System.IO;
 using System.IO;
+using System.Windows;
+using System.Windows.Controls;
 using System.Windows.Media;
 using System.Windows.Media;
 using PixiEditor.Helpers.Converters;
 using PixiEditor.Helpers.Converters;
 
 
@@ -13,7 +15,7 @@ internal class FileSearchResult : SearchResult
 
 
     public override string Text => asReferenceLayer ? $"As reference: ...\\{Path.GetFileName(FilePath)}" : $"...\\{Path.GetFileName(FilePath)}";
     public override string Text => asReferenceLayer ? $"As reference: ...\\{Path.GetFileName(FilePath)}" : $"...\\{Path.GetFileName(FilePath)}";
 
 
-    public override string Description => FilePath;
+    public override FrameworkElement Description => new TextBlock { Text = FilePath, FontSize = 16 };
 
 
     public override bool CanExecute => !asReferenceLayer ||
     public override bool CanExecute => !asReferenceLayer ||
                 CommandController.Current.Commands["PixiEditor.Clipboard.PasteReferenceLayerFromPath"].Methods.CanExecute(FilePath);
                 CommandController.Current.Commands["PixiEditor.Clipboard.PasteReferenceLayerFromPath"].Methods.CanExecute(FilePath);

+ 2 - 1
src/PixiEditor/Models/Commands/Search/SearchResult.cs

@@ -1,4 +1,5 @@
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
+using System.Windows;
 using System.Windows.Documents;
 using System.Windows.Documents;
 using System.Windows.Media;
 using System.Windows.Media;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
@@ -19,7 +20,7 @@ internal abstract class SearchResult : NotifyableObject
 
 
     public abstract string Text { get; }
     public abstract string Text { get; }
 
 
-    public virtual string Description { get; }
+    public virtual FrameworkElement Description { get; }
 
 
     public abstract bool CanExecute { get; }
     public abstract bool CanExecute { get; }
 
 

+ 18 - 0
src/PixiEditor/Models/Commands/XAML/Menu.cs

@@ -16,6 +16,8 @@ internal class Menu : System.Windows.Controls.Menu
             new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, CommandChanged)
             new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, CommandChanged)
         );
         );
 
 
+    public const double IconDimensions = 21;
+    
     public static string GetCommand(UIElement target) => (string)target.GetValue(CommandNameProperty);
     public static string GetCommand(UIElement target) => (string)target.GetValue(CommandNameProperty);
 
 
     public static void SetCommand(UIElement target, string value) => target.SetValue(CommandNameProperty, value);
     public static void SetCommand(UIElement target, string value) => target.SetValue(CommandNameProperty, value);
@@ -35,7 +37,23 @@ internal class Menu : System.Windows.Controls.Menu
 
 
         var command = CommandController.Current.Commands[value];
         var command = CommandController.Current.Commands[value];
 
 
+        var icon = new Image 
+        { 
+            Source = command.GetIcon(), 
+            Width = IconDimensions, Height = IconDimensions,
+            Opacity = command.CanExecute() ? 1 : 0.75
+        };
+        
+        icon.IsVisibleChanged += (_, v) =>
+        {
+            if ((bool)v.NewValue)
+            {
+                icon.Opacity = command.CanExecute() ? 1 : 0.75;
+            }
+        };
+
         item.Command = Command.GetICommand(command, false);
         item.Command = Command.GetICommand(command, false);
+        item.Icon = icon;
         item.SetBinding(MenuItem.InputGestureTextProperty, ShortcutBinding.GetBinding(command));
         item.SetBinding(MenuItem.InputGestureTextProperty, ShortcutBinding.GetBinding(command));
     }
     }
 
 

+ 36 - 3
src/PixiEditor/Models/Controllers/ClipboardController.cs

@@ -6,13 +6,14 @@ using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
 using ChunkyImageLib;
 using ChunkyImageLib;
-using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
+using PixiEditor.Parser;
+using PixiEditor.Parser.Deprecated;
 using PixiEditor.ViewModels.SubViewModels.Document;
 using PixiEditor.ViewModels.SubViewModels.Document;
 
 
 namespace PixiEditor.Models.Controllers;
 namespace PixiEditor.Models.Controllers;
@@ -132,8 +133,39 @@ internal static class ClipboardController
                     continue;
                     continue;
                 try
                 try
                 {
                 {
-                    Surface imported = Surface.Load(path);
-                    string filename = Path.GetFileName(path);
+                    Surface imported;
+                    
+                    if (Path.GetExtension(path) == ".pixi")
+                    {
+                        using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
+                        
+                        try
+                        {
+                            imported = Surface.Load(PixiParser.Deserialize(path).PreviewImage);
+                        }
+                        catch (InvalidFileException e)
+                        {
+                            // Check if it could be a old file
+                            if (!e.Message.StartsWith("Header"))
+                            {
+                                throw;
+                            }
+                            
+                            stream.Position = 0;
+                            using var bitmap = DepractedPixiParser.Deserialize(stream).RenderOldDocument();
+                            var size = new VecI(bitmap.Width, bitmap.Height);
+                            imported = new Surface(size);
+                            imported.DrawBytes(size, bitmap.Bytes, ColorType.RgbaF32, AlphaType.Premul);
+                            
+                            System.Diagnostics.Debug.Write(imported.ToString());
+                        }
+                    }
+                    else
+                    {
+                        imported = Surface.Load(path);
+                    }
+
+                    string filename = Path.GetFullPath(path);
                     surfaces.Add((filename, imported));
                     surfaces.Add((filename, imported));
                 }
                 }
                 catch
                 catch
@@ -145,6 +177,7 @@ internal static class ClipboardController
         return surfaces;
         return surfaces;
     }
     }
 
 
+    [Evaluator.CanExecute("PixiEditor.Clipboard.HasImageInClipboard")]
     public static bool IsImageInClipboard() => IsImage(ClipboardHelper.TryGetDataObject());
     public static bool IsImageInClipboard() => IsImage(ClipboardHelper.TryGetDataObject());
     
     
     public static bool IsImage(DataObject? dataObject)
     public static bool IsImage(DataObject? dataObject)

+ 4 - 0
src/PixiEditor/Models/Dialogs/ImportFileDialog.cs

@@ -55,6 +55,10 @@ internal class ImportFileDialog : CustomDialog
         {
         {
             FilePath = FilePath
             FilePath = FilePath
         };
         };
+
+        if (FileWidth != 0) popup.ImportWidth = FileWidth;
+        if (FileHeight != 0) popup.ImportHeight = FileHeight;
+        
         popup.ShowDialog();
         popup.ShowDialog();
         if (popup.DialogResult == true)
         if (popup.DialogResult == true)
         {
         {

+ 2 - 1
src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -1,4 +1,5 @@
 using System.Collections.Immutable;
 using System.Collections.Immutable;
+using System.IO;
 using ChunkyImageLib;
 using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Actions.Undo;
 using PixiEditor.ChangeableDocument.Actions.Undo;
@@ -120,7 +121,7 @@ internal class DocumentOperationsModule
 
 
         foreach (var imageWithName in images)
         foreach (var imageWithName in images)
         {
         {
-            var layerGuid = Internals.StructureHelper.CreateNewStructureMember(StructureMemberType.Layer, imageWithName.name);
+            var layerGuid = Internals.StructureHelper.CreateNewStructureMember(StructureMemberType.Layer, Path.GetFileName(imageWithName.name));
             DrawImage(imageWithName.image, new ShapeCorners(new RectD(VecD.Zero, imageWithName.image.Size)), layerGuid, true, false, false);
             DrawImage(imageWithName.image, new ShapeCorners(new RectD(VecD.Zero, imageWithName.image.Size)), layerGuid, true, false, false);
         }
         }
         Internals.ActionAccumulator.AddFinishedActions();
         Internals.ActionAccumulator.AddFinishedActions();

+ 3 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/ColorPickerToolExecutor.cs

@@ -24,7 +24,7 @@ internal class ColorPickerToolExecutor : UpdateableChangeExecutor
         includeReference = tool.PickFromReferenceLayer && document!.ReferenceLayerViewModel.ReferenceBitmap is not null;
         includeReference = tool.PickFromReferenceLayer && document!.ReferenceLayerViewModel.ReferenceBitmap is not null;
         includeCanvas = tool.PickFromCanvas;
         includeCanvas = tool.PickFromCanvas;
         
         
-        colorsViewModel.PrimaryColor = document.PickColor(controller.LastPrecisePosition, scope, includeReference, includeCanvas);
+        colorsViewModel.PrimaryColor = document.PickColor(controller.LastPrecisePosition, scope, includeReference, includeCanvas, document.ReferenceLayerViewModel.IsTopMost);
         return ExecutionState.Success;
         return ExecutionState.Success;
     }
     }
 
 
@@ -32,12 +32,12 @@ internal class ColorPickerToolExecutor : UpdateableChangeExecutor
     {
     {
         if (!includeReference)
         if (!includeReference)
             return;
             return;
-        colorsViewModel.PrimaryColor = document.PickColor(pos, scope, includeReference, includeCanvas);
+        colorsViewModel.PrimaryColor = document.PickColor(pos, scope, includeReference, includeCanvas, document.ReferenceLayerViewModel.IsTopMost);
     }
     }
 
 
     public override void OnPixelPositionChange(VecI pos)
     public override void OnPixelPositionChange(VecI pos)
     {
     {
-        colorsViewModel.PrimaryColor = document.PickColor(pos, scope, includeReference, includeCanvas);
+        colorsViewModel.PrimaryColor = document.PickColor(pos, scope, includeReference, includeCanvas, document.ReferenceLayerViewModel.IsTopMost);
     }
     }
 
 
     public override void OnLeftMouseButtonUp()
     public override void OnLeftMouseButtonUp()

+ 13 - 3
src/PixiEditor/Models/IO/Importer.cs

@@ -66,12 +66,17 @@ internal class Importer : NotifyableObject
         }
         }
     }
     }
 
 
-    public static DocumentViewModel ImportDocument(string path)
+    public static DocumentViewModel ImportDocument(string path, bool associatePath = true)
     {
     {
         try
         try
         {
         {
             var doc = PixiParser.Deserialize(path).ToDocument();
             var doc = PixiParser.Deserialize(path).ToDocument();
-            doc.FullFilePath = path;
+            
+            if (associatePath)
+            {
+                doc.FullFilePath = path;
+            }
+
             return doc;
             return doc;
         }
         }
         catch (InvalidFileException)
         catch (InvalidFileException)
@@ -79,7 +84,12 @@ internal class Importer : NotifyableObject
             try
             try
             {
             {
                 var doc = DepractedPixiParser.Deserialize(path).ToDocument();
                 var doc = DepractedPixiParser.Deserialize(path).ToDocument();
-                doc.FullFilePath = path;
+                
+                if (associatePath)
+                {
+                    doc.FullFilePath = path;
+                }
+
                 return doc;
                 return doc;
             }
             }
             catch (InvalidFileException e)
             catch (InvalidFileException e)

+ 14 - 0
src/PixiEditor/PixiEditor.csproj

@@ -415,6 +415,20 @@
 		<Resource Include="Images\LanguageFlags\ru.png" />
 		<Resource Include="Images\LanguageFlags\ru.png" />
 		<None Remove="Images\LanguageFlags\cs.png" />
 		<None Remove="Images\LanguageFlags\cs.png" />
 		<Resource Include="Images\LanguageFlags\cs.png" />
 		<Resource Include="Images\LanguageFlags\cs.png" />
+		<None Remove="Images\Commands\PixiEditor\Clipboard\PasteReferenceLayer.png" />
+		<Resource Include="Images\Commands\PixiEditor\Clipboard\PasteReferenceLayer.png" />
+		<None Remove="Images\Commands\PixiEditor\Clipboard\PasteAsNewLayer.png" />
+		<Resource Include="Images\Commands\PixiEditor\Clipboard\PasteAsNewLayer.png" />
+		<None Remove="Images\Commands\PixiEditor\File\OpenFileFromClipboard.png" />
+		<Resource Include="Images\Commands\PixiEditor\File\OpenFileFromClipboard.png" />
+		<None Remove="Images\Commands\PixiEditor\Window\OpenShortcutWindow.png" />
+		<Resource Include="Images\Commands\PixiEditor\Window\OpenShortcutWindow.png" />
+		<None Remove="Images\Commands\PixiEditor\Window\OpenAboutWindow.png" />
+		<Resource Include="Images\Commands\PixiEditor\Window\OpenAboutWindow.png" />
+		<None Remove="Images\Commands\PixiEditor\Layer\DuplicateSelectedLayer.png" />
+		<Resource Include="Images\Commands\PixiEditor\Layer\DuplicateSelectedLayer.png" />
+		<None Remove="Images\Commands\PixiEditor\Selection\InvertSelection.png" />
+		<Resource Include="Images\Commands\PixiEditor\Selection\InvertSelection.png" />
 	</ItemGroup>
 	</ItemGroup>
 	<ItemGroup>
 	<ItemGroup>
 		<None Include="..\LICENSE">
 		<None Include="..\LICENSE">

+ 2 - 2
src/PixiEditor/Styles/MenuButtonStyle.xaml

@@ -27,13 +27,13 @@
         <Setter Property="Template">
         <Setter Property="Template">
             <Setter.Value>
             <Setter.Value>
                 <ControlTemplate TargetType="{x:Type MenuItem}">
                 <ControlTemplate TargetType="{x:Type MenuItem}">
-                    <Border x:Name="Border" Padding="2.5"
+                    <Border x:Name="Border" Padding="2.5,1,2.5,1"
                             Background="Transparent"
                             Background="Transparent"
                             BorderBrush="{TemplateBinding BorderBrush}"
                             BorderBrush="{TemplateBinding BorderBrush}"
                             BorderThickness="0">
                             BorderThickness="0">
                         <Grid>
                         <Grid>
                             <Grid.ColumnDefinitions>
                             <Grid.ColumnDefinitions>
-                                <ColumnDefinition x:Name="Col0" MinWidth="17" Width="Auto"
+                                <ColumnDefinition x:Name="Col0" Width="Auto" MinWidth="5"
                                                   SharedSizeGroup="MenuItemIconColumnGroup" />
                                                   SharedSizeGroup="MenuItemIconColumnGroup" />
                                 <ColumnDefinition Width="Auto" SharedSizeGroup="MenuTextColumnGroup" />
                                 <ColumnDefinition Width="Auto" SharedSizeGroup="MenuTextColumnGroup" />
                                 <ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIGTColumnGroup" />
                                 <ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIGTColumnGroup" />

+ 1 - 2
src/PixiEditor/Styles/Titlebar.xaml

@@ -7,7 +7,7 @@
             <Setter.Value>
             <Setter.Value>
                 <ControlTemplate TargetType="Button">
                 <ControlTemplate TargetType="Button">
                     <Grid x:Name="LayoutRoot" Background="Transparent" Width="44" Height="35">
                     <Grid x:Name="LayoutRoot" Background="Transparent" Width="44" Height="35">
-                        <TextBlock x:Name="txt" Text="{TemplateBinding Content}" FontFamily="Segoe MDL2 Assets"
+                        <TextBlock x:Name="txt" Text="{TemplateBinding Content}" FontFamily="{DynamicResource NativeIconFont}"
                                    FontSize="10"
                                    FontSize="10"
                                    Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"
                                    Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"
                                    RenderOptions.ClearTypeHint="Auto" TextOptions.TextRenderingMode="Aliased"
                                    RenderOptions.ClearTypeHint="Auto" TextOptions.TextRenderingMode="Aliased"
@@ -47,7 +47,6 @@
                 <ControlTemplate TargetType="Button">
                 <ControlTemplate TargetType="Button">
                     <Grid x:Name="LayoutRoot" Background="Transparent" Width="44" Height="35">
                     <Grid x:Name="LayoutRoot" Background="Transparent" Width="44" Height="35">
                         <TextBlock x:Name="txt" Text="{TemplateBinding Content}" FontFamily="Segoe MDL2 Assets"
                         <TextBlock x:Name="txt" Text="{TemplateBinding Content}" FontFamily="Segoe MDL2 Assets"
-                                   FontSize="10"
                                    Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"
                                    Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"
                                    RenderOptions.ClearTypeHint="Auto" TextOptions.TextRenderingMode="Aliased"
                                    RenderOptions.ClearTypeHint="Auto" TextOptions.TextRenderingMode="Aliased"
                                    TextOptions.TextFormattingMode="Display" />
                                    TextOptions.TextFormattingMode="Display" />

+ 15 - 4
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs

@@ -387,7 +387,8 @@ internal partial class DocumentViewModel : NotifyableObject
     /// </summary>
     /// </summary>
     /// <param name="includeReference">Should the color be picked from the reference layer</param>
     /// <param name="includeReference">Should the color be picked from the reference layer</param>
     /// <param name="includeCanvas">Should the color be picked from the canvas</param>
     /// <param name="includeCanvas">Should the color be picked from the canvas</param>
-    public Color PickColor(VecD pos, DocumentScope scope, bool includeReference, bool includeCanvas)
+    /// <param name="referenceTopmost">Is the reference layer topmost. (Only affects the result is includeReference and includeCanvas are set.)</param>
+    public Color PickColor(VecD pos, DocumentScope scope, bool includeReference, bool includeCanvas, bool referenceTopmost = false)
     {
     {
         if (scope == DocumentScope.SingleLayer && includeReference && includeCanvas)
         if (scope == DocumentScope.SingleLayer && includeReference && includeCanvas)
             includeReference = false;
             includeReference = false;
@@ -395,10 +396,20 @@ internal partial class DocumentViewModel : NotifyableObject
         if (includeCanvas && includeReference)
         if (includeCanvas && includeReference)
         {
         {
             Color canvasColor = PickColorFromCanvas((VecI)pos, scope);
             Color canvasColor = PickColorFromCanvas((VecI)pos, scope);
-            Color? referenceColor = PickColorFromReferenceLayer(pos);
-            if (referenceColor is null)
+            Color? potentialReferenceColor = PickColorFromReferenceLayer(pos);
+            if (potentialReferenceColor is not { } referenceColor)
                 return canvasColor;
                 return canvasColor;
-            return ColorHelpers.BlendColors((Color)referenceColor, canvasColor);
+
+            if (!referenceTopmost)
+            {
+                return ColorHelpers.BlendColors(referenceColor, canvasColor);
+            }
+
+            byte referenceAlpha = canvasColor.A == 0 ? referenceColor.A : (byte)(referenceColor.A * ReferenceLayerViewModel.TopMostOpacity);
+            
+            referenceColor = new Color(referenceColor.R, referenceColor.G, referenceColor.B, referenceAlpha);
+            return ColorHelpers.BlendColors(canvasColor, referenceColor);
+
         }
         }
         if (includeCanvas)
         if (includeCanvas)
             return PickColorFromCanvas((VecI)pos, scope);
             return PickColorFromCanvas((VecI)pos, scope);

+ 18 - 1
src/PixiEditor/ViewModels/SubViewModels/Document/ReferenceLayerViewModel.cs

@@ -8,6 +8,7 @@ using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.DocumentModels;
+using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Document;
 namespace PixiEditor.ViewModels.SubViewModels.Document;
 
 
@@ -17,6 +18,8 @@ internal class ReferenceLayerViewModel : INotifyPropertyChanged
     private readonly DocumentViewModel doc;
     private readonly DocumentViewModel doc;
     private readonly DocumentInternalParts internals;
     private readonly DocumentInternalParts internals;
     public event PropertyChangedEventHandler PropertyChanged;
     public event PropertyChangedEventHandler PropertyChanged;
+
+    public const double TopMostOpacity = 0.6;
     
     
     public WriteableBitmap? ReferenceBitmap { get; private set; }
     public WriteableBitmap? ReferenceBitmap { get; private set; }
 
 
@@ -78,7 +81,7 @@ internal class ReferenceLayerViewModel : INotifyPropertyChanged
     
     
     public bool ShowHighest
     public bool ShowHighest
     {
     {
-        get => IsTopMost || IsTransforming;
+        get => (IsTopMost || IsTransforming) && !IsColorPickerSelected();
     }
     }
 
 
     public ReferenceLayerViewModel(DocumentViewModel doc, DocumentInternalParts internals)
     public ReferenceLayerViewModel(DocumentViewModel doc, DocumentInternalParts internals)
@@ -87,6 +90,18 @@ internal class ReferenceLayerViewModel : INotifyPropertyChanged
         this.internals = internals;
         this.internals = internals;
     }
     }
 
 
+    private bool IsColorPickerSelected()
+    {
+        var viewModel = ViewModelMain.Current.ToolsSubViewModel;
+        
+        if (viewModel.ActiveTool is ColorPickerToolViewModel colorPicker)
+        {
+            return colorPicker.PickFromReferenceLayer && !colorPicker.PickFromCanvas;
+        }
+
+        return false;
+    }
+    
     private void RaisePropertyChanged(string name)
     private void RaisePropertyChanged(string name)
     {
     {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
@@ -94,6 +109,8 @@ internal class ReferenceLayerViewModel : INotifyPropertyChanged
     
     
     #region Internal methods
     #region Internal methods
 
 
+    public void RaiseShowHighestChanged() => RaisePropertyChanged(nameof(ShowHighest));
+    
     public void InternalSetReferenceLayer(ImmutableArray<byte> imagePbgra32Bytes, VecI imageSize, ShapeCorners shape)
     public void InternalSetReferenceLayer(ImmutableArray<byte> imagePbgra32Bytes, VecI imageSize, ShapeCorners shape)
     {
     {
         ReferenceBitmap = WriteableBitmapHelpers.FromPbgra32Array(imagePbgra32Bytes.ToArray(), imageSize);
         ReferenceBitmap = WriteableBitmapHelpers.FromPbgra32Array(imagePbgra32Bytes.ToArray(), imageSize);

+ 6 - 4
src/PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs

@@ -29,7 +29,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
     }
     }
 
 
     [Command.Basic("PixiEditor.Clipboard.Paste", false, "PASTE", "PASTE_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste", Key = Key.V, Modifiers = ModifierKeys.Shift)]
     [Command.Basic("PixiEditor.Clipboard.Paste", false, "PASTE", "PASTE_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste", Key = Key.V, Modifiers = ModifierKeys.Shift)]
-    [Command.Basic("PixiEditor.Clipboard.PasteAsNewLayer", true, "PASTE_AS_NEW_LAYER", "PASTE_AS_NEW_LAYER_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste", IconPath = "$PixiEditor.Clipboard.Paste", Key = Key.V, Modifiers = ModifierKeys.Control)]
+    [Command.Basic("PixiEditor.Clipboard.PasteAsNewLayer", true, "PASTE_AS_NEW_LAYER", "PASTE_AS_NEW_LAYER_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste", Key = Key.V, Modifiers = ModifierKeys.Control)]
     public void Paste(bool pasteAsNewLayer)
     public void Paste(bool pasteAsNewLayer)
     {
     {
         if (Owner.DocumentManagerSubViewModel.ActiveDocument is null) 
         if (Owner.DocumentManagerSubViewModel.ActiveDocument is null) 
@@ -37,7 +37,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         ClipboardController.TryPasteFromClipboard(Owner.DocumentManagerSubViewModel.ActiveDocument, pasteAsNewLayer);
         ClipboardController.TryPasteFromClipboard(Owner.DocumentManagerSubViewModel.ActiveDocument, pasteAsNewLayer);
     }
     }
     
     
-    [Command.Basic("PixiEditor.Clipboard.PasteReferenceLayer", "PASTE_REFERENCE_LAYER", "PASTE_REFERENCE_LAYER_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste", IconPath = "Commands/PixiEditor/Clipboard/Paste.png")]
+    [Command.Basic("PixiEditor.Clipboard.PasteReferenceLayer", "PASTE_REFERENCE_LAYER", "PASTE_REFERENCE_LAYER_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanPaste")]
     public void PasteReferenceLayer(DataObject data)
     public void PasteReferenceLayer(DataObject data)
     {
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -53,6 +53,8 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         doc.Operations.ImportReferenceLayer(
         doc.Operations.ImportReferenceLayer(
             pixels.ToImmutableArray(),
             pixels.ToImmutableArray(),
             surface.image.Size);
             surface.image.Size);
+
+        Application.Current.MainWindow!.Activate();
     }
     }
     
     
     [Command.Internal("PixiEditor.Clipboard.PasteReferenceLayerFromPath")]
     [Command.Internal("PixiEditor.Clipboard.PasteReferenceLayerFromPath")]
@@ -123,9 +125,9 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
     }
     }
 
 
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanPaste")]
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanPaste")]
-    public bool CanPaste()
+    public bool CanPaste(object parameter)
     {
     {
-        return Owner.DocumentIsNotNull(null) && ClipboardController.IsImageInClipboard();
+        return Owner.DocumentIsNotNull(null) && parameter is DataObject data ? ClipboardController.IsImage(data) : ClipboardController.IsImageInClipboard();
     }
     }
 
 
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanPasteColor")]
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanPasteColor")]

+ 77 - 17
src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -76,12 +76,16 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         IPreferences.Current.UpdateLocalPreference(PreferencesConstants.RecentlyOpened, RecentlyOpened.Select(x => x.FilePath));
         IPreferences.Current.UpdateLocalPreference(PreferencesConstants.RecentlyOpened, RecentlyOpened.Select(x => x.FilePath));
     }
     }
 
 
-    public void RemoveRecentlyOpened(object parameter)
+    [Command.Internal("PixiEditor.File.RemoveRecent")]
+    public void RemoveRecentlyOpened(string path)
     {
     {
-        if (RecentlyOpened.Contains((string)parameter))
+        if (!RecentlyOpened.Contains(path))
         {
         {
-            RecentlyOpened.Remove((string)parameter);
+            return;
         }
         }
+
+        RecentlyOpened.Remove(path);
+        IPreferences.Current.UpdateLocalPreference(PreferencesConstants.RecentlyOpened, RecentlyOpened.Select(x => x.FilePath));
     }
     }
 
 
     private void OpenHelloTherePopup()
     private void OpenHelloTherePopup()
@@ -139,6 +143,23 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         OpenFromPath(dialog.FileName);
         OpenFromPath(dialog.FileName);
     }
     }
 
 
+    [Command.Basic("PixiEditor.File.OpenFileFromClipboard", "OPEN_FILE_FROM_CLIPBOARD", "OPEN_FILE_FROM_CLIPBOARD_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.HasImageInClipboard")]
+    public void OpenFromClipboard()
+    {
+        var images = ClipboardController.GetImagesFromClipboard();
+
+        foreach (var (name, image) in images)
+        {
+            if (name == null)
+            {
+                OpenRegularImage(image, null);
+                continue;
+            }
+            
+            OpenFromPath(name, false);
+        }
+    }
+
     private bool MakeExistingDocumentActiveIfOpened(string path)
     private bool MakeExistingDocumentActiveIfOpened(string path)
     {
     {
         foreach (DocumentViewModel document in Owner.DocumentManagerSubViewModel.Documents)
         foreach (DocumentViewModel document in Owner.DocumentManagerSubViewModel.Documents)
@@ -155,8 +176,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
     /// <summary>
     /// <summary>
     /// Tries to open the passed file if it isn't already open
     /// Tries to open the passed file if it isn't already open
     /// </summary>
     /// </summary>
-    /// <param name="path"></param>
-    public void OpenFromPath(string path)
+    public void OpenFromPath(string path, bool associatePath = true)
     {
     {
         if (MakeExistingDocumentActiveIfOpened(path))
         if (MakeExistingDocumentActiveIfOpened(path))
             return;
             return;
@@ -165,11 +185,11 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         {
         {
             if (path.EndsWith(".pixi"))
             if (path.EndsWith(".pixi"))
             {
             {
-                OpenDotPixi(path);
+                OpenDotPixi(path, associatePath);
             }
             }
             else
             else
             {
             {
-                OpenRegularImage(path);
+                OpenRegularImage(path, associatePath);
             }
             }
         }
         }
         catch (RecoverableException ex)
         catch (RecoverableException ex)
@@ -185,9 +205,9 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
     /// <summary>
     /// <summary>
     /// Opens a .pixi file from path, creates a document from it, and adds it to the system
     /// Opens a .pixi file from path, creates a document from it, and adds it to the system
     /// </summary>
     /// </summary>
-    private void OpenDotPixi(string path)
+    private void OpenDotPixi(string path, bool associatePath = true)
     {
     {
-        DocumentViewModel document = Importer.ImportDocument(path);
+        DocumentViewModel document = Importer.ImportDocument(path, associatePath);
         AddDocumentViewModelToTheSystem(document);
         AddDocumentViewModelToTheSystem(document);
         AddRecentlyOpened(document.FullFilePath);
         AddRecentlyOpened(document.FullFilePath);
     }
     }
@@ -205,7 +225,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
     /// <summary>
     /// <summary>
     /// Opens a regular image file from path, creates a document from it, and adds it to the system.
     /// Opens a regular image file from path, creates a document from it, and adds it to the system.
     /// </summary>
     /// </summary>
-    private void OpenRegularImage(string path)
+    private void OpenRegularImage(string path, bool associatePath)
     {
     {
         ImportFileDialog dialog = new ImportFileDialog();
         ImportFileDialog dialog = new ImportFileDialog();
 
 
@@ -214,17 +234,57 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
             dialog.FilePath = path;
             dialog.FilePath = path;
         }
         }
 
 
-        if (dialog.ShowDialog())
+        if (!dialog.ShowDialog())
         {
         {
-            DocumentViewModel doc = NewDocument(b => b
+            return;
+        }
+
+        DocumentViewModel doc = NewDocument(b => b
+            .WithSize(dialog.FileWidth, dialog.FileHeight)
+            .WithLayer(l => l
+                .WithName("Image")
                 .WithSize(dialog.FileWidth, dialog.FileHeight)
                 .WithSize(dialog.FileWidth, dialog.FileHeight)
-                .WithLayer(l => l
-                    .WithName("Image")
-                    .WithSize(dialog.FileWidth, dialog.FileHeight)
-                    .WithSurface(Importer.ImportImage(dialog.FilePath, new VecI(dialog.FileWidth, dialog.FileHeight)))));
+                .WithSurface(Importer.ImportImage(dialog.FilePath, new VecI(dialog.FileWidth, dialog.FileHeight)))));
+
+        if (associatePath)
+        {
             doc.FullFilePath = path;
             doc.FullFilePath = path;
-            AddRecentlyOpened(path);
         }
         }
+
+        AddRecentlyOpened(path);
+    }
+
+    /// <summary>
+    /// Opens a regular image file from path, creates a document from it, and adds it to the system.
+    /// </summary>
+    private void OpenRegularImage(Surface surface, string path)
+    {
+        ImportFileDialog dialog = new ImportFileDialog( );
+
+        dialog.FileWidth = surface.Size.X;
+        dialog.FileHeight = surface.Size.Y;
+        
+        if (!dialog.ShowDialog())
+        {
+            return;
+        }
+
+        surface.ResizeNearestNeighbor(new VecI(dialog.FileWidth, dialog.FileHeight));
+            
+        DocumentViewModel doc = NewDocument(b => b
+            .WithSize(dialog.FileWidth, dialog.FileHeight)
+            .WithLayer(l => l
+                .WithName("Image")
+                .WithSize(dialog.FileWidth, dialog.FileHeight)
+                .WithSurface(surface)));
+
+        if (path == null)
+        {
+            return;
+        }
+
+        doc.FullFilePath = path;
+        AddRecentlyOpened(path);
     }
     }
 
 
     [Command.Basic("PixiEditor.File.New", "NEW_IMAGE", "CREATE_NEW_IMAGE", Key = Key.N, Modifiers = ModifierKeys.Control)]
     [Command.Basic("PixiEditor.File.New", "NEW_IMAGE", "CREATE_NEW_IMAGE", Key = Key.N, Modifiers = ModifierKeys.Control)]

+ 0 - 16
src/PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -327,22 +327,6 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
     public bool ReferenceLayerDoesntExist() => 
     public bool ReferenceLayerDoesntExist() => 
         Owner.DocumentManagerSubViewModel.ActiveDocument is not null && Owner.DocumentManagerSubViewModel.ActiveDocument.ReferenceLayerViewModel.ReferenceBitmap is null;
         Owner.DocumentManagerSubViewModel.ActiveDocument is not null && Owner.DocumentManagerSubViewModel.ActiveDocument.ReferenceLayerViewModel.ReferenceBitmap is null;
 
 
-    [Evaluator.CanExecute("PixiEditor.Layer.ReferenceLayerDoesntExistAndHasClipboardContent")]
-    public bool ReferenceLayerDoesntExistAndHasClipboardContent(DataObject data)
-    {
-        if (!ReferenceLayerDoesntExist())
-        {
-            return false;
-        }
-        
-        if (data != null)
-        {
-            return Owner.DocumentIsNotNull(null) && ClipboardController.IsImage(data);
-        }
-        
-        return Owner.ClipboardSubViewModel.CanPaste();
-    }
-
     [Command.Basic("PixiEditor.Layer.ImportReferenceLayer", "ADD_REFERENCE_LAYER", "ADD_REFERENCE_LAYER", CanExecute = "PixiEditor.Layer.ReferenceLayerDoesntExist")]
     [Command.Basic("PixiEditor.Layer.ImportReferenceLayer", "ADD_REFERENCE_LAYER", "ADD_REFERENCE_LAYER", CanExecute = "PixiEditor.Layer.ReferenceLayerDoesntExist")]
     public void ImportReferenceLayer()
     public void ImportReferenceLayer()
     {
     {

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

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

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/Main/WindowViewModel.cs

@@ -44,7 +44,7 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>
         this.commandController = commandController;
         this.commandController = commandController;
     }
     }
 
 
-    [Command.Basic("PixiEditor.Window.CreateNewViewport", "NEW_WINDOW_FOR_IMG", "NEW_WINDOW_FOR_IMG", CanExecute = "PixiEditor.HasDocument")]
+    [Command.Basic("PixiEditor.Window.CreateNewViewport", "NEW_WINDOW_FOR_IMG", "NEW_WINDOW_FOR_IMG", IconPath = "@Images/Plus-square.png", CanExecute = "PixiEditor.HasDocument")]
     public void CreateNewViewport()
     public void CreateNewViewport()
     {
     {
         var doc = ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument;
         var doc = ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument;

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

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

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

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

+ 67 - 36
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/ToolbarFactory.cs

@@ -6,60 +6,91 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
 
 internal static class ToolbarFactory
 internal static class ToolbarFactory
 {
 {
-    public static Toolbar Create<T>() where T : ToolViewModel => Create<T, EmptyToolbar>();
+    public static Toolbar Create<T>(T tool) where T : ToolViewModel => Create<T, EmptyToolbar>(tool);
 
 
-    public static TToolbar Create<T, TToolbar>() where T : ToolViewModel where TToolbar : Toolbar, new()
+    public static TToolbar Create<T, TToolbar>(T tool) where T : ToolViewModel where TToolbar : Toolbar, new()
     {
     {
-        var tool = typeof(T);
+        var toolType = typeof(T);
         var toolbar = new TToolbar();
         var toolbar = new TToolbar();
 
 
-        foreach (var property in tool.GetProperties())
+        foreach (var property in toolType.GetProperties())
         {
         {
             var attribute = property.GetCustomAttribute<Settings.SettingsAttribute>();
             var attribute = property.GetCustomAttribute<Settings.SettingsAttribute>();
+            if (attribute == null) continue;
 
 
-            if (attribute == null)
-                continue;
-            
             var name = attribute.Name ?? property.Name;
             var name = attribute.Name ?? property.Name;
+            var label = attribute.LabelKey ?? name;
 
 
             if (attribute is Settings.InheritedAttribute)
             if (attribute is Settings.InheritedAttribute)
             {
             {
-                var inherited = toolbar.GetSetting(name);
-                
-                if (inherited == null)
-                {
-                    throw new NullReferenceException($"There's no inherited setting '{name}' on inherited toolbar of type '{typeof(TToolbar).FullName}' (Tool: {typeof(T).FullName})");
-                }
+                ProcessInheritedSetting(toolType, tool, toolbar, property, attribute, name);
+            }
+            else
+            {
+                var setting = CreateSetting(property.PropertyType, name, attribute, label);
+                AddValueChangedHandlerIfRequired(toolType, tool, setting, attribute);
+                toolbar.Settings.Add(setting);
+            }
+        }
 
 
-                if (inherited.GetSettingType() != property.PropertyType)
-                {
-                    throw new InvalidCastException($"Inherited setting '{name}' does not match property type '{property.PropertyType}' (Tool: {typeof(T).FullName})");
-                }
+        return toolbar;
+    }
 
 
-                continue;
-            }
-            
-            var label = attribute.LabelKey ?? name;
+    private static void ProcessInheritedSetting(Type toolType, ToolViewModel tool, Toolbar toolbar,
+        PropertyInfo property, Settings.SettingsAttribute attribute, string name)
+    {
+        var inherited = toolbar.GetSetting(name);
+        if (inherited == null || inherited.GetSettingType() != property.PropertyType)
+        {
+            throw new InvalidOperationException(
+                $"Inherited setting '{name}' does not match property type '{property.PropertyType}' (Tool: {toolType.FullName})");
+        }
 
 
-            var setting = attribute switch
-            {
-                Settings.BoolAttribute => new BoolSetting(name, (bool)(attribute.DefaultValue ?? false), label),
-                Settings.ColorAttribute => new ColorSetting(name, ((Color)(attribute.DefaultValue ?? Colors.White)).ToColor(), label),
-                Settings.EnumAttribute => GetEnumSetting(property.PropertyType, name, attribute),
-                Settings.FloatAttribute => new FloatSetting(name, (float)(attribute.DefaultValue ?? 0f), attribute.LabelKey),
-                Settings.SizeAttribute => new SizeSetting(name, label),
-                _ => throw new NotImplementedException($"SettingsAttribute of type '{attribute.GetType().FullName}' has not been implemented")
-            };
-            
-            if (setting.GetSettingType() != property.PropertyType)
+        AddValueChangedHandlerIfRequired(toolType, tool, inherited, attribute);
+    }
+
+    private static Setting CreateSetting(Type propertyType, string name, Settings.SettingsAttribute attribute,
+        string label)
+    {
+        return attribute switch
+        {
+            Settings.BoolAttribute => new BoolSetting(name, (bool)(attribute.DefaultValue ?? false), label),
+            Settings.ColorAttribute => new ColorSetting(name,
+                ((Color)(attribute.DefaultValue ?? Colors.White)).ToColor(), label),
+            Settings.EnumAttribute => GetEnumSetting(propertyType, name, attribute),
+            Settings.FloatAttribute => new FloatSetting(name, (float)(attribute.DefaultValue ?? 0f), label),
+            Settings.SizeAttribute => new SizeSetting(name, label),
+            _ => throw new NotImplementedException(
+                $"SettingsAttribute of type '{attribute.GetType().FullName}' has not been implemented")
+        };
+    }
+
+    private static void AddValueChangedHandlerIfRequired(Type toolType, ToolViewModel tool, Setting setting,
+        Settings.SettingsAttribute attribute)
+    {
+        if (attribute.Notify != null)
+        {
+            AddValueChangedHandler(toolType, tool, setting, attribute);
+        }
+    }
+
+    private static void AddValueChangedHandler<T>(Type toolType, T tool, Setting setting,
+        Settings.SettingsAttribute attribute) where T : ToolViewModel
+    {
+        if (attribute.Notify != null)
+        {
+            var method = toolType.GetMethod(attribute.Notify,
+                BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static,
+                Array.Empty<Type>());
+
+            if (method is null)
             {
             {
-                throw new InvalidCastException($"Setting '{name}' does not match property type '{property.PropertyType}' (Tool: {typeof(T).FullName})");
+                throw new NullReferenceException(
+                    $"No method found with the name '{attribute.Notify}' that does not have any parameters");
             }
             }
 
 
-            toolbar.Settings.Add(setting);
+            setting.ValueChanged += (_, _) => method.Invoke(tool, null);
         }
         }
-
-        return toolbar;
     }
     }
 
 
     private static Setting GetEnumSetting(Type enumType, string name, Settings.SettingsAttribute attribute)
     private static Setting GetEnumSetting(Type enumType, string name, Settings.SettingsAttribute attribute)

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

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

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

@@ -19,7 +19,7 @@ internal class BrightnessToolViewModel : ToolViewModel
     public BrightnessToolViewModel()
     public BrightnessToolViewModel()
     {
     {
         ActionDisplay = defaultActionDisplay;
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<BrightnessToolViewModel, BasicToolbar>();
+        Toolbar = ToolbarFactory.Create<BrightnessToolViewModel, BasicToolbar>(this);
     }
     }
 
 
     public override LocalizedString Tooltip => new LocalizedString("BRIGHTNESS_TOOL_TOOLTIP", Shortcut);
     public override LocalizedString Tooltip => new LocalizedString("BRIGHTNESS_TOOL_TOOLTIP", Shortcut);

+ 21 - 5
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/ColorPickerToolViewModel.cs

@@ -24,24 +24,38 @@ internal class ColorPickerToolViewModel : ToolViewModel
     private bool pickFromCanvas = true;
     private bool pickFromCanvas = true;
     public bool PickFromCanvas
     public bool PickFromCanvas
     {
     {
-        get => pickFromCanvas; 
-        private set => SetProperty(ref pickFromCanvas, value);
+        get => pickFromCanvas;
+        private set
+        {
+            if (SetProperty(ref pickFromCanvas, value))
+            {
+                RaisePropertyChanged(nameof(PickOnlyFromReferenceLayer));
+            }
+        }
     }
     }
     
     
     private bool pickFromReferenceLayer = true;
     private bool pickFromReferenceLayer = true;
     public bool PickFromReferenceLayer
     public bool PickFromReferenceLayer
     {
     {
-        get => pickFromReferenceLayer; 
-        private set => SetProperty(ref pickFromReferenceLayer, value);
+        get => pickFromReferenceLayer;
+        private set
+        {
+            if (SetProperty(ref pickFromReferenceLayer, value))
+            {
+                RaisePropertyChanged(nameof(PickOnlyFromReferenceLayer));
+            }
+        }
     }
     }
 
 
+    public bool PickOnlyFromReferenceLayer => !pickFromCanvas && pickFromReferenceLayer;
+
     [Settings.Enum("SCOPE_LABEL", DocumentScope.AllLayers)]
     [Settings.Enum("SCOPE_LABEL", DocumentScope.AllLayers)]
     public DocumentScope Mode => GetValue<DocumentScope>();
     public DocumentScope Mode => GetValue<DocumentScope>();
 
 
     public ColorPickerToolViewModel()
     public ColorPickerToolViewModel()
     {
     {
         ActionDisplay = defaultActionDisplay;
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<ColorPickerToolViewModel, EmptyToolbar>();
+        Toolbar = ToolbarFactory.Create<ColorPickerToolViewModel, EmptyToolbar>(this);
     }
     }
 
 
     public override void OnLeftMouseButtonDown(VecD pos)
     public override void OnLeftMouseButtonDown(VecD pos)
@@ -70,5 +84,7 @@ internal class ColorPickerToolViewModel : ToolViewModel
             PickFromReferenceLayer = true;
             PickFromReferenceLayer = true;
             ActionDisplay = defaultActionDisplay;
             ActionDisplay = defaultActionDisplay;
         }
         }
+        
+        ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument?.ReferenceLayerViewModel.RaiseShowHighestChanged();
     }
     }
 }
 }

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

@@ -14,7 +14,7 @@ internal class EraserToolViewModel : ToolViewModel
     public EraserToolViewModel()
     public EraserToolViewModel()
     {
     {
         ActionDisplay = "ERASER_TOOL_ACTION_DISPLAY";
         ActionDisplay = "ERASER_TOOL_ACTION_DISPLAY";
-        Toolbar = ToolbarFactory.Create<EraserToolViewModel, BasicToolbar>();
+        Toolbar = ToolbarFactory.Create<EraserToolViewModel, BasicToolbar>(this);
     }
     }
 
 
     [Settings.Inherited]
     [Settings.Inherited]

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

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

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

@@ -15,7 +15,7 @@ internal class LineToolViewModel : ShapeTool
     public LineToolViewModel()
     public LineToolViewModel()
     {
     {
         ActionDisplay = defaultActionDisplay;
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<LineToolViewModel, BasicToolbar>();
+        Toolbar = ToolbarFactory.Create<LineToolViewModel, BasicToolbar>(this);
     }
     }
 
 
     public override string ToolNameLocalizationKey => "LINE_TOOL";
     public override string ToolNameLocalizationKey => "LINE_TOOL";

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

@@ -25,7 +25,7 @@ internal class MagicWandToolViewModel : ToolViewModel
     
     
     public MagicWandToolViewModel()
     public MagicWandToolViewModel()
     {
     {
-        Toolbar = ToolbarFactory.Create<MagicWandToolViewModel>();
+        Toolbar = ToolbarFactory.Create(this);
         ActionDisplay = "MAGIC_WAND_ACTION_DISPLAY";
         ActionDisplay = "MAGIC_WAND_ACTION_DISPLAY";
     }
     }
     
     

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

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

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

@@ -4,8 +4,11 @@ using ChunkyImageLib.DataHolders;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Localization;
 using PixiEditor.Localization;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Commands.Attributes.Commands;
+using PixiEditor.Models.Events;
+using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Settings;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Settings;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
+using PixiEditor.Views.UserControls;
 using PixiEditor.Views.UserControls.Overlays.BrushShapeOverlay;
 using PixiEditor.Views.UserControls.Overlays.BrushShapeOverlay;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools
 namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools
@@ -13,12 +16,17 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools
     [Command.Tool(Key = Key.B)]
     [Command.Tool(Key = Key.B)]
     internal class PenToolViewModel : ShapeTool
     internal class PenToolViewModel : ShapeTool
     {
     {
+        private int actualToolSize;
+
         public override string ToolNameLocalizationKey => "PEN_TOOL";
         public override string ToolNameLocalizationKey => "PEN_TOOL";
         public override BrushShape BrushShape => BrushShape.Circle;
         public override BrushShape BrushShape => BrushShape.Circle;
+
         public PenToolViewModel()
         public PenToolViewModel()
         {
         {
             Cursor = Cursors.Pen;
             Cursor = Cursors.Pen;
-            Toolbar = ToolbarFactory.Create<PenToolViewModel, BasicToolbar>();
+            Toolbar = ToolbarFactory.Create<PenToolViewModel, BasicToolbar>(this);
+            
+            ViewModelMain.Current.ToolsSubViewModel.SelectedToolChanged += SelectedToolChanged;
         }
         }
 
 
         public override LocalizedString Tooltip => new LocalizedString("PEN_TOOL_TOOLTIP", Shortcut);
         public override LocalizedString Tooltip => new LocalizedString("PEN_TOOL_TOOLTIP", Shortcut);
@@ -26,7 +34,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools
         [Settings.Inherited]
         [Settings.Inherited]
         public int ToolSize => GetValue<int>();
         public int ToolSize => GetValue<int>();
 
 
-        [Settings.Bool("PIXEL_PERFECT_SETTING")]
+        [Settings.Bool("PIXEL_PERFECT_SETTING", Notify = nameof(PixelPerfectChanged))]
         public bool PixelPerfectEnabled => GetValue<bool>();
         public bool PixelPerfectEnabled => GetValue<bool>();
 
 
         public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
         public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
@@ -38,5 +46,58 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools
         {
         {
             ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UsePenTool();
             ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UsePenTool();
         }
         }
+
+        private void SelectedToolChanged(object sender, SelectedToolEventArgs e)
+        {
+            if (e.NewTool == this && PixelPerfectEnabled)
+            {
+                var toolbar = (BasicToolbar)Toolbar;
+                var setting = (SizeSetting)toolbar.Settings.First(x => x.Name == "ToolSize");
+                setting.Value = 1;
+            }
+            
+            if (!IPreferences.Current.GetPreference<bool>("EnableSharedToolbar"))
+            {
+                return;
+            }
+
+            if (e.OldTool is not { Toolbar: BasicToolbar oldToolbar })
+            {
+                return;
+            }
+            
+            var oldSetting = (SizeSetting)oldToolbar.Settings[0];
+            actualToolSize = oldSetting.Value;
+        }
+
+        public override void OnDeselecting()
+        {
+            if (!PixelPerfectEnabled)
+            {
+                return;
+            }
+
+            var toolbar = (BasicToolbar)Toolbar;
+            var setting = (SizeSetting)toolbar.Settings[0];
+            setting.Value = actualToolSize;
+        }
+
+        private void PixelPerfectChanged()
+        {
+            var toolbar = (BasicToolbar)Toolbar;
+            var setting = (SizeSetting)toolbar.Settings[0];
+
+            setting.SettingControl.IsEnabled = !PixelPerfectEnabled;
+
+            if (PixelPerfectEnabled)
+            {
+                actualToolSize = ToolSize;
+                setting.Value = 1;
+            }
+            else
+            {
+                setting.Value = actualToolSize;
+            }
+        }
     }
     }
 }
 }

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

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

+ 3 - 2
src/PixiEditor/ViewModels/SubViewModels/UserPreferences/Settings/GeneralSettings.cs

@@ -7,9 +7,10 @@ namespace PixiEditor.ViewModels.SubViewModels.UserPreferences.Settings;
 internal class GeneralSettings : SettingsGroup
 internal class GeneralSettings : SettingsGroup
 {
 {
     private bool imagePreviewInTaskbar = GetPreference(nameof(ImagePreviewInTaskbar), false);
     private bool imagePreviewInTaskbar = GetPreference(nameof(ImagePreviewInTaskbar), false);
-    private LanguageData selectedLanguage = ILocalizationProvider.Current.CurrentLanguage.LanguageData;
+    private LanguageData selectedLanguage = ILocalizationProvider.Current.SelectedLanguage;
     private List<LanguageData> availableLanguages = ILocalizationProvider.Current.LocalizationData.Languages
     private List<LanguageData> availableLanguages = ILocalizationProvider.Current.LocalizationData.Languages
-        .OrderByDescending(x => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == x.Code || CultureInfo.InstalledUICulture.TwoLetterISOLanguageName == x.Code)
+        .OrderByDescending(x => x == ILocalizationProvider.Current.FollowSystem)
+        .ThenByDescending(x => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == x.Code || CultureInfo.InstalledUICulture.TwoLetterISOLanguageName == x.Code)
         .ThenBy(x => x.Name).ToList();
         .ThenBy(x => x.Name).ToList();
 
 
     public bool ImagePreviewInTaskbar
     public bool ImagePreviewInTaskbar

+ 11 - 8
src/PixiEditor/Views/Dialogs/ConfirmationPopup.xaml

@@ -14,7 +14,8 @@
         Name="popup" WindowStartupLocation="CenterScreen" 
         Name="popup" WindowStartupLocation="CenterScreen" 
         Height="180" Width="400" MinHeight="180" MinWidth="400"
         Height="180" Width="400" MinHeight="180" MinWidth="400"
         WindowStyle="None"
         WindowStyle="None"
-        FlowDirection="{helpers:Localization FlowDirection}">
+        FlowDirection="{helpers:Localization FlowDirection}"
+        d:DataContext="{d:DesignInstance dial:ConfirmationPopup}">
 
 
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
         <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
         <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
@@ -29,27 +30,29 @@
         <dial:DialogTitleBar DockPanel.Dock="Top"
         <dial:DialogTitleBar DockPanel.Dock="Top"
             TitleText="{Binding ElementName=popup, Path=Title}" CloseCommand="{Binding DataContext.CancelCommand, ElementName=popup}" />
             TitleText="{Binding ElementName=popup, Path=Title}" CloseCommand="{Binding DataContext.CancelCommand, ElementName=popup}" />
 
 
-        <StackPanel DockPanel.Dock="Bottom" Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center"
+        <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Center"
                     Margin="0,0,10,15">
                     Margin="0,0,10,15">
-            <Button Margin="10,0,10,0" MinWidth="70" IsDefault="True" Padding="5 0"
+            <Button Margin="10,0,10,0" IsDefault="True" Padding="5 0"
                     Command="{Binding Path=DataContext.SetResultAndCloseCommand, ElementName=popup}"
                     Command="{Binding Path=DataContext.SetResultAndCloseCommand, ElementName=popup}"
-                    Style="{StaticResource DarkRoundButton}" Content="{Binding Path=DataContext.FirstOptionText}">
+                    views:Translator.LocalizedString="{Binding FirstOptionText}"
+                    Style="{StaticResource DarkRoundButton}">
                 <Button.CommandParameter>
                 <Button.CommandParameter>
                     <system:Boolean>True</system:Boolean>
                     <system:Boolean>True</system:Boolean>
                 </Button.CommandParameter>
                 </Button.CommandParameter>
             </Button>
             </Button>
-            <Button MinWidth="70" Padding="5 0"
+            <Button Padding="5 0"
                     Command="{Binding Path=DataContext.SetResultAndCloseCommand, ElementName=popup}"
                     Command="{Binding Path=DataContext.SetResultAndCloseCommand, ElementName=popup}"
-                    Style="{StaticResource DarkRoundButton}" Content="{Binding Path=DataContext.SecondOptionText}">
+                    views:Translator.LocalizedString="{Binding SecondOptionText}"
+                    Style="{StaticResource DarkRoundButton}">
                 <Button.CommandParameter>
                 <Button.CommandParameter>
                     <system:Boolean>False</system:Boolean>
                     <system:Boolean>False</system:Boolean>
                 </Button.CommandParameter>
                 </Button.CommandParameter>
             </Button>
             </Button>
-            <Button Margin="10,0,10,0" Width="70" Style="{StaticResource DarkRoundButton}" views:Translator.Key="CANCEL"
+            <Button Margin="10,0,10,0" Style="{StaticResource DarkRoundButton}" views:Translator.Key="CANCEL"
                     Command="{Binding DataContext.CancelCommand, ElementName=popup}" />
                     Command="{Binding DataContext.CancelCommand, ElementName=popup}" />
         </StackPanel>
         </StackPanel>
 
 
-        <TextBlock Grid.Row="1" 
+        <TextBlock
                    Text="{Binding Body, ElementName=popup}" 
                    Text="{Binding Body, ElementName=popup}" 
                    HorizontalAlignment="Center" Margin="20,0" 
                    HorizontalAlignment="Center" Margin="20,0" 
                    TextWrapping="WrapWithOverflow" 
                    TextWrapping="WrapWithOverflow" 

+ 6 - 6
src/PixiEditor/Views/Dialogs/ConfirmationPopup.xaml.cs

@@ -17,20 +17,20 @@ internal partial class ConfirmationPopup : Window
         DependencyProperty.Register(nameof(Body), typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(""));
         DependencyProperty.Register(nameof(Body), typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(""));
 
 
     public static readonly DependencyProperty FirstOptionTextProperty = DependencyProperty.Register(
     public static readonly DependencyProperty FirstOptionTextProperty = DependencyProperty.Register(
-        nameof(FirstOptionText), typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(new LocalizedString("YES").Value));
+        nameof(FirstOptionText), typeof(LocalizedString), typeof(ConfirmationPopup), new PropertyMetadata(new LocalizedString("YES")));
 
 
-    public string FirstOptionText
+    public LocalizedString FirstOptionText
     {
     {
-        get { return (string)GetValue(FirstOptionTextProperty); }
+        get { return (LocalizedString)GetValue(FirstOptionTextProperty); }
         set { SetValue(FirstOptionTextProperty, value); }
         set { SetValue(FirstOptionTextProperty, value); }
     }
     }
 
 
     public static readonly DependencyProperty SecondOptionTextProperty = DependencyProperty.Register(
     public static readonly DependencyProperty SecondOptionTextProperty = DependencyProperty.Register(
-        nameof(SecondOptionText), typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(new LocalizedString("NO").Value));
+        nameof(SecondOptionText), typeof(LocalizedString), typeof(ConfirmationPopup), new PropertyMetadata(new LocalizedString("NO")));
 
 
-    public string SecondOptionText
+    public LocalizedString SecondOptionText
     {
     {
-        get { return (string)GetValue(SecondOptionTextProperty); }
+        get { return (LocalizedString)GetValue(SecondOptionTextProperty); }
         set { SetValue(SecondOptionTextProperty, value); }
         set { SetValue(SecondOptionTextProperty, value); }
     }
     }
     
     

+ 25 - 14
src/PixiEditor/Views/Dialogs/HelloTherePopup.xaml

@@ -188,24 +188,35 @@
                                                         </Grid.Style>
                                                         </Grid.Style>
                                                         <TextBlock x:Name="extension" VerticalAlignment="Top" Text="{Binding FileExtension}" FontSize="15" TextAlignment="Center"/>
                                                         <TextBlock x:Name="extension" VerticalAlignment="Top" Text="{Binding FileExtension}" FontSize="15" TextAlignment="Center"/>
                                                         <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center">
                                                         <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center">
-                                                            <Button Margin="0,0,0,5" Height="25" Width="25"
-                                                                    Command="{Binding DataContext.OpenInExplorerCommand, RelativeSource={RelativeSource AncestorType=uc:AlignableWrapPanel}}"
+                                                            <StackPanel.Resources>
+                                                                <Style TargetType="Button" BasedOn="{StaticResource BaseDarkButton}">
+                                                                    <Setter Property="Margin" Value="0,0,0,5"/>
+                                                                    <Setter Property="Width" Value="25"/>
+                                                                    <Setter Property="Height" Value="25"/>
+                                                                    <Setter Property="MinWidth" Value="25"/>
+                                                                    <Setter Property="MinHeight" Value="25"/>
+                                                                    
+                                                                    <Style.Triggers>
+                                                                        <Trigger Property="IsMouseOver" Value="False">
+                                                                            <Setter Property="Background" Value="Transparent"/>
+                                                                        </Trigger>
+                                                                        <Trigger Property="IsMouseOver" Value="True">
+                                                                            <Setter Property="Background" Value="#70FFFFFF"/>
+                                                                        </Trigger>
+                                                                    </Style.Triggers>
+                                                                </Style>
+                                                            </StackPanel.Resources>
+                                                            <Button Command="{Binding DataContext.OpenInExplorerCommand, RelativeSource={RelativeSource AncestorType=uc:AlignableWrapPanel}}"
                                                                     CommandParameter="{Binding FilePath}"
                                                                     CommandParameter="{Binding FilePath}"
                                                                     ToolTip="Open in File Explorer">
                                                                     ToolTip="Open in File Explorer">
                                                                 <TextBlock Text="&#xEC50;" FontFamily="Segoe MDL2 Assets"
                                                                 <TextBlock Text="&#xEC50;" FontFamily="Segoe MDL2 Assets"
                                                                            TextAlignment="Center" FontSize="18"/>
                                                                            TextAlignment="Center" FontSize="18"/>
-                                                                <Button.Style>
-                                                                    <Style TargetType="Button" BasedOn="{StaticResource BaseDarkButton}">
-                                                                        <Style.Triggers>
-                                                                            <Trigger Property="IsMouseOver" Value="False">
-                                                                                <Setter Property="Background" Value="Transparent"/>
-                                                                            </Trigger>
-                                                                            <Trigger Property="IsMouseOver" Value="True">
-                                                                                <Setter Property="Background" Value="#70FFFFFF"/>
-                                                                            </Trigger>
-                                                                        </Style.Triggers>
-                                                                    </Style>
-                                                                </Button.Style>
+                                                            </Button>
+                                                            <Button Command="{cmds:Command Name=PixiEditor.File.RemoveRecent, UseProvided=True}"
+                                                                    CommandParameter="{Binding FilePath}"
+                                                                    ToolTip="Remove from list">
+                                                                <TextBlock Text="" FontFamily="{StaticResource Feather}"
+                                                                           TextAlignment="Center" FontSize="20"/>
                                                             </Button>
                                                             </Button>
                                                         </StackPanel>
                                                         </StackPanel>
                                                     </Grid>
                                                     </Grid>

+ 1 - 1
src/PixiEditor/Views/Dialogs/ImportFilePopup.xaml.cs

@@ -19,7 +19,7 @@ internal partial class ImportFilePopup : Window
     public int ImportHeight
     public int ImportHeight
     {
     {
         get => dc.ImportHeight;
         get => dc.ImportHeight;
-        set => dc.ImportWidth = value;
+        set => dc.ImportHeight = value;
     }
     }
 
 
 
 

+ 36 - 7
src/PixiEditor/Views/MainWindow.xaml

@@ -180,8 +180,19 @@
                             <MenuItem.ItemTemplate>
                             <MenuItem.ItemTemplate>
                                 <DataTemplate
                                 <DataTemplate
                                     DataType="{x:Type dataHolders:RecentlyOpenedDocument}">
                                     DataType="{x:Type dataHolders:RecentlyOpenedDocument}">
-                                    <TextBlock
-                                        Text="{Binding FilePath}" />
+                                    <Grid>
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition/>
+                                            <ColumnDefinition Width="Auto"/>
+                                        </Grid.ColumnDefinitions>
+                                        
+                                        <TextBlock Text="{Binding FilePath}"/>
+                                        <TextBlock Grid.Column="1" Margin="20,0,0,0" VerticalAlignment="Center">
+                                            <Hyperlink Foreground="#FFF" FontFamily="{StaticResource Feather}"
+                                                       Command="{cmds:Command Name=PixiEditor.File.RemoveRecent, UseProvided=True}"
+                                                       CommandParameter="{Binding FilePath}"></Hyperlink>
+                                        </TextBlock>
+                                    </Grid>
                                 </DataTemplate>
                                 </DataTemplate>
                             </MenuItem.ItemTemplate>
                             </MenuItem.ItemTemplate>
                         </MenuItem>
                         </MenuItem>
@@ -198,7 +209,11 @@
                         <Separator />
                         <Separator />
                         <MenuItem
                         <MenuItem
                             views:Translator.Key="EXIT"
                             views:Translator.Key="EXIT"
-                            Command="{x:Static SystemCommands.CloseWindowCommand}" />
+                            Command="{x:Static SystemCommands.CloseWindowCommand}">
+                            <MenuItem.Icon>
+                                <TextBlock Text="&#xE106;" FontFamily="{DynamicResource NativeIconFont}" FontSize="20"/>
+                            </MenuItem.Icon>
+                        </MenuItem>
                     </MenuItem>
                     </MenuItem>
                     <MenuItem
                     <MenuItem
                         Focusable="False"
                         Focusable="False"
@@ -280,13 +295,22 @@
                             IsCheckable="True"
                             IsCheckable="True"
                             IsEnabled="{Binding DocumentManagerSubViewModel.ActiveDocument, Source={vm:MainVM}, Converter={converters:NotNullToBoolConverter}}"
                             IsEnabled="{Binding DocumentManagerSubViewModel.ActiveDocument, Source={vm:MainVM}, Converter={converters:NotNullToBoolConverter}}"
                             IsChecked="{Binding DocumentManagerSubViewModel.ActiveDocument.HorizontalSymmetryAxisEnabledBindable}"
                             IsChecked="{Binding DocumentManagerSubViewModel.ActiveDocument.HorizontalSymmetryAxisEnabledBindable}"
-                            views:Translator.Key="HORIZONTAL_LINE_SYMMETRY"
-                            />
+                            views:Translator.Key="HORIZONTAL_LINE_SYMMETRY">
+                            <MenuItem.Icon>
+                                <Image Source="../Images/SymmetryHorizontal.png"
+                                       Width="{x:Static cmds:Menu.IconDimensions}" Height="{x:Static cmds:Menu.IconDimensions}"/>
+                            </MenuItem.Icon>
+                        </MenuItem>
                         <MenuItem
                         <MenuItem
                             IsCheckable="True"
                             IsCheckable="True"
                             IsEnabled="{Binding DocumentManagerSubViewModel.ActiveDocument, Source={vm:MainVM}, Converter={converters:NotNullToBoolConverter}}"
                             IsEnabled="{Binding DocumentManagerSubViewModel.ActiveDocument, Source={vm:MainVM}, Converter={converters:NotNullToBoolConverter}}"
                             IsChecked="{Binding DocumentManagerSubViewModel.ActiveDocument.VerticalSymmetryAxisEnabledBindable}"
                             IsChecked="{Binding DocumentManagerSubViewModel.ActiveDocument.VerticalSymmetryAxisEnabledBindable}"
-                            views:Translator.Key="VERTICAL_LINE_SYMMETRY"/>
+                            views:Translator.Key="VERTICAL_LINE_SYMMETRY">
+                            <MenuItem.Icon>
+                                <Image Source="../Images/SymmetryVertical.png"
+                                       Width="{x:Static cmds:Menu.IconDimensions}" Height="{x:Static cmds:Menu.IconDimensions}"/>
+                            </MenuItem.Icon>
+                        </MenuItem>
                         <Separator/>
                         <Separator/>
                         <MenuItem views:Translator.Key="ROTATION">
                         <MenuItem views:Translator.Key="ROTATION">
                             <MenuItem views:Translator.Key="ROT_IMG_90_D" cmds:Menu.Command="PixiEditor.Document.Rotate90Deg"/>
                             <MenuItem views:Translator.Key="ROT_IMG_90_D" cmds:Menu.Command="PixiEditor.Document.Rotate90Deg"/>
@@ -327,7 +351,12 @@
                             views:Translator.Key="TOGGLE_GRIDLINES"
                             views:Translator.Key="TOGGLE_GRIDLINES"
                             IsChecked="{Binding ViewportSubViewModel.GridLinesEnabled, Mode=TwoWay}"
                             IsChecked="{Binding ViewportSubViewModel.GridLinesEnabled, Mode=TwoWay}"
                             IsCheckable="True"
                             IsCheckable="True"
-                            InputGestureText="{cmds:ShortcutBinding PixiEditor.View.ToggleGrid}" />
+                            InputGestureText="{cmds:ShortcutBinding PixiEditor.View.ToggleGrid}">
+                            <MenuItem.Icon>
+                                <Image Source="../Images/Commands/PixiEditor/View/ToggleGrid.png"
+                                       Width="{x:Static cmds:Menu.IconDimensions}" Height="{x:Static cmds:Menu.IconDimensions}"/>
+                            </MenuItem.Icon>
+                        </MenuItem>
                     </MenuItem>
                     </MenuItem>
                     <MenuItem
                     <MenuItem
                         Focusable="False"
                         Focusable="False"

+ 5 - 2
src/PixiEditor/Views/MainWindow.xaml.cs

@@ -202,10 +202,12 @@ internal partial class MainWindow : Window
                 return;
                 return;
             }
             }
 
 
+            e.Effects = DragDropEffects.Copy;
             DataContext.ColorsSubViewModel.PrimaryColor = color.Value;
             DataContext.ColorsSubViewModel.PrimaryColor = color.Value;
             return;
             return;
         }
         }
 
 
+        Activate();
         string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
         string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
         
         
         if (files is { Length: > 0 } && Importer.IsSupportedFile(files[0]))
         if (files is { Length: > 0 } && Importer.IsSupportedFile(files[0]))
@@ -220,7 +222,8 @@ internal partial class MainWindow : Window
         {
         {
             if (ColorHelper.ParseAnyFormat(e.Data, out _))
             if (ColorHelper.ParseAnyFormat(e.Data, out _))
             {
             {
-                DataContext.ActionDisplays[nameof(MainWindow_Drop)] = "Paste as primary color";
+                DataContext.ActionDisplays[nameof(MainWindow_Drop)] = "PASTE_AS_PRIMARY_COLOR";
+                e.Effects = DragDropEffects.Copy;
                 return;
                 return;
             }
             }
             
             
@@ -229,7 +232,7 @@ internal partial class MainWindow : Window
             return;
             return;
         }
         }
 
 
-        DataContext.ActionDisplays[nameof(MainWindow_Drop)] = "Import as new file";
+        DataContext.ActionDisplays[nameof(MainWindow_Drop)] = "IMPORT_AS_NEW_FILE";
     }
     }
 
 
     private void MainWindow_DragLeave(object sender, DragEventArgs e)
     private void MainWindow_DragLeave(object sender, DragEventArgs e)

+ 2 - 2
src/PixiEditor/Views/UserControls/CommandSearch/CommandSearchControl.xaml

@@ -134,8 +134,8 @@
             </Grid>
             </Grid>
         </Border>
         </Border>
         <Border Grid.Row="2" BorderThickness="1" BorderBrush="{StaticResource BrighterAccentColor}"
         <Border Grid.Row="2" BorderThickness="1" BorderBrush="{StaticResource BrighterAccentColor}"
-                CornerRadius="0,0,5,5" Background="{StaticResource AccentColor}">
-            <TextBlock Margin="5" FontSize="16" Text="{Binding SelectedResult.Description, ElementName=uc, FallbackValue=''}" />
+                CornerRadius="0,0,5,5" Background="{StaticResource AccentColor}" Padding="3">
+            <ContentPresenter Content="{Binding SelectedResult.Description, Mode=OneWay, ElementName=uc}"/>
         </Border>
         </Border>
     </Grid>
     </Grid>
 </UserControl>
 </UserControl>

+ 1 - 0
src/PixiEditor/Views/UserControls/CommandSearch/CommandSearchControl.xaml.cs

@@ -60,6 +60,7 @@ internal partial class CommandSearchControl : UserControl, INotifyPropertyChange
             if (value is not null)
             if (value is not null)
                 value.IsSelected = true;
                 value.IsSelected = true;
             selectedResult = value;
             selectedResult = value;
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedResult)));
         }
         }
     }
     }
 
 

+ 8 - 8
src/PixiEditor/Views/UserControls/DiscordRPPreview.xaml

@@ -39,30 +39,30 @@
                 <Border Grid.Row="1" Background="#18191C" Margin="10,10" CornerRadius="5" Padding="10">
                 <Border Grid.Row="1" Background="#18191C" Margin="10,10" CornerRadius="5" Padding="10">
                     <StackPanel>
                     <StackPanel>
                         <StackPanel Orientation="Horizontal" Margin="0,0">
                         <StackPanel Orientation="Horizontal" Margin="0,0">
-                            <TextBlock Foreground="White" FontSize="22" FontWeight="Black">PixiBot</TextBlock>
-                            <TextBlock Foreground="#b9bbbe" FontSize="22" FontWeight="Black">#8523</TextBlock>
+                            <TextBlock Foreground="White" FontWeight="DemiBold" FontSize="22">PixiBot</TextBlock>
+                            <TextBlock Foreground="White" FontWeight="DemiBold" FontSize="22">#8523</TextBlock>
                             <Border CornerRadius="3" BorderThickness="1" Background="#5865f2" Margin="5,0,0,0" VerticalAlignment="Center">
                             <Border CornerRadius="3" BorderThickness="1" Background="#5865f2" Margin="5,0,0,0" VerticalAlignment="Center">
                                 <TextBlock Foreground="White" FontSize="10" Margin="4,2,4,2" FontWeight="Medium">BOT</TextBlock>
                                 <TextBlock Foreground="White" FontSize="10" Margin="4,2,4,2" FontWeight="Medium">BOT</TextBlock>
                             </Border>
                             </Border>
                         </StackPanel>
                         </StackPanel>
                         <Grid Margin="0,5">
                         <Grid Margin="0,5">
                             <StackPanel Orientation="Vertical">
                             <StackPanel Orientation="Vertical">
-                                <Grid Margin="0,15" Background="#262729" Height="1"/>
-                                <TextBlock FontWeight="Black" FontSize="12" Foreground="White" Margin="0,0,0,10" Text="ABOUT ME"/>
+                                <Grid Margin="0,15" Background="#262729" Height="2"/>
+                                <TextBlock FontWeight="Bold" FontSize="12" Foreground="White" Margin="0,0,0,10" Text="ABOUT ME"/>
                                 <TextBlock Foreground="White" Text="Use me to display your .pixi files in Discord. &#10;Start by sending a file in any appropriate channel" />
                                 <TextBlock Foreground="White" Text="Use me to display your .pixi files in Discord. &#10;Start by sending a file in any appropriate channel" />
                             </StackPanel>
                             </StackPanel>
                         </Grid>
                         </Grid>
                         <Grid Margin="0,5">
                         <Grid Margin="0,5">
                             <StackPanel Orientation="Vertical">
                             <StackPanel Orientation="Vertical">
-                                <TextBlock FontWeight="Black" FontSize="12" Foreground="White" Margin="0,0,0,10" Text="DISCORD MEMBER SINCE"/>
-                                <TextBlock Foreground="White" Text="Mar 24, 2023"></TextBlock>
+                                <TextBlock FontWeight="Bold" Foreground="White" Margin="0,0,0,10" Text="DISCORD MEMBER SINCE"/>
+                                <TextBlock Foreground="White" Text="Oct 29, 2020"></TextBlock>
                             </StackPanel>
                             </StackPanel>
                         </Grid>
                         </Grid>
                         <Grid Visibility="{Binding ElementName=uc, Path=IsPlaying, Converter={BoolToVisibilityConverter}}" Margin="0,5, 0, 0">
                         <Grid Visibility="{Binding ElementName=uc, Path=IsPlaying, Converter={BoolToVisibilityConverter}}" Margin="0,5, 0, 0">
                             <StackPanel Orientation="Vertical">
                             <StackPanel Orientation="Vertical">
-                                <TextBlock FontWeight="Black" FontSize="12" Foreground="White" Margin="0,0,0,10" Text="PLAYING A GAME"/>
+                                <TextBlock FontWeight="Bold" FontSize="12" Foreground="White" Margin="0,0,0,10" Text="PLAYING A GAME"/>
                                 <StackPanel Orientation="Horizontal">
                                 <StackPanel Orientation="Horizontal">
-                                    <Image Source="../../Images/PixiEditorLogo.png" Height="70"/>
+                                    <Image Source="../../Images/PixiEditorLogo.png" Height="60"/>
                                     <StackPanel Margin="15,0,0,0" VerticalAlignment="Center">
                                     <StackPanel Margin="15,0,0,0" VerticalAlignment="Center">
                                         <TextBlock Foreground="White" FontSize="12" FontWeight="SemiBold">PixiEditor</TextBlock>
                                         <TextBlock Foreground="White" FontSize="12" FontWeight="SemiBold">PixiEditor</TextBlock>
                                         <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=Detail}" Visibility="{Binding ElementName=uc, 
                                         <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=Detail}" Visibility="{Binding ElementName=uc, 

+ 1 - 1
src/PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs

@@ -154,7 +154,7 @@ internal partial class LayersManager : UserControl
                 return;
                 return;
             }
             }
 
 
-            ViewModelMain.Current.ActionDisplays[nameof(LayersManager)] = "Import as new layer";
+            ViewModelMain.Current.ActionDisplays[nameof(LayersManager)] = "IMPORT_AS_NEW_LAYER";
             e.Effects = DragDropEffects.Copy;
             e.Effects = DragDropEffects.Copy;
         }
         }
         else
         else

+ 1 - 1
src/PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml.cs

@@ -37,7 +37,7 @@ internal partial class ReferenceLayer : UserControl
             return;
             return;
         }
         }
 
 
-        ViewModelMain.Current.ActionDisplays[nameof(ReferenceLayer_Drop)] = "Import as reference layer";
+        ViewModelMain.Current.ActionDisplays[nameof(ReferenceLayer_Drop)] = "IMPORT_AS_REFERENCE_LAYER";
         e.Handled = true;
         e.Handled = true;
     }
     }
 
 

+ 5 - 4
src/PixiEditor/Views/UserControls/Overlays/SymmetryOverlay/SymmetryOverlay.cs

@@ -7,6 +7,7 @@ using ChunkyImageLib.DataHolders;
 using PixiEditor;
 using PixiEditor;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Localization;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Views;
 using PixiEditor.Views;
 using PixiEditor.Views.UserControls;
 using PixiEditor.Views.UserControls;
@@ -204,10 +205,10 @@ internal class SymmetryOverlay : Control
         drawingContext.DrawLine(rulerPen, new Point((RulerOffset - RulerWidth) * PenThickness + xOffset, start), new Point((RulerOffset + RulerWidth) * PenThickness + xOffset, start));
         drawingContext.DrawLine(rulerPen, new Point((RulerOffset - RulerWidth) * PenThickness + xOffset, start), new Point((RulerOffset + RulerWidth) * PenThickness + xOffset, start));
         drawingContext.DrawLine(rulerPen, new Point((RulerOffset - RulerWidth) * PenThickness + xOffset, horizontalAxisY), new Point((RulerOffset + RulerWidth) * PenThickness + xOffset, horizontalAxisY));
         drawingContext.DrawLine(rulerPen, new Point((RulerOffset - RulerWidth) * PenThickness + xOffset, horizontalAxisY), new Point((RulerOffset + RulerWidth) * PenThickness + xOffset, horizontalAxisY));
 
 
-        string text = upper ? $"{start - horizontalAxisY}px ({(start - horizontalAxisY) / RenderSize.Height * 100:F1}%)" : $"{horizontalAxisY}px ({horizontalAxisY / RenderSize.Height * 100:F1}%)";
+        string text = upper ? $"{start - horizontalAxisY}{new LocalizedString("PIXEL_UNIT")} ({(start - horizontalAxisY) / RenderSize.Height * 100:F1}%)‎" : $"{horizontalAxisY}{new LocalizedString("PIXEL_UNIT")} ({horizontalAxisY / RenderSize.Height * 100:F1}%)";
 
 
         var formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
         var formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
-            FlowDirection.LeftToRight, new Typeface("Segeo UI"), 14.0 / ZoomboxScale, Brushes.White,
+            ILocalizationProvider.Current.CurrentLanguage.FlowDirection, new Typeface("Segeo UI"), 14.0 / ZoomboxScale, Brushes.White,
             VisualTreeHelper.GetDpi(this).PixelsPerDip);
             VisualTreeHelper.GetDpi(this).PixelsPerDip);
 
 
         if (ActualHeight < formattedText.Height * 2.5 || horizontalAxisY == (int)RenderSize.Height && upper || horizontalAxisY == 0 && !upper)
         if (ActualHeight < formattedText.Height * 2.5 || horizontalAxisY == (int)RenderSize.Height && upper || horizontalAxisY == 0 && !upper)
@@ -237,10 +238,10 @@ internal class SymmetryOverlay : Control
         drawingContext.DrawLine(rulerPen, new Point(start, (RulerOffset - RulerWidth) * PenThickness + yOffset), new Point(start, (RulerOffset + RulerWidth) * PenThickness + yOffset));
         drawingContext.DrawLine(rulerPen, new Point(start, (RulerOffset - RulerWidth) * PenThickness + yOffset), new Point(start, (RulerOffset + RulerWidth) * PenThickness + yOffset));
         drawingContext.DrawLine(rulerPen, new Point(verticalAxisX, (RulerOffset - RulerWidth) * PenThickness + yOffset), new Point(verticalAxisX, (RulerOffset + RulerWidth) * PenThickness + yOffset));
         drawingContext.DrawLine(rulerPen, new Point(verticalAxisX, (RulerOffset - RulerWidth) * PenThickness + yOffset), new Point(verticalAxisX, (RulerOffset + RulerWidth) * PenThickness + yOffset));
 
 
-        string text = right ? $"{start - verticalAxisX}px ({(start - verticalAxisX) / RenderSize.Width * 100:F1}%)" : $"{verticalAxisX}px ({verticalAxisX / RenderSize.Width * 100:F1}%)";
+        string text = right ? $"{start - verticalAxisX}{new LocalizedString("PIXEL_UNIT")} ({(start - verticalAxisX) / RenderSize.Width * 100:F1}%)‎" : $"{verticalAxisX}{new LocalizedString("PIXEL_UNIT")} ({verticalAxisX / RenderSize.Width * 100:F1}%)‎";
 
 
         var formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
         var formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
-            FlowDirection.LeftToRight, new Typeface("Segeo UI"), 14.0 / ZoomboxScale, Brushes.White,
+            ILocalizationProvider.Current.CurrentLanguage.FlowDirection, new Typeface("Segeo UI"), 14.0 / ZoomboxScale, Brushes.White,
             VisualTreeHelper.GetDpi(this).PixelsPerDip);
             VisualTreeHelper.GetDpi(this).PixelsPerDip);
 
 
         if (ActualWidth < formattedText.Width * 2.5 || verticalAxisX == (int)RenderSize.Width && right || verticalAxisX == 0 && !right)
         if (ActualWidth < formattedText.Width * 2.5 || verticalAxisX == (int)RenderSize.Width && right || verticalAxisX == 0 && !right)

+ 15 - 1
src/PixiEditor/Views/UserControls/Palettes/PaletteViewer.xaml.cs

@@ -158,7 +158,13 @@ internal partial class PaletteViewer : UserControl
         if (IsSupportedFilePresent(e, out _))
         if (IsSupportedFilePresent(e, out _))
         {
         {
             dragDropGrid.Visibility = Visibility.Visible;
             dragDropGrid.Visibility = Visibility.Visible;
-            ViewModelMain.Current.ActionDisplays[nameof(PaletteViewer)] = "Import palette file";
+            ViewModelMain.Current.ActionDisplays[nameof(PaletteViewer)] = "IMPORT_PALETTE_FILE";
+        }
+        else if (ColorHelper.ParseAnyFormatList(e.Data, out var list))
+        {
+            e.Effects = DragDropEffects.Copy;
+            ViewModelMain.Current.ActionDisplays[nameof(PaletteViewer)] = list.Count > 1 ? "IMPORT_MULTIPLE_PALETTE_COLORS" : "IMPORT_SINGLE_PALETTE_COLOR";
+            e.Handled = true;
         }
         }
     }
     }
 
 
@@ -174,6 +180,14 @@ internal partial class PaletteViewer : UserControl
         
         
         if (!IsSupportedFilePresent(e, out string filePath))
         if (!IsSupportedFilePresent(e, out string filePath))
         {
         {
+            if (!ColorHelper.ParseAnyFormatList(e.Data, out var colors))
+            {
+                return;
+            }
+            
+            e.Effects = DragDropEffects.Copy;
+            Colors.AddRange(colors.Where(x => !Colors.Contains(x)).ToList());
+            e.Handled = true;
             return;
             return;
         }
         }
 
 

+ 1 - 1
src/PixiEditor/Views/UserControls/PreviewWindow.xaml.cs

@@ -73,7 +73,7 @@ internal partial class PreviewWindow : UserControl
     {
     {
         if (ViewModelMain.Current != null)
         if (ViewModelMain.Current != null)
         {
         {
-            ViewModelMain.Current.ActionDisplays[nameof(PreviewWindow)] = "Right-click to pick color, Shift-right-click to copy color to clipboard";
+            ViewModelMain.Current.ActionDisplays[nameof(PreviewWindow)] = "NAVIGATOR_PICK_ACTION_DISPLAY";
         }
         }
     }
     }
 
 

+ 84 - 6
src/PixiEditor/Views/UserControls/Viewport.xaml

@@ -20,6 +20,7 @@
     xmlns:cmds="clr-namespace:PixiEditor.Models.Commands.XAML"
     xmlns:cmds="clr-namespace:PixiEditor.Models.Commands.XAML"
     xmlns:tools ="clr-namespace:PixiEditor.ViewModels.SubViewModels.Tools.Tools"
     xmlns:tools ="clr-namespace:PixiEditor.ViewModels.SubViewModels.Tools.Tools"
     xmlns:views="clr-namespace:PixiEditor.Views"
     xmlns:views="clr-namespace:PixiEditor.Views"
+    xmlns:subviews="clr-namespace:PixiEditor.ViewModels.SubViewModels.Document"
     mc:Ignorable="d"
     mc:Ignorable="d"
     x:Name="vpUc"
     x:Name="vpUc"
     d:DesignHeight="450"
     d:DesignHeight="450"
@@ -149,10 +150,8 @@
                 </Border.Background>
                 </Border.Background>
                 <Grid>
                 <Grid>
                     <Canvas
                     <Canvas
-                        Visibility="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickFromReferenceLayer, Converter={converters:BoolToVisibilityConverter}}"
                         ZIndex="{Binding Document.ReferenceLayerViewModel.ShowHighest, Converter={converters:BoolToIntConverter}}"
                         ZIndex="{Binding Document.ReferenceLayerViewModel.ShowHighest, Converter={converters:BoolToIntConverter}}"
-                        IsHitTestVisible="{Binding Document.ReferenceLayerViewModel.IsTransforming}"
-                        Opacity="{Binding Document.ReferenceLayerViewModel.ShowHighest, Converter={converters:BoolToValueConverter FalseValue=1.0, TrueValue=0.6}}">
+                        IsHitTestVisible="{Binding Document.ReferenceLayerViewModel.IsTransforming}">
                         <Image
                         <Image
                             Focusable="False"
                             Focusable="False"
                             Width="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap.Width}"
                             Width="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap.Width}"
@@ -167,15 +166,93 @@
                                         Matrix="{Binding Document.ReferenceLayerViewModel.ReferenceTransformMatrix}" />
                                         Matrix="{Binding Document.ReferenceLayerViewModel.ReferenceTransformMatrix}" />
                                 </TransformGroup>
                                 </TransformGroup>
                             </Image.RenderTransform>
                             </Image.RenderTransform>
+                            <Image.Style>
+                                <Style>
+                                    <Style.Triggers>
+                                        <DataTrigger Binding="{Binding Document.ReferenceLayerViewModel.ShowHighest, Mode=OneWay}" Value="True">
+                                            <DataTrigger.EnterActions>
+                                                <BeginStoryboard>
+                                                    <Storyboard>
+                                                        <DoubleAnimation 
+                                                            Storyboard.TargetProperty="(Button.Opacity)" 
+                                                            From="1" To="{x:Static subviews:ReferenceLayerViewModel.TopMostOpacity}" Duration="0:0:0.1" /> 
+                                                    </Storyboard>
+                                                </BeginStoryboard>
+                                            </DataTrigger.EnterActions>
+                                            <DataTrigger.ExitActions>
+                                                <BeginStoryboard>
+                                                    <Storyboard>
+                                                        <DoubleAnimation 
+                                                            Storyboard.TargetProperty="(Button.Opacity)" 
+                                                            From="{x:Static subviews:ReferenceLayerViewModel.TopMostOpacity}" To="1" Duration="0:0:0.1" /> 
+                                                    </Storyboard>
+                                                </BeginStoryboard>
+                                            </DataTrigger.ExitActions>
+                                        </DataTrigger>
+                                    </Style.Triggers>
+                                </Style>
+                            </Image.Style>
                         </Image>
                         </Image>
+                        <Canvas.Style>
+                            <Style>
+                                <Style.Triggers>
+                                    <DataTrigger Binding="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickFromReferenceLayer, Mode=OneWay}" Value="False">
+                                        <DataTrigger.EnterActions>
+                                            <BeginStoryboard>
+                                                <Storyboard>
+                                                    <DoubleAnimation 
+                                                        Storyboard.TargetProperty="(Button.Opacity)" 
+                                                        From="1" To="0" Duration="0:0:0.1" /> 
+                                                </Storyboard>
+                                            </BeginStoryboard>
+                                        </DataTrigger.EnterActions>
+                                        <DataTrigger.ExitActions>
+                                            <BeginStoryboard>
+                                                <Storyboard>
+                                                    <DoubleAnimation 
+                                                        Storyboard.TargetProperty="(Button.Opacity)" 
+                                                        From="0" To="1" Duration="0:0:0.1" /> 
+                                                </Storyboard>
+                                            </BeginStoryboard>
+                                        </DataTrigger.ExitActions>
+                                    </DataTrigger>
+                                </Style.Triggers>
+                            </Style>
+                        </Canvas.Style>
                     </Canvas>
                     </Canvas>
                     <Image
                     <Image
                         Focusable="False"
                         Focusable="False"
                         Width="{Binding Document.Width}"
                         Width="{Binding Document.Width}"
                         Height="{Binding Document.Height}"
                         Height="{Binding Document.Height}"
                         Source="{Binding TargetBitmap}"
                         Source="{Binding TargetBitmap}"
-                        Visibility="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickFromCanvas, Converter={converters:BoolToHiddenVisibilityConverter}}"
-                        RenderOptions.BitmapScalingMode="{Binding Zoombox.Scale, Converter={converters:ScaleToBitmapScalingModeConverter}}"/>
+                        RenderOptions.BitmapScalingMode="{Binding Zoombox.Scale, Converter={converters:ScaleToBitmapScalingModeConverter}}">
+                        <Image.Style>
+                            <Style>
+                                <Style.Triggers>
+                                    <DataTrigger Binding="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}" Value="True">
+                                        <DataTrigger.EnterActions>
+                                            <BeginStoryboard>
+                                                <Storyboard>
+                                                    <DoubleAnimation 
+                                                        Storyboard.TargetProperty="(Button.Opacity)" 
+                                                        From="1" To="0" Duration="0:0:0.1" /> 
+                                                </Storyboard>
+                                            </BeginStoryboard>
+                                        </DataTrigger.EnterActions>
+                                        <DataTrigger.ExitActions>
+                                            <BeginStoryboard>
+                                                <Storyboard>
+                                                    <DoubleAnimation 
+                                                        Storyboard.TargetProperty="(Button.Opacity)" 
+                                                        From="0" To="1" Duration="0:0:0.1" /> 
+                                                </Storyboard>
+                                            </BeginStoryboard>
+                                        </DataTrigger.ExitActions>
+                                    </DataTrigger>
+                                </Style.Triggers>
+                            </Style>
+                        </Image.Style>
+                    </Image>
                     <Grid ZIndex="5">
                     <Grid ZIndex="5">
                         <symOverlay:SymmetryOverlay
                         <symOverlay:SymmetryOverlay
                             Focusable="False"
                             Focusable="False"
@@ -187,7 +264,8 @@
                             VerticalAxisX="{Binding Document.VerticalSymmetryAxisXBindable, Mode=OneWay}"
                             VerticalAxisX="{Binding Document.VerticalSymmetryAxisXBindable, Mode=OneWay}"
                             DragCommand="{cmds:Command PixiEditor.Document.DragSymmetry, UseProvided=True}"
                             DragCommand="{cmds:Command PixiEditor.Document.DragSymmetry, UseProvided=True}"
                             DragEndCommand="{cmds:Command PixiEditor.Document.EndDragSymmetry, UseProvided=True}"
                             DragEndCommand="{cmds:Command PixiEditor.Document.EndDragSymmetry, UseProvided=True}"
-                            DragStartCommand="{cmds:Command PixiEditor.Document.StartDragSymmetry, UseProvided=True}" />
+                            DragStartCommand="{cmds:Command PixiEditor.Document.StartDragSymmetry, UseProvided=True}"
+                            FlowDirection="LeftToRight" />
                         <overlays:SelectionOverlay
                         <overlays:SelectionOverlay
                             Focusable="False"
                             Focusable="False"
                             ShowFill="{Binding ToolsSubViewModel.ActiveTool, Source={vm:MainVM}, Converter={converters:IsSelectionToolConverter}}"
                             ShowFill="{Binding ToolsSubViewModel.ActiveTool, Source={vm:MainVM}, Converter={converters:IsSelectionToolConverter}}"