Browse Source

Merge pull request #943 from PixiEditor/api/documents

Api/documents
Krzysztof Krysiński 2 months ago
parent
commit
28602c014b
100 changed files with 860 additions and 137 deletions
  1. 6 0
      samples/PixiEditorExtensionSamples.sln
  2. 11 3
      samples/Sample7_FlyUI/WindowContentElement.cs
  3. 24 0
      samples/Sample9_Document/CommandsSampleExtension.cs
  4. 8 0
      samples/Sample9_Document/Program.cs
  5. BIN
      samples/Sample9_Document/Resources/cs.png
  6. 45 0
      samples/Sample9_Document/Sample9_Document.csproj
  7. 44 0
      samples/Sample9_Document/extension.json
  8. 3 2
      src/PixiEditor.Browser/Program.cs
  9. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Root/ReferenceLayerChangeInfos/SetReferenceLayer_ChangeInfo.cs
  10. 1 0
      src/PixiEditor.Desktop/Program.cs
  11. 7 0
      src/PixiEditor.Extensions.CommonApi/Documents/IDocument.cs
  12. 37 4
      src/PixiEditor.Extensions.CommonApi/FlyUI/Events/ElementEventArgs.cs
  13. 3 1
      src/PixiEditor.Extensions.CommonApi/FlyUI/Events/ElementEventHandler.cs
  14. 19 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Events/NumberEventArgs.cs
  15. 19 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Events/TextEventArgs.cs
  16. 9 1
      src/PixiEditor.Extensions.CommonApi/FlyUI/Events/ToggleEventArgs.cs
  17. 5 1
      src/PixiEditor.Extensions.CommonApi/IO/IDocumentProvider.cs
  18. 62 0
      src/PixiEditor.Extensions.CommonApi/Utilities/ByteReader.cs
  19. 60 0
      src/PixiEditor.Extensions.CommonApi/Utilities/ByteWriter.cs
  20. 2 2
      src/PixiEditor.Extensions.Runtime/ExtensionException.cs
  21. 21 0
      src/PixiEditor.Extensions.Sdk/Api/Documents/Document.cs
  22. 4 1
      src/PixiEditor.Extensions.Sdk/Api/FlyUI/CheckBox.cs
  23. 15 6
      src/PixiEditor.Extensions.Sdk/Api/FlyUI/LayoutElement.cs
  24. 39 0
      src/PixiEditor.Extensions.Sdk/Api/FlyUI/SizeInputField.cs
  25. 4 0
      src/PixiEditor.Extensions.Sdk/Api/FlyUI/StatelessElement.cs
  26. 27 0
      src/PixiEditor.Extensions.Sdk/Api/FlyUI/TextField.cs
  27. 14 2
      src/PixiEditor.Extensions.Sdk/Api/IO/DocumentProvider.cs
  28. 25 0
      src/PixiEditor.Extensions.Sdk/Bridge/Interop.Document.cs
  29. 7 1
      src/PixiEditor.Extensions.Sdk/Bridge/Native.Document.cs
  30. 16 2
      src/PixiEditor.Extensions.Sdk/Bridge/Native.cs
  31. BIN
      src/PixiEditor.Extensions.Sdk/build/PixiEditor.Api.CGlueMSBuild.dll
  32. BIN
      src/PixiEditor.Extensions.Sdk/build/PixiEditor.Extensions.MSPackageBuilder.dll
  33. 30 4
      src/PixiEditor.Extensions.WasmRuntime/Api/DocumentsApi.cs
  34. 7 2
      src/PixiEditor.Extensions.WasmRuntime/Api/FlyUiApi.cs
  35. 1 1
      src/PixiEditor.Extensions.WasmRuntime/Api/LocalizationApi.cs
  36. 2 4
      src/PixiEditor.Extensions.WasmRuntime/Api/Palettes/PalettesApi.cs
  37. 2 4
      src/PixiEditor.Extensions.WasmRuntime/Api/WindowingApi.cs
  38. 11 8
      src/PixiEditor.Extensions.WasmRuntime/WasmExtensionInstance.cs
  39. 1 1
      src/PixiEditor.Extensions/Exceptions/RecoverableException.cs
  40. 1 1
      src/PixiEditor.Extensions/FlyUI/Elements/Border.cs
  41. 20 1
      src/PixiEditor.Extensions/FlyUI/Elements/CheckBox.cs
  42. 20 1
      src/PixiEditor.Extensions/FlyUI/Elements/LayoutElement.cs
  43. 72 0
      src/PixiEditor.Extensions/FlyUI/Elements/SizeInputField.cs
  44. 48 0
      src/PixiEditor.Extensions/FlyUI/Elements/TextField.cs
  45. 1 1
      src/PixiEditor.Extensions/Metadata/ExtensionMetadata.cs
  46. 7 1
      src/PixiEditor.Extensions/PixiEditor.Extensions.csproj
  47. 1 0
      src/PixiEditor.Extensions/UI/Panels/ColumnPanel.cs
  48. 2 2
      src/PixiEditor.UI.Common/Behaviors/TextBoxFocusBehavior.cs
  49. 6 5
      src/PixiEditor.UI.Common/Controls/NumberInput.cs
  50. 10 11
      src/PixiEditor.UI.Common/Controls/SizeInput.axaml
  51. 2 3
      src/PixiEditor.UI.Common/Controls/SizeInput.axaml.cs
  52. 1 1
      src/PixiEditor.UI.Common/Localization/EnumHelpers.cs
  53. 1 2
      src/PixiEditor.UI.Common/Localization/ExternalProperty.cs
  54. 1 1
      src/PixiEditor.UI.Common/Localization/ICustomTranslatorElement.cs
  55. 1 1
      src/PixiEditor.UI.Common/Localization/ILocalizationProvider.cs
  56. 1 2
      src/PixiEditor.UI.Common/Localization/Language.cs
  57. 1 1
      src/PixiEditor.UI.Common/Localization/LanguageData.cs
  58. 1 2
      src/PixiEditor.UI.Common/Localization/LocalizationData.cs
  59. 1 1
      src/PixiEditor.UI.Common/Localization/LocalizationKeyShowMode.cs
  60. 1 1
      src/PixiEditor.UI.Common/Localization/LocalizedString.cs
  61. 1 5
      src/PixiEditor.UI.Common/Localization/Translator.cs
  62. 8 0
      src/PixiEditor.UI.Common/PixiEditor.UI.Common.csproj
  63. 1 1
      src/PixiEditor.Windows/Win32.cs
  64. 0 1
      src/PixiEditor.Windows/WindowsInputKeys.cs
  65. 0 1
      src/PixiEditor.Windows/WindowsOperatingSystem.cs
  66. 2 2
      src/PixiEditor/App.axaml
  67. 1 1
      src/PixiEditor/Exceptions/CorruptedFileException.cs
  68. 1 1
      src/PixiEditor/Exceptions/InvalidFileTypeException.cs
  69. 1 1
      src/PixiEditor/Exceptions/MissingFileException.cs
  70. 1 1
      src/PixiEditor/Helpers/Collections/ActionDisplayList.cs
  71. 1 1
      src/PixiEditor/Helpers/Converters/BlendModeToStringConverter.cs
  72. 1 1
      src/PixiEditor/Helpers/Converters/BoolToValueConverter.cs
  73. 1 0
      src/PixiEditor/Helpers/Converters/EnumToLocalizedStringConverter.cs
  74. 1 1
      src/PixiEditor/Helpers/Converters/KeyToStringConverter.cs
  75. 1 0
      src/PixiEditor/Helpers/EnumDescriptionConverter.cs
  76. 1 1
      src/PixiEditor/Helpers/ServiceCollectionHelpers.cs
  77. 15 1
      src/PixiEditor/Initialization/ClassicDesktopEntry.cs
  78. 1 1
      src/PixiEditor/Models/Commands/Attributes/Commands/CommandAttribute.cs
  79. 1 1
      src/PixiEditor/Models/Commands/Attributes/Commands/FilterAttribute.cs
  80. 1 1
      src/PixiEditor/Models/Commands/Attributes/Commands/GroupAttribute.cs
  81. 1 1
      src/PixiEditor/Models/Commands/CommandController.cs
  82. 1 1
      src/PixiEditor/Models/Commands/CommandGroup.cs
  83. 1 1
      src/PixiEditor/Models/Commands/Commands/Command.cs
  84. 0 1
      src/PixiEditor/Models/Commands/XAML/NativeMenu.cs
  85. 1 1
      src/PixiEditor/Models/Constants.cs
  86. 1 1
      src/PixiEditor/Models/Controllers/ClipboardController.cs
  87. 1 6
      src/PixiEditor/Models/Controllers/ShortcutController.cs
  88. 1 1
      src/PixiEditor/Models/Dialogs/ConfirmationDialog.cs
  89. 1 1
      src/PixiEditor/Models/Dialogs/NoticeDialog.cs
  90. 1 1
      src/PixiEditor/Models/Dialogs/OptionsDialog.cs
  91. 1 1
      src/PixiEditor/Models/DocumentModels/DocumentStructureHelper.cs
  92. 1 1
      src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs
  93. 1 1
      src/PixiEditor/Models/ExceptionHandling/CrashReport.cs
  94. 11 2
      src/PixiEditor/Models/ExtensionServices/DocumentProvider.cs
  95. 1 1
      src/PixiEditor/Models/ExtensionServices/WindowProvider.cs
  96. 1 1
      src/PixiEditor/Models/Files/BmpFileType.cs
  97. 1 1
      src/PixiEditor/Models/Files/GifFileType.cs
  98. 1 1
      src/PixiEditor/Models/Files/ImageFileType.cs
  99. 1 1
      src/PixiEditor/Models/Files/JpegFileType.cs
  100. 1 1
      src/PixiEditor/Models/Files/Mp4FileType.cs

+ 6 - 0
samples/PixiEditorExtensionSamples.sln

@@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample8_Commands", "Sample8
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample8_CommandLibrary", "Sample8_CommandLibrary\Sample8_CommandLibrary.csproj", "{3559A288-DF82-4429-B23C-CFF9E55B372E}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample8_CommandLibrary", "Sample8_CommandLibrary\Sample8_CommandLibrary.csproj", "{3559A288-DF82-4429-B23C-CFF9E55B372E}"
 EndProject
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample9_Document", "Sample9_Document\Sample9_Document.csproj", "{E018D2C3-2DD7-4BC7-AAAF-91DA949789E4}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
@@ -74,6 +76,10 @@ Global
 		{3559A288-DF82-4429-B23C-CFF9E55B372E}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{3559A288-DF82-4429-B23C-CFF9E55B372E}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{3559A288-DF82-4429-B23C-CFF9E55B372E}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{3559A288-DF82-4429-B23C-CFF9E55B372E}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{3559A288-DF82-4429-B23C-CFF9E55B372E}.Release|Any CPU.Build.0 = Release|Any CPU
 		{3559A288-DF82-4429-B23C-CFF9E55B372E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E018D2C3-2DD7-4BC7-AAAF-91DA949789E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E018D2C3-2DD7-4BC7-AAAF-91DA949789E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E018D2C3-2DD7-4BC7-AAAF-91DA949789E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E018D2C3-2DD7-4BC7-AAAF-91DA949789E4}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
 	GlobalSection(NestedProjects) = preSolution
 		{FD9B4C32-4D2E-410E-BC6B-787779BEB6E2} = {7CC35BC4-829F-4EF4-8EB6-E1D46206E7DC}
 		{FD9B4C32-4D2E-410E-BC6B-787779BEB6E2} = {7CC35BC4-829F-4EF4-8EB6-E1D46206E7DC}

+ 11 - 3
samples/Sample7_FlyUI/WindowContentElement.cs

@@ -7,13 +7,20 @@ using PixiEditor.Extensions.Sdk.Api.Window;
 
 
 namespace FlyUISample;
 namespace FlyUISample;
 
 
-[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "FlyUI style")]
+[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines",
+    Justification = "FlyUI style")]
 public class WindowContentElement : StatelessElement
 public class WindowContentElement : StatelessElement
 {
 {
     public PopupWindow Window { get; set; }
     public PopupWindow Window { get; set; }
 
 
     public override ControlDefinition BuildNative()
     public override ControlDefinition BuildNative()
     {
     {
+        SizeInputField field = new SizeInputField();
+        field.SizeChanged += args =>
+        {
+            PixiEditorExtension.Api.Logger.Log(field.Value.ToString());
+        };
+
         Layout layout = new Layout(body:
         Layout layout = new Layout(body:
             new Container(margin: Edges.All(25), child:
             new Container(margin: Edges.All(25), child:
                 new Column(
                 new Column(
@@ -29,7 +36,8 @@ public class WindowContentElement : StatelessElement
                         ),
                         ),
                         new Align(
                         new Align(
                             alignment: Alignment.CenterRight,
                             alignment: Alignment.CenterRight,
-                            child: new Text("- Paulo Coelho, The Alchemist (1233)", textStyle: new TextStyle(fontStyle: FontStyle.Italic))
+                            child: new Text("- Paulo Coelho, The Alchemist (1233)",
+                                textStyle: new TextStyle(fontStyle: FontStyle.Italic))
                         ),
                         ),
                         new Container(
                         new Container(
                             margin: Edges.Symmetric(25, 0),
                             margin: Edges.Symmetric(25, 0),
@@ -47,6 +55,7 @@ public class WindowContentElement : StatelessElement
                                     ? "Checked"
                                     ? "Checked"
                                     : "Unchecked");
                                     : "Unchecked");
                             }),
                             }),
+                        field,
                         new Center(
                         new Center(
                             new Button(
                             new Button(
                                 child: new Text("Close"), onClick: _ => { Window.Close(); }))
                                 child: new Text("Close"), onClick: _ => { Window.Close(); }))
@@ -57,5 +66,4 @@ public class WindowContentElement : StatelessElement
 
 
         return layout.BuildNative();
         return layout.BuildNative();
     }
     }
-
 }
 }

+ 24 - 0
samples/Sample9_Document/CommandsSampleExtension.cs

@@ -0,0 +1,24 @@
+using PixiEditor.Extensions.CommonApi.Commands;
+using PixiEditor.Extensions.Sdk;
+
+namespace Sample9_Commands;
+
+public class CommandsSampleExtension : PixiEditorExtension
+{
+    /// <summary>
+    ///     This method is called when extension is loaded.
+    ///  All extensions are first loaded and then initialized. This method is called before <see cref="OnInitialized"/>.
+    /// </summary>
+    public override void OnLoaded()
+    {
+    }
+
+    /// <summary>
+    ///     This method is called when extension is initialized. After this method is called, you can use Api property to access PixiEditor API.
+    /// </summary>
+    public override void OnInitialized()
+    {
+        var doc = Api.Documents.ImportFile("Resources/cs.png", true); // Open file from the extension resources
+        doc?.Resize(128, 128); // Resizes whole document
+    }
+}

+ 8 - 0
samples/Sample9_Document/Program.cs

@@ -0,0 +1,8 @@
+namespace Sample9_Commands;
+
+public static class Program
+{
+    public static void Main()
+    {
+    }
+}

BIN
samples/Sample9_Document/Resources/cs.png


+ 45 - 0
samples/Sample9_Document/Sample9_Document.csproj

@@ -0,0 +1,45 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net8.0</TargetFramework>
+        <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
+        <OutputType>Exe</OutputType>
+        <PublishTrimmed>true</PublishTrimmed>
+        <WasmSingleFileBundle>true</WasmSingleFileBundle>
+        <GenerateExtensionPackage>true</GenerateExtensionPackage>
+        <PixiExtOutputPath>..\..\src\PixiEditor.Desktop\bin\Debug\net8.0\Extensions</PixiExtOutputPath>
+        <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+        <ValidateExecutableReferencesMatchSelfContained>false</ValidateExecutableReferencesMatchSelfContained>
+        <RootNamespace>Sample9_Commands</RootNamespace>
+    </PropertyGroup>
+
+    <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
+    <ItemGroup>
+        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Sdk\PixiEditor.Extensions.Sdk.csproj"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <None Remove="extension.json"/>
+        <Content Include="extension.json">
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </Content>
+    </ItemGroup>
+
+    <ItemGroup>
+        <Content Include="Localization\*">
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </Content>
+    </ItemGroup>
+
+    <ItemGroup>
+        <Content Include="Resources\*">
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </Content>
+    </ItemGroup>
+
+    <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.props"/>
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.targets"/>
+
+
+</Project>

+ 44 - 0
samples/Sample9_Document/extension.json

@@ -0,0 +1,44 @@
+{
+  "displayName": "Sample Extension - Document",
+  "uniqueName": "yourCompany.Samples.Document",
+  "description": "Sample Document extension for PixiEditor",
+  "version": "1.0.0",
+  "localization": {
+    "languages": [
+      {
+        "name": "English",
+        "code": "en",
+        "localeFileName": "Localization/en.json"
+      },
+      {
+        "name": "Polish",
+        "code": "pl",
+        "localeFileName": "Localization/pl.json"
+      }
+    ]
+  },
+  "author": {
+    "name": "PixiEditor",
+    "email": "[email protected]",
+    "website": "https://pixieditor.net"
+  },
+  "publisher": {
+    "name": "PixiEditor",
+    "email": "[email protected]",
+    "website": "https://pixieditor.net"
+  },
+  "contributors": [
+    {
+      "name": "flabbet",
+      "email": "[email protected]",
+      "website": "https://github.com/flabbet"
+    }
+  ],
+  "license": "MIT",
+  "categories": [
+    "Extension"
+  ],
+  "permissions": [
+    "OpenDocuments"
+  ]
+}

+ 3 - 2
src/PixiEditor.Browser/Program.cs

@@ -2,10 +2,11 @@
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Avalonia;
 using Avalonia;
 using Avalonia.Browser;
 using Avalonia.Browser;
-using PixiEditor;
 
 
 [assembly: SupportedOSPlatform("browser")]
 [assembly: SupportedOSPlatform("browser")]
 
 
+namespace PixiEditor.Avalonia.Browser;
+
 internal sealed partial class Program
 internal sealed partial class Program
 {
 {
     private static Task Main(string[] args) => BuildAvaloniaApp()
     private static Task Main(string[] args) => BuildAvaloniaApp()
@@ -13,4 +14,4 @@ internal sealed partial class Program
 
 
     public static AppBuilder BuildAvaloniaApp()
     public static AppBuilder BuildAvaloniaApp()
         => AppBuilder.Configure<App>();
         => AppBuilder.Configure<App>();
-}
+}

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Root/ReferenceLayerChangeInfos/SetReferenceLayer_ChangeInfo.cs

@@ -2,6 +2,6 @@
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Numerics;
 using Drawie.Numerics;
 
 
-namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
+namespace PixiEditor.ChangeableDocument.ChangeInfos.Root.ReferenceLayerChangeInfos;
 
 
 public record class SetReferenceLayer_ChangeInfo(ImmutableArray<byte> ImagePbgra8888Bytes, VecI ImageSize, ShapeCorners Shape) : IChangeInfo;
 public record class SetReferenceLayer_ChangeInfo(ImmutableArray<byte> ImagePbgra8888Bytes, VecI ImageSize, ShapeCorners Shape) : IChangeInfo;

+ 1 - 0
src/PixiEditor.Desktop/Program.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using Avalonia;
 using Avalonia;
 using Avalonia.Logging;
 using Avalonia.Logging;
+using Drawie.Interop.Avalonia;
 using Drawie.Interop.VulkanAvalonia;
 using Drawie.Interop.VulkanAvalonia;
 
 
 namespace PixiEditor.Desktop;
 namespace PixiEditor.Desktop;

+ 7 - 0
src/PixiEditor.Extensions.CommonApi/Documents/IDocument.cs

@@ -0,0 +1,7 @@
+namespace PixiEditor.Extensions.CommonApi.Documents;
+
+public interface IDocument
+{
+    public Guid Id { get; }
+    public void Resize(int width, int height);
+}

+ 37 - 4
src/PixiEditor.Extensions.CommonApi/FlyUI/Events/ElementEventArgs.cs

@@ -1,12 +1,45 @@
-namespace PixiEditor.Extensions.CommonApi.FlyUI.Events;
+using System.Collections;
+using PixiEditor.Extensions.CommonApi.Utilities;
+
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Events;
 
 
 public class ElementEventArgs
 public class ElementEventArgs
 {
 {
-    public object Sender { get; set; } 
-    public static ElementEventArgs Empty { get; } = new ElementEventArgs();
+    public object Sender { get; set; }
+
+    public static ElementEventArgs Deserialize(byte[] data)
+    {
+        if (data == null) return new ElementEventArgs();
+
+        ByteReader reader = new ByteReader(data);
+        string eventType = reader.ReadString();
+        ElementEventArgs eventArgs = eventType switch // TODO: more generic implementation
+        {
+            nameof(ToggleEventArgs) => new ToggleEventArgs(reader.ReadBool()),
+            nameof(TextEventArgs) => new TextEventArgs(reader.ReadString()),
+            nameof(NumberEventArgs) => new NumberEventArgs(reader.ReadDouble()),
+            nameof(ElementEventArgs) => new ElementEventArgs(),
+            _ => throw new NotSupportedException($"Event type '{eventType}' is not supported.")
+        };
+
+        return eventArgs;
+    }
+
+    public byte[] Serialize()
+    {
+        ByteWriter writer = new ByteWriter();
+        writer.WriteString(GetType().Name);
+        SerializeArgs(writer);
+
+        return writer.ToArray();
+    }
+
+    protected virtual void SerializeArgs(ByteWriter writer)
+    {
+        // Default implementation does nothing. Override in derived classes to serialize specific properties.
+    }
 }
 }
 
 
 public class ElementEventArgs<TEventArgs> : ElementEventArgs where TEventArgs : ElementEventArgs
 public class ElementEventArgs<TEventArgs> : ElementEventArgs where TEventArgs : ElementEventArgs
 {
 {
-    public static new ElementEventArgs<TEventArgs> Empty { get; } = new ElementEventArgs<TEventArgs>();
 }
 }

+ 3 - 1
src/PixiEditor.Extensions.CommonApi/FlyUI/Events/ElementEventHandler.cs

@@ -1,4 +1,6 @@
 namespace PixiEditor.Extensions.CommonApi.FlyUI.Events;
 namespace PixiEditor.Extensions.CommonApi.FlyUI.Events;
 
 
 public delegate void ElementEventHandler(ElementEventArgs args);
 public delegate void ElementEventHandler(ElementEventArgs args);
-public delegate void ElementEventHandler<in TEventArgs>(TEventArgs args) where TEventArgs : ElementEventArgs<TEventArgs>;
+
+public delegate void ElementEventHandler<in TEventArgs>(TEventArgs args)
+    where TEventArgs : ElementEventArgs<TEventArgs>;

+ 19 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Events/NumberEventArgs.cs

@@ -0,0 +1,19 @@
+using System.Numerics;
+using PixiEditor.Extensions.CommonApi.Utilities;
+
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Events;
+
+public class NumberEventArgs : ElementEventArgs<NumberEventArgs>
+{
+    public double Value { get; }
+
+    public NumberEventArgs(double value)
+    {
+        Value = value;
+    }
+
+    protected override void SerializeArgs(ByteWriter writer)
+    {
+        writer.WriteDouble(Value);
+    }
+}

+ 19 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Events/TextEventArgs.cs

@@ -0,0 +1,19 @@
+using System.Collections;
+using PixiEditor.Extensions.CommonApi.Utilities;
+
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Events;
+
+public class TextEventArgs : ElementEventArgs<TextEventArgs>
+{
+    public string Text { get; set; }
+
+    public TextEventArgs(string newText)
+    {
+        Text = newText;
+    }
+
+    protected override void SerializeArgs(ByteWriter writer)
+    {
+        writer.WriteString(Text);
+    }
+}

+ 9 - 1
src/PixiEditor.Extensions.CommonApi/FlyUI/Events/ToggleEventArgs.cs

@@ -1,4 +1,7 @@
-namespace PixiEditor.Extensions.CommonApi.FlyUI.Events;
+using System.Collections;
+using PixiEditor.Extensions.CommonApi.Utilities;
+
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Events;
 
 
 public class ToggleEventArgs : ElementEventArgs<ToggleEventArgs>
 public class ToggleEventArgs : ElementEventArgs<ToggleEventArgs>
 {
 {
@@ -8,4 +11,9 @@ public class ToggleEventArgs : ElementEventArgs<ToggleEventArgs>
     {
     {
         IsToggled = isToggled;
         IsToggled = isToggled;
     }
     }
+
+    protected override void SerializeArgs(ByteWriter writer)
+    {
+        writer.WriteBool(IsToggled);
+    }
 }
 }

+ 5 - 1
src/PixiEditor.Extensions.CommonApi/IO/IDocumentProvider.cs

@@ -1,6 +1,10 @@
+using PixiEditor.Extensions.CommonApi.Documents;
+
 namespace PixiEditor.Extensions.CommonApi.IO;
 namespace PixiEditor.Extensions.CommonApi.IO;
 
 
 public interface IDocumentProvider
 public interface IDocumentProvider
 {
 {
-   public void ImportFile(string path, bool associatePath = true);
+   public IDocument? ActiveDocument { get; }
+   public IDocument? ImportFile(string path, bool associatePath = true);
+   public IDocument? GetDocument(Guid id);
 }
 }

+ 62 - 0
src/PixiEditor.Extensions.CommonApi/Utilities/ByteReader.cs

@@ -0,0 +1,62 @@
+namespace PixiEditor.Extensions.CommonApi.Utilities;
+
+public class ByteReader
+{
+    private byte[] _buffer;
+    private int _position;
+
+    public ByteReader(byte[] buffer)
+    {
+        _buffer = buffer;
+        _position = 0;
+    }
+
+    public byte ReadByte()
+    {
+        return _buffer[_position++];
+    }
+
+    public string ReadString()
+    {
+        int length = ReadInt();
+        string result = System.Text.Encoding.UTF8.GetString(_buffer, _position, length);
+        _position += length;
+        return result;
+    }
+
+    public int ReadInt()
+    {
+        int result = BitConverter.ToInt32(_buffer, _position);
+        _position += sizeof(int);
+        return result;
+    }
+
+    public float ReadFloat()
+    {
+        float result = BitConverter.ToSingle(_buffer, _position);
+        _position += sizeof(float);
+        return result;
+    }
+
+    public bool ReadBool()
+    {
+        bool result = BitConverter.ToBoolean(_buffer, _position);
+        _position += sizeof(bool);
+        return result;
+    }
+
+    public byte[] ReadBytes(int length)
+    {
+        byte[] result = new byte[length];
+        Array.Copy(_buffer, _position, result, 0, length);
+        _position += length;
+        return result;
+    }
+
+    public double ReadDouble()
+    {
+        double result = BitConverter.ToDouble(_buffer, _position);
+        _position += sizeof(double);
+        return result;
+    }
+}

+ 60 - 0
src/PixiEditor.Extensions.CommonApi/Utilities/ByteWriter.cs

@@ -0,0 +1,60 @@
+namespace PixiEditor.Extensions.CommonApi.Utilities;
+
+public class ByteWriter
+{
+    private List<byte> _buffer;
+    private int _position;
+
+    public ByteWriter()
+    {
+        _buffer = new List<byte>();
+        _position = 0;
+    }
+
+    public void WriteByte(byte value)
+    {
+        _buffer.Add(value);
+        _position++;
+    }
+
+    public void WriteString(string value)
+    {
+        byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(value);
+        WriteInt(stringBytes.Length);
+        _buffer.AddRange(stringBytes);
+    }
+
+    public void WriteInt(int value)
+    {
+        byte[] intBytes = BitConverter.GetBytes(value);
+        _buffer.AddRange(intBytes);
+    }
+
+    public void WriteFloat(float value)
+    {
+        byte[] floatBytes = BitConverter.GetBytes(value);
+        _buffer.AddRange(floatBytes);
+    }
+
+    public void WriteDouble(double value)
+    {
+        byte[] doubleBytes = BitConverter.GetBytes(value);
+        _buffer.AddRange(doubleBytes);
+    }
+
+    public void WriteBool(bool value)
+    {
+        byte[] boolBytes = BitConverter.GetBytes(value);
+        _buffer.AddRange(boolBytes);
+    }
+
+    public void WriteBytes(byte[] value)
+    {
+        _buffer.AddRange(value);
+    }
+
+    public byte[] ToArray()
+    {
+        return _buffer.ToArray();
+    }
+}

+ 2 - 2
src/PixiEditor.Extensions.Runtime/ExtensionException.cs

@@ -1,5 +1,5 @@
-using PixiEditor.Extensions.Common.Localization;
-using PixiEditor.Extensions.Exceptions;
+using PixiEditor.Extensions.Exceptions;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Extensions.Runtime;
 namespace PixiEditor.Extensions.Runtime;
 
 

+ 21 - 0
src/PixiEditor.Extensions.Sdk/Api/Documents/Document.cs

@@ -0,0 +1,21 @@
+using PixiEditor.Extensions.CommonApi.Documents;
+using PixiEditor.Extensions.Sdk.Bridge;
+
+namespace PixiEditor.Extensions.Sdk.Api.Documents;
+
+public class Document : IDocument
+{
+    public Guid Id => documentId;
+    private Guid documentId;
+
+    internal Document(Guid documentId)
+    {
+        this.documentId = documentId;
+    }
+
+
+    public void Resize(int width, int height)
+    {
+        Native.resize_document(documentId.ToString(), width, height);
+    }
+}

+ 4 - 1
src/PixiEditor.Extensions.Sdk/Api/FlyUI/CheckBox.cs

@@ -13,9 +13,10 @@ public class CheckBox : SingleChildLayoutElement
 
 
     public bool IsChecked { get; set; }
     public bool IsChecked { get; set; }
 
 
-    public CheckBox(ILayoutElement<ControlDefinition> child = null, ElementEventHandler onCheckedChanged = null, Cursor? cursor = null) : base(cursor)
+    public CheckBox(ILayoutElement<ControlDefinition> child = null, bool isChecked = false, ElementEventHandler onCheckedChanged = null, Cursor? cursor = null) : base(cursor)
     {
     {
         Child = child;
         Child = child;
+        IsChecked = isChecked;
         
         
         if (onCheckedChanged != null)
         if (onCheckedChanged != null)
         {
         {
@@ -38,6 +39,8 @@ public class CheckBox : SingleChildLayoutElement
         if (Child != null)
         if (Child != null)
             checkbox.AddChild(Child.BuildNative());
             checkbox.AddChild(Child.BuildNative());
 
 
+        checkbox.AddProperty(IsChecked);
+
         return checkbox;
         return checkbox;
     }
     }
 }
 }

+ 15 - 6
src/PixiEditor.Extensions.Sdk/Api/FlyUI/LayoutElement.cs

@@ -6,6 +6,8 @@ namespace PixiEditor.Extensions.Sdk.Api.FlyUI;
 public abstract class LayoutElement : ILayoutElement<ControlDefinition>
 public abstract class LayoutElement : ILayoutElement<ControlDefinition>
 {
 {
     private Dictionary<string, List<ElementEventHandler>> _events;
     private Dictionary<string, List<ElementEventHandler>> _events;
+    private Dictionary<(string, Delegate), ElementEventHandler> _wrappedHandlers = new();
+
     public List<string> BuildQueuedEvents = new List<string>();
     public List<string> BuildQueuedEvents = new List<string>();
     public int UniqueId { get; set; }
     public int UniqueId { get; set; }
 
 
@@ -74,7 +76,7 @@ public abstract class LayoutElement : ILayoutElement<ControlDefinition>
         BuildQueuedEvents.Add(eventName);
         BuildQueuedEvents.Add(eventName);
     }
     }
 
 
-    /*public void AddEvent<TEventArgs>(string eventName, ElementEventHandler<TEventArgs> eventHandler) where TEventArgs : ElementEventArgs<TEventArgs>
+    public void AddEvent<T>(string eventName, ElementEventHandler<T> eventHandler) where T : ElementEventArgs<T>
     {
     {
         if (_events == null)
         if (_events == null)
         {
         {
@@ -86,9 +88,12 @@ public abstract class LayoutElement : ILayoutElement<ControlDefinition>
             _events.Add(eventName, new List<ElementEventHandler>());
             _events.Add(eventName, new List<ElementEventHandler>());
         }
         }
 
 
-        _events[eventName].Add((args => eventHandler((TEventArgs)args)));
+        ElementEventHandler wrapped = x => eventHandler(x as T);
+
+        _wrappedHandlers.Add((eventName, eventHandler), wrapped);
+        _events[eventName].Add(wrapped);
         BuildQueuedEvents.Add(eventName);
         BuildQueuedEvents.Add(eventName);
-    }*/
+    }
 
 
     public void RemoveEvent(string eventName, ElementEventHandler eventHandler)
     public void RemoveEvent(string eventName, ElementEventHandler eventHandler)
     {
     {
@@ -105,7 +110,7 @@ public abstract class LayoutElement : ILayoutElement<ControlDefinition>
         _events[eventName].Remove(eventHandler);
         _events[eventName].Remove(eventHandler);
     }
     }
 
 
-    /*public void RemoveEvent<TEventArgs>(string eventName, ElementEventHandler<TEventArgs> eventHandler) where TEventArgs : ElementEventArgs<TEventArgs>
+    public void RemoveEvent<T>(string eventName, ElementEventHandler<T> eventHandler) where T : ElementEventArgs<T>
     {
     {
         if (_events == null)
         if (_events == null)
         {
         {
@@ -117,8 +122,12 @@ public abstract class LayoutElement : ILayoutElement<ControlDefinition>
             return;
             return;
         }
         }
 
 
-        _events[eventName].Remove((args => eventHandler((TEventArgs)args)));
-    }*/
+        if (_wrappedHandlers.TryGetValue((eventName, eventHandler), out ElementEventHandler wrapped))
+        {
+            _wrappedHandlers.Remove((eventName, eventHandler));
+            _events[eventName].Remove(wrapped);
+        }
+    }
 
 
     public void RaiseEvent(string eventName, ElementEventArgs args)
     public void RaiseEvent(string eventName, ElementEventArgs args)
     {
     {

+ 39 - 0
src/PixiEditor.Extensions.Sdk/Api/FlyUI/SizeInputField.cs

@@ -0,0 +1,39 @@
+using PixiEditor.Extensions.CommonApi.FlyUI;
+using PixiEditor.Extensions.CommonApi.FlyUI.Events;
+
+namespace PixiEditor.Extensions.Sdk.Api.FlyUI;
+
+public class SizeInputField : LayoutElement
+{
+    public event ElementEventHandler<NumberEventArgs> SizeChanged
+    {
+        add => AddEvent(nameof(SizeChanged), value);
+        remove => RemoveEvent(nameof(SizeChanged), value);
+    }
+    public double Value { get; set; }
+    public double Min { get; set; }
+    public double Max { get; set; }
+    public int Decimals { get; set; }
+    public string Unit { get; set; }
+
+    public SizeInputField(double value = 0, double min = 1, double max = double.MaxValue, int decimals = 0, string unit = "px", Cursor? cursor = null) : base(cursor)
+    {
+        Value = value;
+        Min = min;
+        Max = max;
+        Decimals = decimals;
+        Unit = unit;
+        SizeChanged += e => Value = e.Value;
+    }
+
+    protected override ControlDefinition CreateControl()
+    {
+        ControlDefinition field = new ControlDefinition(UniqueId, "SizeInputField");
+        field.AddProperty(Value);
+        field.AddProperty(Min);
+        field.AddProperty(Max);
+        field.AddProperty(Decimals);
+        field.AddProperty(Unit);
+        return field;
+    }
+}

+ 4 - 0
src/PixiEditor.Extensions.Sdk/Api/FlyUI/StatelessElement.cs

@@ -9,6 +9,10 @@ public abstract class StatelessElement : LayoutElement, IStatelessElement<Contro
     {
     {
     }
     }
 
 
+    protected StatelessElement(Cursor? cursor) : base(cursor)
+    {
+    }
+
     public virtual ILayoutElement<ControlDefinition> Build()
     public virtual ILayoutElement<ControlDefinition> Build()
     {
     {
         return this;
         return this;

+ 27 - 0
src/PixiEditor.Extensions.Sdk/Api/FlyUI/TextField.cs

@@ -0,0 +1,27 @@
+using PixiEditor.Extensions.CommonApi.FlyUI;
+using PixiEditor.Extensions.CommonApi.FlyUI.Events;
+
+namespace PixiEditor.Extensions.Sdk.Api.FlyUI;
+
+public class TextField : LayoutElement
+{
+    public event ElementEventHandler<TextEventArgs> TextChanged
+    {
+        add => AddEvent(nameof(TextChanged), value);
+        remove => RemoveEvent(nameof(TextChanged), value);
+    }
+    public string Text { get; set; }
+
+    public TextField(string? text = null, Cursor? cursor = null) : base(cursor)
+    {
+        Text = text ?? string.Empty;
+        TextChanged += e => Text = e.Text;
+    }
+
+    protected override ControlDefinition CreateControl()
+    {
+        ControlDefinition textField = new ControlDefinition(UniqueId, "TextField");
+        textField.AddProperty(Text);
+        return textField;
+    }
+}

+ 14 - 2
src/PixiEditor.Extensions.Sdk/Api/IO/DocumentProvider.cs

@@ -1,12 +1,24 @@
+using PixiEditor.Extensions.CommonApi.Documents;
 using PixiEditor.Extensions.CommonApi.IO;
 using PixiEditor.Extensions.CommonApi.IO;
+using PixiEditor.Extensions.Sdk.Api.Documents;
 using PixiEditor.Extensions.Sdk.Bridge;
 using PixiEditor.Extensions.Sdk.Bridge;
 
 
 namespace PixiEditor.Extensions.Sdk.Api.IO;
 namespace PixiEditor.Extensions.Sdk.Api.IO;
 
 
 public class DocumentProvider : IDocumentProvider
 public class DocumentProvider : IDocumentProvider
 {
 {
-    public void ImportFile(string path, bool associatePath = true)
+    public IDocument ActiveDocument => Interop.GetActiveDocument();
+
+    public IDocument? ImportFile(string path, bool associatePath = true)
     {
     {
-        Native.import_file(path, associatePath);
+        return Interop.ImportFile(path, associatePath);
+    }
+
+    public IDocument? GetDocument(Guid id)
+    {
+        if (id == Guid.Empty)
+            throw new ArgumentException("Invalid document ID");
+
+        return new Document(id);
     }
     }
 }
 }

+ 25 - 0
src/PixiEditor.Extensions.Sdk/Bridge/Interop.Document.cs

@@ -0,0 +1,25 @@
+using PixiEditor.Extensions.CommonApi.Documents;
+using PixiEditor.Extensions.Sdk.Api.Documents;
+
+namespace PixiEditor.Extensions.Sdk.Bridge;
+
+internal static partial class Interop
+{
+    public static IDocument GetActiveDocument()
+    {
+        string document = Native.get_active_document();
+        if (document == null || !Guid.TryParse(document, out Guid id))
+            return null;
+
+        return new Document(id);
+    }
+
+    public static IDocument? ImportFile(string path, bool associatePath)
+    {
+        string document = Native.import_file(path, associatePath);
+        if (document == null || !Guid.TryParse(document, out Guid id))
+            return null;
+
+        return new Document(id);
+    }
+}

+ 7 - 1
src/PixiEditor.Extensions.Sdk/Bridge/Native.Document.cs

@@ -5,5 +5,11 @@ namespace PixiEditor.Extensions.Sdk.Bridge;
 internal partial class Native
 internal partial class Native
 {
 {
     [MethodImpl(MethodImplOptions.InternalCall)]
     [MethodImpl(MethodImplOptions.InternalCall)]
-    internal static extern void import_file(string path, bool associatePath);
+    internal static extern string import_file(string path, bool associatePath);
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    internal static extern string get_active_document();
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    internal static extern void resize_document(string documentId, int width, int height);
 }
 }

+ 16 - 2
src/PixiEditor.Extensions.Sdk/Bridge/Native.cs

@@ -5,6 +5,8 @@ using System.Runtime.CompilerServices;
 using PixiEditor.Extensions.CommonApi.FlyUI;
 using PixiEditor.Extensions.CommonApi.FlyUI;
 using PixiEditor.Extensions.CommonApi.FlyUI.Events;
 using PixiEditor.Extensions.CommonApi.FlyUI.Events;
 using PixiEditor.Extensions.Sdk.Api.FlyUI;
 using PixiEditor.Extensions.Sdk.Api.FlyUI;
+using PixiEditor.Extensions.Sdk.Utilities;
+using ProtoBuf;
 
 
 namespace PixiEditor.Extensions.Sdk.Bridge;
 namespace PixiEditor.Extensions.Sdk.Bridge;
 
 
@@ -50,11 +52,23 @@ internal static partial class Native
     }
     }
 
 
     [ApiExport("raise_element_event")]
     [ApiExport("raise_element_event")]
-    internal static void EventRaised(int internalControlId, string eventName) //TOOD: Args
+    internal static void EventRaised(int internalControlId, string eventName, IntPtr eventData, int dataLength)
     {
     {
         if (LayoutElementsStore.LayoutElements.TryGetValue((int)internalControlId, out ILayoutElement<ControlDefinition> element))
         if (LayoutElementsStore.LayoutElements.TryGetValue((int)internalControlId, out ILayoutElement<ControlDefinition> element))
         {
         {
-            element.RaiseEvent(eventName ?? "", new ElementEventArgs { Sender = element });
+            byte[] data = InteropUtility.IntPtrToByteArray(eventData, dataLength);
+            ElementEventArgs args = ElementEventArgs.Deserialize(data);
+            args.Sender = element;
+            element.RaiseEvent(eventName ?? "", args);
+        }
+    }
+
+    [ApiExport("raise_element_text_event")]
+    internal static void TextEventRaised(int internalControlId, string eventName, string text)
+    {
+        if (LayoutElementsStore.LayoutElements.TryGetValue((int)internalControlId, out ILayoutElement<ControlDefinition> element))
+        {
+            element.RaiseEvent(eventName ?? "", new TextEventArgs(text) { Sender = element });
         }
         }
     }
     }
 
 

BIN
src/PixiEditor.Extensions.Sdk/build/PixiEditor.Api.CGlueMSBuild.dll


BIN
src/PixiEditor.Extensions.Sdk/build/PixiEditor.Extensions.MSPackageBuilder.dll


+ 30 - 4
src/PixiEditor.Extensions.WasmRuntime/Api/DocumentsApi.cs

@@ -6,17 +6,43 @@ namespace PixiEditor.Extensions.WasmRuntime.Api;
 internal class DocumentsApi : ApiGroupHandler
 internal class DocumentsApi : ApiGroupHandler
 {
 {
     [ApiFunction("import_file")]
     [ApiFunction("import_file")]
-    public void ImportFile(string path, bool associatePath = false)
+    public string ImportFile(string path, bool associatePath = false)
     {
     {
         PermissionUtility.ThrowIfLacksPermissions(Extension.Metadata, ExtensionPermissions.OpenDocuments, "ImportFile");
         PermissionUtility.ThrowIfLacksPermissions(Extension.Metadata, ExtensionPermissions.OpenDocuments, "ImportFile");
 
 
         string fullPath = ResourcesUtility.ToResourcesFullPath(Extension, path);
         string fullPath = ResourcesUtility.ToResourcesFullPath(Extension, path);
 
 
-        if (!File.Exists(fullPath))
+        string id = string.Empty;
+        if (File.Exists(fullPath))
         {
         {
-            return;
+            id = Api.Documents.ImportFile(fullPath, associatePath)?.Id.ToString() ?? string.Empty;
         }
         }
 
 
-        Api.Documents.ImportFile(fullPath, associatePath);
+        return id;
+    }
+
+    [ApiFunction("get_active_document")]
+    public string GetActiveDocument()
+    {
+        var activeDocument = Api.Documents.ActiveDocument;
+        string id = activeDocument?.Id.ToString() ?? string.Empty;
+        return id;
+    }
+
+    [ApiFunction("resize_document")]
+    public void ResizeDocument(string documentId, int width, int height)
+    {
+        if (!Guid.TryParse(documentId, out Guid id))
+        {
+            throw new ArgumentException("Invalid document ID");
+        }
+
+        var document = Api.Documents.GetDocument(id);
+        if (document == null)
+        {
+            throw new ArgumentException("Document not found");
+        }
+
+        document.Resize(width, height);
     }
     }
 }
 }

+ 7 - 2
src/PixiEditor.Extensions.WasmRuntime/Api/FlyUiApi.cs

@@ -13,11 +13,16 @@ internal class FlyUIApi : ApiGroupHandler
 
 
         LayoutBuilder.ManagedElements[controlId].AddEvent(eventName, (args) =>
         LayoutBuilder.ManagedElements[controlId].AddEvent(eventName, (args) =>
         {
         {
-            var action = Instance.GetAction<int, int>("raise_element_event");
+            var action = Instance.GetAction<int, int, int, int>("raise_element_event");
             var ptr = WasmMemoryUtility.WriteString(eventName);
             var ptr = WasmMemoryUtility.WriteString(eventName);
 
 
-            action.Invoke(controlId, ptr);
+            var data = args.Serialize();
+            var dataPtr = WasmMemoryUtility.WriteBytes(data);
+            int dataSize = data.Length;
+
+            action.Invoke(controlId, ptr, dataPtr, dataSize);
             WasmMemoryUtility.Free(ptr);
             WasmMemoryUtility.Free(ptr);
+            WasmMemoryUtility.Free(dataPtr);
         });
         });
     }
     }
 
 

+ 1 - 1
src/PixiEditor.Extensions.WasmRuntime/Api/LocalizationApi.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Extensions.WasmRuntime.Api;
 namespace PixiEditor.Extensions.WasmRuntime.Api;
 
 

+ 2 - 4
src/PixiEditor.Extensions.WasmRuntime/Api/Palettes/PalettesApi.cs

@@ -1,9 +1,7 @@
-using CommunityToolkit.HighPerformance;
-using PixiEditor.Extensions.CommonApi.Palettes;
-using PixiEditor.Extensions.WasmRuntime.Api.Palettes;
+using PixiEditor.Extensions.CommonApi.Palettes;
 using ProtoBuf;
 using ProtoBuf;
 
 
-namespace PixiEditor.Extensions.WasmRuntime.Api;
+namespace PixiEditor.Extensions.WasmRuntime.Api.Palettes;
 
 
 internal class PalettesApi : ApiGroupHandler
 internal class PalettesApi : ApiGroupHandler
 {
 {

+ 2 - 4
src/PixiEditor.Extensions.WasmRuntime/Api/WindowingApi.cs

@@ -1,10 +1,8 @@
-using PixiEditor.Extensions.Common.Localization;
-using PixiEditor.Extensions.CommonApi.Async;
-using PixiEditor.Extensions.CommonApi.Utilities;
-using PixiEditor.Extensions.CommonApi.Windowing;
+using PixiEditor.Extensions.CommonApi.Windowing;
 using PixiEditor.Extensions.FlyUI.Elements;
 using PixiEditor.Extensions.FlyUI.Elements;
 using PixiEditor.Extensions.WasmRuntime.Utilities;
 using PixiEditor.Extensions.WasmRuntime.Utilities;
 using PixiEditor.Extensions.Windowing;
 using PixiEditor.Extensions.Windowing;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Extensions.WasmRuntime.Api;
 namespace PixiEditor.Extensions.WasmRuntime.Api;
 
 

+ 11 - 8
src/PixiEditor.Extensions.WasmRuntime/WasmExtensionInstance.cs

@@ -21,7 +21,7 @@ public partial class WasmExtensionInstance : Extension
     private Linker Linker { get; }
     private Linker Linker { get; }
     private Store Store { get; }
     private Store Store { get; }
     private Module Module { get; }
     private Module Module { get; }
-    
+
     private LayoutBuilder LayoutBuilder { get; set; }
     private LayoutBuilder LayoutBuilder { get; set; }
     internal ObjectManager NativeObjectManager { get; set; }
     internal ObjectManager NativeObjectManager { get; set; }
     internal AsyncCallsManager AsyncHandleManager { get; set; }
     internal AsyncCallsManager AsyncHandleManager { get; set; }
@@ -30,7 +30,7 @@ public partial class WasmExtensionInstance : Extension
 
 
     private string modulePath;
     private string modulePath;
     private List<ApiModule> modules = new();
     private List<ApiModule> modules = new();
-    
+
     public override string Location => modulePath;
     public override string Location => modulePath;
 
 
     partial void LinkApiFunctions();
     partial void LinkApiFunctions();
@@ -49,7 +49,7 @@ public partial class WasmExtensionInstance : Extension
         AsyncHandleManager = new AsyncCallsManager();
         AsyncHandleManager = new AsyncCallsManager();
         AsyncHandleManager.OnAsyncCallCompleted += OnAsyncCallCompleted;
         AsyncHandleManager.OnAsyncCallCompleted += OnAsyncCallCompleted;
         AsyncHandleManager.OnAsyncCallFaulted += OnAsyncCallFaulted;
         AsyncHandleManager.OnAsyncCallFaulted += OnAsyncCallFaulted;
-        
+
         LinkApiFunctions();
         LinkApiFunctions();
         Linker.DefineModule(Store, Module);
         Linker.DefineModule(Store, Module);
 
 
@@ -66,22 +66,25 @@ public partial class WasmExtensionInstance : Extension
     protected override void OnInitialized()
     protected override void OnInitialized()
     {
     {
         modules.Add(new PreferencesModule(this, Api.Preferences));
         modules.Add(new PreferencesModule(this, Api.Preferences));
-        modules.Add(new CommandModule(this, Api.Commands, (ICommandSupervisor)Api.Services.GetService(typeof(ICommandSupervisor))));
+        modules.Add(new CommandModule(this, Api.Commands,
+            (ICommandSupervisor)Api.Services.GetService(typeof(ICommandSupervisor))));
         LayoutBuilder = new LayoutBuilder((ElementMap)Api.Services.GetService(typeof(ElementMap)));
         LayoutBuilder = new LayoutBuilder((ElementMap)Api.Services.GetService(typeof(ElementMap)));
 
 
         //SetElementMap();
         //SetElementMap();
         Instance.GetAction("initialize").Invoke();
         Instance.GetAction("initialize").Invoke();
         base.OnInitialized();
         base.OnInitialized();
     }
     }
-    
+
     private void OnAsyncCallCompleted(int handle, int result)
     private void OnAsyncCallCompleted(int handle, int result)
     {
     {
-        Instance.GetAction<int, int>("async_call_completed").Invoke(handle, result);
+        Dispatcher.UIThread.Invoke(() =>
+            Instance.GetAction<int, int>("async_call_completed").Invoke(handle, result));
     }
     }
-    
+
     private void OnAsyncCallFaulted(int handle, string exceptionMessage)
     private void OnAsyncCallFaulted(int handle, string exceptionMessage)
     {
     {
-        Instance.GetAction<int, string>("async_call_faulted").Invoke(handle, exceptionMessage);
+        Dispatcher.UIThread.Invoke(() =>
+            Instance.GetAction<int, string>("async_call_faulted").Invoke(handle, exceptionMessage));
     }
     }
 
 
     private void SetElementMap()
     private void SetElementMap()

+ 1 - 1
src/PixiEditor.Extensions/Exceptions/RecoverableException.cs

@@ -1,5 +1,5 @@
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Extensions.Exceptions;
 namespace PixiEditor.Extensions.Exceptions;
 
 

+ 1 - 1
src/PixiEditor.Extensions/FlyUI/Elements/Border.cs

@@ -106,9 +106,9 @@ public class Border : SingleChildLayoutElement
         yield return CornerRadius;
         yield return CornerRadius;
         yield return Padding;
         yield return Padding;
         yield return Margin;
         yield return Margin;
-        yield return BackgroundColor;
         yield return Width;
         yield return Width;
         yield return Height;
         yield return Height;
+        yield return BackgroundColor;
     }
     }
 
 
     protected override void DeserializeControlProperties(List<object> values)
     protected override void DeserializeControlProperties(List<object> values)

+ 20 - 1
src/PixiEditor.Extensions/FlyUI/Elements/CheckBox.cs

@@ -6,20 +6,29 @@ namespace PixiEditor.Extensions.FlyUI.Elements;
 
 
 public class CheckBox : SingleChildLayoutElement
 public class CheckBox : SingleChildLayoutElement
 {
 {
-    private Avalonia.Controls.CheckBox checkbox;    
+    private bool isChecked;
+    private Avalonia.Controls.CheckBox checkbox;
+
     public event ElementEventHandler<ToggleEventArgs> CheckedChanged
     public event ElementEventHandler<ToggleEventArgs> CheckedChanged
     {
     {
         add => AddEvent(nameof(CheckedChanged), value);
         add => AddEvent(nameof(CheckedChanged), value);
         remove => RemoveEvent(nameof(CheckedChanged), value);
         remove => RemoveEvent(nameof(CheckedChanged), value);
     }
     }
 
 
+    public bool IsChecked { get => isChecked; set => SetField(ref isChecked, value); }
     protected override Control CreateNativeControl()
     protected override Control CreateNativeControl()
     {
     {
         checkbox = new Avalonia.Controls.CheckBox();
         checkbox = new Avalonia.Controls.CheckBox();
+
         Binding binding =
         Binding binding =
             new Binding(nameof(Child)) { Source = this, Converter = LayoutElementToNativeControlConverter.Instance };
             new Binding(nameof(Child)) { Source = this, Converter = LayoutElementToNativeControlConverter.Instance };
         checkbox.Bind(ContentControl.ContentProperty, binding);
         checkbox.Bind(ContentControl.ContentProperty, binding);
 
 
+        Binding isCheckedBinding =
+            new Binding(nameof(IsChecked)) { Source = this, Mode = BindingMode.TwoWay };
+
+        checkbox.Bind(Avalonia.Controls.CheckBox.IsCheckedProperty, isCheckedBinding);
+
         checkbox.IsCheckedChanged += (sender, args) => RaiseEvent(
         checkbox.IsCheckedChanged += (sender, args) => RaiseEvent(
             nameof(CheckedChanged),
             nameof(CheckedChanged),
             new ToggleEventArgs((sender as Avalonia.Controls.CheckBox).IsChecked.Value) { Sender = this });
             new ToggleEventArgs((sender as Avalonia.Controls.CheckBox).IsChecked.Value) { Sender = this });
@@ -36,4 +45,14 @@ public class CheckBox : SingleChildLayoutElement
     {
     {
         checkbox.Content = null;
         checkbox.Content = null;
     }
     }
+
+    protected override IEnumerable<object> GetControlProperties()
+    {
+        yield return IsChecked;
+    }
+
+    protected override void DeserializeControlProperties(List<object> values)
+    {
+        IsChecked = (bool)values[0];
+    }
 }
 }

+ 20 - 1
src/PixiEditor.Extensions/FlyUI/Elements/LayoutElement.cs

@@ -56,7 +56,8 @@ public abstract class LayoutElement : ILayoutElement<Control>, INotifyPropertyCh
     {
     {
         if (Cursor != null)
         if (Cursor != null)
         {
         {
-            control.Cursor = new Avalonia.Input.Cursor((StandardCursorType)(Cursor.Value.BuiltInCursor ?? BuiltInCursor.None));
+            control.Cursor =
+                new Avalonia.Input.Cursor((StandardCursorType)(Cursor.Value.BuiltInCursor ?? BuiltInCursor.None));
         }
         }
 
 
         SubscribeBasicEvents(control);
         SubscribeBasicEvents(control);
@@ -88,6 +89,15 @@ public abstract class LayoutElement : ILayoutElement<Control>, INotifyPropertyCh
             _events.Add(eventName, new List<ElementEventHandler>());
             _events.Add(eventName, new List<ElementEventHandler>());
         }
         }
 
 
+        // I'm unsure if it's a correct solution, it prevents resubscription of the same event during
+        // state change. If event count for the same name is bigger than 1, the same event will be called
+        // twice in extension, it won't resolve correct handle within the extension.
+        // TODO: Research if it's a correct solution
+        if (_events[eventName].Count > 0)
+        {
+            _events[eventName].Clear();
+        }
+
         _events[eventName].Add(eventHandler);
         _events[eventName].Add(eventHandler);
     }
     }
 
 
@@ -103,6 +113,15 @@ public abstract class LayoutElement : ILayoutElement<Control>, INotifyPropertyCh
             _events.Add(eventName, new List<ElementEventHandler>());
             _events.Add(eventName, new List<ElementEventHandler>());
         }
         }
 
 
+        // I'm unsure if it's a correct solution, it prevents resubscription of the same event during
+        // state change. If event count for the same name is bigger than 1, the same event will be called
+        // twice in extension, it won't resolve correct handle within the extension.
+        // TODO: Research if it's a correct solution
+        if (_events[eventName].Count > 0)
+        {
+            _events[eventName].Clear();
+        }
+
         _events[eventName].Add((args => eventHandler((T)args)));
         _events[eventName].Add((args => eventHandler((T)args)));
     }
     }
 
 

+ 72 - 0
src/PixiEditor.Extensions/FlyUI/Elements/SizeInputField.cs

@@ -0,0 +1,72 @@
+using Avalonia.Controls;
+using Avalonia.Data;
+using PixiEditor.Extensions.CommonApi.FlyUI.Events;
+using PixiEditor.UI.Common.Controls;
+
+namespace PixiEditor.Extensions.FlyUI.Elements;
+
+public class SizeInputField : LayoutElement
+{
+    private double value;
+    private double min = 1;
+    private double max;
+    private int decimals;
+    private string unit;
+
+    public event ElementEventHandler<NumberEventArgs> SizeChanged
+    {
+        add => AddEvent(nameof(SizeChanged), value);
+        remove => RemoveEvent(nameof(SizeChanged), value);
+    }
+
+    public double Value
+    {
+        get => value;
+        set => SetField(ref this.value, value, nameof(Value));
+    }
+    public double Min { get => min; set => SetField(ref min, value); }
+    public double Max { get => max; set => SetField(ref max, value); }
+    public int Decimals { get => decimals; set => SetField(ref decimals, value); }
+    public string Unit { get => unit; set => SetField(ref unit, value); }
+
+    private bool suppressSizeChanged;
+
+    protected override Control CreateNativeControl()
+    {
+        SizeInput sizeInput = new SizeInput { MinSize = Min, MaxSize = Max, Decimals = Decimals, Unit = Unit };
+
+        Binding binding = new Binding { Source = this, Path = nameof(Value), Mode = BindingMode.TwoWay, };
+
+        sizeInput.Bind(SizeInput.SizeProperty, binding);
+
+        sizeInput.PropertyChanged += (sender, args) =>
+        {
+            if (args.Property != SizeInput.SizeProperty || suppressSizeChanged) return;
+
+            Value = sizeInput.Size;
+            RaiseEvent(nameof(SizeChanged), new NumberEventArgs(Value));
+        };
+
+        return sizeInput;
+    }
+
+    protected override IEnumerable<object> GetControlProperties()
+    {
+        yield return Value;
+        yield return Min;
+        yield return Max;
+        yield return Decimals;
+        yield return Unit;
+    }
+
+    protected override void DeserializeControlProperties(List<object> values)
+    {
+        suppressSizeChanged = true;
+        Value = (double)values[0];
+        Min = (double)values[1];
+        Max = (double)values[2];
+        Decimals = (int)values[3];
+        Unit = (string)values[4];
+        suppressSizeChanged = false;
+    }
+}

+ 48 - 0
src/PixiEditor.Extensions/FlyUI/Elements/TextField.cs

@@ -0,0 +1,48 @@
+using Avalonia.Controls;
+using Avalonia.Data;
+using PixiEditor.Extensions.CommonApi.FlyUI.Events;
+
+namespace PixiEditor.Extensions.FlyUI.Elements;
+
+internal class TextField : LayoutElement
+{
+    private string text;
+    public event ElementEventHandler TextChanged
+    {
+        add => AddEvent(nameof(TextChanged), value);
+        remove => RemoveEvent(nameof(TextChanged), value);
+    }
+
+    public string Text { get => text; set => SetField(ref text, value); }
+
+    public TextField(string text)
+    {
+        Text = text;
+    }
+
+    protected override Control CreateNativeControl()
+    {
+        TextBox textBox = new TextBox();
+
+        Binding binding =
+            new Binding(nameof(Text)) { Source = this, Mode = BindingMode.TwoWay };
+        textBox.Bind(TextBox.TextProperty, binding);
+
+        textBox.TextChanged += (s, e) =>
+        {
+            RaiseEvent(nameof(TextChanged), new TextEventArgs(textBox.Text));
+        };
+
+        return textBox;
+    }
+
+    protected override IEnumerable<object> GetControlProperties()
+    {
+        yield return Text;
+    }
+
+    protected override void DeserializeControlProperties(List<object> values)
+    {
+        Text = (string)values[0];
+    }
+}

+ 1 - 1
src/PixiEditor.Extensions/Metadata/ExtensionMetadata.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Extensions.Metadata;
 namespace PixiEditor.Extensions.Metadata;
 
 

+ 7 - 1
src/PixiEditor.Extensions/PixiEditor.Extensions.csproj

@@ -9,7 +9,7 @@
       <Title>PixiEditor Extensions</Title>
       <Title>PixiEditor Extensions</Title>
       <Authors>PixiEditor Organization</Authors>
       <Authors>PixiEditor Organization</Authors>
       <Copyright>PixiEditor Organization</Copyright>
       <Copyright>PixiEditor Organization</Copyright>
-      <Description>Package for creating custom extensions for pixel art editor PixiEditor</Description>
+      <Description>Package for creating custom extensions for univrsal 2D editor PixiEditor</Description>
     </PropertyGroup>
     </PropertyGroup>
 
 
     <ItemGroup>
     <ItemGroup>
@@ -25,5 +25,11 @@
       <ProjectReference Include="..\Drawie\src\Drawie.Numerics\Drawie.Numerics.csproj" />
       <ProjectReference Include="..\Drawie\src\Drawie.Numerics\Drawie.Numerics.csproj" />
       <ProjectReference Include="..\PixiEditor.Extensions.CommonApi\PixiEditor.Extensions.CommonApi.csproj" />
       <ProjectReference Include="..\PixiEditor.Extensions.CommonApi\PixiEditor.Extensions.CommonApi.csproj" />
       <ProjectReference Include="..\PixiEditor.OperatingSystem\PixiEditor.OperatingSystem.csproj" />
       <ProjectReference Include="..\PixiEditor.OperatingSystem\PixiEditor.OperatingSystem.csproj" />
+      <ProjectReference Include="..\PixiEditor.UI.Common\PixiEditor.UI.Common.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <Folder Include="Common\Localization\" />
+      <Folder Include="Controls\" />
     </ItemGroup>
     </ItemGroup>
 </Project>
 </Project>

+ 1 - 0
src/PixiEditor.Extensions/UI/Panels/ColumnPanel.cs

@@ -57,6 +57,7 @@ public class ColumnPanel : Panel
             or MainAxisAlignment.SpaceEvenly;
             or MainAxisAlignment.SpaceEvenly;
         double spaceBetween = 0;
         double spaceBetween = 0;
         double spaceBeforeAfter = 0;
         double spaceBeforeAfter = 0;
+
         if (stretchPlacement)
         if (stretchPlacement)
         {
         {
             double freeSpace = finalSize.Height - totalYSpace;
             double freeSpace = finalSize.Height - totalYSpace;

+ 2 - 2
src/PixiEditor/Helpers/Behaviours/TextBoxFocusBehavior.cs → src/PixiEditor.UI.Common/Behaviors/TextBoxFocusBehavior.cs

@@ -5,9 +5,9 @@ using Avalonia.Interactivity;
 using Avalonia.Threading;
 using Avalonia.Threading;
 using Avalonia.Xaml.Interactivity;
 using Avalonia.Xaml.Interactivity;
 
 
-namespace PixiEditor.Helpers.Behaviours;
+namespace PixiEditor.UI.Common.Behaviors;
 
 
-internal class TextBoxFocusBehavior : Behavior<TextBox>
+public class TextBoxFocusBehavior : Behavior<TextBox>
 {
 {
     public static readonly StyledProperty<bool> SelectOnMouseClickProperty =
     public static readonly StyledProperty<bool> SelectOnMouseClickProperty =
         AvaloniaProperty.Register<TextBoxFocusBehavior, bool>(
         AvaloniaProperty.Register<TextBoxFocusBehavior, bool>(

+ 6 - 5
src/PixiEditor/Views/Input/NumberInput.cs → src/PixiEditor.UI.Common/Controls/NumberInput.cs

@@ -1,6 +1,5 @@
 using System.Data;
 using System.Data;
 using System.Globalization;
 using System.Globalization;
-using System.Linq;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
@@ -11,13 +10,15 @@ using Avalonia.Interactivity;
 using Avalonia.Layout;
 using Avalonia.Layout;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.Xaml.Interactivity;
 using Avalonia.Xaml.Interactivity;
-using PixiEditor.Helpers.Behaviours;
+using PixiEditor.UI.Common.Behaviors;
 using Action = System.Action;
 using Action = System.Action;
 
 
-namespace PixiEditor.Views.Input;
+namespace PixiEditor.UI.Common.Controls;
 
 
-internal partial class NumberInput : TextBox
+public partial class NumberInput : TextBox
 {
 {
+    public static event Action<BehaviorCollection> AttachGlobalBehaviors;
+
     public static readonly StyledProperty<double> ValueProperty =
     public static readonly StyledProperty<double> ValueProperty =
         AvaloniaProperty.Register<NumberInput, double>(
         AvaloniaProperty.Register<NumberInput, double>(
             nameof(Value), 0, coerce: CoerceValue);
             nameof(Value), 0, coerce: CoerceValue);
@@ -154,7 +155,7 @@ internal partial class NumberInput : TextBox
     public NumberInput()
     public NumberInput()
     {
     {
         BehaviorCollection behaviors = Interaction.GetBehaviors(this);
         BehaviorCollection behaviors = Interaction.GetBehaviors(this);
-        behaviors.Add(new GlobalShortcutFocusBehavior());
+        AttachGlobalBehaviors?.Invoke(behaviors);
         TextBoxFocusBehavior behavior = new() { DeselectOnFocusLoss = true, SelectOnMouseClick = true };
         TextBoxFocusBehavior behavior = new() { DeselectOnFocusLoss = true, SelectOnMouseClick = true };
         BindTextBoxBehavior(behavior);
         BindTextBoxBehavior(behavior);
         behaviors.Add(behavior);
         behaviors.Add(behavior);

+ 10 - 11
src/PixiEditor/Views/Input/SizeInput.axaml → src/PixiEditor.UI.Common/Controls/SizeInput.axaml

@@ -1,11 +1,10 @@
-<UserControl x:Class="PixiEditor.Views.Input.SizeInput"
-             x:ClassModifier="internal"
+<UserControl x:Class="PixiEditor.UI.Common.Controls.SizeInput"
              xmlns="https://github.com/avaloniaui"
              xmlns="https://github.com/avaloniaui"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-             xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
-             xmlns:input="clr-namespace:PixiEditor.Views.Input"
+             xmlns:controls="clr-namespace:PixiEditor.UI.Common.Controls;assembly=PixiEditor.UI.Common"
+             xmlns:localization="clr-namespace:PixiEditor.UI.Common.Localization"
              mc:Ignorable="d" Focusable="True"
              mc:Ignorable="d" Focusable="True"
              d:DesignHeight="30" Name="uc"
              d:DesignHeight="30" Name="uc"
              FlowDirection="LeftToRight">
              FlowDirection="LeftToRight">
@@ -28,23 +27,23 @@
                 <ColumnDefinition Width="2"/>
                 <ColumnDefinition Width="2"/>
                 <ColumnDefinition Width="Auto"/>
                 <ColumnDefinition Width="Auto"/>
             </Grid.ColumnDefinitions>
             </Grid.ColumnDefinitions>
-            <input:NumberInput IsEnabled="{Binding IsEnabled, ElementName=uc}" HorizontalContentAlignment="Right"
+            <controls:NumberInput IsEnabled="{Binding IsEnabled, ElementName=uc}" HorizontalContentAlignment="Right"
                      BorderThickness="0" Background="Transparent"
                      BorderThickness="0" Background="Transparent"
                      Foreground="{Binding Foreground, ElementName=uc}" Focusable="True"
                      Foreground="{Binding Foreground, ElementName=uc}" Focusable="True"
                      Margin="0,0,5,0" VerticalAlignment="Center"
                      Margin="0,0,5,0" VerticalAlignment="Center"
-                     Decimals="{Binding Decimals, RelativeSource={RelativeSource FindAncestor, AncestorType=input:SizeInput}}"
+                     Decimals="{Binding Decimals, RelativeSource={RelativeSource FindAncestor, AncestorType=controls:SizeInput}}"
                      x:Name="input"
                      x:Name="input"
-                     Value="{Binding Size, RelativeSource={RelativeSource FindAncestor, AncestorType=input:SizeInput}, Mode=TwoWay}"
-                     Min="{Binding MinSize, RelativeSource={RelativeSource FindAncestor, AncestorType=input:SizeInput}}"
-                     Max="{Binding MaxSize, RelativeSource={RelativeSource FindAncestor, AncestorType=input:SizeInput}}"
+                     Value="{Binding Size, RelativeSource={RelativeSource FindAncestor, AncestorType=controls:SizeInput}, Mode=TwoWay}"
+                     Min="{Binding MinSize, RelativeSource={RelativeSource FindAncestor, AncestorType=controls:SizeInput}}"
+                     Max="{Binding MaxSize, RelativeSource={RelativeSource FindAncestor, AncestorType=controls:SizeInput}}"
                      d:Value="22"
                      d:Value="22"
                      FocusNext="{Binding FocusNext, ElementName=uc}"
                      FocusNext="{Binding FocusNext, ElementName=uc}"
                      SelectOnMouseClick="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
                      SelectOnMouseClick="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
                      ConfirmOnEnter="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
                      ConfirmOnEnter="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
-                     Width="43"/>
+                     Width="53"/>
             <Grid Grid.Column="1" Background="{Binding BorderBrush, ElementName=border}"
             <Grid Grid.Column="1" Background="{Binding BorderBrush, ElementName=border}"
                   d:Background="{DynamicResource ThemeAccentBrush}"/>
                   d:Background="{DynamicResource ThemeAccentBrush}"/>
-            <TextBlock ui:Translator.Key="{Binding Unit, ElementName=uc}" 
+            <TextBlock localization:Translator.Key="{Binding Unit, ElementName=uc}"
                        TextAlignment="Right"
                        TextAlignment="Right"
                        Grid.Column="2" Margin="5,0" VerticalAlignment="Center"
                        Grid.Column="2" Margin="5,0" VerticalAlignment="Center"
             />
             />

+ 2 - 3
src/PixiEditor/Views/Input/SizeInput.axaml.cs → src/PixiEditor.UI.Common/Controls/SizeInput.axaml.cs

@@ -1,14 +1,13 @@
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Input;
-using PixiEditor.Models.Dialogs;
 
 
-namespace PixiEditor.Views.Input;
+namespace PixiEditor.UI.Common.Controls;
 
 
 /// <summary>
 /// <summary>
 ///     Interaction logic for SizeInput.xaml.
 ///     Interaction logic for SizeInput.xaml.
 /// </summary>
 /// </summary>
-internal partial class SizeInput : UserControl
+public partial class SizeInput : UserControl
 {
 {
     public static readonly StyledProperty<double> SizeProperty =
     public static readonly StyledProperty<double> SizeProperty =
         AvaloniaProperty.Register<SizeInput, double>(nameof(Size), defaultValue: 1);
         AvaloniaProperty.Register<SizeInput, double>(nameof(Size), defaultValue: 1);

+ 1 - 1
src/PixiEditor.Extensions/Helpers/EnumHelpers.cs → src/PixiEditor.UI.Common/Localization/EnumHelpers.cs

@@ -1,6 +1,6 @@
 using System.ComponentModel;
 using System.ComponentModel;
 
 
-namespace PixiEditor.Extensions.Helpers;
+namespace PixiEditor.UI.Common.Localization;
 
 
 public static class EnumHelpers
 public static class EnumHelpers
 {
 {

+ 1 - 2
src/PixiEditor.Extensions/UI/ExternalProperty.cs → src/PixiEditor.UI.Common/Localization/ExternalProperty.cs

@@ -1,7 +1,6 @@
 using Avalonia;
 using Avalonia;
-using PixiEditor.Extensions.Common.Localization;
 
 
-namespace PixiEditor.Extensions.UI;
+namespace PixiEditor.UI.Common.Localization;
 
 
 public abstract class ExternalProperty
 public abstract class ExternalProperty
 {
 {

+ 1 - 1
src/PixiEditor.Extensions/UI/ICustomTranslatorElement.cs → src/PixiEditor.UI.Common/Localization/ICustomTranslatorElement.cs

@@ -1,6 +1,6 @@
 using Avalonia;
 using Avalonia;
 
 
-namespace PixiEditor.Extensions.UI;
+namespace PixiEditor.UI.Common.Localization;
 
 
 public interface ICustomTranslatorElement
 public interface ICustomTranslatorElement
 {
 {

+ 1 - 1
src/PixiEditor.Extensions/Common/Localization/ILocalizationProvider.cs → src/PixiEditor.UI.Common/Localization/ILocalizationProvider.cs

@@ -1,4 +1,4 @@
-namespace PixiEditor.Extensions.Common.Localization;
+namespace PixiEditor.UI.Common.Localization;
 
 
 public interface ILocalizationProvider
 public interface ILocalizationProvider
 {
 {

+ 1 - 2
src/PixiEditor.Extensions/Common/Localization/Language.cs → src/PixiEditor.UI.Common/Localization/Language.cs

@@ -1,8 +1,7 @@
 using System.Diagnostics;
 using System.Diagnostics;
-using System.Windows;
 using Avalonia.Media;
 using Avalonia.Media;
 
 
-namespace PixiEditor.Extensions.Common.Localization;
+namespace PixiEditor.UI.Common.Localization;
 
 
 [DebuggerDisplay("{LanguageData.Name}, strings: {Locale.Count}")]
 [DebuggerDisplay("{LanguageData.Name}, strings: {Locale.Count}")]
 public class Language
 public class Language

+ 1 - 1
src/PixiEditor.Extensions/Common/Localization/LanguageData.cs → src/PixiEditor.UI.Common/Localization/LanguageData.cs

@@ -1,7 +1,7 @@
 using System.Globalization;
 using System.Globalization;
 using Newtonsoft.Json;
 using Newtonsoft.Json;
 
 
-namespace PixiEditor.Extensions.Common.Localization;
+namespace PixiEditor.UI.Common.Localization;
 
 
 public class LanguageData
 public class LanguageData
 {
 {

+ 1 - 2
src/PixiEditor.Extensions/Common/Localization/LocalizationData.cs → src/PixiEditor.UI.Common/Localization/LocalizationData.cs

@@ -1,7 +1,6 @@
 using System.Diagnostics;
 using System.Diagnostics;
-using System.IO;
 
 
-namespace PixiEditor.Extensions.Common.Localization;
+namespace PixiEditor.UI.Common.Localization;
 
 
 [DebuggerDisplay("{Languages.Count} Language(s)")]
 [DebuggerDisplay("{Languages.Count} Language(s)")]
 public class LocalizationData
 public class LocalizationData

+ 1 - 1
src/PixiEditor.Extensions/Common/Localization/LocalizationKeyShowMode.cs → src/PixiEditor.UI.Common/Localization/LocalizationKeyShowMode.cs

@@ -1,4 +1,4 @@
-namespace PixiEditor.Extensions.Common.Localization;
+namespace PixiEditor.UI.Common.Localization;
 
 
 public enum LocalizationKeyShowMode
 public enum LocalizationKeyShowMode
 {
 {

+ 1 - 1
src/PixiEditor.Extensions/Common/Localization/LocalizedString.cs → src/PixiEditor.UI.Common/Localization/LocalizedString.cs

@@ -1,4 +1,4 @@
-namespace PixiEditor.Extensions.Common.Localization;
+namespace PixiEditor.UI.Common.Localization;
 
 
 public struct LocalizedString
 public struct LocalizedString
 {
 {

+ 1 - 5
src/PixiEditor.Extensions/UI/Translator.cs → src/PixiEditor.UI.Common/Localization/Translator.cs

@@ -2,14 +2,10 @@
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Controls.Documents;
 using Avalonia.Controls.Documents;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
-using Avalonia.Data;
-using Avalonia.Data.Core;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.Reactive;
 using Avalonia.Reactive;
-using PixiEditor.Extensions.Common.Localization;
-using PixiEditor.Extensions.Helpers;
 
 
-namespace PixiEditor.Extensions.UI;
+namespace PixiEditor.UI.Common.Localization;
 
 
 public class Translator : Control
 public class Translator : Control
 {
 {

+ 8 - 0
src/PixiEditor.UI.Common/PixiEditor.UI.Common.csproj

@@ -17,5 +17,13 @@
       <PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
       <PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
       <PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)" />
       <PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)" />
       <PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.5" />
       <PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.5" />
+      <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <Compile Update="Controls\SizeInput.axaml.cs">
+        <DependentUpon>SizeInput.axaml</DependentUpon>
+        <SubType>Code</SubType>
+      </Compile>
     </ItemGroup>
     </ItemGroup>
 </Project>
 </Project>

+ 1 - 1
src/PixiEditor.Windows/Win32.cs

@@ -1,7 +1,7 @@
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Text;
 using System.Text;
 
 
-namespace PixiEditor.Helpers;
+namespace PixiEditor.Windows;
 internal class Win32
 internal class Win32
 {
 {
     public const uint MONITOR_DEFAULTTONEAREST = 0x00000002;
     public const uint MONITOR_DEFAULTTONEAREST = 0x00000002;

+ 0 - 1
src/PixiEditor.Windows/WindowsInputKeys.cs

@@ -1,7 +1,6 @@
 using System.Text;
 using System.Text;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Win32.Input;
 using Avalonia.Win32.Input;
-using PixiEditor.Helpers;
 using PixiEditor.OperatingSystem;
 using PixiEditor.OperatingSystem;
 
 
 namespace PixiEditor.Windows;
 namespace PixiEditor.Windows;

+ 0 - 1
src/PixiEditor.Windows/WindowsOperatingSystem.cs

@@ -1,7 +1,6 @@
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Threading;
 using Avalonia.Threading;
-using PixiEditor.Helpers;
 using PixiEditor.OperatingSystem;
 using PixiEditor.OperatingSystem;
 
 
 namespace PixiEditor.Windows;
 namespace PixiEditor.Windows;

+ 2 - 2
src/PixiEditor/App.axaml

@@ -2,8 +2,8 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:themes="clr-namespace:PixiEditor.UI.Common.Themes;assembly=PixiEditor.UI.Common"
              xmlns:themes="clr-namespace:PixiEditor.UI.Common.Themes;assembly=PixiEditor.UI.Common"
              xmlns:avaloniaUi="clr-namespace:PixiEditor"
              xmlns:avaloniaUi="clr-namespace:PixiEditor"
-             xmlns:templates="clr-namespace:ColorPicker.AvaloniaUI.Templates;assembly=ColorPicker.AvaloniaUI"
              xmlns:avalonia="clr-namespace:PixiDocks.Avalonia;assembly=PixiDocks.Avalonia"
              xmlns:avalonia="clr-namespace:PixiDocks.Avalonia;assembly=PixiDocks.Avalonia"
+             xmlns:templates1="clr-namespace:ColorPicker.AvaloniaUI.Templates;assembly=ColorPicker.AvaloniaUI"
              x:Class="PixiEditor.App"
              x:Class="PixiEditor.App"
              Name="PixiEditor"
              Name="PixiEditor"
              RequestedThemeVariant="Dark">
              RequestedThemeVariant="Dark">
@@ -14,7 +14,7 @@
     <Application.Styles>
     <Application.Styles>
         <themes:PixiEditorTheme />
         <themes:PixiEditorTheme />
         <avalonia:PixiDockSimpleTheme />
         <avalonia:PixiDockSimpleTheme />
-        <templates:PixiPerfectColorPickerTheme />
+        <templates1:PixiPerfectColorPickerTheme />
         <StyleInclude Source="/Styles/PixiEditor.Controls.axaml" />
         <StyleInclude Source="/Styles/PixiEditor.Controls.axaml" />
         <StyleInclude Source="/Styles/PixiEditor.Animators.axaml" />
         <StyleInclude Source="/Styles/PixiEditor.Animators.axaml" />
         <StyleInclude Source="/Styles/PixiEditor.Handles.axaml" />
         <StyleInclude Source="/Styles/PixiEditor.Handles.axaml" />

+ 1 - 1
src/PixiEditor/Exceptions/CorruptedFileException.cs

@@ -1,6 +1,6 @@
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Exceptions;
 using PixiEditor.Extensions.Exceptions;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Exceptions;
 namespace PixiEditor.Exceptions;
 
 

+ 1 - 1
src/PixiEditor/Exceptions/InvalidFileTypeException.cs

@@ -1,6 +1,6 @@
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Exceptions;
 using PixiEditor.Extensions.Exceptions;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Exceptions;
 namespace PixiEditor.Exceptions;
 
 

+ 1 - 1
src/PixiEditor/Exceptions/MissingFileException.cs

@@ -1,6 +1,6 @@
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Exceptions;
 using PixiEditor.Extensions.Exceptions;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Exceptions;
 namespace PixiEditor.Exceptions;
 
 

+ 1 - 1
src/PixiEditor/Helpers/Collections/ActionDisplayList.cs

@@ -1,7 +1,7 @@
 using System.Collections;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Helpers.Collections;
 namespace PixiEditor.Helpers.Collections;
 
 

+ 1 - 1
src/PixiEditor/Helpers/Converters/BlendModeToStringConverter.cs

@@ -1,7 +1,7 @@
 using System.Globalization;
 using System.Globalization;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.ChangeableDocument.Enums;
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Helpers.Converters;
 namespace PixiEditor.Helpers.Converters;
 internal class BlendModeToStringConverter : SingleInstanceConverter<BlendModeToStringConverter>
 internal class BlendModeToStringConverter : SingleInstanceConverter<BlendModeToStringConverter>

+ 1 - 1
src/PixiEditor/Helpers/Converters/BoolToValueConverter.cs

@@ -1,6 +1,6 @@
 using System.Globalization;
 using System.Globalization;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.UI.Common.Converters;
 using PixiEditor.UI.Common.Converters;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Helpers.Converters;
 namespace PixiEditor.Helpers.Converters;
 
 

+ 1 - 0
src/PixiEditor/Helpers/Converters/EnumToLocalizedStringConverter.cs

@@ -1,5 +1,6 @@
 using System.Globalization;
 using System.Globalization;
 using PixiEditor.Extensions.Helpers;
 using PixiEditor.Extensions.Helpers;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Helpers.Converters;
 namespace PixiEditor.Helpers.Converters;
 
 

+ 1 - 1
src/PixiEditor/Helpers/Converters/KeyToStringConverter.cs

@@ -1,10 +1,10 @@
 using System.Globalization;
 using System.Globalization;
 using System.Text;
 using System.Text;
 using Avalonia.Input;
 using Avalonia.Input;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Helpers;
 using PixiEditor.Extensions.Helpers;
 using PixiEditor.Models.Input;
 using PixiEditor.Models.Input;
 using PixiEditor.OperatingSystem;
 using PixiEditor.OperatingSystem;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Helpers.Converters;
 namespace PixiEditor.Helpers.Converters;
 
 

+ 1 - 0
src/PixiEditor/Helpers/EnumDescriptionConverter.cs

@@ -1,6 +1,7 @@
 using System.Globalization;
 using System.Globalization;
 using PixiEditor.Extensions.Helpers;
 using PixiEditor.Extensions.Helpers;
 using PixiEditor.Helpers.Converters;
 using PixiEditor.Helpers.Converters;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Helpers;
 namespace PixiEditor.Helpers;
 
 

+ 1 - 1
src/PixiEditor/Helpers/ServiceCollectionHelpers.cs

@@ -5,7 +5,6 @@ using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.AnimationRenderer.Core;
 using PixiEditor.AnimationRenderer.Core;
 using PixiEditor.AnimationRenderer.FFmpeg;
 using PixiEditor.AnimationRenderer.FFmpeg;
 using PixiEditor.Extensions.Commands;
 using PixiEditor.Extensions.Commands;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.Commands;
 using PixiEditor.Extensions.CommonApi.Commands;
 using PixiEditor.Extensions.CommonApi.IO;
 using PixiEditor.Extensions.CommonApi.IO;
 using PixiEditor.Extensions.CommonApi.Logging;
 using PixiEditor.Extensions.CommonApi.Logging;
@@ -30,6 +29,7 @@ using PixiEditor.Models.Localization;
 using PixiEditor.Models.Palettes;
 using PixiEditor.Models.Palettes;
 using PixiEditor.Models.Preferences;
 using PixiEditor.Models.Preferences;
 using PixiEditor.Models.Serialization.Factories;
 using PixiEditor.Models.Serialization.Factories;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.ViewModels.Dock;
 using PixiEditor.ViewModels.Dock;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Menu;
 using PixiEditor.ViewModels.Menu;

+ 15 - 1
src/PixiEditor/Initialization/ClassicDesktopEntry.cs

@@ -8,17 +8,21 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Threading;
 using Avalonia.Threading;
-using PixiEditor.Extensions.Common.Localization;
+using Avalonia.Xaml.Interactivity;
 using PixiEditor.Extensions.Runtime;
 using PixiEditor.Extensions.Runtime;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
+using PixiEditor.Helpers.Behaviours;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.ExceptionHandling;
 using PixiEditor.Models.ExceptionHandling;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
 using PixiEditor.OperatingSystem;
 using PixiEditor.OperatingSystem;
 using PixiEditor.Platform;
 using PixiEditor.Platform;
+using PixiEditor.UI.Common.Controls;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.Views;
 using PixiEditor.Views;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Views.Dialogs;
+using PixiEditor.Views.Input;
 using ViewModelMain = PixiEditor.ViewModels.ViewModelMain;
 using ViewModelMain = PixiEditor.ViewModels.ViewModelMain;
 using ViewModels_ViewModelMain = PixiEditor.ViewModels.ViewModelMain;
 using ViewModels_ViewModelMain = PixiEditor.ViewModels.ViewModelMain;
 
 
@@ -113,6 +117,8 @@ internal class ClassicDesktopEntry
 
 
         InitPlatform();
         InitPlatform();
 
 
+        NumberInput.AttachGlobalBehaviors += AttachGlobalShortcutBehavior;
+
         ExtensionLoader extensionLoader = new ExtensionLoader(Paths.ExtensionPackagesPath, Paths.UserExtensionsPath);
         ExtensionLoader extensionLoader = new ExtensionLoader(Paths.ExtensionPackagesPath, Paths.UserExtensionsPath);
         //TODO: fetch from extension store
         //TODO: fetch from extension store
         extensionLoader.AddOfficialExtension("pixieditor.founderspack",
         extensionLoader.AddOfficialExtension("pixieditor.founderspack",
@@ -224,4 +230,12 @@ internal class ClassicDesktopEntry
             });
             });
         }
         }
     }
     }
+
+    private void AttachGlobalShortcutBehavior(BehaviorCollection collection)
+    {
+        if (collection is null)
+            return;
+
+        collection.Add(new GlobalShortcutFocusBehavior());
+    }
 }
 }

+ 1 - 1
src/PixiEditor/Models/Commands/Attributes/Commands/CommandAttribute.cs

@@ -1,6 +1,6 @@
 using Avalonia.Input;
 using Avalonia.Input;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Input;
 using PixiEditor.Models.Input;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Models.Commands.Attributes.Commands;
 namespace PixiEditor.Models.Commands.Attributes.Commands;
 
 

+ 1 - 1
src/PixiEditor/Models/Commands/Attributes/Commands/FilterAttribute.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Models.Commands.Attributes.Commands;
 namespace PixiEditor.Models.Commands.Attributes.Commands;
 
 

+ 1 - 1
src/PixiEditor/Models/Commands/Attributes/Commands/GroupAttribute.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Models.Commands.Attributes.Commands;
 namespace PixiEditor.Models.Commands.Attributes.Commands;
 
 

+ 1 - 1
src/PixiEditor/Models/Commands/CommandController.cs

@@ -9,7 +9,6 @@ using Microsoft.Extensions.DependencyInjection;
 using Newtonsoft.Json;
 using Newtonsoft.Json;
 using PixiEditor.Exceptions;
 using PixiEditor.Exceptions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.AnalyticsAPI;
 using PixiEditor.Models.AnalyticsAPI;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Commands.Attributes.Evaluators;
 using PixiEditor.Models.Commands.Attributes.Evaluators;
@@ -21,6 +20,7 @@ using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Input;
 using PixiEditor.Models.Input;
 using PixiEditor.Models.Structures;
 using PixiEditor.Models.Structures;
 using PixiEditor.OperatingSystem;
 using PixiEditor.OperatingSystem;
+using PixiEditor.UI.Common.Localization;
 using Command = PixiEditor.Models.Commands.Commands.Command;
 using Command = PixiEditor.Models.Commands.Commands.Command;
 using CommandAttribute = PixiEditor.Models.Commands.Attributes.Commands.Command;
 using CommandAttribute = PixiEditor.Models.Commands.Attributes.Commands.Command;
 
 

+ 1 - 1
src/PixiEditor/Models/Commands/CommandGroup.cs

@@ -3,9 +3,9 @@ using System.Linq;
 using Avalonia.Input;
 using Avalonia.Input;
 using CommunityToolkit.Mvvm.ComponentModel;
 using CommunityToolkit.Mvvm.ComponentModel;
 using PixiEditor.Models.Commands.Evaluators;
 using PixiEditor.Models.Commands.Evaluators;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.Input;
 using PixiEditor.Models.Input;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Models.Commands;
 namespace PixiEditor.Models.Commands;
 
 

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

@@ -1,9 +1,9 @@
 using System.Diagnostics;
 using System.Diagnostics;
 using Avalonia.Media;
 using Avalonia.Media;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Commands.CommandContext;
 using PixiEditor.Models.Commands.CommandContext;
 using PixiEditor.Models.Commands.Evaluators;
 using PixiEditor.Models.Commands.Evaluators;
 using PixiEditor.Models.Input;
 using PixiEditor.Models.Input;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
 
 
 namespace PixiEditor.Models.Commands.Commands;
 namespace PixiEditor.Models.Commands.Commands;

+ 0 - 1
src/PixiEditor/Models/Commands/XAML/NativeMenu.cs

@@ -4,7 +4,6 @@ using Avalonia.Layout;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.Media.Imaging;
 using Avalonia.Media.Imaging;
 using Avalonia.Platform;
 using Avalonia.Platform;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Commands.CommandContext;
 using PixiEditor.Models.Commands.CommandContext;

+ 1 - 1
src/PixiEditor/Models/Constants.cs

@@ -6,7 +6,7 @@ internal class Constants
     public const int MaxPreviewWidth = 128;
     public const int MaxPreviewWidth = 128;
     public const int MaxPreviewHeight = 128;
     public const int MaxPreviewHeight = 128;
 
 
-    public const int MaxCanvasSize = 9999;
+    public const int MaxCanvasSize = 99999;
 
 
     public const string NativeExtensionNoDot = "pixi";
     public const string NativeExtensionNoDot = "pixi";
     public const string NativeExtension = "." + NativeExtensionNoDot;
     public const string NativeExtension = "." + NativeExtensionNoDot;

+ 1 - 1
src/PixiEditor/Models/Controllers/ClipboardController.cs

@@ -19,7 +19,6 @@ using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers.Constants;
 using PixiEditor.Helpers.Constants;
 using PixiEditor.Models.Clipboard;
 using PixiEditor.Models.Clipboard;
@@ -30,6 +29,7 @@ using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Parser;
 using PixiEditor.Parser;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Document;
 using Bitmap = Avalonia.Media.Imaging.Bitmap;
 using Bitmap = Avalonia.Media.Imaging.Bitmap;
 
 

+ 1 - 6
src/PixiEditor/Models/Controllers/ShortcutController.cs

@@ -1,11 +1,8 @@
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Input;
+using Avalonia.Input;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.Commands.CommandContext;
 using PixiEditor.Models.Commands.CommandContext;
 using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.Input;
 using PixiEditor.Models.Input;
-using PixiEditor.ViewModels.Tools;
 
 
 namespace PixiEditor.Models.Controllers;
 namespace PixiEditor.Models.Controllers;
 
 
@@ -17,8 +14,6 @@ internal class ShortcutController
 
 
     public IEnumerable<Command> LastCommands { get; private set; }
     public IEnumerable<Command> LastCommands { get; private set; }
 
 
-    public Dictionary<KeyCombination, ToolViewModel> TransientShortcuts { get; set; } = new();
-    
     public Type? ActiveContext { get; private set; }
     public Type? ActiveContext { get; private set; }
 
 
     public static void BlockShortcutExecution(string blocker)
     public static void BlockShortcutExecution(string blocker)

+ 1 - 1
src/PixiEditor/Models/Dialogs/ConfirmationDialog.cs

@@ -1,7 +1,7 @@
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.ApplicationLifetimes;
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Views.Dialogs;
 
 
 namespace PixiEditor.Models.Dialogs;
 namespace PixiEditor.Models.Dialogs;

+ 1 - 1
src/PixiEditor/Models/Dialogs/NoticeDialog.cs

@@ -1,6 +1,6 @@
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.ApplicationLifetimes;
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Views.Dialogs;
 
 
 namespace PixiEditor.Models.Dialogs;
 namespace PixiEditor.Models.Dialogs;

+ 1 - 1
src/PixiEditor/Models/Dialogs/OptionsDialog.cs

@@ -6,7 +6,7 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Layout;
 using Avalonia.Layout;
 using Avalonia.Media;
 using Avalonia.Media;
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Views.Dialogs;
 
 
 namespace PixiEditor.Models.Dialogs;
 namespace PixiEditor.Models.Dialogs;

+ 1 - 1
src/PixiEditor/Models/DocumentModels/DocumentStructureHelper.cs

@@ -5,9 +5,9 @@ using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.ChangeableDocument.Enums;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Models.DocumentModels;
 namespace PixiEditor.Models.DocumentModels;
 #nullable enable
 #nullable enable

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

@@ -310,7 +310,7 @@ internal class DocumentOperationsModule : IDocumentOperations
     /// <param name="anchor">Where the existing content should be put</param>
     /// <param name="anchor">Where the existing content should be put</param>
     public void ResizeCanvas(VecI newSize, ResizeAnchor anchor)
     public void ResizeCanvas(VecI newSize, ResizeAnchor anchor)
     {
     {
-        if (Internals.ChangeController.IsBlockingChangeActive || newSize.X > 9999 || newSize.Y > 9999 ||
+        if (Internals.ChangeController.IsBlockingChangeActive || newSize.X > Constants.MaxCanvasSize || newSize.Y > Constants.MaxCanvasSize ||
             newSize.X < 1 ||
             newSize.X < 1 ||
             newSize.Y < 1)
             newSize.Y < 1)
             return;
             return;

+ 1 - 1
src/PixiEditor/Models/ExceptionHandling/CrashReport.cs

@@ -4,7 +4,6 @@ using System.IO.Compression;
 using System.Reflection;
 using System.Reflection;
 using System.Text;
 using System.Text;
 using Newtonsoft.Json;
 using Newtonsoft.Json;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
@@ -16,6 +15,7 @@ using PixiEditor.Models.Commands;
 using PixiEditor.Models.DocumentModels.Autosave;
 using PixiEditor.Models.DocumentModels.Autosave;
 using PixiEditor.OperatingSystem;
 using PixiEditor.OperatingSystem;
 using PixiEditor.Parser;
 using PixiEditor.Parser;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.Views;
 using PixiEditor.Views;

+ 11 - 2
src/PixiEditor/Models/ExtensionServices/DocumentProvider.cs

@@ -1,3 +1,5 @@
+using Avalonia.Threading;
+using PixiEditor.Extensions.CommonApi.Documents;
 using PixiEditor.Extensions.CommonApi.IO;
 using PixiEditor.Extensions.CommonApi.IO;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
@@ -7,6 +9,7 @@ namespace PixiEditor.Models.ExtensionServices;
 
 
 internal class DocumentProvider : IDocumentProvider
 internal class DocumentProvider : IDocumentProvider
 {
 {
+    public IDocument? ActiveDocument => fileViewModel.Owner.DocumentManagerSubViewModel.ActiveDocument;
     private FileViewModel fileViewModel;
     private FileViewModel fileViewModel;
 
 
     public DocumentProvider(FileViewModel fileViewModel)
     public DocumentProvider(FileViewModel fileViewModel)
@@ -14,8 +17,14 @@ internal class DocumentProvider : IDocumentProvider
         this.fileViewModel = fileViewModel;
         this.fileViewModel = fileViewModel;
     }
     }
 
 
-    public void ImportFile(string path, bool associatePath = true)
+    public IDocument ImportFile(string path, bool associatePath = true)
     {
     {
-        fileViewModel.OpenFromPath(path, associatePath);
+        return fileViewModel.OpenFromPath(path, associatePath);
+    }
+
+    public IDocument? GetDocument(Guid id)
+    {
+        var document = fileViewModel.Owner.DocumentManagerSubViewModel.Documents.FirstOrDefault(x => x.Id == id);
+        return document;
     }
     }
 }
 }

+ 1 - 1
src/PixiEditor/Models/ExtensionServices/WindowProvider.cs

@@ -1,11 +1,11 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.Windowing;
 using PixiEditor.Extensions.CommonApi.Windowing;
 using PixiEditor.Extensions.Helpers;
 using PixiEditor.Extensions.Helpers;
 using PixiEditor.Extensions.Runtime;
 using PixiEditor.Extensions.Runtime;
 using PixiEditor.Extensions.Windowing;
 using PixiEditor.Extensions.Windowing;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Views.Dialogs;
 
 
 namespace PixiEditor.Models.ExtensionServices;
 namespace PixiEditor.Models.ExtensionServices;

+ 1 - 1
src/PixiEditor/Models/Files/BmpFileType.cs

@@ -1,6 +1,6 @@
 using Avalonia.Media;
 using Avalonia.Media;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Models.Files;
 namespace PixiEditor.Models.Files;
 
 

+ 1 - 1
src/PixiEditor/Models/Files/GifFileType.cs

@@ -1,5 +1,5 @@
 using Avalonia.Media;
 using Avalonia.Media;
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Models.Files;
 namespace PixiEditor.Models.Files;
 
 

+ 1 - 1
src/PixiEditor/Models/Files/ImageFileType.cs

@@ -5,10 +5,10 @@ using Drawie.Backend.Core;
 using Drawie.Backend.Core.Bridge;
 using Drawie.Backend.Core.Bridge;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
-using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO.FileEncoders;
 using PixiEditor.Models.IO.FileEncoders;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Document;
 
 
 namespace PixiEditor.Models.Files;
 namespace PixiEditor.Models.Files;

+ 1 - 1
src/PixiEditor/Models/Files/JpegFileType.cs

@@ -1,6 +1,6 @@
 using Avalonia.Media;
 using Avalonia.Media;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Models.Files;
 namespace PixiEditor.Models.Files;
 
 

+ 1 - 1
src/PixiEditor/Models/Files/Mp4FileType.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.UI.Common.Localization;
 
 
 namespace PixiEditor.Models.Files;
 namespace PixiEditor.Models.Files;
 
 

Some files were not shown because too many files changed in this diff