Browse Source

Merge branch 'master' into text

Krzysztof Krysiński 5 months ago
parent
commit
c5e38e6053

+ 2 - 2
README.md

@@ -12,9 +12,9 @@
 
 
 ### Check out our website [pixieditor.net](https://pixieditor.net) and [PixiEditor Forum](https://forum.pixieditor.net/)
 ### Check out our website [pixieditor.net](https://pixieditor.net) and [PixiEditor Forum](https://forum.pixieditor.net/)
 
 
-# Contributions temporarily freezed!
+# Feature Contributions temporarily freezed!
 
 
-PixiEditor is undergoing massive changes, master branch is unstable. We will not accept any contributions at the moment, until version 2.0 comes out.
+PixiEditor is undergoing massive changes, master branch is unstable. We will not accept any feature contributions at the moment, until version 2.0 comes out. Feel free to fix bugs though. But before you do, let us know on [Discord](https://discord.gg/qSRMYmq), since we already might've fixed them.
 
 
 ## About PixiEditor
 ## About PixiEditor
 
 

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs

@@ -54,12 +54,12 @@ public class MergeNode : RenderNode
         if (Bottom.Value != null && Top.Value != null)
         if (Bottom.Value != null && Top.Value != null)
         {
         {
             int saved = target.Canvas.SaveLayer();
             int saved = target.Canvas.SaveLayer();
-            Bottom.Value.Paint(context, target);
+            Bottom.Value?.Paint(context, target);
 
 
             paint.BlendMode = RenderContext.GetDrawingBlendMode(BlendMode.Value);
             paint.BlendMode = RenderContext.GetDrawingBlendMode(BlendMode.Value);
             target.Canvas.SaveLayer(paint);
             target.Canvas.SaveLayer(paint);
             
             
-            Top.Value.Paint(context, target);
+            Top.Value?.Paint(context, target);
             target.Canvas.RestoreToCount(saved);
             target.Canvas.RestoreToCount(saved);
             return;
             return;
         }
         }

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PointsVectorData.cs

@@ -3,6 +3,7 @@ using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Vector;
 using Drawie.Backend.Core.Vector;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using Drawie.Numerics.Helpers;
 
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 
 
@@ -47,8 +48,7 @@ public class PointsVectorData : ShapeVectorData
             canvas.SetMatrix(final);
             canvas.SetMatrix(final);
         }
         }
 
 
-        canvas.DrawPoints(PointMode.Points, Points.Select(p => new VecF((float)p.X, (float)p.Y)).ToArray(),
-            paint);
+        canvas.DrawPoints(PointMode.Points, Points.ToVecFArray(), paint);
 
 
         if (applyTransform)
         if (applyTransform)
         {
         {

+ 2 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RemoveClosePointsNode.cs

@@ -26,6 +26,7 @@ public class RemoveClosePointsNode : ShapeNode<PointsVectorData>
         var data = Input.Value;
         var data = Input.Value;
 
 
         var distance = MinDistance.Value;
         var distance = MinDistance.Value;
+        var minDistanceSquared = distance * distance;
 
 
         if (distance == 0 || data == null || data.Points == null)
         if (distance == 0 || data == null || data.Points == null)
         {
         {
@@ -34,9 +35,6 @@ public class RemoveClosePointsNode : ShapeNode<PointsVectorData>
 
 
         var availablePoints = data.Points.Distinct().ToList();
         var availablePoints = data.Points.Distinct().ToList();
         List<VecD> newPoints = new List<VecD>();
         List<VecD> newPoints = new List<VecD>();
-        
-        var minDistance = MinDistance.Value;
-        var documentSize = context.DocumentSize;
 
 
         var random = new Random(Seed.Value);
         var random = new Random(Seed.Value);
         while (availablePoints.Count > 1)
         while (availablePoints.Count > 1)
@@ -55,7 +53,7 @@ public class RemoveClosePointsNode : ShapeNode<PointsVectorData>
             continue;
             continue;
 
 
             bool InRange(VecD other) =>
             bool InRange(VecD other) =>
-                (other - point).Length <= minDistance;
+                (other - point).LengthSquared <= minDistanceSquared;
         }
         }
 
 
         if (availablePoints.Count == 1)
         if (availablePoints.Count == 1)

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

@@ -848,5 +848,6 @@
   "TEXT_TOOL_TOOLTIP": "Create text ({0}).",
   "TEXT_TOOL_TOOLTIP": "Create text ({0}).",
   "BOLD_TOOLTIP": "Bold",
   "BOLD_TOOLTIP": "Bold",
   "ITALIC_TOOLTIP": "Italic",
   "ITALIC_TOOLTIP": "Italic",
-  "CUSTOM_FONT": "Custom font"
+  "CUSTOM_FONT": "Custom font",
+  "DUMP_GPU_DIAGNOSTICS": "Dump GPU diagnostics"
 }
 }

+ 2 - 2
src/PixiEditor/Properties/AssemblyInfo.cs

@@ -41,5 +41,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.0.0.52")]
-[assembly: AssemblyFileVersion("2.0.0.52")]
+[assembly: AssemblyVersion("2.0.0.53")]
+[assembly: AssemblyFileVersion("2.0.0.53")]

+ 97 - 43
src/PixiEditor/ViewModels/SubViewModels/DebugViewModel.cs

@@ -7,6 +7,10 @@ using System.Threading.Tasks;
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Platform.Storage;
 using Avalonia.Platform.Storage;
+using Drawie.Backend.Core.Bridge;
+using Drawie.Backend.Core.Debug;
+using Drawie.Interop.Avalonia.Core;
+using DrawiEngine;
 using Newtonsoft.Json;
 using Newtonsoft.Json;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Commands.Attributes.Evaluators;
 using PixiEditor.Models.Commands.Attributes.Evaluators;
@@ -26,7 +30,8 @@ using PixiEditor.Views.Dialogs.Debugging.Localization;
 
 
 namespace PixiEditor.ViewModels.SubViewModels;
 namespace PixiEditor.ViewModels.SubViewModels;
 
 
-[Command.Group("PixiEditor.Debug", "DEBUG", IsVisibleMenuProperty = $"{nameof(ViewModelMain.DebugSubViewModel)}.{nameof(UseDebug)}")]
+[Command.Group("PixiEditor.Debug", "DEBUG",
+    IsVisibleMenuProperty = $"{nameof(ViewModelMain.DebugSubViewModel)}.{nameof(UseDebug)}")]
 internal class DebugViewModel : SubViewModel<ViewModelMain>
 internal class DebugViewModel : SubViewModel<ViewModelMain>
 {
 {
     public static bool IsDebugBuild { get; set; }
     public static bool IsDebugBuild { get; set; }
@@ -57,7 +62,7 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
     }
     }
 
 
     private bool forceOtherFlowDirection;
     private bool forceOtherFlowDirection;
-    
+
     public bool ForceOtherFlowDirection
     public bool ForceOtherFlowDirection
     {
     {
         get => forceOtherFlowDirection;
         get => forceOtherFlowDirection;
@@ -89,19 +94,25 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
 
 
         IOperatingSystem.Current.OpenFolder(path);
         IOperatingSystem.Current.OpenFolder(path);
     }
     }
-    
 
 
-    [Command.Debug("PixiEditor.Debug.IO.OpenLocalAppDataDirectory", @"PixiEditor", "OPEN_LOCAL_APPDATA_DIR", "OPEN_LOCAL_APPDATA_DIR",
-        MenuItemPath = "DEBUG/OPEN_LOCAL_APPDATA_DIR", MenuItemOrder = 5, Icon = PixiPerfectIcons.Folder, AnalyticsTrack = true)]
-    [Command.Debug("PixiEditor.Debug.IO.OpenCrashReportsDirectory", @"PixiEditor\crash_logs", "OPEN_CRASH_REPORTS_DIR", "OPEN_CRASH_REPORTS_DIR",
-        MenuItemPath = "DEBUG/OPEN_CRASH_REPORTS_DIR", MenuItemOrder = 6, Icon = PixiPerfectIcons.Folder, AnalyticsTrack = true)]
+
+    [Command.Debug("PixiEditor.Debug.IO.OpenLocalAppDataDirectory", @"PixiEditor", "OPEN_LOCAL_APPDATA_DIR",
+        "OPEN_LOCAL_APPDATA_DIR",
+        MenuItemPath = "DEBUG/OPEN_LOCAL_APPDATA_DIR", MenuItemOrder = 5, Icon = PixiPerfectIcons.Folder,
+        AnalyticsTrack = true)]
+    [Command.Debug("PixiEditor.Debug.IO.OpenCrashReportsDirectory", @"PixiEditor\crash_logs", "OPEN_CRASH_REPORTS_DIR",
+        "OPEN_CRASH_REPORTS_DIR",
+        MenuItemPath = "DEBUG/OPEN_CRASH_REPORTS_DIR", MenuItemOrder = 6, Icon = PixiPerfectIcons.Folder,
+        AnalyticsTrack = true)]
     public static void OpenLocalAppDataFolder(string subDirectory)
     public static void OpenLocalAppDataFolder(string subDirectory)
     {
     {
-        var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), subDirectory);
+        var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+            subDirectory);
         OpenFolder(path);
         OpenFolder(path);
     }
     }
 
 
-    [Command.Debug("PixiEditor.Debug.IO.OpenRoamingAppDataDirectory", @"PixiEditor", "OPEN_ROAMING_APPDATA_DIR", "OPEN_ROAMING_APPDATA_DIR", Icon = PixiPerfectIcons.Folder,
+    [Command.Debug("PixiEditor.Debug.IO.OpenRoamingAppDataDirectory", @"PixiEditor", "OPEN_ROAMING_APPDATA_DIR",
+        "OPEN_ROAMING_APPDATA_DIR", Icon = PixiPerfectIcons.Folder,
         MenuItemPath = "DEBUG/OPEN_ROAMING_APPDATA_DIR", MenuItemOrder = 7)]
         MenuItemPath = "DEBUG/OPEN_ROAMING_APPDATA_DIR", MenuItemOrder = 7)]
     public static void OpenAppDataFolder(string subDirectory)
     public static void OpenAppDataFolder(string subDirectory)
     {
     {
@@ -109,7 +120,8 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         OpenFolder(path);
         OpenFolder(path);
     }
     }
 
 
-    [Command.Debug("PixiEditor.Debug.IO.OpenTempDirectory", @"PixiEditor", "OPEN_TEMP_DIR", "OPEN_TEMP_DIR", Icon = PixiPerfectIcons.Folder,
+    [Command.Debug("PixiEditor.Debug.IO.OpenTempDirectory", @"PixiEditor", "OPEN_TEMP_DIR", "OPEN_TEMP_DIR",
+        Icon = PixiPerfectIcons.Folder,
         MenuItemPath = "DEBUG/OPEN_TEMP_DIR", MenuItemOrder = 8, AnalyticsTrack = true)]
         MenuItemPath = "DEBUG/OPEN_TEMP_DIR", MenuItemOrder = 8, AnalyticsTrack = true)]
     public static void OpenTempFolder(string subDirectory)
     public static void OpenTempFolder(string subDirectory)
     {
     {
@@ -117,14 +129,40 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         OpenFolder(path);
         OpenFolder(path);
     }
     }
 
 
-    [Command.Debug("PixiEditor.Debug.DumpAllCommands", "DUMP_ALL_COMMANDS", "DUMP_ALL_COMMANDS_DESCRIPTIVE", AnalyticsTrack = true)]
+    [Command.Debug("PixiEditor.Debug.DumpGPUDiagnostics", "DUMP_GPU_DIAGNOSTICS", "DUMP_GPU_DIAGNOSTICS",
+        AnalyticsTrack = true)]
+    public async Task DumpGpuDiagnostics()
+    {
+        await Application.Current.ForDesktopMainWindowAsync(async desktop =>
+        {
+            FilePickerSaveOptions options = new FilePickerSaveOptions();
+            options.DefaultExtension = "txt";
+            options.FileTypeChoices =
+                new FilePickerFileType[] { new FilePickerFileType("Text") { Patterns = new[] { "*.txt" } } };
+            var pickedFile = desktop.StorageProvider.SaveFilePickerAsync(options).Result;
+
+            if (pickedFile != null)
+            {
+                GpuDiagnostics diagnostics = IDrawieInteropContext.Current.GetGpuDiagnostics();
+
+                await using StreamWriter writer = new StreamWriter(pickedFile.Path.LocalPath);
+                await writer.WriteAsync(diagnostics.ToString());
+
+                IOperatingSystem.Current.OpenFolder(pickedFile.Path.LocalPath);
+            }
+        });
+    }
+
+    [Command.Debug("PixiEditor.Debug.DumpAllCommands", "DUMP_ALL_COMMANDS", "DUMP_ALL_COMMANDS_DESCRIPTIVE",
+        AnalyticsTrack = true)]
     public async Task DumpAllCommands()
     public async Task DumpAllCommands()
     {
     {
         await Application.Current.ForDesktopMainWindowAsync(async desktop =>
         await Application.Current.ForDesktopMainWindowAsync(async desktop =>
         {
         {
             FilePickerSaveOptions options = new FilePickerSaveOptions();
             FilePickerSaveOptions options = new FilePickerSaveOptions();
             options.DefaultExtension = "txt";
             options.DefaultExtension = "txt";
-            options.FileTypeChoices = new FilePickerFileType[] { new FilePickerFileType("Text") {Patterns = new [] {"*.txt"}} };
+            options.FileTypeChoices =
+                new FilePickerFileType[] { new FilePickerFileType("Text") { Patterns = new[] { "*.txt" } } };
             var pickedFile = desktop.StorageProvider.SaveFilePickerAsync(options).Result;
             var pickedFile = desktop.StorageProvider.SaveFilePickerAsync(options).Result;
 
 
             if (pickedFile != null)
             if (pickedFile != null)
@@ -142,15 +180,17 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
             }
             }
         });
         });
     }
     }
-    
-    [Command.Debug("PixiEditor.Debug.GenerateKeysTemplate", "GENERATE_KEY_BINDINGS_TEMPLATE", "GENERATE_KEY_BINDINGS_TEMPLATE_DESCRIPTIVE", AnalyticsTrack = true)]
+
+    [Command.Debug("PixiEditor.Debug.GenerateKeysTemplate", "GENERATE_KEY_BINDINGS_TEMPLATE",
+        "GENERATE_KEY_BINDINGS_TEMPLATE_DESCRIPTIVE", AnalyticsTrack = true)]
     public async Task GenerateKeysTemplate()
     public async Task GenerateKeysTemplate()
     {
     {
         await Application.Current.ForDesktopMainWindowAsync(async desktop =>
         await Application.Current.ForDesktopMainWindowAsync(async desktop =>
         {
         {
             FilePickerSaveOptions options = new FilePickerSaveOptions();
             FilePickerSaveOptions options = new FilePickerSaveOptions();
             options.DefaultExtension = "json";
             options.DefaultExtension = "json";
-            options.FileTypeChoices = new FilePickerFileType[] { new FilePickerFileType("Json") {Patterns = new [] {"*.json"}} };
+            options.FileTypeChoices =
+                new FilePickerFileType[] { new FilePickerFileType("Json") { Patterns = new[] { "*.json" } } };
             var pickedFile = await desktop.StorageProvider.SaveFilePickerAsync(options);
             var pickedFile = await desktop.StorageProvider.SaveFilePickerAsync(options);
 
 
             if (pickedFile != null)
             if (pickedFile != null)
@@ -161,9 +201,11 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
                 Dictionary<string, KeyDefinition> keyDefinitions = new Dictionary<string, KeyDefinition>();
                 Dictionary<string, KeyDefinition> keyDefinitions = new Dictionary<string, KeyDefinition>();
                 foreach (var command in commands)
                 foreach (var command in commands)
                 {
                 {
-                    if(command.IsDebug)
+                    if (command.IsDebug)
                         continue;
                         continue;
-                    keyDefinitions.Add($"(provider).{command.InternalName}", new KeyDefinition(command.InternalName, new HumanReadableKeyCombination("None"), Array.Empty<string>()));
+                    keyDefinitions.Add($"(provider).{command.InternalName}",
+                        new KeyDefinition(command.InternalName, new HumanReadableKeyCombination("None"),
+                            Array.Empty<string>()));
                 }
                 }
 
 
                 writer.Write(JsonConvert.SerializeObject(keyDefinitions, Formatting.Indented));
                 writer.Write(JsonConvert.SerializeObject(keyDefinitions, Formatting.Indented));
@@ -171,7 +213,7 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
                 string file = await File.ReadAllTextAsync(pickedFile.Path.LocalPath);
                 string file = await File.ReadAllTextAsync(pickedFile.Path.LocalPath);
                 foreach (var command in commands)
                 foreach (var command in commands)
                 {
                 {
-                    if(command.IsDebug)
+                    if (command.IsDebug)
                         continue;
                         continue;
                     file = file.Replace($"(provider).{command.InternalName}", "");
                     file = file.Replace($"(provider).{command.InternalName}", "");
                 }
                 }
@@ -182,19 +224,21 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         });
         });
     }
     }
 
 
-    [Command.Debug("PixiEditor.Debug.ValidateShortcutMap", "VALIDATE_SHORTCUT_MAP", "VALIDATE_SHORTCUT_MAP_DESCRIPTIVE", AnalyticsTrack = true)]
+    [Command.Debug("PixiEditor.Debug.ValidateShortcutMap", "VALIDATE_SHORTCUT_MAP", "VALIDATE_SHORTCUT_MAP_DESCRIPTIVE",
+        AnalyticsTrack = true)]
     public async Task ValidateShortcutMap()
     public async Task ValidateShortcutMap()
     {
     {
         await Application.Current.ForDesktopMainWindowAsync(async desktop =>
         await Application.Current.ForDesktopMainWindowAsync(async desktop =>
         {
         {
             FilePickerOpenOptions options = new FilePickerOpenOptions
             FilePickerOpenOptions options = new FilePickerOpenOptions
-                {
-                    SuggestedStartLocation =
-                        await desktop.StorageProvider.TryGetFolderFromPathAsync(
-                            Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Data",
-                                "ShortcutActionMaps")),
-                    FileTypeFilter = new FilePickerFileType[] { new FilePickerFileType("Json") {Patterns = new [] {"*.json"}} }
-                };
+            {
+                SuggestedStartLocation =
+                    await desktop.StorageProvider.TryGetFolderFromPathAsync(
+                        Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Data",
+                            "ShortcutActionMaps")),
+                FileTypeFilter =
+                    new FilePickerFileType[] { new FilePickerFileType("Json") { Patterns = new[] { "*.json" } } }
+            };
             var pickedFile = desktop.StorageProvider.OpenFilePickerAsync(options).Result.FirstOrDefault();
             var pickedFile = desktop.StorageProvider.OpenFilePickerAsync(options).Result.FirstOrDefault();
 
 
             if (pickedFile != null)
             if (pickedFile != null)
@@ -212,7 +256,8 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
                     }
                     }
                 }
                 }
 
 
-                NoticeDialog.Show(new LocalizedString("VALIDATION_KEYS_NOTICE_DIALOG", emptyKeys, unknownCommands), "RESULT");
+                NoticeDialog.Show(new LocalizedString("VALIDATION_KEYS_NOTICE_DIALOG", emptyKeys, unknownCommands),
+                    "RESULT");
             }
             }
         });
         });
     }
     }
@@ -239,20 +284,22 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         new PointerDebugPopup().Show();
         new PointerDebugPopup().Show();
     }
     }
 
 
-    [Command.Debug("PixiEditor.Debug.OpenLocalizationDebugWindow", "OPEN_LOCALIZATION_DEBUG_WINDOW", "OPEN_LOCALIZATION_DEBUG_WINDOW",
+    [Command.Debug("PixiEditor.Debug.OpenLocalizationDebugWindow", "OPEN_LOCALIZATION_DEBUG_WINDOW",
+        "OPEN_LOCALIZATION_DEBUG_WINDOW",
         MenuItemPath = "DEBUG/OPEN_LOCALIZATION_DEBUG_WINDOW", MenuItemOrder = 2, AnalyticsTrack = true)]
         MenuItemPath = "DEBUG/OPEN_LOCALIZATION_DEBUG_WINDOW", MenuItemOrder = 2, AnalyticsTrack = true)]
     public void OpenLocalizationDebugWindow()
     public void OpenLocalizationDebugWindow()
     {
     {
         if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
         if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
         {
         {
-            var window = desktop.Windows.OfType<LocalizationDebugWindow>().FirstOrDefault(new LocalizationDebugWindow());
+            var window = desktop.Windows.OfType<LocalizationDebugWindow>()
+                .FirstOrDefault(new LocalizationDebugWindow());
             window.Show();
             window.Show();
             window.Activate();
             window.Activate();
         }
         }
-
     }
     }
 
 
-    [Command.Debug("PixiEditor.Debug.OpenPerformanceDebugWindow", "Open Performance Debug Window", "Open Performance Debug Window",
+    [Command.Debug("PixiEditor.Debug.OpenPerformanceDebugWindow", "Open Performance Debug Window",
+        "Open Performance Debug Window",
         MenuItemPath = "DEBUG/Open Performance Debug Window", MenuItemOrder = 4, AnalyticsTrack = true)]
         MenuItemPath = "DEBUG/Open Performance Debug Window", MenuItemOrder = 4, AnalyticsTrack = true)]
     public void OpenPerformanceDebugWindow()
     public void OpenPerformanceDebugWindow()
     {
     {
@@ -275,7 +322,10 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
                     await desktop.StorageProvider.TryGetFolderFromPathAsync(
                     await desktop.StorageProvider.TryGetFolderFromPathAsync(
                         Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Data",
                         Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "Data",
                             "Languages")),
                             "Languages")),
-                FileTypeFilter = new FilePickerFileType[] { new FilePickerFileType("key-value json") {Patterns = new [] {"*.json"}} }
+                FileTypeFilter = new FilePickerFileType[]
+                {
+                    new FilePickerFileType("key-value json") { Patterns = new[] { "*.json" } }
+                }
             };
             };
             var pickedFile = desktop.StorageProvider.OpenFilePickerAsync(options).Result.FirstOrDefault();
             var pickedFile = desktop.StorageProvider.OpenFilePickerAsync(options).Result.FirstOrDefault();
 
 
@@ -289,7 +339,8 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         });
         });
     }
     }
 
 
-    [Command.Debug("PixiEditor.Debug.IO.OpenInstallDirectory", "OPEN_INSTALLATION_DIR", "OPEN_INSTALLATION_DIR", Icon = PixiPerfectIcons.Folder,
+    [Command.Debug("PixiEditor.Debug.IO.OpenInstallDirectory", "OPEN_INSTALLATION_DIR", "OPEN_INSTALLATION_DIR",
+        Icon = PixiPerfectIcons.Folder,
         MenuItemPath = "DEBUG/OPEN_INSTALLATION_DIR", MenuItemOrder = 9, AnalyticsTrack = true)]
         MenuItemPath = "DEBUG/OPEN_INSTALLATION_DIR", MenuItemOrder = 9, AnalyticsTrack = true)]
     public static void OpenInstallLocation()
     public static void OpenInstallLocation()
     {
     {
@@ -314,11 +365,14 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         }
         }
     }
     }
 
 
-    [Command.Debug("PixiEditor.Debug.DeleteUserPreferences", @"%appdata%/PixiEditor/user_preferences.json", "DELETE_USR_PREFS", "DELETE_USR_PREFS",
+    [Command.Debug("PixiEditor.Debug.DeleteUserPreferences", @"%appdata%/PixiEditor/user_preferences.json",
+        "DELETE_USR_PREFS", "DELETE_USR_PREFS",
         MenuItemPath = "DEBUG/DELETE/USER_PREFS", MenuItemOrder = 11, AnalyticsTrack = true)]
         MenuItemPath = "DEBUG/DELETE/USER_PREFS", MenuItemOrder = 11, AnalyticsTrack = true)]
-    [Command.Debug("PixiEditor.Debug.DeleteShortcutFile", @"%appdata%/PixiEditor/shortcuts.json", "DELETE_SHORTCUT_FILE", "DELETE_SHORTCUT_FILE",
+    [Command.Debug("PixiEditor.Debug.DeleteShortcutFile", @"%appdata%/PixiEditor/shortcuts.json",
+        "DELETE_SHORTCUT_FILE", "DELETE_SHORTCUT_FILE",
         MenuItemPath = "DEBUG/DELETE/SHORTCUT_FILE", MenuItemOrder = 12, AnalyticsTrack = true)]
         MenuItemPath = "DEBUG/DELETE/SHORTCUT_FILE", MenuItemOrder = 12, AnalyticsTrack = true)]
-    [Command.Debug("PixiEditor.Debug.DeleteEditorData", @"%localappdata%/PixiEditor/editor_data.json", "DELETE_EDITOR_DATA", "DELETE_EDITOR_DATA",
+    [Command.Debug("PixiEditor.Debug.DeleteEditorData", @"%localappdata%/PixiEditor/editor_data.json",
+        "DELETE_EDITOR_DATA", "DELETE_EDITOR_DATA",
         MenuItemPath = "DEBUG/DELETE/EDITOR_DATA", MenuItemOrder = 13, AnalyticsTrack = true)]
         MenuItemPath = "DEBUG/DELETE/EDITOR_DATA", MenuItemOrder = 13, AnalyticsTrack = true)]
     public static async Task DeleteFile(string path)
     public static async Task DeleteFile(string path)
     {
     {
@@ -326,7 +380,7 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
             return;
             return;
         string[] parts = path.Split('/');
         string[] parts = path.Split('/');
         path = Path.Combine(parts); // os specific path
         path = Path.Combine(parts); // os specific path
-        
+
         string file = Environment.ExpandEnvironmentVariables(path);
         string file = Environment.ExpandEnvironmentVariables(path);
         if (!File.Exists(file))
         if (!File.Exists(file))
         {
         {
@@ -339,12 +393,12 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
             }
             }
         }
         }
 
 
-        OptionsDialog<string> dialog = new("ARE_YOU_SURE", new LocalizedString("ARE_YOU_SURE_PATH_FULL_PATH", path, file), MainWindow.Current)
-        {
-            // TODO: seems like this should be localized strings
-            { new LocalizedString("YES"), x => File.Delete(file) },
-            new LocalizedString("CANCEL")
-        };
+        OptionsDialog<string> dialog =
+            new("ARE_YOU_SURE", new LocalizedString("ARE_YOU_SURE_PATH_FULL_PATH", path, file), MainWindow.Current)
+            {
+                // TODO: seems like this should be localized strings
+                { new LocalizedString("YES"), x => File.Delete(file) }, new LocalizedString("CANCEL")
+            };
 
 
         await dialog.ShowDialog();
         await dialog.ShowDialog();
     }
     }

+ 1 - 18
src/PixiEditor/Views/Dock/ColorPickerDockView.axaml.cs

@@ -19,23 +19,6 @@ public partial class ColorPickerDockView : UserControl
         base.OnLoaded(e);
         base.OnLoaded(e);
         var textBoxes = this.GetVisualDescendants().OfType<TextBox>().ToArray();
         var textBoxes = this.GetVisualDescendants().OfType<TextBox>().ToArray();
 
 
-        foreach (var textBox in textBoxes)
-        {
-            var existingBehaviors = Interaction.GetBehaviors(textBox);
-            if(existingBehaviors.Any(x => x is GlobalShortcutFocusBehavior)) continue;
-            bool attach = false;
-            if (existingBehaviors == null)
-            {
-                attach = true;
-                existingBehaviors = new BehaviorCollection();
-            }
-
-            existingBehaviors.Add(new GlobalShortcutFocusBehavior());
-
-            if (attach)
-            {
-                Interaction.SetBehaviors(textBox, existingBehaviors);
-            }
-        }
+        ColorSlidersDockView.AttachBehavioursToTextBoxes(textBoxes); 
     }
     }
 }
 }

+ 41 - 1
src/PixiEditor/Views/Dock/ColorSlidersDockView.axaml.cs

@@ -1,6 +1,10 @@
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
+using Avalonia.Interactivity;
 using Avalonia.Markup.Xaml;
 using Avalonia.Markup.Xaml;
+using Avalonia.VisualTree;
+using Avalonia.Xaml.Interactivity;
+using PixiEditor.Helpers.Behaviours;
 
 
 namespace PixiEditor.Views.Dock;
 namespace PixiEditor.Views.Dock;
 
 
@@ -10,5 +14,41 @@ public partial class ColorSlidersDockView : UserControl
     {
     {
         InitializeComponent();
         InitializeComponent();
     }
     }
-}
 
 
+    protected override void OnLoaded(RoutedEventArgs e)
+    {
+        base.OnLoaded(e);
+        var textBoxes = this.GetVisualDescendants().OfType<TextBox>().ToArray();
+
+        AttachBehavioursToTextBoxes(textBoxes);
+    }
+
+    internal static void AttachBehavioursToTextBoxes(TextBox[] textBoxes)
+    {
+        foreach (var textBox in textBoxes)
+        {
+            var existingBehaviors = Interaction.GetBehaviors(textBox);
+            if (existingBehaviors.Any(x => x is GlobalShortcutFocusBehavior)) continue;
+            bool attach = false;
+            if (existingBehaviors == null)
+            {
+                attach = true;
+                existingBehaviors = new BehaviorCollection();
+            }
+
+            try
+            {
+                existingBehaviors.Add(new GlobalShortcutFocusBehavior());
+
+                if (attach)
+                {
+                    Interaction.SetBehaviors(textBox, existingBehaviors);
+                }
+            }
+            catch (ArgumentOutOfRangeException)
+            {
+                // avalonia's bug
+            }
+        }
+    }
+}