Browse Source

Merge branch 'refs/heads/avalonia-rewrite' into settings-wrapper

# Conflicts:
#	src/PixiEditor.AvaloniaUI/Models/Palettes/LocalPalettesFetcher.cs
#	src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/DebugViewModel.cs
#	src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/DiscordViewModel.cs
#	src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/FileViewModel.cs
#	src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/UpdateViewModel.cs
#	src/PixiEditor.AvaloniaUI/Views/Windows/PalettesBrowser.axaml.cs
#	src/PixiEditor.Extensions.CommonApi/PixiEditor.Extensions.CommonApi.csproj
#	src/PixiEditor.sln
CPKreuz 1 year ago
parent
commit
9d0e993da7
100 changed files with 1572 additions and 625 deletions
  1. 97 0
      samples/Custom.ruleset
  2. 103 0
      samples/Directory.Build.props
  3. 19 1
      samples/PixiEditorExtensionSamples.sln
  4. 2 2
      samples/Sample1_HelloWorld/HelloWorldExtension.cs
  5. 3 3
      samples/Sample1_HelloWorld/Sample1_HelloWorld.csproj
  6. 3 3
      samples/Sample2_LocalizationSample/LocalizationSampleExtension.cs
  7. 3 3
      samples/Sample2_LocalizationSample/Sample2_LocalizationSample.csproj
  8. 11 4
      samples/Sample3_Preferences/PreferencesSampleExtension.cs
  9. 3 3
      samples/Sample3_Preferences/Sample3_Preferences.csproj
  10. 1 1
      samples/Sample3_Preferences/extension.json
  11. 3 3
      samples/Sample4_CreatePopup/CreatePopupSampleExtension.cs
  12. 3 3
      samples/Sample4_CreatePopup/Sample4_CreatePopup.csproj
  13. 16 0
      samples/Sample5_Resources/Program.cs
  14. 1 0
      samples/Sample5_Resources/Resources/ExampleFile.txt
  15. 32 0
      samples/Sample5_Resources/ResourcesSampleExtension.cs
  16. 42 0
      samples/Sample5_Resources/Sample5_Resources.csproj
  17. 27 0
      samples/Sample5_Resources/extension.json
  18. 25 0
      samples/Sample6_Palettes/ExamplePaletteDataSource.cs
  19. 27 0
      samples/Sample6_Palettes/PalettesSampleExtension.cs
  20. 14 0
      samples/Sample6_Palettes/Program.cs
  21. 42 0
      samples/Sample6_Palettes/Sample6_Palettes.csproj
  22. 27 0
      samples/Sample6_Palettes/extension.json
  23. 18 0
      samples/Sample7_FlyUI/FlyUISampleExtension.cs
  24. 9 0
      samples/Sample7_FlyUI/Program.cs
  25. BIN
      samples/Sample7_FlyUI/Resources/Pizza.png
  26. 42 0
      samples/Sample7_FlyUI/Sample7_FlyUI.csproj
  27. 46 0
      samples/Sample7_FlyUI/WindowContentElement.cs
  28. 27 0
      samples/Sample7_FlyUI/extension.json
  29. 20 0
      samples/stylecop.json
  30. 2 2
      src/PixiEditor.Api.CGlueMSBuild/CApiGenerator.cs
  31. 1 1
      src/PixiEditor.Api.CGlueMSBuild/PixiEditor.Api.CGlueMSBuild.csproj
  32. 0 228
      src/PixiEditor.Api.CGlueMSBuild/build/PixiEditor.Api.CGlueMSBuild.deps.json
  33. BIN
      src/PixiEditor.Api.CGlueMSBuild/build/PixiEditor.Api.CGlueMSBuild.dll
  34. 0 1
      src/PixiEditor.AvaloniaUI.Browser/Program.cs
  35. 0 1
      src/PixiEditor.AvaloniaUI.Desktop/Program.cs
  36. 1 1
      src/PixiEditor.AvaloniaUI/Helpers/ServiceCollectionHelpers.cs
  37. 25 3
      src/PixiEditor.AvaloniaUI/Models/ExtensionServices/PaletteProvider.cs
  38. 1 1
      src/PixiEditor.AvaloniaUI/Models/ExtensionServices/WindowProvider.cs
  39. 10 10
      src/PixiEditor.AvaloniaUI/Models/Preferences/PreferencesSettings.cs
  40. 0 5
      src/PixiEditor.AvaloniaUI/PixiEditor.AvaloniaUI.csproj
  41. 7 0
      src/PixiEditor.AvaloniaUI/ViewModels/Menu/MenuBarViewModel.cs
  42. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/SettingsWindowViewModel.cs
  43. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/ColorsViewModel.cs
  44. 2 1
      src/PixiEditor.AvaloniaUI/ViewModels/ViewModelMain.cs
  45. 3 2
      src/PixiEditor.AvaloniaUI/Views/Dialogs/DialogTitleBar.axaml
  46. 9 1
      src/PixiEditor.AvaloniaUI/Views/Dialogs/DialogTitleBar.axaml.cs
  47. 3 0
      src/PixiEditor.AvaloniaUI/Views/Dialogs/PixiEditorPopup.cs
  48. 2 2
      src/PixiEditor.AvaloniaUI/Views/Layers/FolderControl.axaml
  49. 24 15
      src/PixiEditor.AvaloniaUI/Views/Main/MainTitleBar.axaml
  50. 2 1
      src/PixiEditor.AvaloniaUI/Views/Main/Navigation.axaml
  51. 3 2
      src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/FixedViewport.axaml
  52. 1 1
      src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml
  53. 2 2
      src/PixiEditor.AvaloniaUI/Views/Palettes/PaletteItem.axaml
  54. 2 2
      src/PixiEditor.AvaloniaUI/Views/Windows/HelloTherePopup.axaml
  55. 1 0
      src/PixiEditor.AvaloniaUI/Views/Windows/PalettesBrowser.axaml.cs
  56. 0 14
      src/PixiEditor.DevTools/DevToolsExtension.cs
  57. 0 34
      src/PixiEditor.DevTools/HotReloader.cs
  58. 0 33
      src/PixiEditor.DevTools/LayoutDeserializer.cs
  59. 0 11
      src/PixiEditor.DevTools/Layouts/LiveLayoutPreviewWindow.cs
  60. 0 69
      src/PixiEditor.DevTools/Layouts/LivePreviewWindowState.cs
  61. 0 32
      src/PixiEditor.DevTools/PixiEditor.DevTools.csproj
  62. 0 21
      src/PixiEditor.DevTools/extension.json
  63. 13 0
      src/PixiEditor.Extensions.CommonApi/.config/dotnet-tools.json
  64. 97 8
      src/PixiEditor.Extensions.CommonApi/Async/AsyncCall.cs
  65. 14 0
      src/PixiEditor.Extensions.CommonApi/DataContracts/ExtensionPalette.proto
  66. 14 0
      src/PixiEditor.Extensions.CommonApi/DataContracts/FetchPaletteListQuery.proto
  67. 21 0
      src/PixiEditor.Extensions.CommonApi/DataContracts/FilteringSettings.proto
  68. 11 0
      src/PixiEditor.Extensions.CommonApi/DataContracts/PaletteColor.proto
  69. 11 0
      src/PixiEditor.Extensions.CommonApi/DataContracts/PaletteListResult.proto
  70. 8 1
      src/PixiEditor.Extensions.CommonApi/FlyUI/ByteMap.cs
  71. 14 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/Alignment.cs
  72. 35 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/Color.cs
  73. 14 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/Colors.cs
  74. 69 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/Edges.cs
  75. 9 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/FillMode.cs
  76. 10 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/FilterQuality.cs
  77. 8 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/FontStyle.cs
  78. 7 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/IStructProperty.cs
  79. 8 0
      src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/TextWrap.cs
  80. 0 9
      src/PixiEditor.Extensions.CommonApi/Palettes/ColorsNumberMode.cs
  81. 13 0
      src/PixiEditor.Extensions.CommonApi/Palettes/ExtensionPalette.Impl.cs
  82. 0 17
      src/PixiEditor.Extensions.CommonApi/Palettes/ExtensionPalette.cs
  83. 23 0
      src/PixiEditor.Extensions.CommonApi/Palettes/FetchPaletteListQuery.Impl.cs
  84. 1 7
      src/PixiEditor.Extensions.CommonApi/Palettes/FilteringSettings.Impl.cs
  85. 0 29
      src/PixiEditor.Extensions.CommonApi/Palettes/IPaletteProvider.cs
  86. 10 0
      src/PixiEditor.Extensions.CommonApi/Palettes/IPalettesProvider.cs
  87. 34 12
      src/PixiEditor.Extensions.CommonApi/Palettes/PaletteColor.Impl.cs
  88. 9 0
      src/PixiEditor.Extensions.CommonApi/Palettes/PaletteListResult.Impl.cs
  89. 1 1
      src/PixiEditor.Extensions.CommonApi/Palettes/Parsers/PaletteFileData.cs
  90. 92 0
      src/PixiEditor.Extensions.CommonApi/PixiEditor.Extensions.CommonApi.csproj
  91. 38 0
      src/PixiEditor.Extensions.CommonApi/ProtoAutogen/ExtensionPalette.cs
  92. 37 0
      src/PixiEditor.Extensions.CommonApi/ProtoAutogen/FetchPaletteListQuery.cs
  93. 49 0
      src/PixiEditor.Extensions.CommonApi/ProtoAutogen/FilteringSettings.cs
  94. 33 0
      src/PixiEditor.Extensions.CommonApi/ProtoAutogen/PaletteColor.cs
  95. 27 0
      src/PixiEditor.Extensions.CommonApi/ProtoAutogen/PaletteListResult.cs
  96. 15 0
      src/PixiEditor.Extensions.CommonApi/ProtoAutogen/README.md
  97. 4 4
      src/PixiEditor.Extensions.CommonApi/UserPreferences/IPreferences.cs
  98. 52 0
      src/PixiEditor.Extensions.CommonApi/Utilities/PrefixedNameUtility.cs
  99. 2 2
      src/PixiEditor.Extensions.CommonApi/Windowing/IWindowProvider.cs
  100. 0 11
      src/PixiEditor.Extensions.MSBuildLayoutCompiler/PixiEditor.Extensions.MSBuildLayoutCompiler.csproj

+ 97 - 0
samples/Custom.ruleset

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RuleSet Name="Name" Description="Description" ToolsVersion="17.0">
+  <Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp" RuleNamespace="Microsoft.CodeAnalysis.CSharp">
+    <Rule Id="AD0001" Action="None" />
+  </Rules>
+  <Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.Features" RuleNamespace="Microsoft.CodeAnalysis.CSharp.Features">
+    <Rule Id="IDE0090" Action="None" />
+  </Rules>
+  <Rules AnalyzerId="Microsoft.CodeAnalysis.NetAnalyzers" RuleNamespace="Microsoft.CodeAnalysis.NetAnalyzers">
+    <Rule Id="CA1416" Action="None" />
+  </Rules>
+  <Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
+    <Rule Id="CA1303" Action="None" />
+    <Rule Id="CA1416" Action="None" />
+  </Rules>
+  <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
+    <Rule Id="SA0001" Action="None" />
+    <Rule Id="SA1000" Action="None" />
+    <Rule Id="SA1005" Action="None" />
+    <Rule Id="SA1008" Action="None" />
+    <Rule Id="SA1009" Action="None" />
+    <Rule Id="SA1011" Action="None" />
+    <Rule Id="SA1023" Action="None" />
+    <Rule Id="SA1028" Action="None" />
+    <Rule Id="SA1101" Action="None" />
+    <Rule Id="SA1110" Action="None" />
+    <Rule Id="SA1111" Action="None" />
+    <Rule Id="SA1112" Action="None" />
+    <Rule Id="SA1117" Action="None" />
+    <Rule Id="SA1119" Action="None" />
+    <Rule Id="SA1121" Action="None" />
+    <Rule Id="SA1122" Action="None" />
+    <Rule Id="SA1124" Action="None" />
+    <Rule Id="SA1127" Action="None" />
+    <Rule Id="SA1128" Action="None" />
+    <Rule Id="SA1129" Action="None" />
+    <Rule Id="SA1130" Action="None" />
+    <Rule Id="SA1132" Action="None" />
+    <Rule Id="SA1135" Action="None" />
+    <Rule Id="SA1136" Action="None" />
+    <Rule Id="SA1139" Action="None" />
+    <Rule Id="SA1200" Action="None" />
+    <Rule Id="SA1201" Action="None" />
+    <Rule Id="SA1202" Action="None" />
+    <Rule Id="SA1204" Action="None" />
+    <Rule Id="SA1207" Action="None" />
+    <Rule Id="SA1208" Action="None" />
+    <Rule Id="SA1209" Action="None" />
+    <Rule Id="SA1210" Action="None" />
+    <Rule Id="SA1211" Action="None" />
+    <Rule Id="SA1214" Action="None" />
+    <Rule Id="SA1216" Action="None" />
+    <Rule Id="SA1217" Action="None" />
+    <Rule Id="SA1303" Action="None" />
+    <Rule Id="SA1304" Action="None" />
+    <Rule Id="SA1307" Action="None" />
+    <Rule Id="SA1309" Action="None" />
+    <Rule Id="SA1310" Action="None" />
+    <Rule Id="SA1311" Action="None" />
+    <Rule Id="SA1313" Action="None" />
+    <Rule Id="SA1316" Action="None" />
+    <Rule Id="SA1400" Action="None" />
+    <Rule Id="SA1401" Action="None" />
+    <Rule Id="SA1402" Action="None" />
+    <Rule Id="SA1405" Action="None" />
+    <Rule Id="SA1406" Action="None" />
+    <Rule Id="SA1407" Action="None" />
+    <Rule Id="SA1408" Action="None" />
+    <Rule Id="SA1410" Action="None" />
+    <Rule Id="SA1411" Action="None" />
+    <Rule Id="SA1413" Action="None" />
+    <Rule Id="SA1501" Action="None" />
+    <Rule Id="SA1502" Action="None" />
+    <Rule Id="SA1503" Action="None" />
+    <Rule Id="SA1505" Action="None" />
+    <Rule Id="SA1507" Action="None" />
+    <Rule Id="SA1508" Action="None" />
+    <Rule Id="SA1512" Action="None" />
+    <Rule Id="SA1513" Action="None" />
+    <Rule Id="SA1515" Action="None" />
+    <Rule Id="SA1516" Action="None" />
+    <Rule Id="SA1518" Action="None" />
+    <Rule Id="SA1600" Action="None" />
+    <Rule Id="SA1601" Action="None" />
+    <Rule Id="SA1602" Action="None" />
+    <Rule Id="SA1604" Action="None" />
+    <Rule Id="SA1605" Action="None" />
+    <Rule Id="SA1606" Action="None" />
+    <Rule Id="SA1607" Action="None" />
+    <Rule Id="SA1623" Action="None" />
+    <Rule Id="SA1629" Action="None" />
+    <Rule Id="SA1633" Action="None" />
+    <Rule Id="SA1642" Action="None" />
+    <Rule Id="SA1643" Action="None" />
+    <Rule Id="SA1648" Action="None" />
+  </Rules>
+</RuleSet>

+ 103 - 0
samples/Directory.Build.props

@@ -0,0 +1,103 @@
+<Project>
+    <PropertyGroup>
+        <CodeAnalysisRuleSet>../Custom.ruleset</CodeAnalysisRuleSet>
+		    <AvaloniaVersion>11.0.10</AvaloniaVersion>
+    </PropertyGroup>
+    <ItemGroup>
+        <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
+    </ItemGroup>
+    <ItemGroup>
+        <AdditionalFiles Include="../stylecop.json" />
+    </ItemGroup>
+
+  <PropertyGroup Condition="$([MSBuild]::IsOsPlatform('Windows')) AND '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X64'">
+    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
+  </PropertyGroup>
+  <PropertyGroup Condition="$([MSBuild]::IsOsPlatform('Windows')) AND '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'Arm64'">
+    <RuntimeIdentifier>win-arm64</RuntimeIdentifier>
+  </PropertyGroup>
+  <PropertyGroup Condition="$([MSBuild]::IsOsPlatform('Linux')) AND '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X64'">
+    <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
+  </PropertyGroup>
+  <PropertyGroup Condition="$([MSBuild]::IsOsPlatform('Linux')) AND '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'Arm64'">
+    <RuntimeIdentifier>linux-arm64</RuntimeIdentifier>
+  </PropertyGroup>
+  <PropertyGroup Condition="$([MSBuild]::IsOsPlatform('OSX')) AND '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X64'">
+    <RuntimeIdentifier>osx-x64</RuntimeIdentifier>
+  </PropertyGroup>
+  <PropertyGroup Condition="$([MSBuild]::IsOsPlatform('OSX')) AND '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'Arm64'">
+    <RuntimeIdentifier>osx-arm64</RuntimeIdentifier>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Platform)'=='x64'">
+    <PlatformTarget>x64</PlatformTarget>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Platform)'=='arm64'">
+    <PlatformTarget>arm64</PlatformTarget>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)'=='MSIX Debug'">
+    <DebugType>full</DebugType>
+    <DebugSymbols>true</DebugSymbols>
+    <Optimize>false</Optimize>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)'=='MSIX'">
+    <DefineConstants>TRACE;RELEASE</DefineConstants>
+    <Optimize>true</Optimize>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)'=='Release'">
+    <DefineConstants>TRACE;UPDATE</DefineConstants>
+    <Optimize>true</Optimize>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+    <DebugType>full</DebugType>
+    <DebugSymbols>true</DebugSymbols>
+    <WarningLevel>0</WarningLevel>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)'=='Steam'">
+    <DefineConstants>TRACE;RELEASE;STEAM</DefineConstants>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)'=='DevSteam'">
+    <DefineConstants>TRACE;RELEASE;STEAM</DefineConstants>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(Configuration)' == 'DevRelease' ">
+    <DefineConstants>TRACE;UPDATE;RELEASE</DefineConstants>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(RuntimeIdentifier)'=='win-x64'">
+    <DefineConstants>WINDOWS</DefineConstants>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(RuntimeIdentifier)'=='win-arm64'">
+    <DefineConstants>WINDOWS</DefineConstants>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(RuntimeIdentifier)'=='linux-x64'">
+    <DefineConstants>LINUX</DefineConstants>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(RuntimeIdentifier)'=='linux-arm64'">
+    <DefineConstants>LINUX</DefineConstants>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(RuntimeIdentifier)'=='osx-x64'">
+    <DefineConstants>MACOS</DefineConstants>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(RuntimeIdentifier)'=='osx-arm64'">
+    <DefineConstants>MACOS</DefineConstants>
+  </PropertyGroup>
+  
+</Project>

+ 19 - 1
samples/PixiEditorExtensionSamples.sln

@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 17
 # Visual Studio Version 17
 VisualStudioVersion = 17.0.31903.59
 VisualStudioVersion = 17.0.31903.59
 MinimumVisualStudioVersion = 10.0.40219.1
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Extensions.Wasm", "..\src\PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj", "{FD9B4C32-4D2E-410E-BC6B-787779BEB6E2}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Extensions.Sdk", "..\src\PixiEditor.Extensions.Sdk\PixiEditor.Extensions.Sdk.csproj", "{FD9B4C32-4D2E-410E-BC6B-787779BEB6E2}"
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample1_HelloWorld", "Sample1_HelloWorld\Sample1_HelloWorld.csproj", "{82A85041-A666-42DB-8F84-7D91EF9A5C9D}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample1_HelloWorld", "Sample1_HelloWorld\Sample1_HelloWorld.csproj", "{82A85041-A666-42DB-8F84-7D91EF9A5C9D}"
 EndProject
 EndProject
@@ -15,6 +15,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_References", "_References"
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample4_CreatePopup", "Sample4_CreatePopup\Sample4_CreatePopup.csproj", "{93ADCE51-F671-4374-84AC-5AB07A098F27}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample4_CreatePopup", "Sample4_CreatePopup\Sample4_CreatePopup.csproj", "{93ADCE51-F671-4374-84AC-5AB07A098F27}"
 EndProject
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample5_Resources", "Sample5_Resources\Sample5_Resources.csproj", "{51E1742D-132F-4CE9-9313-67FF1AC785D6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample6_Palettes", "Sample6_Palettes\Sample6_Palettes.csproj", "{6FF1D3AB-358A-4D78-8877-DACC01D34B87}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample7_FlyUI", "Sample7_FlyUI\Sample7_FlyUI.csproj", "{432A224A-8035-47C1-AC41-6715021B3AA3}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
@@ -44,6 +50,18 @@ Global
 		{93ADCE51-F671-4374-84AC-5AB07A098F27}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{93ADCE51-F671-4374-84AC-5AB07A098F27}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{93ADCE51-F671-4374-84AC-5AB07A098F27}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{93ADCE51-F671-4374-84AC-5AB07A098F27}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{93ADCE51-F671-4374-84AC-5AB07A098F27}.Release|Any CPU.Build.0 = Release|Any CPU
 		{93ADCE51-F671-4374-84AC-5AB07A098F27}.Release|Any CPU.Build.0 = Release|Any CPU
+		{51E1742D-132F-4CE9-9313-67FF1AC785D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{51E1742D-132F-4CE9-9313-67FF1AC785D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{51E1742D-132F-4CE9-9313-67FF1AC785D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{51E1742D-132F-4CE9-9313-67FF1AC785D6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6FF1D3AB-358A-4D78-8877-DACC01D34B87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6FF1D3AB-358A-4D78-8877-DACC01D34B87}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6FF1D3AB-358A-4D78-8877-DACC01D34B87}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6FF1D3AB-358A-4D78-8877-DACC01D34B87}.Release|Any CPU.Build.0 = Release|Any CPU
+		{432A224A-8035-47C1-AC41-6715021B3AA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{432A224A-8035-47C1-AC41-6715021B3AA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{432A224A-8035-47C1-AC41-6715021B3AA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{432A224A-8035-47C1-AC41-6715021B3AA3}.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}

+ 2 - 2
samples/Sample1_HelloWorld/HelloWorldExtension.cs

@@ -1,8 +1,8 @@
 namespace HelloWorld;
 namespace HelloWorld;
 
 
-using PixiEditor.Extensions.Wasm;
+using PixiEditor.Extensions.Sdk;
 
 
-public class HelloWorldExtension : WasmExtension
+public class HelloWorldExtension : PixiEditorExtension
 {
 {
     /// <summary>
     /// <summary>
     ///     This method is called when extension is loaded.
     ///     This method is called when extension is loaded.

+ 3 - 3
samples/Sample1_HelloWorld/Sample1_HelloWorld.csproj

@@ -12,12 +12,12 @@
     </PropertyGroup>
     </PropertyGroup>
     
     
     <ItemGroup>
     <ItemGroup>
-        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj" />
+        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Sdk\PixiEditor.Extensions.Sdk.csproj" />
     </ItemGroup>
     </ItemGroup>
     
     
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
-    <Import Project="..\..\src\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.props"/>
-    <Import Project="..\..\src\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.targets" />
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.props"/>
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.targets" />
     
     
     <ItemGroup>
     <ItemGroup>
         <None Remove="extension.json" />
         <None Remove="extension.json" />

+ 3 - 3
samples/Sample2_LocalizationSample/LocalizationSampleExtension.cs

@@ -1,9 +1,9 @@
-using PixiEditor.Extensions.Wasm;
-using PixiEditor.Extensions.Wasm.Api.Localization;
+using PixiEditor.Extensions.Sdk;
+using PixiEditor.Extensions.Sdk.Api.Localization;
 
 
 namespace LocalizationSample;
 namespace LocalizationSample;
 
 
-public class LocalizationSampleExtension : WasmExtension
+public class LocalizationSampleExtension : PixiEditorExtension
 {
 {
     /// <summary>
     /// <summary>
     ///     This method is called when extension is loaded.
     ///     This method is called when extension is loaded.

+ 3 - 3
samples/Sample2_LocalizationSample/Sample2_LocalizationSample.csproj

@@ -26,11 +26,11 @@
 
 
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <ItemGroup>
     <ItemGroup>
-        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj" />
+        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Sdk\PixiEditor.Extensions.Sdk.csproj" />
     </ItemGroup>
     </ItemGroup>
 
 
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
-    <Import Project="..\..\src\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.props"/>
-    <Import Project="..\..\src\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.targets" />
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.props"/>
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.targets" />
 
 
 </Project>
 </Project>

+ 11 - 4
samples/Sample3_Preferences/PreferencesSampleExtension.cs

@@ -1,8 +1,8 @@
-using PixiEditor.Extensions.Wasm;
+using PixiEditor.Extensions.Sdk;
 
 
 namespace Preferences;
 namespace Preferences;
 
 
-public class PreferencesSampleExtension : WasmExtension
+public class PreferencesSampleExtension : PixiEditorExtension
 {
 {
     /// <summary>
     /// <summary>
     ///     This method is called when extension is loaded.
     ///     This method is called when extension is loaded.
@@ -17,12 +17,19 @@ public class PreferencesSampleExtension : WasmExtension
     /// </summary>
     /// </summary>
     public override void OnInitialized()
     public override void OnInitialized()
     {
     {
+        Api.Preferences.AddCallback<int>("HelloCount", (name, value) => Api.Logger.Log($"Hello count changed to {value}!"));
+        Api.Preferences.AddCallback<double>("TestDouble", (name, value) => Api.Logger.Log($"Test double changed to {value}!"));
+        Api.Preferences.AddCallback<string>("TestString", (name, value) => Api.Logger.Log($"Test string changed to {value}!"));
+        Api.Preferences.AddCallback<bool>("TestBool", (name, value) => Api.Logger.Log($"Test bool changed to {value}!"));
+        
         // Internally this preference will have name "yourCompany.Samples.Preferences:HelloCount".
         // Internally this preference will have name "yourCompany.Samples.Preferences:HelloCount".
         int helloCount = Api.Preferences.GetPreference<int>("HelloCount");
         int helloCount = Api.Preferences.GetPreference<int>("HelloCount");
 
 
-        Api.Logger.Log($"Hello count: {helloCount}");
-
         Api.Preferences.UpdatePreference("HelloCount", helloCount + 1);
         Api.Preferences.UpdatePreference("HelloCount", helloCount + 1);
+        
+        Api.Preferences.UpdatePreference("TestDouble", 3.14);
+        Api.Preferences.UpdatePreference("TestString", "Hello, World!");
+        Api.Preferences.UpdatePreference("TestBool", true);
 
 
         // This will overwrite built-in PixiEditor preference. Extension must have WriteNonOwnedPreferences permission.
         // This will overwrite built-in PixiEditor preference. Extension must have WriteNonOwnedPreferences permission.
         // Prepending "PixiEditor:" to preference name will access built-in PixiEditor preferences. If you set it to other extension unique name,
         // Prepending "PixiEditor:" to preference name will access built-in PixiEditor preferences. If you set it to other extension unique name,

+ 3 - 3
samples/Sample3_Preferences/Sample3_Preferences.csproj

@@ -26,11 +26,11 @@
 
 
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <ItemGroup>
     <ItemGroup>
-        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj" />
+        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Sdk\PixiEditor.Extensions.Sdk.csproj" />
     </ItemGroup>
     </ItemGroup>
 
 
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
-    <Import Project="..\..\src\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.props"/>
-    <Import Project="..\..\src\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.targets" />
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.props"/>
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.targets" />
 
 
 </Project>
 </Project>

+ 1 - 1
samples/Sample3_Preferences/extension.json

@@ -1,7 +1,7 @@
 {
 {
   "displayName": "Sample Extension - Preferences",
   "displayName": "Sample Extension - Preferences",
   "uniqueName": "yourCompany.Samples.Preferences",
   "uniqueName": "yourCompany.Samples.Preferences",
-  "description": "Sample localization extension for PixiEditor",
+  "description": "Sample preferences extension for PixiEditor",
   "version": "1.0.0",
   "version": "1.0.0",
   "author": {
   "author": {
     "name": "PixiEditor",
     "name": "PixiEditor",

+ 3 - 3
samples/Sample4_CreatePopup/CreatePopupSampleExtension.cs

@@ -1,10 +1,10 @@
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using PixiEditor.Extensions.Wasm;
-using PixiEditor.Extensions.Wasm.Api.FlyUI;
+using PixiEditor.Extensions.Sdk;
+using PixiEditor.Extensions.Sdk.Api.FlyUI;
 
 
 namespace CreatePopupSample;
 namespace CreatePopupSample;
 
 
-public class CreatePopupSampleExtension : WasmExtension
+public class CreatePopupSampleExtension : PixiEditorExtension
 {
 {
     /// <summary>
     /// <summary>
     ///     This method is called when extension is loaded.
     ///     This method is called when extension is loaded.

+ 3 - 3
samples/Sample4_CreatePopup/Sample4_CreatePopup.csproj

@@ -26,11 +26,11 @@
 
 
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <ItemGroup>
     <ItemGroup>
-        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj" />
+        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Sdk\PixiEditor.Extensions.Sdk.csproj" />
     </ItemGroup>
     </ItemGroup>
 
 
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
     <!--Below is not required if you use Nuget package, this sample references project directly, so it must be here-->
-    <Import Project="..\..\src\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.props"/>
-    <Import Project="..\..\src\PixiEditor.Extensions.Wasm\build\PixiEditor.Extensions.Wasm.targets" />
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.props"/>
+    <Import Project="..\..\src\PixiEditor.Extensions.Sdk\build\PixiEditor.Extensions.Sdk.targets" />
 
 
 </Project>
 </Project>

+ 16 - 0
samples/Sample5_Resources/Program.cs

@@ -0,0 +1,16 @@
+using ResourcesSample;
+
+namespace CreatePopupSample;
+
+public static class Program
+{
+    /// <summary>
+    ///     The entry point of the application. This will be executed when extension is loaded.
+    /// You can use this method, but there are special methods that are used for initialization. You won't be able to access PixiEditor Api at this point.
+    /// See <see cref="ResourcesSampleExtension"/> for more information.
+    /// </summary>
+    public static void Main()
+    {
+
+    }
+}

+ 1 - 0
samples/Sample5_Resources/Resources/ExampleFile.txt

@@ -0,0 +1 @@
+I am loaded from resources

+ 32 - 0
samples/Sample5_Resources/ResourcesSampleExtension.cs

@@ -0,0 +1,32 @@
+using System.IO;
+using PixiEditor.Extensions.Sdk;
+
+namespace ResourcesSample;
+
+public class ResourcesSampleExtension : 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()
+    {
+        // By default, you can't access any files from the file system, however you can access files from the Resources folder.
+        // This folder contains files that you put in the Resources folder in the extension project.
+        Api.Logger.Log(File.ReadAllText("Resources/ExampleFile.txt"));
+
+        Api.Logger.Log("Writing to file...");
+
+        File.WriteAllText("Resources/ExampleFile.txt", "Hello from extension!");
+
+        Api.Logger.Log(File.ReadAllText("Resources/ExampleFile.txt"));
+    }
+}

+ 42 - 0
samples/Sample5_Resources/Sample5_Resources.csproj

@@ -0,0 +1,42 @@
+<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.AvaloniaUI.Desktop\bin\Debug\net8.0\win-x64\Extensions</PixiExtOutputPath>
+        <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+        <RootNamespace>ResourcesSample</RootNamespace>
+    </PropertyGroup>
+
+    <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-->
+    <ItemGroup>
+        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Sdk\PixiEditor.Extensions.Sdk.csproj" />
+    </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>

+ 27 - 0
samples/Sample5_Resources/extension.json

@@ -0,0 +1,27 @@
+{
+  "displayName": "Sample Extension - Resources",
+  "uniqueName": "yourCompany.Samples.Resources",
+  "description": "Sample localization extension for PixiEditor",
+  "version": "1.0.0",
+  "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"
+  ]
+}

+ 25 - 0
samples/Sample6_Palettes/ExamplePaletteDataSource.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using PixiEditor.Extensions.CommonApi.Async;
+using PixiEditor.Extensions.CommonApi.Palettes;
+using PixiEditor.Extensions.Sdk;
+
+namespace PalettesSample;
+
+public class ExamplePaletteDataSource : PaletteListDataSource
+{
+    public ExamplePaletteDataSource(string name) : base(name)
+    {
+    }
+
+    public override AsyncCall<List<IPalette>> FetchPaletteList(int startIndex, int items, FilteringSettings filtering)
+    {
+        return AsyncCall<List<IPalette>>.FromResult([
+            new ExtensionPalette("Example Palette", new List<PaletteColor>
+            {
+                new PaletteColor(255, 0, 0),
+                new PaletteColor(0, 255, 0),
+                new PaletteColor(0, 0, 255)
+            }, this)
+        ]);
+    }
+}

+ 27 - 0
samples/Sample6_Palettes/PalettesSampleExtension.cs

@@ -0,0 +1,27 @@
+using System.IO;
+using PixiEditor.Extensions.Sdk;
+
+namespace PalettesSample;
+
+public class PalettesSampleExtension : 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()
+    {
+        ExamplePaletteDataSource dataSource = new ExamplePaletteDataSource("Palettes Sample");
+        Api.Palettes.RegisterDataSource(dataSource);
+        
+        Api.Logger.Log("Palettes registered!");
+    }
+}

+ 14 - 0
samples/Sample6_Palettes/Program.cs

@@ -0,0 +1,14 @@
+namespace PalettesSample;
+
+public static class Program
+{
+    /// <summary>
+    ///     The entry point of the application. This will be executed when extension is loaded.
+    /// You can use this method, but there are special methods that are used for initialization. You won't be able to access PixiEditor Api at this point.
+    /// See <see cref="PalettesSampleExtension"/> for more information.
+    /// </summary>
+    public static void Main()
+    {
+
+    }
+}

+ 42 - 0
samples/Sample6_Palettes/Sample6_Palettes.csproj

@@ -0,0 +1,42 @@
+<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.AvaloniaUI.Desktop\bin\Debug\net8.0\win-x64\Extensions</PixiExtOutputPath>
+        <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+        <RootNamespace>PalettesSample</RootNamespace>
+    </PropertyGroup>
+
+    <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-->
+    <ItemGroup>
+        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Sdk\PixiEditor.Extensions.Sdk.csproj" />
+    </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>

+ 27 - 0
samples/Sample6_Palettes/extension.json

@@ -0,0 +1,27 @@
+{
+  "displayName": "Sample Extension - Palettes",
+  "uniqueName": "yourCompany.Samples.Palettes",
+  "description": "Sample palettes extension for PixiEditor",
+  "version": "1.0.0",
+  "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"
+  ]
+}

+ 18 - 0
samples/Sample7_FlyUI/FlyUISampleExtension.cs

@@ -0,0 +1,18 @@
+using PixiEditor.Extensions.Sdk;
+
+namespace FlyUISample;
+
+public class FlyUiSampleExtension : PixiEditorExtension
+{
+    public override void OnInitialized()
+    {
+        WindowContentElement content = new WindowContentElement();
+        var popup = Api.WindowProvider.CreatePopupWindow("Sample Window", content);
+        content.Window = popup;
+
+        popup.Width = 800;
+        popup.Height = 720;
+        
+        popup.Show();
+    }
+}

+ 9 - 0
samples/Sample7_FlyUI/Program.cs

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

BIN
samples/Sample7_FlyUI/Resources/Pizza.png


+ 42 - 0
samples/Sample7_FlyUI/Sample7_FlyUI.csproj

@@ -0,0 +1,42 @@
+<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.AvaloniaUI.Desktop\bin\Debug\net8.0\win-x64\Extensions</PixiExtOutputPath>
+        <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+        <RootNamespace>FlyUISample</RootNamespace>
+    </PropertyGroup>
+
+    <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-->
+    <ItemGroup>
+        <ProjectReference Include="..\..\src\PixiEditor.Extensions.Sdk\PixiEditor.Extensions.Sdk.csproj" />
+    </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>

+ 46 - 0
samples/Sample7_FlyUI/WindowContentElement.cs

@@ -0,0 +1,46 @@
+using PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+using PixiEditor.Extensions.Sdk.Api.FlyUI;
+using PixiEditor.Extensions.Sdk.Api.Window;
+
+namespace FlyUISample;
+
+public class WindowContentElement : StatelessElement
+{
+    public PopupWindow Window { get; set; }
+    public override CompiledControl BuildNative()
+    {
+        Layout layout = new Layout(body:
+            new Container(margin: Edges.All(25), child:
+                new Column(
+                    new Center(
+                        new Text(
+                            "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vitae neque nibh. Duis sed pharetra dolor. Donec dui sapien, aliquam id sodales in, ornare et urna. Mauris nunc odio, sagittis eget lectus at, imperdiet ornare quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam euismod pellentesque blandit. Vestibulum sagittis, ligula non finibus lobortis, dolor lacus consectetur turpis, id facilisis ligula dolor vitae augue.",
+                            wrap: TextWrap.Wrap,
+                            fontSize: 16)
+                    ),
+                    new Align(
+                        alignment: Alignment.CenterRight,
+                        child: new Text("- Paulo Coelho, The Alchemist (1233)", fontStyle: FontStyle.Italic)
+                    ),
+                    new Container(
+                        margin: Edges.Symmetric(25, 0),
+                        backgroundColor: Color.FromRgba(25, 25, 25, 255),
+                        child: new Column(
+                            new Image(
+                                "/Pizza.png",
+                                filterQuality: FilterQuality.None,
+                                width: 256, height: 256))
+                    ),
+                    new Center(
+                        new Button(
+                            child: new Text("Close"), onClick: _ =>
+                            {
+                                Window.Close();
+                            }))
+                )
+            )
+        );
+
+        return layout.BuildNative();
+    }
+}

+ 27 - 0
samples/Sample7_FlyUI/extension.json

@@ -0,0 +1,27 @@
+{
+  "displayName": "Sample Extension - FlyUI",
+  "uniqueName": "yourCompany.Samples.FlyUI",
+  "description": "Sample FlyUI extension for PixiEditor",
+  "version": "1.0.0",
+  "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"
+  ]
+}

+ 20 - 0
samples/stylecop.json

@@ -0,0 +1,20 @@
+{
+  "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
+  "settings": {
+    "indentation": {
+      "indentationSize": 4
+    },
+    "maintainabilityRules": {
+      "topLevelTypes": [ "class", "interface", "enum", "struct" ]
+    },
+    "readabilityRules": {
+      "allowBuiltInTypeAliases": false
+    },
+    "documentationRules": {
+      "xmlHeader": false,
+      "documentInterfaces": false,
+      "documentExposedElements": false,
+      "documentInternalElements": false
+    }
+  }
+}

+ 2 - 2
src/PixiEditor.Api.CGlueMSBuild/CApiGenerator.cs

@@ -44,7 +44,7 @@ public class CApiGenerator
     {
     {
         var exportedMethods = types
         var exportedMethods = types
             .SelectMany(t => t.Methods)
             .SelectMany(t => t.Methods)
-            .Where(m => m.IsStatic && m.CustomAttributes.Any(a => a.AttributeType.FullName == "PixiEditor.Extensions.Wasm.ApiExportAttribute"))
+            .Where(m => m.IsStatic && m.CustomAttributes.Any(a => a.AttributeType.FullName == "PixiEditor.Extensions.Sdk.ApiExportAttribute"))
             .ToArray();
             .ToArray();
         return exportedMethods;
         return exportedMethods;
     }
     }
@@ -113,7 +113,7 @@ public class CApiGenerator
 
 
     private string BuildExportFunction(MethodDefinition method)
     private string BuildExportFunction(MethodDefinition method)
     {
     {
-        string exportName = method.CustomAttributes.First(a => a.AttributeType.FullName == "PixiEditor.Extensions.Wasm.ApiExportAttribute").ConstructorArguments[0].Value.ToString();
+        string exportName = method.CustomAttributes.First(a => a.AttributeType.FullName == "PixiEditor.Extensions.Sdk.ApiExportAttribute").ConstructorArguments[0].Value.ToString();
         StringBuilder sb = new StringBuilder();
         StringBuilder sb = new StringBuilder();
         sb.Append($"__attribute__((export_name(\"{exportName}\")))");
         sb.Append($"__attribute__((export_name(\"{exportName}\")))");
         sb.AppendLine();
         sb.AppendLine();

+ 1 - 1
src/PixiEditor.Api.CGlueMSBuild/PixiEditor.Api.CGlueMSBuild.csproj

@@ -6,7 +6,7 @@
     <Nullable>enable</Nullable>
     <Nullable>enable</Nullable>
     <LangVersion>latest</LangVersion>
     <LangVersion>latest</LangVersion>
     <OutputType>Library</OutputType>
     <OutputType>Library</OutputType>
-    <OutputPath>../PixiEditor.Extensions.Wasm/build/</OutputPath>
+    <OutputPath>../PixiEditor.Extensions.Sdk/build/</OutputPath>
     <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
     <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
     <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
     <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
     <DebugType>None</DebugType>
     <DebugType>None</DebugType>

+ 0 - 228
src/PixiEditor.Api.CGlueMSBuild/build/PixiEditor.Api.CGlueMSBuild.deps.json

@@ -1,228 +0,0 @@
-{
-  "runtimeTarget": {
-    "name": ".NETStandard,Version=v2.0/",
-    "signature": ""
-  },
-  "compilationOptions": {},
-  "targets": {
-    ".NETStandard,Version=v2.0": {},
-    ".NETStandard,Version=v2.0/": {
-      "PixiEditor.Api.CGlueMSBuild/1.0.0": {
-        "dependencies": {
-          "Microsoft.Build.Utilities.Core": "17.9.5",
-          "NETStandard.Library": "2.0.3",
-          "StyleCop.Analyzers": "1.1.118"
-        },
-        "runtime": {
-          "PixiEditor.Api.CGlueMSBuild.dll": {}
-        }
-      },
-      "Microsoft.Build.Framework/17.9.5": {
-        "dependencies": {
-          "Microsoft.Win32.Registry": "5.0.0",
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
-          "System.Security.Principal.Windows": "5.0.0"
-        }
-      },
-      "Microsoft.Build.Utilities.Core/17.9.5": {
-        "dependencies": {
-          "Microsoft.Build.Framework": "17.9.5",
-          "Microsoft.NET.StringTools": "17.9.5",
-          "Microsoft.Win32.Registry": "5.0.0",
-          "System.Collections.Immutable": "8.0.0",
-          "System.Configuration.ConfigurationManager": "8.0.0",
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
-          "System.Security.Principal.Windows": "5.0.0",
-          "System.Text.Encoding.CodePages": "7.0.0"
-        }
-      },
-      "Microsoft.NET.StringTools/17.9.5": {
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "Microsoft.NETCore.Platforms/1.1.0": {},
-      "Microsoft.Win32.Registry/5.0.0": {
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Memory": "4.5.5",
-          "System.Security.AccessControl": "5.0.0",
-          "System.Security.Principal.Windows": "5.0.0"
-        }
-      },
-      "NETStandard.Library/2.0.3": {
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0"
-        }
-      },
-      "StyleCop.Analyzers/1.1.118": {},
-      "System.Buffers/4.5.1": {},
-      "System.Collections.Immutable/8.0.0": {
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Configuration.ConfigurationManager/8.0.0": {
-        "dependencies": {
-          "System.Security.Cryptography.ProtectedData": "8.0.0"
-        }
-      },
-      "System.Memory/4.5.5": {
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Numerics.Vectors/4.4.0": {},
-      "System.Runtime.CompilerServices.Unsafe/6.0.0": {},
-      "System.Security.AccessControl/5.0.0": {
-        "dependencies": {
-          "System.Security.Principal.Windows": "5.0.0"
-        }
-      },
-      "System.Security.Cryptography.ProtectedData/8.0.0": {
-        "dependencies": {
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Security.Principal.Windows/5.0.0": {},
-      "System.Text.Encoding.CodePages/7.0.0": {
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      }
-    }
-  },
-  "libraries": {
-    "PixiEditor.Api.CGlueMSBuild/1.0.0": {
-      "type": "project",
-      "serviceable": false,
-      "sha512": ""
-    },
-    "Microsoft.Build.Framework/17.9.5": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-CjRmqu9Wv2fyC1d7NKOuBDXcNMI8+GiXGM6izygB+skGGu4Vf0cBcoPq7AFqZCcMpn5DtZ+y7RpaLpB2qrzanQ==",
-      "path": "microsoft.build.framework/17.9.5",
-      "hashPath": "microsoft.build.framework.17.9.5.nupkg.sha512"
-    },
-    "Microsoft.Build.Utilities.Core/17.9.5": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-H2hpVdm7cX/uGJD1HOfab3RKgD5tlnvzQkFqvsrAqGHRi0sqb2w1+hRwERFm23witCjmERnqNgiQjYks6/ds8A==",
-      "path": "microsoft.build.utilities.core/17.9.5",
-      "hashPath": "microsoft.build.utilities.core.17.9.5.nupkg.sha512"
-    },
-    "Microsoft.NET.StringTools/17.9.5": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-C/oPRnjcIZBRzcpl1V06R1eEMCxOGt6mIm+8ioyblELgJEXLM8XjUPuCwljMO52VetsHw54xMcYwU8UEeHEIEg==",
-      "path": "microsoft.net.stringtools/17.9.5",
-      "hashPath": "microsoft.net.stringtools.17.9.5.nupkg.sha512"
-    },
-    "Microsoft.NETCore.Platforms/1.1.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
-      "path": "microsoft.netcore.platforms/1.1.0",
-      "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
-    },
-    "Microsoft.Win32.Registry/5.0.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
-      "path": "microsoft.win32.registry/5.0.0",
-      "hashPath": "microsoft.win32.registry.5.0.0.nupkg.sha512"
-    },
-    "NETStandard.Library/2.0.3": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
-      "path": "netstandard.library/2.0.3",
-      "hashPath": "netstandard.library.2.0.3.nupkg.sha512"
-    },
-    "StyleCop.Analyzers/1.1.118": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-Onx6ovGSqXSK07n/0eM3ZusiNdB6cIlJdabQhWGgJp3Vooy9AaLS/tigeybOJAobqbtggTamoWndz72JscZBvw==",
-      "path": "stylecop.analyzers/1.1.118",
-      "hashPath": "stylecop.analyzers.1.1.118.nupkg.sha512"
-    },
-    "System.Buffers/4.5.1": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==",
-      "path": "system.buffers/4.5.1",
-      "hashPath": "system.buffers.4.5.1.nupkg.sha512"
-    },
-    "System.Collections.Immutable/8.0.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==",
-      "path": "system.collections.immutable/8.0.0",
-      "hashPath": "system.collections.immutable.8.0.0.nupkg.sha512"
-    },
-    "System.Configuration.ConfigurationManager/8.0.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==",
-      "path": "system.configuration.configurationmanager/8.0.0",
-      "hashPath": "system.configuration.configurationmanager.8.0.0.nupkg.sha512"
-    },
-    "System.Memory/4.5.5": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-      "path": "system.memory/4.5.5",
-      "hashPath": "system.memory.4.5.5.nupkg.sha512"
-    },
-    "System.Numerics.Vectors/4.4.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==",
-      "path": "system.numerics.vectors/4.4.0",
-      "hashPath": "system.numerics.vectors.4.4.0.nupkg.sha512"
-    },
-    "System.Runtime.CompilerServices.Unsafe/6.0.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
-      "path": "system.runtime.compilerservices.unsafe/6.0.0",
-      "hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
-    },
-    "System.Security.AccessControl/5.0.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
-      "path": "system.security.accesscontrol/5.0.0",
-      "hashPath": "system.security.accesscontrol.5.0.0.nupkg.sha512"
-    },
-    "System.Security.Cryptography.ProtectedData/8.0.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==",
-      "path": "system.security.cryptography.protecteddata/8.0.0",
-      "hashPath": "system.security.cryptography.protecteddata.8.0.0.nupkg.sha512"
-    },
-    "System.Security.Principal.Windows/5.0.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==",
-      "path": "system.security.principal.windows/5.0.0",
-      "hashPath": "system.security.principal.windows.5.0.0.nupkg.sha512"
-    },
-    "System.Text.Encoding.CodePages/7.0.0": {
-      "type": "package",
-      "serviceable": true,
-      "sha512": "sha512-LSyCblMpvOe0N3E+8e0skHcrIhgV2huaNcjUUEa8hRtgEAm36aGkRoC8Jxlb6Ra6GSfF29ftduPNywin8XolzQ==",
-      "path": "system.text.encoding.codepages/7.0.0",
-      "hashPath": "system.text.encoding.codepages.7.0.0.nupkg.sha512"
-    }
-  }
-}

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


+ 0 - 1
src/PixiEditor.AvaloniaUI.Browser/Program.cs

@@ -9,7 +9,6 @@ using PixiEditor.AvaloniaUI;
 internal partial class Program
 internal partial class Program
 {
 {
     private static async Task Main(string[] args) => await BuildAvaloniaApp()
     private static async Task Main(string[] args) => await BuildAvaloniaApp()
-        .WithInterFont()
         .StartBrowserAppAsync("out");
         .StartBrowserAppAsync("out");
 
 
     public static AppBuilder BuildAvaloniaApp()
     public static AppBuilder BuildAvaloniaApp()

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

@@ -17,6 +17,5 @@ public class Program
     public static AppBuilder BuildAvaloniaApp()
     public static AppBuilder BuildAvaloniaApp()
         => AppBuilder.Configure<App>()
         => AppBuilder.Configure<App>()
             .UsePlatformDetect()
             .UsePlatformDetect()
-            .WithInterFont()
             .LogToTrace();
             .LogToTrace();
 }
 }

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

@@ -100,6 +100,7 @@ internal static class ServiceCollectionHelpers
             .AddSingleton<IToolHandler, BrightnessToolViewModel>(x => (BrightnessToolViewModel)x.GetService<IBrightnessToolHandler>())
             .AddSingleton<IToolHandler, BrightnessToolViewModel>(x => (BrightnessToolViewModel)x.GetService<IBrightnessToolHandler>())
             .AddSingleton<IToolHandler, ZoomToolViewModel>()
             .AddSingleton<IToolHandler, ZoomToolViewModel>()
             // Palette Parsers
             // Palette Parsers
+            .AddSingleton<IPalettesProvider, PaletteProvider>()
             .AddSingleton<PaletteFileParser, JascFileParser>()
             .AddSingleton<PaletteFileParser, JascFileParser>()
             .AddSingleton<PaletteFileParser, ClsFileParser>()
             .AddSingleton<PaletteFileParser, ClsFileParser>()
             .AddSingleton<PaletteFileParser, DeluxePaintParser>()
             .AddSingleton<PaletteFileParser, DeluxePaintParser>()
@@ -126,7 +127,6 @@ internal static class ServiceCollectionHelpers
 
 
     public static IServiceCollection AddExtensionServices(this IServiceCollection collection, ExtensionLoader loader) =>
     public static IServiceCollection AddExtensionServices(this IServiceCollection collection, ExtensionLoader loader) =>
         collection.AddSingleton<IWindowProvider, WindowProvider>(x => new WindowProvider(loader, x))
         collection.AddSingleton<IWindowProvider, WindowProvider>(x => new WindowProvider(loader, x))
-            .AddSingleton<IPaletteProvider, PaletteProvider>()
             .AddSingleton<ElementMap>(x =>
             .AddSingleton<ElementMap>(x =>
             {
             {
                 ElementMap elementMap = new ElementMap();
                 ElementMap elementMap = new ElementMap();

+ 25 - 3
src/PixiEditor.AvaloniaUI/Models/ExtensionServices/PaletteProvider.cs

@@ -10,7 +10,7 @@ using PixiEditor.Platform;
 
 
 namespace PixiEditor.AvaloniaUI.Models.ExtensionServices;
 namespace PixiEditor.AvaloniaUI.Models.ExtensionServices;
 
 
-internal sealed class PaletteProvider : IPaletteProvider
+internal sealed class PaletteProvider : IPalettesProvider
 {
 {
     public ObservableCollection<PaletteFileParser> AvailableParsers { get; set; }
     public ObservableCollection<PaletteFileParser> AvailableParsers { get; set; }
     public ObservableCollection<PaletteListDataSource> DataSources => dataSources;
     public ObservableCollection<PaletteListDataSource> DataSources => dataSources;
@@ -21,18 +21,40 @@ internal sealed class PaletteProvider : IPaletteProvider
         dataSources = new ObservableCollection<PaletteListDataSource>();
         dataSources = new ObservableCollection<PaletteListDataSource>();
     }
     }
 
 
+    /// <summary>
+    ///     Fetches palettes from the provider.
+    /// </summary>
+    /// <param name="startIndex">Starting fetch index. Palettes before said index won't be fetched.</param>
+    /// <param name="items">Max amount of palettes to fetch.</param>
+    /// <param name="filtering">Filtering settings for fetching.</param>
+    /// <returns>List of palettes.</returns>
     public async AsyncCall<List<IPalette>> FetchPalettes(int startIndex, int items, FilteringSettings filtering)
     public async AsyncCall<List<IPalette>> FetchPalettes(int startIndex, int items, FilteringSettings filtering)
     {
     {
         List<IPalette> allPalettes = new();
         List<IPalette> allPalettes = new();
         foreach (PaletteListDataSource dataSource in dataSources)
         foreach (PaletteListDataSource dataSource in dataSources)
         {
         {
-            var palettes = await dataSource.FetchPaletteList(startIndex, items, filtering);
-            allPalettes.AddRange(palettes);
+            try
+            {
+                var palettes = await dataSource.FetchPaletteList(startIndex, items, filtering);
+                allPalettes.AddRange(palettes);
+            }
+            catch
+            {
+#if DEBUG
+                throw;
+#endif
+            }
         }
         }
 
 
         return allPalettes;
         return allPalettes;
     }
     }
 
 
+    /// <summary>
+    ///     Adds a palette to the provider. This means that the palette will be saved in local storage.
+    /// </summary>
+    /// <param name="palette">Palette to save.</param>
+    /// <param name="overwrite">If true and palette with the same name exists, it will be overwritten. If false and palette with the same name exists, it will not be added.</param>
+    /// <returns>True if adding palette was successful.</returns>
     public async AsyncCall<bool> AddPalette(IPalette palette, bool overwrite = false)
     public async AsyncCall<bool> AddPalette(IPalette palette, bool overwrite = false)
     {
     {
         LocalPalettesFetcher localPalettesFetcher = dataSources.OfType<LocalPalettesFetcher>().FirstOrDefault();
         LocalPalettesFetcher localPalettesFetcher = dataSources.OfType<LocalPalettesFetcher>().FirstOrDefault();

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

@@ -44,7 +44,7 @@ public class WindowProvider : IWindowProvider
         return new PopupWindow(new PixiEditorPopup { Title = new LocalizedString(title), Content = body });
         return new PopupWindow(new PixiEditorPopup { Title = new LocalizedString(title), Content = body });
     }
     }
 
 
-    public IPopupWindow GetWindow(WindowType type)
+    public IPopupWindow GetWindow(BuiltInWindowType type)
     {
     {
         string id = type.GetDescription();
         string id = type.GetDescription();
         return GetWindow($"PixiEditor.{id}");
         return GetWindow($"PixiEditor.{id}");

+ 10 - 10
src/PixiEditor.AvaloniaUI/Models/Preferences/PreferencesSettings.cs

@@ -55,7 +55,7 @@ internal class PreferencesSettings : IPreferences
         {
         {
             foreach (var action in callback)
             foreach (var action in callback)
             {
             {
-                action.Invoke(value);
+                action.Invoke(name, value);
             }
             }
         }
         }
 
 
@@ -77,7 +77,7 @@ internal class PreferencesSettings : IPreferences
         {
         {
             foreach (var action in callback)
             foreach (var action in callback)
             {
             {
-                action.Invoke(value);
+                action.Invoke(name, value);
             }
             }
         }
         }
 
 
@@ -95,9 +95,9 @@ internal class PreferencesSettings : IPreferences
         File.WriteAllText(PathToLocalPreferences, JsonConvert.SerializeObject(LocalPreferences));
         File.WriteAllText(PathToLocalPreferences, JsonConvert.SerializeObject(LocalPreferences));
     }
     }
 
 
-    public Dictionary<string, List<Action<object>>> Callbacks { get; set; } = new Dictionary<string, List<Action<object>>>();
+    public Dictionary<string, List<Action<string, object>>> Callbacks { get; set; } = new Dictionary<string, List<Action<string, object>>>();
 
 
-    public void AddCallback(string name, Action<object> action)
+    public void AddCallback(string name, Action<string, object> action)
     {
     {
         name = TrimPrefix(name);
         name = TrimPrefix(name);
 
 
@@ -112,10 +112,10 @@ internal class PreferencesSettings : IPreferences
             return;
             return;
         }
         }
 
 
-        Callbacks.Add(name, new List<Action<object>>() { action });
+        Callbacks.Add(name, new List<Action<string, object>>() { action });
     }
     }
 
 
-    public void AddCallback<T>(string name, Action<T> action)
+    public void AddCallback<T>(string name, Action<string, T> action)
     {
     {
         name = TrimPrefix(name);
         name = TrimPrefix(name);
 
 
@@ -124,10 +124,10 @@ internal class PreferencesSettings : IPreferences
             throw new ArgumentNullException(nameof(action));
             throw new ArgumentNullException(nameof(action));
         }
         }
 
 
-        AddCallback(name, new Action<object>(o => action((T)o)));
+        AddCallback(name, new Action<string, object>((n, o) => action(n, (T)o)));
     }
     }
 
 
-    public void RemoveCallback(string name, Action<object> action)
+    public void RemoveCallback(string name, Action<string, object> action)
     {
     {
         name = TrimPrefix(name);
         name = TrimPrefix(name);
 
 
@@ -142,7 +142,7 @@ internal class PreferencesSettings : IPreferences
         }
         }
     }
     }
 
 
-    public void RemoveCallback<T>(string name, Action<T> action)
+    public void RemoveCallback<T>(string name, Action<string, T> action)
     {
     {
         name = TrimPrefix(name);
         name = TrimPrefix(name);
 
 
@@ -151,7 +151,7 @@ internal class PreferencesSettings : IPreferences
             throw new ArgumentNullException(nameof(action));
             throw new ArgumentNullException(nameof(action));
         }
         }
 
 
-        RemoveCallback(name, new Action<object>(o => action((T)o)));
+        RemoveCallback(name, new Action<string, object>((n, o) => action(n, (T)o)));
     }
     }
 
 
 #nullable enable
 #nullable enable

+ 0 - 5
src/PixiEditor.AvaloniaUI/PixiEditor.AvaloniaUI.csproj

@@ -59,7 +59,6 @@
     <PackageReference Include="Avalonia.Headless" Version="$(AvaloniaVersion)"/>
     <PackageReference Include="Avalonia.Headless" Version="$(AvaloniaVersion)"/>
     <PackageReference Include="Avalonia.Labs.Lottie" Version="11.0.10.1"/>
     <PackageReference Include="Avalonia.Labs.Lottie" Version="11.0.10.1"/>
     <PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)"/>
     <PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)"/>
-    <PackageReference Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)"/>
     <PackageReference Include="Avalonia.Skia" Version="$(AvaloniaVersion)"/>
     <PackageReference Include="Avalonia.Skia" Version="$(AvaloniaVersion)"/>
     <PackageReference Include="Avalonia.Svg.Skia" Version="11.0.0.18"/>
     <PackageReference Include="Avalonia.Svg.Skia" Version="11.0.0.18"/>
     <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
     <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
@@ -117,8 +116,4 @@
     </Content>
     </Content>
   </ItemGroup>
   </ItemGroup>
 
 
-  <ItemGroup>
-    <Folder Include="Fonts\" />
-  </ItemGroup>
-
 </Project>
 </Project>

+ 7 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Menu/MenuBarViewModel.cs

@@ -8,6 +8,7 @@ using Avalonia.Data;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.AvaloniaUI.Models.Commands;
 using PixiEditor.AvaloniaUI.Models.Commands;
 using PixiEditor.AvaloniaUI.Models.Commands.XAML;
 using PixiEditor.AvaloniaUI.Models.Commands.XAML;
+using PixiEditor.AvaloniaUI.ViewModels.SubViewModels.AdditionalContent;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Common.Localization;
 using Command = PixiEditor.AvaloniaUI.Models.Commands.Commands.Command;
 using Command = PixiEditor.AvaloniaUI.Models.Commands.Commands.Command;
 
 
@@ -15,6 +16,7 @@ namespace PixiEditor.AvaloniaUI.ViewModels.Menu;
 
 
 internal class MenuBarViewModel : PixiObservableObject
 internal class MenuBarViewModel : PixiObservableObject
 {
 {
+    public AdditionalContentViewModel AdditionalContentSubViewModel { get; set; }
     public ObservableCollection<MenuItem> MenuEntries { get; set; } = new();
     public ObservableCollection<MenuItem> MenuEntries { get; set; } = new();
 
 
     private Dictionary<string, MenuTreeItem> menuItems = new();
     private Dictionary<string, MenuTreeItem> menuItems = new();
@@ -30,6 +32,11 @@ internal class MenuBarViewModel : PixiObservableObject
         { "DEBUG", 1000 },
         { "DEBUG", 1000 },
     };
     };
 
 
+    public MenuBarViewModel(AdditionalContentViewModel? additionalContentSubViewModel)
+    {
+        AdditionalContentSubViewModel = additionalContentSubViewModel;
+    }
+
     public void Init(IServiceProvider serviceProvider, CommandController controller)
     public void Init(IServiceProvider serviceProvider, CommandController controller)
     {
     {
         MenuItemBuilder[] builders = serviceProvider.GetServices<MenuItemBuilder>().ToArray();
         MenuItemBuilder[] builders = serviceProvider.GetServices<MenuItemBuilder>().ToArray();

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/SettingsWindowViewModel.cs

@@ -276,7 +276,7 @@ internal partial class SettingsWindowViewModel : ViewModelBase
         Commands = new(CommandController.Current.CommandGroups.Select(x => new GroupSearchResult(x)));
         Commands = new(CommandController.Current.CommandGroups.Select(x => new GroupSearchResult(x)));
         UpdateSearchResults();
         UpdateSearchResults();
         SettingsSubViewModel = new SettingsViewModel(this);
         SettingsSubViewModel = new SettingsViewModel(this);
-        ViewModelMain.Current.Preferences.AddCallback("IsDebugModeEnabled", _ => UpdateSearchResults());
+        ViewModelMain.Current.Preferences.AddCallback("IsDebugModeEnabled", (_, _) => UpdateSearchResults());
         VisibleGroups = Commands.Count(x => x.IsVisible);
         VisibleGroups = Commands.Count(x => x.IsVisible);
     }
     }
 
 

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

@@ -369,7 +369,7 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
 
 
     public void SetupPaletteProviders(IServiceProvider services)
     public void SetupPaletteProviders(IServiceProvider services)
     {
     {
-        PaletteProvider = (PaletteProvider)services.GetService<IPaletteProvider>();
+        PaletteProvider = (PaletteProvider)services.GetService<IPalettesProvider>();
         PaletteProvider.AvailableParsers =
         PaletteProvider.AvailableParsers =
             new ObservableCollection<PaletteFileParser>(services.GetServices<PaletteFileParser>());
             new ObservableCollection<PaletteFileParser>(services.GetServices<PaletteFileParser>());
         var dataSources = services.GetServices<PaletteListDataSource>();
         var dataSources = services.GetServices<PaletteListDataSource>();

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/ViewModelMain.cs

@@ -74,7 +74,7 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
 
 
     public LayoutViewModel LayoutSubViewModel { get; set; }
     public LayoutViewModel LayoutSubViewModel { get; set; }
 
 
-    public MenuBarViewModel MenuBarViewModel { get; set; } = new();
+    public MenuBarViewModel MenuBarViewModel { get; set; }
 
 
     public IPreferences Preferences { get; set; }
     public IPreferences Preferences { get; set; }
     public ILocalizationProvider LocalizationProvider { get; set; }
     public ILocalizationProvider LocalizationProvider { get; set; }
@@ -147,6 +147,7 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
         RegistrySubViewModel = services.GetService<RegistryViewModel>();
         RegistrySubViewModel = services.GetService<RegistryViewModel>();
 
 
         AdditionalContentSubViewModel = services.GetService<AdditionalContentViewModel>();
         AdditionalContentSubViewModel = services.GetService<AdditionalContentViewModel>();
+        MenuBarViewModel = new MenuBarViewModel(AdditionalContentSubViewModel);
 
 
         CommandController.Init(services);
         CommandController.Init(services);
         LayoutSubViewModel.LayoutManager.InitLayout(this);
         LayoutSubViewModel.LayoutManager.InitLayout(this);

+ 3 - 2
src/PixiEditor.AvaloniaUI/Views/Dialogs/DialogTitleBar.axaml

@@ -11,17 +11,18 @@
     x:Name="uc"
     x:Name="uc"
     Height="36"
     Height="36"
     d:DesignWidth="300">
     d:DesignWidth="300">
-    <Grid>
+    <Grid DataContext="{Binding ElementName=uc}">
         <Grid Background="{DynamicResource ThemeBackgroundBrush1}" IsHitTestVisible="False"/>
         <Grid Background="{DynamicResource ThemeBackgroundBrush1}" IsHitTestVisible="False"/>
         <TextBlock
         <TextBlock
             IsHitTestVisible="False"
             IsHitTestVisible="False"
             TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center"
             TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center"
-            ui:Translator.Key="{Binding ElementName=uc, Path=TitleKey}"
+            ui:Translator.Key="{Binding Path=TitleKey}"
             Foreground="{DynamicResource ThemeForegroundBrush}"
             Foreground="{DynamicResource ThemeForegroundBrush}"
             FontSize="13"
             FontSize="13"
             Margin="5,0,0,0"/>
             Margin="5,0,0,0"/>
         <DockPanel IsHitTestVisible="True">
         <DockPanel IsHitTestVisible="True">
             <CaptionButtons Name="captionButtons" DockPanel.Dock="Right" />
             <CaptionButtons Name="captionButtons" DockPanel.Dock="Right" />
+            <ContentPresenter DockPanel.Dock="Right" IsVisible="{Binding !!AdditionalElement}" Content="{Binding Path=AdditionalElement}"/>
             <Control /><!-- dummy control to occupy dockpanel center -->
             <Control /><!-- dummy control to occupy dockpanel center -->
         </DockPanel>
         </DockPanel>
     </Grid>
     </Grid>

+ 9 - 1
src/PixiEditor.AvaloniaUI/Views/Dialogs/DialogTitleBar.axaml.cs

@@ -22,6 +22,8 @@ internal partial class DialogTitleBar : UserControl, ICustomTranslatorElement
     public static readonly StyledProperty<ICommand?> CloseCommandProperty =
     public static readonly StyledProperty<ICommand?> CloseCommandProperty =
         AvaloniaProperty.Register<DialogTitleBar, ICommand?>(nameof(CloseCommand));
         AvaloniaProperty.Register<DialogTitleBar, ICommand?>(nameof(CloseCommand));
 
 
+    public static readonly StyledProperty<Control> AdditionalElementProperty = AvaloniaProperty.Register<DialogTitleBar, Control>("AdditionalElement");
+
     public ICommand? CloseCommand
     public ICommand? CloseCommand
     {
     {
         get => GetValue(CloseCommandProperty);
         get => GetValue(CloseCommandProperty);
@@ -48,7 +50,13 @@ internal partial class DialogTitleBar : UserControl, ICustomTranslatorElement
         get => GetValue(CanFullscreenProperty);
         get => GetValue(CanFullscreenProperty);
         set => SetValue(CanFullscreenProperty, value);
         set => SetValue(CanFullscreenProperty, value);
     }
     }
-    
+
+    public Control AdditionalElement
+    {
+        get { return (Control)GetValue(AdditionalElementProperty); }
+        set { SetValue(AdditionalElementProperty, value); }
+    }
+
     public DialogTitleBar()
     public DialogTitleBar()
     {
     {
         InitializeComponent();
         InitializeComponent();

+ 3 - 0
src/PixiEditor.AvaloniaUI/Views/Dialogs/PixiEditorPopup.cs

@@ -46,6 +46,9 @@ public partial class PixiEditorPopup : Window, IPopupWindow
     public PixiEditorPopup()
     public PixiEditorPopup()
     {
     {
         CloseCommand = new RelayCommand(ClosePopup);
         CloseCommand = new RelayCommand(ClosePopup);
+#if DEBUG
+        this.AttachDevTools();
+#endif
     }
     }
 
 
     public override void Show()
     public override void Show()

+ 2 - 2
src/PixiEditor.AvaloniaUI/Views/Layers/FolderControl.axaml

@@ -63,12 +63,12 @@
                                 </ImageBrush>
                                 </ImageBrush>
                             </Border.Background>
                             </Border.Background>
                             <Image Source="{Binding Folder.PreviewBitmap, ElementName=folderControl}" Stretch="Uniform" Width="30" Height="30">
                             <Image Source="{Binding Folder.PreviewBitmap, ElementName=folderControl}" Stretch="Uniform" Width="30" Height="30">
-                                <helpers:RenderOptionsBindable.BitmapInterpolationMode>
+                                <ui:RenderOptionsBindable.BitmapInterpolationMode>
                                     <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
                                     <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
                                         <Binding Path="Folder.PreviewBitmap.PixelSize.Width" ElementName="folderControl"/>
                                         <Binding Path="Folder.PreviewBitmap.PixelSize.Width" ElementName="folderControl"/>
                                         <Binding RelativeSource="{RelativeSource Mode=Self}" Path="Bounds.Width"/>
                                         <Binding RelativeSource="{RelativeSource Mode=Self}" Path="Bounds.Width"/>
                                     </MultiBinding>
                                     </MultiBinding>
-                                </helpers:RenderOptionsBindable.BitmapInterpolationMode>
+                                </ui:RenderOptionsBindable.BitmapInterpolationMode>
                             </Image>
                             </Image>
                         </Border>
                         </Border>
                         <Border 
                         <Border 

+ 24 - 15
src/PixiEditor.AvaloniaUI/Views/Main/MainTitleBar.axaml

@@ -13,19 +13,28 @@
              x:DataType="menu:MenuBarViewModel"
              x:DataType="menu:MenuBarViewModel"
              x:Class="PixiEditor.AvaloniaUI.Views.Main.MainTitleBar">
              x:Class="PixiEditor.AvaloniaUI.Views.Main.MainTitleBar">
     <Design.DataContext>
     <Design.DataContext>
-        <menu:MenuBarViewModel/>
+        <menu:MenuBarViewModel />
     </Design.DataContext>
     </Design.DataContext>
     <Grid>
     <Grid>
-        <dialogs:DialogTitleBar 
-            DockPanel.Dock="Top"/>
-        <Svg DockPanel.Dock="Left" Margin="10, 0, 0, 0" HorizontalAlignment="Left" Path="/Images/PixiEditorLogo.svg" Width="20" Height="20"/>
+        <dialogs:DialogTitleBar
+            DockPanel.Dock="Top">
+            <dialogs:DialogTitleBar.AdditionalElement>
+                <Border BorderThickness="1" BorderBrush="{DynamicResource AccentColor}"
+                        Padding="5 0" CornerRadius="5" Height="25"
+                        IsVisible="{Binding Path=AdditionalContentSubViewModel.IsSupporterPackAvailable}">
+                    <TextBlock VerticalAlignment="Center" ui:Translator.Key="PixiEditor.SupporterPack:AWESOME_SUPPORTER" />
+                </Border>
+            </dialogs:DialogTitleBar.AdditionalElement>
+        </dialogs:DialogTitleBar>
+        <Svg DockPanel.Dock="Left" Margin="10, 0, 0, 0" HorizontalAlignment="Left" Path="/Images/PixiEditorLogo.svg"
+             Width="20" Height="20" />
         <xaml:Menu
         <xaml:Menu
             Margin="40, 0, 0, 0"
             Margin="40, 0, 0, 0"
-                DockPanel.Dock="Left"
-                HorizontalAlignment="Left"
-                VerticalAlignment="Center"
-                ItemsSource="{Binding MenuEntries}"
-                Background="Transparent"/>
+            DockPanel.Dock="Left"
+            HorizontalAlignment="Left"
+            VerticalAlignment="Center"
+            ItemsSource="{Binding MenuEntries}"
+            Background="Transparent" />
         <Border Width="300" Height="25"
         <Border Width="300" Height="25"
                 Background="{DynamicResource ThemeBackgroundBrush}"
                 Background="{DynamicResource ThemeBackgroundBrush}"
                 CornerRadius="5" BorderThickness="1"
                 CornerRadius="5" BorderThickness="1"
@@ -33,24 +42,24 @@
                 Cursor="IBeam">
                 Cursor="IBeam">
             <Border.Styles>
             <Border.Styles>
                 <Style Selector="Border">
                 <Style Selector="Border">
-                    <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
+                    <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}" />
                 </Style>
                 </Style>
                 <Style Selector="Border:pointerover">
                 <Style Selector="Border:pointerover">
-                    <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/>
+                    <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}" />
                 </Style>
                 </Style>
             </Border.Styles>
             </Border.Styles>
             <Interaction.Behaviors>
             <Interaction.Behaviors>
                 <EventTriggerBehavior
                 <EventTriggerBehavior
                     EventName="PointerPressed">
                     EventName="PointerPressed">
                     <InvokeCommandAction
                     <InvokeCommandAction
-                        Command="{xaml:Command PixiEditor.Search.Toggle}"/>
+                        Command="{xaml:Command PixiEditor.Search.Toggle}" />
                 </EventTriggerBehavior>
                 </EventTriggerBehavior>
             </Interaction.Behaviors>
             </Interaction.Behaviors>
             <Grid Margin="5,0" VerticalAlignment="Center">
             <Grid Margin="5,0" VerticalAlignment="Center">
-                <TextBlock ui:Translator.Key="SEARCH"/>
+                <TextBlock ui:Translator.Key="SEARCH" />
                 <TextBlock Text="{xaml:ShortcutBinding PixiEditor.Search.Toggle}"
                 <TextBlock Text="{xaml:ShortcutBinding PixiEditor.Search.Toggle}"
-                           HorizontalAlignment="Right"/>
+                           HorizontalAlignment="Right" />
             </Grid>
             </Grid>
         </Border>
         </Border>
     </Grid>
     </Grid>
-</UserControl>
+</UserControl>

+ 2 - 1
src/PixiEditor.AvaloniaUI/Views/Main/Navigation.axaml

@@ -6,6 +6,7 @@
              xmlns:converters="clr-namespace:PixiEditor.AvaloniaUI.Helpers.Converters"
              xmlns:converters="clr-namespace:PixiEditor.AvaloniaUI.Helpers.Converters"
              xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
              xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
              xmlns:ui1="clr-namespace:PixiEditor.AvaloniaUI.Helpers.UI"
              xmlns:ui1="clr-namespace:PixiEditor.AvaloniaUI.Helpers.UI"
+             xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              Name="uc"
              Name="uc"
              x:Class="PixiEditor.AvaloniaUI.Views.Main.Navigation">
              x:Class="PixiEditor.AvaloniaUI.Views.Main.Navigation">
@@ -60,7 +61,7 @@
                 </TextBlock.Text>
                 </TextBlock.Text>
             </TextBlock>
             </TextBlock>
         </StackPanel>
         </StackPanel>
-        <Grid Grid.Row="2" HorizontalAlignment="Right" Margin="0,0,5,0" ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding ElementName=backgroundButton, Path=ActiveItem.ScalingMode}">
+        <Grid Grid.Row="2" HorizontalAlignment="Right" Margin="0,0,5,0" ui:RenderOptionsBindable.BitmapInterpolationMode="{Binding ElementName=backgroundButton, Path=ActiveItem.ScalingMode}">
             <StackPanel Orientation="Horizontal">
             <StackPanel Orientation="Horizontal">
                 <input:ListSwitchButton x:Name="formatButton" Margin="0,0,5,0" Height="20">
                 <input:ListSwitchButton x:Name="formatButton" Margin="0,0,5,0" Height="20">
                     <input:ListSwitchButton.Items>
                     <input:ListSwitchButton.Items>

+ 3 - 2
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/FixedViewport.axaml

@@ -7,6 +7,7 @@
              xmlns:converters1="clr-namespace:PixiEditor.AvaloniaUI.Helpers.Converters"
              xmlns:converters1="clr-namespace:PixiEditor.AvaloniaUI.Helpers.Converters"
              xmlns:ui="clr-namespace:PixiEditor.AvaloniaUI.Helpers.UI"
              xmlns:ui="clr-namespace:PixiEditor.AvaloniaUI.Helpers.UI"
              xmlns:visuals="clr-namespace:PixiEditor.AvaloniaUI.Views.Visuals"
              xmlns:visuals="clr-namespace:PixiEditor.AvaloniaUI.Views.Visuals"
+             xmlns:ui1="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
              mc:Ignorable="d"
              mc:Ignorable="d"
              x:Name="uc"
              x:Name="uc"
              HorizontalAlignment="Center"
              HorizontalAlignment="Center"
@@ -19,11 +20,11 @@
         Surface="{Binding TargetBitmap, ElementName=uc}"
         Surface="{Binding TargetBitmap, ElementName=uc}"
         Stretch="Uniform"
         Stretch="Uniform"
         SizeChanged="OnImageSizeChanged">
         SizeChanged="OnImageSizeChanged">
-        <ui:RenderOptionsBindable.BitmapInterpolationMode>
+        <ui1:RenderOptionsBindable.BitmapInterpolationMode>
             <MultiBinding Converter="{converters1:WidthToBitmapScalingModeConverter}">
             <MultiBinding Converter="{converters1:WidthToBitmapScalingModeConverter}">
                 <Binding ElementName="uc" Path="TargetBitmap.Size.X"/>
                 <Binding ElementName="uc" Path="TargetBitmap.Size.X"/>
                 <Binding ElementName="mainImage" Path="Bounds.Width"/>
                 <Binding ElementName="mainImage" Path="Bounds.Width"/>
             </MultiBinding>
             </MultiBinding>
-        </ui:RenderOptionsBindable.BitmapInterpolationMode>
+        </ui1:RenderOptionsBindable.BitmapInterpolationMode>
     </visuals:SurfaceControl>
     </visuals:SurfaceControl>
 </UserControl>
 </UserControl>

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml

@@ -137,7 +137,7 @@
             FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
             FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
             DefaultCursor="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ToolCursor, Mode=OneWay}"
             DefaultCursor="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ToolCursor, Mode=OneWay}"
             CheckerImagePath="/Images/CheckerTile.png"
             CheckerImagePath="/Images/CheckerTile.png"
-            ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}"/>
+            ui:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}"/>
         <!--<zoombox:Zoombox
         <!--<zoombox:Zoombox
             Tag="{Binding ElementName=vpUc}"
             Tag="{Binding ElementName=vpUc}"
             x:Name="zoombox"
             x:Name="zoombox"

+ 2 - 2
src/PixiEditor.AvaloniaUI/Views/Palettes/PaletteItem.axaml

@@ -28,7 +28,7 @@
                     <input:EditableTextBlock x:Name="titleTextBlock" OnSubmit="EditableTextBlock_OnSubmit"
                     <input:EditableTextBlock x:Name="titleTextBlock" OnSubmit="EditableTextBlock_OnSubmit"
                                                     Text="{Binding Palette.Name, ElementName=paletteItem, Mode=TwoWay}"
                                                     Text="{Binding Palette.Name, ElementName=paletteItem, Mode=TwoWay}"
                                                     FontSize="20" MaxChars="50"/>
                                                     FontSize="20" MaxChars="50"/>
-                <Button IsVisible="{Binding ElementName=paletteItem, Path=IsMouseOver}"
+                <Button IsVisible="{Binding ElementName=paletteItem, Path=IsPointerOver}"
                         Click="RenameButton_Click"
                         Click="RenameButton_Click"
                         Cursor="Hand" Width="20" Height="20">
                         Cursor="Hand" Width="20" Height="20">
                     <Image Source="/Images/Edit.png"/>
                     <Image Source="/Images/Edit.png"/>
@@ -38,7 +38,7 @@
                        Source="/Images/SupperterPack.png" Width="24"
                        Source="/Images/SupperterPack.png" Width="24"
                        DockPanel.Dock="Right" HorizontalAlignment="Right"/>-->
                        DockPanel.Dock="Right" HorizontalAlignment="Right"/>-->
                 <decorators:Chip Margin="0 5 5 0"
                 <decorators:Chip Margin="0 5 5 0"
-                                   ui:Translator.Key="{Binding ElementName=paletteItem, Path=Palette.Source.Name.Key}"
+                                   ui:Translator.Key="{Binding ElementName=paletteItem, Path=Palette.Source.Name}"
                                    DockPanel.Dock="Right" HorizontalAlignment="Right"/>
                                    DockPanel.Dock="Right" HorizontalAlignment="Right"/>
             </DockPanel>
             </DockPanel>
             <TextBlock Margin="0 5 0 0" />
             <TextBlock Margin="0 5 0 0" />

+ 2 - 2
src/PixiEditor.AvaloniaUI/Views/Windows/HelloTherePopup.axaml

@@ -120,12 +120,12 @@
                                                         Margin="10"
                                                         Margin="10"
                                                         Stretch="Uniform"
                                                         Stretch="Uniform"
                                                         x:Name="image">
                                                         x:Name="image">
-                                                        <ui1:RenderOptionsBindable.BitmapInterpolationMode>
+                                                        <ui:RenderOptionsBindable.BitmapInterpolationMode>
                                                             <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
                                                             <MultiBinding Converter="{converters:WidthToBitmapScalingModeConverter}">
                                                                 <Binding Path="PreviewBitmap.Size.X"/>
                                                                 <Binding Path="PreviewBitmap.Size.X"/>
                                                                 <Binding ElementName="image" Path="Width"/>
                                                                 <Binding ElementName="image" Path="Width"/>
                                                             </MultiBinding>
                                                             </MultiBinding>
-                                                        </ui1:RenderOptionsBindable.BitmapInterpolationMode>
+                                                        </ui:RenderOptionsBindable.BitmapInterpolationMode>
                                                     </visuals:SurfaceControl>
                                                     </visuals:SurfaceControl>
                                                     <Border Grid.Row="1" Height="8" Width="8" x:Name="extensionBorder" Margin="5"
                                                     <Border Grid.Row="1" Height="8" Width="8" x:Name="extensionBorder" Margin="5"
                                                             Background="{Binding FileExtension, Converter={converters:FileExtensionToColorConverter}}" 
                                                             Background="{Binding FileExtension, Converter={converters:FileExtensionToColorConverter}}" 

+ 1 - 0
src/PixiEditor.AvaloniaUI/Views/Windows/PalettesBrowser.axaml.cs

@@ -422,6 +422,7 @@ internal partial class PalettesBrowser : PixiEditorPopup, IPopupWindow
 
 
     public async Task UpdatePaletteList()
     public async Task UpdatePaletteList()
     {
     {
+        if (IsFetching) return;
         IsFetching = true;
         IsFetching = true;
         _lastScrolledOffset = -1;
         _lastScrolledOffset = -1;
         PaletteList?.Palettes?.Clear();
         PaletteList?.Palettes?.Clear();

+ 0 - 14
src/PixiEditor.DevTools/DevToolsExtension.cs

@@ -1,14 +0,0 @@
-using PixiEditor.DevTools.Layouts;
-using PixiEditor.Extensions;
-
-namespace PixiEditor.DevTools;
-
-public class DevToolsExtension : Extension
-{
-    public static ExtensionServices PixiEditorApi { get; private set; } = null!;
-    protected override void OnInitialized()
-    {
-        PixiEditorApi = Api;
-        Api.Windowing.CreatePopupWindow("Elements UI Builder", new LiveLayoutPreviewWindow().BuildNative()).Show();
-    }
-}

+ 0 - 34
src/PixiEditor.DevTools/HotReloader.cs

@@ -1,34 +0,0 @@
-namespace PixiEditor.DevTools;
-
-public class HotReloader
-{
-    public List<string> WatchedFiles { get; } = new();
-    public Action<string>? OnFileChanged { get; set; }
-
-    private List<FileSystemWatcher> _watchers = new();
-
-    public void WatchFile(string path, string filter)
-    {
-        string directory = Path.GetDirectoryName(path);
-        WatchedFiles.Add(path);
-
-        FileSystemWatcher watcher = new(directory, filter);
-        watcher.Changed += WatcherOnChanged;
-        watcher.EnableRaisingEvents = true;
-        _watchers.Add(watcher);
-    }
-
-    private void WatcherOnChanged(object sender, FileSystemEventArgs e)
-    {
-        try
-        {
-            (sender as FileSystemWatcher).EnableRaisingEvents = false;
-            OnFileChanged?.Invoke(e.FullPath);
-        }
-
-        finally
-        {
-            (sender as FileSystemWatcher).EnableRaisingEvents = true;
-        }
-    }
-}

+ 0 - 33
src/PixiEditor.DevTools/LayoutDeserializer.cs

@@ -1,33 +0,0 @@
-using PixiEditor.Extensions.FlyUI;
-using PixiEditor.Extensions.FlyUI.Elements;
-
-namespace PixiEditor.DevTools;
-
-public class LayoutDeserializer
-{
-    private LayoutBuilder _builder;
-    public string LayoutFilePath { get; set; }
-
-    public LayoutDeserializer(string layoutFilePath)
-    {
-        LayoutFilePath = layoutFilePath;
-        _builder = new LayoutBuilder(
-            (ElementMap)DevToolsExtension.PixiEditorApi.Services.GetService(typeof(ElementMap)));
-    }
-
-    public LayoutElement DeserializeLayout()
-    {
-        List<byte> bytes = new();
-        using (FileStream fileStream = new(LayoutFilePath, FileMode.Open))
-        {
-            byte[] buffer = new byte[1024];
-            int bytesRead;
-            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
-            {
-                bytes.AddRange(buffer.Take(bytesRead));
-            }
-        }
-
-        return (LayoutElement)_builder.Deserialize(bytes.ToArray().AsSpan(), DuplicateResolutionTactic.ReplaceRemoveChildren);
-    }
-}

+ 0 - 11
src/PixiEditor.DevTools/Layouts/LiveLayoutPreviewWindow.cs

@@ -1,11 +0,0 @@
-using PixiEditor.Extensions.FlyUI.Elements;
-
-namespace PixiEditor.DevTools.Layouts;
-
-public class LiveLayoutPreviewWindow : StatefulElement<LivePreviewWindowState>
-{
-    public override LivePreviewWindowState CreateState()
-    {
-        return new();
-    }
-}

+ 0 - 69
src/PixiEditor.DevTools/Layouts/LivePreviewWindowState.cs

@@ -1,69 +0,0 @@
-using PixiEditor.Extensions.CommonApi.FlyUI.Events;
-using PixiEditor.Extensions.FlyUI.Elements;
-using PixiEditor.Extensions.IO;
-using PixiEditor.Extensions.Runtime;
-
-namespace PixiEditor.DevTools.Layouts;
-
-public class LivePreviewWindowState : State
-{
-    private HotReloader reloader;
-
-    private LayoutElement? _element;
-    private LayoutDeserializer? _deserializer;
-    public string? SelectedProjectFile { get; set; }
-
-    public LivePreviewWindowState()
-    {
-        reloader = new HotReloader();
-        reloader.OnFileChanged += OnFileChanged;
-    }
-
-    public override LayoutElement BuildElement()
-    {
-        return new Column(
-            new Align(
-                alignment: Alignment.TopLeft,
-                child: new Button(
-                    child: SelectedProjectFile != null
-                        ? new Text($"Selected extension: {SelectedProjectFile}")
-                        : new Text("Select extension"),
-                    onClick: OnClick)),
-            new Center(
-                child: _element ?? new Text("No layout element selected"))
-        );
-    }
-
-    private void OnFileChanged(string obj)
-    {
-        SetState(BuildLayoutElement);
-    }
-
-    private void BuildLayoutElement()
-    {
-        if (_deserializer != null)
-        {
-            _element = _deserializer.DeserializeLayout();
-        }
-    }
-
-    private void OnClick(ElementEventArgs args)
-    {
-        if (DevToolsExtension.PixiEditorApi.FileSystem.OpenFileDialog(
-                new FileFilter().AddFilter("Layout file", "*.layout"), out string? path))
-        {
-            SetState(() =>
-            {
-                SelectedProjectFile = path;
-                InitProject();
-            });
-        }
-    }
-
-    private void InitProject()
-    {
-        _deserializer = new LayoutDeserializer(SelectedProjectFile);
-        reloader.WatchFile(SelectedProjectFile, "*.layout");
-        BuildLayoutElement();
-    }
-}

+ 0 - 32
src/PixiEditor.DevTools/PixiEditor.DevTools.csproj

@@ -1,32 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-    <PropertyGroup>
-        <TargetFramework>net8.0</TargetFramework>
-        <ImplicitUsings>enable</ImplicitUsings>
-        <Nullable>enable</Nullable>
-       <OutputPath>..\PixiEditor.AvaloniaUI.Desktop\bin\Debug\net8.0\Extensions\DevTools</OutputPath>
-       <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
-      <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
-    </PropertyGroup>
-
-    <ItemGroup>
-      <ProjectReference Include="..\PixiEditor.Extensions.Runtime\PixiEditor.Extensions.Runtime.csproj" />
-      <ProjectReference Include="..\PixiEditor.Extensions.Wasm\PixiEditor.Extensions.Wasm.csproj" />
-      <ProjectReference Include="..\PixiEditor.Extensions\PixiEditor.Extensions.csproj" />
-    </ItemGroup>
-
-    <ItemGroup>
-      <None Remove="extension.json" />
-      <Content Include="extension.json">
-        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-      </Content>
-    </ItemGroup>
-
-    <ItemGroup>
-      <PackageReference Include="Microsoft.Build.Locator" Version="1.6.10" />
-      <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
-      <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
-      <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.8.0" />
-    </ItemGroup>
-
-</Project>

+ 0 - 21
src/PixiEditor.DevTools/extension.json

@@ -1,21 +0,0 @@
-{
-  "displayName": "PixiEditor Dev Tools",
-  "uniqueName": "Pexeedytoo.DevTools",
-  "description": "PixiEditor Dev Tools contains tools for developers to use when developing PixiEditor extensions.",
-  "version": "0.0.1",
-  "author": {
-    "name": "PixiEditor",
-    "email": "[email protected]",
-    "website": "https://pixieditor.net"
-  },
-  "publisher": {
-    "name": "PixiEditor",
-    "email": "[email protected]",
-    "website": "https://pixieditor.net"
-  },
-  "license": "Copyright PixiEditor Organization",
-  "categories": [
-    "Development",
-    "Utilities"
-  ]
-}

+ 13 - 0
src/PixiEditor.Extensions.CommonApi/.config/dotnet-tools.json

@@ -0,0 +1,13 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "protobuf-net.protogen": {
+      "version": "3.2.42",
+      "commands": [
+        "protogen"
+      ],
+      "rollForward": false
+    }
+  }
+}

+ 97 - 8
src/PixiEditor.Extensions.CommonApi/Async/AsyncCall.cs

@@ -9,6 +9,8 @@ public delegate void AsyncCallFailed(Exception exception);
 [AsyncMethodBuilder(typeof(AsyncCallAsyncMethodBuilder))]
 [AsyncMethodBuilder(typeof(AsyncCallAsyncMethodBuilder))]
 public class AsyncCall
 public class AsyncCall
 {
 {
+    private List<AsyncCallCompleted> completedCalls = new List<AsyncCallCompleted>();
+    private List<AsyncCallFailed> failedCalls = new List<AsyncCallFailed>();
     private object? _result;
     private object? _result;
     protected Action continuation;
     protected Action continuation;
     public AsyncCallState State { get; protected set; } = AsyncCallState.Pending;
     public AsyncCallState State { get; protected set; } = AsyncCallState.Pending;
@@ -27,8 +29,42 @@ public class AsyncCall
         }
         }
     }
     }
     
     
-    public event AsyncCallCompleted Completed;
-    public event AsyncCallFailed Failed;
+    public event AsyncCallCompleted Completed
+    {
+        add
+        {
+            if (State == AsyncCallState.Completed)
+            {
+                value();
+            }
+            else
+            {
+                completedCalls.Add(value);
+            }
+        }
+        remove
+        {
+            completedCalls.Remove(value);
+        }
+    }
+    public event AsyncCallFailed Failed
+    {
+        add
+        {
+            if (State == AsyncCallState.Failed)
+            {
+                value(Exception);
+            }
+            else
+            {
+                failedCalls.Add(value);
+            }
+        }
+        remove
+        {
+            failedCalls.Remove(value);
+        }
+    }
     
     
     public void SetException(Exception exception)
     public void SetException(Exception exception)
     {
     {
@@ -40,9 +76,10 @@ public class AsyncCall
         State = AsyncCallState.Failed;
         State = AsyncCallState.Failed;
         Exception = exception;
         Exception = exception;
         this.continuation?.Invoke();
         this.continuation?.Invoke();
-        Failed?.Invoke(exception);
+
+        InvokeFailed(exception);
     }
     }
-    
+
     public void SetResult(object? result)
     public void SetResult(object? result)
     {
     {
         if (State != AsyncCallState.Pending)
         if (State != AsyncCallState.Pending)
@@ -53,7 +90,7 @@ public class AsyncCall
         State = AsyncCallState.Completed;
         State = AsyncCallState.Completed;
         Result = result;
         Result = result;
         this.continuation?.Invoke();
         this.continuation?.Invoke();
-        Completed?.Invoke();
+        InvokeCompleted();
     }
     }
     
     
     public AsyncCallAwaiter GetAwaiter()
     public AsyncCallAwaiter GetAwaiter()
@@ -65,6 +102,22 @@ public class AsyncCall
     {
     {
         return result;
         return result;
     }
     }
+
+    private void InvokeCompleted()
+    {
+        foreach (var completedCall in completedCalls)
+        {
+            completedCall();
+        }
+    }
+    
+    private void InvokeFailed(Exception exception)
+    {
+        foreach (var failedCall in failedCalls)
+        {
+            failedCall(exception);
+        }
+    }
     
     
     internal void RegisterContinuation(Action cont)
     internal void RegisterContinuation(Action cont)
     {
     {
@@ -94,17 +147,38 @@ public class AsyncCall
 [AsyncMethodBuilder(typeof(AsyncCallAsyncMethodBuilder<>))]
 [AsyncMethodBuilder(typeof(AsyncCallAsyncMethodBuilder<>))]
 public class AsyncCall<TResult> : AsyncCall
 public class AsyncCall<TResult> : AsyncCall
 {
 {
+    private List<AsyncCallCompleted<TResult>> completedCalls = new List<AsyncCallCompleted<TResult>>();
     public new TResult Result
     public new TResult Result
     {
     {
         get => (TResult) base.Result;
         get => (TResult) base.Result;
         protected set => base.Result = value;
         protected set => base.Result = value;
     }
     }
     
     
-    public new event AsyncCallCompleted<TResult> Completed;
+    public new event AsyncCallCompleted<TResult> Completed
+    {
+        add
+        {
+            if (State == AsyncCallState.Completed)
+            {
+                value(Result);
+            }
+            else
+            {
+                completedCalls.Add(value);
+            }
+        }
+        remove
+        {
+            completedCalls.Remove(value);
+        }
+    }
     
     
     public AsyncCall()
     public AsyncCall()
     {
     {
-        base.Completed += () => Completed?.Invoke(Result);
+        base.Completed += () =>
+        {
+            InvokeCompleted(Result);
+        };
     }
     }
     
     
     public AsyncCallAwaiter<TResult> GetAwaiter()
     public AsyncCallAwaiter<TResult> GetAwaiter()
@@ -130,7 +204,7 @@ public class AsyncCall<TResult> : AsyncCall
         State = AsyncCallState.Completed;
         State = AsyncCallState.Completed;
         Result = result;
         Result = result;
         continuation?.Invoke();
         continuation?.Invoke();
-        Completed?.Invoke(result);
+        InvokeCompleted(result);
     }
     }
     
     
     public AsyncCall<T> ContinueWith<T>(Func<AsyncCall<TResult>, T> action)
     public AsyncCall<T> ContinueWith<T>(Func<AsyncCall<TResult>, T> action)
@@ -152,6 +226,13 @@ public class AsyncCall<TResult> : AsyncCall
         Failed += (exception) => asyncCall.SetException(exception);
         Failed += (exception) => asyncCall.SetException(exception);
         return asyncCall;
         return asyncCall;
     }
     }
+    
+    public static AsyncCall<TResult> FromResult(TResult result)
+    {
+        AsyncCall<TResult> asyncCall = new AsyncCall<TResult>();
+        asyncCall.SetResult(result);
+        return asyncCall;
+    }
 
 
     public static AsyncCall<TResult> FromTask(Task<TResult> task)
     public static AsyncCall<TResult> FromTask(Task<TResult> task)
     {
     {
@@ -169,6 +250,14 @@ public class AsyncCall<TResult> : AsyncCall
         });
         });
         return asyncCall;
         return asyncCall;
     }
     }
+    
+    private void InvokeCompleted(TResult result)
+    {
+        foreach (var completedCall in completedCalls)
+        {
+            completedCall(result);
+        }
+    }
 }
 }
 
 
 public enum AsyncCallState
 public enum AsyncCallState

+ 14 - 0
src/PixiEditor.Extensions.CommonApi/DataContracts/ExtensionPalette.proto

@@ -0,0 +1,14 @@
+syntax = "proto3";
+package PixiEditor.Palettes;
+
+import "PaletteColor.proto";
+
+option csharp_namespace = "PixiEditor.Extensions.CommonApi.Palettes";
+
+message ExtensionPalette
+{
+  string Name = 1;
+  repeated PixiEditor.Palettes.PaletteColor Colors = 2;
+  bool IsFavourite = 3;
+  string FileName = 4;
+}

+ 14 - 0
src/PixiEditor.Extensions.CommonApi/DataContracts/FetchPaletteListQuery.proto

@@ -0,0 +1,14 @@
+syntax = "proto3";
+package PixiEditor.Palettes;
+
+import "FilteringSettings.proto";
+
+option csharp_namespace = "PixiEditor.Extensions.CommonApi.Palettes";
+
+message FetchPaletteListQuery
+{
+  string DataSourceName = 1;
+  int32 StartIndex = 2;
+  int32 Items = 3;
+  FilteringSettings Filtering = 4;
+}

+ 21 - 0
src/PixiEditor.Extensions.CommonApi/DataContracts/FilteringSettings.proto

@@ -0,0 +1,21 @@
+syntax = "proto3";
+package PixiEditor.Palettes;
+
+option csharp_namespace = "PixiEditor.Extensions.CommonApi.Palettes";
+
+enum ColorsNumberMode
+{
+    Any = 0;
+    Max = 1;
+    Min = 2;
+    Exact = 3;
+}
+
+message FilteringSettings
+{
+  ColorsNumberMode ColorsNumberMode = 1;
+  int32 ColorsCount = 2;
+  string Name = 3;
+  bool ShowOnlyFavourites = 4;
+  repeated string Favourites = 5;
+}

+ 11 - 0
src/PixiEditor.Extensions.CommonApi/DataContracts/PaletteColor.proto

@@ -0,0 +1,11 @@
+syntax = "proto3";
+package PixiEditor.Palettes;
+
+option csharp_namespace = "PixiEditor.Extensions.CommonApi.Palettes";
+
+message PaletteColor
+{
+  uint32 R_Value = 1;
+  uint32 G_Value = 2;
+  uint32 B_Value = 3;
+}

+ 11 - 0
src/PixiEditor.Extensions.CommonApi/DataContracts/PaletteListResult.proto

@@ -0,0 +1,11 @@
+syntax = "proto3";
+package PixiEditor.Palettes;
+
+import "ExtensionPalette.proto";
+
+option csharp_namespace = "PixiEditor.Extensions.CommonApi.Palettes";
+
+message PaletteListResult
+{
+    repeated ExtensionPalette Palettes = 1;  
+}

+ 8 - 1
src/PixiEditor.Extensions.CommonApi/FlyUI/ByteMap.cs

@@ -1,4 +1,6 @@
-namespace PixiEditor.Extensions.CommonApi.FlyUI;
+using PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+namespace PixiEditor.Extensions.CommonApi.FlyUI;
 
 
 public static class ByteMap
 public static class ByteMap
 {
 {
@@ -40,6 +42,10 @@ public static class ByteMap
         {
         {
             return 8;
             return 8;
         }
         }
+        if(type == typeof(byte[]))
+        {
+            return 9;
+        }
 
 
         throw new Exception($"Unknown unmanaged type: {type}");
         throw new Exception($"Unknown unmanaged type: {type}");
     }
     }
@@ -57,6 +63,7 @@ public static class ByteMap
             6 => typeof(byte),
             6 => typeof(byte),
             7 => typeof(char),
             7 => typeof(char),
             8 => typeof(string),
             8 => typeof(string),
+            9 => typeof(byte[]),
             _ => throw new Exception($"Unknown unmanaged type id: {id}")
             _ => throw new Exception($"Unknown unmanaged type id: {id}")
         };
         };
     }
     }

+ 14 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/Alignment.cs

@@ -0,0 +1,14 @@
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+public enum Alignment
+{
+    TopLeft,
+    TopCenter,
+    TopRight,
+    CenterLeft,
+    Center,
+    CenterRight,
+    BottomLeft,
+    BottomCenter,
+    BottomRight
+}

+ 35 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/Color.cs

@@ -0,0 +1,35 @@
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+public struct Color : IStructProperty
+{
+    public byte R { get; set; }
+    public byte G { get; set; }
+    public byte B { get; set; }
+    public byte A { get; set; }
+
+    public Color(byte r, byte g, byte b, byte a)
+    {
+        R = r;
+        G = g;
+        B = b;
+        A = a;
+    }
+
+    public static Color FromRgba(byte r, byte g, byte b, byte a)
+    {
+        return new Color(r, g, b, a);
+    }
+
+    byte[] IStructProperty.Serialize()
+    {
+        return new byte[] { R, G, B, A };
+    }
+
+    void IStructProperty.Deserialize(byte[] data)
+    {
+        R = data[0];
+        G = data[1];
+        B = data[2];
+        A = data[3];
+    }
+}

+ 14 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/Colors.cs

@@ -0,0 +1,14 @@
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+public static class Colors
+{
+    public static Color Black { get; } = new Color(0, 0, 0, 255);
+    public static Color White { get; } = new Color(255, 255, 255, 255);
+    public static Color Red { get; } = new Color(255, 0, 0, 255);
+    public static Color Green { get; } = new Color(0, 255, 0, 255);
+    public static Color Blue { get; } = new Color(0, 0, 255, 255);
+    public static Color Yellow { get; } = new Color(255, 255, 0, 255);
+    public static Color Cyan { get; } = new Color(0, 255, 255, 255);
+    public static Color Magenta { get; } = new Color(255, 0, 255, 255);
+    public static Color Transparent { get; } = new Color(0, 0, 0, 0);
+}

+ 69 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/Edges.cs

@@ -0,0 +1,69 @@
+using System.Runtime.InteropServices;
+
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+public struct Edges : IStructProperty
+{
+    public double Left { get; set; }
+    public double Top { get; set; }
+    public double Right { get; set; }
+    public double Bottom { get; set; }
+
+    public Edges(double left, double top, double right, double bottom)
+    {
+        Left = left;
+        Top = top;
+        Right = right;
+        Bottom = bottom;
+    }
+    
+    public static Edges All(double value)
+    {
+        return new Edges(value, value, value, value);
+    }
+    
+    public static Edges Symmetric(double vertical, double horizontal)
+    {
+        return new Edges(horizontal, vertical, horizontal, vertical);
+    }
+    
+    public static Edges operator +(Edges a, Edges b)
+    {
+        return new Edges(a.Left + b.Left, a.Top + b.Top, a.Right + b.Right, a.Bottom + b.Bottom);
+    }
+    
+    public static Edges operator -(Edges a, Edges b)
+    {
+        return new Edges(a.Left - b.Left, a.Top - b.Top, a.Right - b.Right, a.Bottom - b.Bottom);
+    }
+    
+    public static Edges operator *(Edges a, double b)
+    {
+        return new Edges(a.Left * b, a.Top * b, a.Right * b, a.Bottom * b);
+    }
+    
+    public static Edges operator /(Edges a, double b)
+    {
+        return new Edges(a.Left / b, a.Top / b, a.Right / b, a.Bottom / b);
+    }
+
+    byte[] IStructProperty.Serialize()
+    {
+        byte[] data = new byte[32];
+        
+        BitConverter.GetBytes(Left).CopyTo(data, 0);
+        BitConverter.GetBytes(Top).CopyTo(data, 8);
+        BitConverter.GetBytes(Right).CopyTo(data, 16);
+        BitConverter.GetBytes(Bottom).CopyTo(data, 24);
+        
+        return data;
+    }
+
+    void IStructProperty.Deserialize(byte[] data)
+    {
+        Left = BitConverter.ToDouble(data, 0);
+        Top = BitConverter.ToDouble(data, 8);
+        Right = BitConverter.ToDouble(data, 16);
+        Bottom = BitConverter.ToDouble(data, 24);
+    }
+}

+ 9 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/FillMode.cs

@@ -0,0 +1,9 @@
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+public enum FillMode
+{
+    None,
+    Fill,
+    Uniform,
+    UniformToFill,
+}

+ 10 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/FilterQuality.cs

@@ -0,0 +1,10 @@
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+public enum FilterQuality : byte
+{
+    Unspecified,
+    None,
+    Low,
+    Medium,
+    High,
+}

+ 8 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/FontStyle.cs

@@ -0,0 +1,8 @@
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+public enum FontStyle
+{
+    Normal,
+    Italic,
+    Oblique
+}

+ 7 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/IStructProperty.cs

@@ -0,0 +1,7 @@
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+public interface IStructProperty
+{
+    public byte[] Serialize();
+    public void Deserialize(byte[] data);
+}

+ 8 - 0
src/PixiEditor.Extensions.CommonApi/FlyUI/Properties/TextWrap.cs

@@ -0,0 +1,8 @@
+namespace PixiEditor.Extensions.CommonApi.FlyUI.Properties;
+
+public enum TextWrap
+{
+    None,
+    Wrap,
+    WrapWithOverflow
+}

+ 0 - 9
src/PixiEditor.Extensions.CommonApi/Palettes/ColorsNumberMode.cs

@@ -1,9 +0,0 @@
-namespace PixiEditor.Extensions.CommonApi.Palettes;
-
-public enum ColorsNumberMode
-{
-    Any,
-    Max,
-    Min,
-    Exact
-}

+ 13 - 0
src/PixiEditor.Extensions.CommonApi/Palettes/ExtensionPalette.Impl.cs

@@ -0,0 +1,13 @@
+namespace PixiEditor.Extensions.CommonApi.Palettes;
+
+public partial class ExtensionPalette : IPalette
+{
+    public PaletteListDataSource Source { get; set; }
+    
+    public ExtensionPalette(string name, List<PaletteColor> colors, PaletteListDataSource source)
+    {
+        Name = name;
+        Colors = colors;
+        Source = source;
+    }
+}

+ 0 - 17
src/PixiEditor.Extensions.CommonApi/Palettes/ExtensionPalette.cs

@@ -1,17 +0,0 @@
-namespace PixiEditor.Extensions.CommonApi.Palettes;
-
-public class ExtensionPalette : IPalette
-{
-    public string Name { get; }
-    public List<PaletteColor> Colors { get; }
-    public bool IsFavourite { get; set; }
-    public string FileName { get; set; }
-    public PaletteListDataSource Source { get; }
-
-    public ExtensionPalette(string name, List<PaletteColor> colors, PaletteListDataSource source)
-    {
-        Name = name;
-        Colors = colors;
-        Source = source;
-    }
-}

+ 23 - 0
src/PixiEditor.Extensions.CommonApi/Palettes/FetchPaletteListQuery.Impl.cs

@@ -0,0 +1,23 @@
+using ProtoBuf;
+
+namespace PixiEditor.Extensions.CommonApi.Palettes;
+
+public partial class FetchPaletteListQuery
+{
+    public FetchPaletteListQuery()
+    {
+    }
+    
+    public FetchPaletteListQuery(string dataSourceName, int startIndex, int items, FilteringSettings filtering)
+    {
+        DataSourceName = dataSourceName;
+        StartIndex = startIndex;
+        Items = items;
+        Filtering = filtering;
+    }
+    
+    public override string ToString()
+    {
+        return $"DataSourceName: {DataSourceName}, StartIndex: {StartIndex}, Items: {Items}, Filtering: {Filtering}";
+    }
+}

+ 1 - 7
src/PixiEditor.Extensions.CommonApi/Palettes/FilteringSettings.cs → src/PixiEditor.Extensions.CommonApi/Palettes/FilteringSettings.Impl.cs

@@ -1,13 +1,7 @@
 namespace PixiEditor.Extensions.CommonApi.Palettes;
 namespace PixiEditor.Extensions.CommonApi.Palettes;
 
 
-public sealed class FilteringSettings
+public partial class FilteringSettings
 {
 {
-    public ColorsNumberMode ColorsNumberMode { get; set; }
-    public int ColorsCount { get; set; }
-    public string Name { get; set; }
-    public bool ShowOnlyFavourites { get; set; }
-    public List<string> Favourites { get; set; }
-
     public FilteringSettings(ColorsNumberMode colorsNumberMode, int colorsCount, string name, bool showOnlyFavourites, List<string> favourites)
     public FilteringSettings(ColorsNumberMode colorsNumberMode, int colorsCount, string name, bool showOnlyFavourites, List<string> favourites)
     {
     {
         ColorsNumberMode = colorsNumberMode;
         ColorsNumberMode = colorsNumberMode;

+ 0 - 29
src/PixiEditor.Extensions.CommonApi/Palettes/IPaletteProvider.cs

@@ -1,29 +0,0 @@
-using PixiEditor.Extensions.CommonApi.Async;
-
-namespace PixiEditor.Extensions.CommonApi.Palettes;
-
-public interface IPaletteProvider
-{
-    /// <summary>
-    ///     Fetches palettes from the provider.
-    /// </summary>
-    /// <param name="startIndex">Starting fetch index. Palettes before said index won't be fetched.</param>
-    /// <param name="items">Max amount of palettes to fetch.</param>
-    /// <param name="filtering">Filtering settings for fetching.</param>
-    /// <returns>List of palettes.</returns>
-    public AsyncCall<List<IPalette>> FetchPalettes(int startIndex, int items, FilteringSettings filtering);
-
-    /// <summary>
-    ///     Adds a palette to the provider. This means that the palette will be saved in local storage.
-    /// </summary>
-    /// <param name="palette">Palette to save.</param>
-    /// <param name="overwrite">If true and palette with the same name exists, it will be overwritten. If false and palette with the same name exists, it will not be added.</param>
-    /// <returns>True if adding palette was successful.</returns>
-    public AsyncCall<bool> AddPalette(IPalette palette, bool overwrite = false);
-
-    /// <summary>
-    ///     Registers a palette list data source. This means that the provider will use the data source to fetch palettes.
-    /// </summary>
-    /// <param name="dataSource">Data source to register.</param>
-    public void RegisterDataSource(PaletteListDataSource dataSource);
-}

+ 10 - 0
src/PixiEditor.Extensions.CommonApi/Palettes/IPalettesProvider.cs

@@ -0,0 +1,10 @@
+namespace PixiEditor.Extensions.CommonApi.Palettes;
+
+public interface IPalettesProvider
+{
+    /// <summary>
+    ///     Registers a data source of palettes.
+    /// </summary>
+    /// <param name="dataSource">Palettes data source</param>
+    public void RegisterDataSource(PaletteListDataSource dataSource);
+}

+ 34 - 12
src/PixiEditor.Extensions.CommonApi/Palettes/PaletteColor.cs → src/PixiEditor.Extensions.CommonApi/Palettes/PaletteColor.Impl.cs

@@ -1,23 +1,45 @@
 namespace PixiEditor.Extensions.CommonApi.Palettes;
 namespace PixiEditor.Extensions.CommonApi.Palettes;
 
 
-public struct PaletteColor
+public partial class PaletteColor
 {
 {
-    public static PaletteColor Empty => new(0, 0, 0);
-    public static PaletteColor Black => new(0, 0, 0);
-    public static PaletteColor White => new(255, 255, 255);
-    public byte R { get; set; }
-    public byte G { get; set; }
-    public byte B { get; set; }
+    public static PaletteColor Empty => new PaletteColor(0, 0, 0);
+    public static PaletteColor Black => new PaletteColor(0, 0, 0);
+    public static PaletteColor White => new PaletteColor(255, 255, 255);
 
 
-    public string Hex => $"#{R:X2}{G:X2}{B:X2}";
+    public byte R
+    {
+        get => (byte)RValue;
+        set => RValue = value;
+    }
 
 
+    public byte G
+    {
+        get => (byte)GValue;
+        set => GValue = value;
+    }
+    
+    public byte B
+    {
+        get => (byte)BValue;
+        set => BValue = value;
+    }
+    
+    public string Hex => $"#{R:X2}{G:X2}{B:X2}";
+    
     public PaletteColor(byte r, byte g, byte b)
     public PaletteColor(byte r, byte g, byte b)
     {
     {
-        R = r;
-        G = g;
-        B = b;
+        RValue = r;
+        GValue = g;
+        BValue = b;
     }
     }
-
+    
+    public PaletteColor(uint r, uint g, uint b)
+    {
+        RValue = (byte)r;
+        GValue = (byte)g;
+        BValue = (byte)b;
+    }
+    
     public override string ToString()
     public override string ToString()
     {
     {
         return Hex;
         return Hex;

+ 9 - 0
src/PixiEditor.Extensions.CommonApi/Palettes/PaletteListResult.Impl.cs

@@ -0,0 +1,9 @@
+namespace PixiEditor.Extensions.CommonApi.Palettes;
+
+public partial class PaletteListResult
+{
+    public PaletteListResult(IEnumerable<ExtensionPalette> palettes)
+    {
+        Palettes = new List<ExtensionPalette>(palettes);
+    }
+}

+ 1 - 1
src/PixiEditor.Extensions.CommonApi/Palettes/Parsers/PaletteFileData.cs

@@ -36,7 +36,7 @@ public class PaletteFileData
         for (int i = 0; i < Colors.Length; i++)
         for (int i = 0; i < Colors.Length; i++)
         {
         {
             PaletteColor color = Colors[i];
             PaletteColor color = Colors[i];
-            colors[i] = new PaletteColor(color.R, color.G, color.B);
+            colors[i] = new PaletteColor((byte)color.R, (byte)color.G, (byte)color.B);
         }
         }
 
 
         return colors;
         return colors;

+ 92 - 0
src/PixiEditor.Extensions.CommonApi/PixiEditor.Extensions.CommonApi.csproj

@@ -10,8 +10,100 @@
       <Folder Include="Preferences\" />
       <Folder Include="Preferences\" />
     </ItemGroup>
     </ItemGroup>
 
 
+    <ItemGroup>
+      <PackageReference Include="protobuf-net" Version="3.2.30" />
+    </ItemGroup>
+  
+  <PropertyGroup>
+    <ProtogenVersion>3.2.42</ProtogenVersion>
+  </PropertyGroup>
+  
+  <Target Name="ProtogenCheck" BeforeTargets="GenerateProtoContracts">
+    <PropertyGroup>
+      <ProtogenExists>false</ProtogenExists>
+    </PropertyGroup>
+    <Exec Command="dotnet tool run protogen --version" IgnoreExitCode="true">
+      <Output TaskParameter="ExitCode" PropertyName="ProtogenExitCode"/>
+    </Exec>
+    <PropertyGroup>
+      <ProtogenExists Condition="'$(ProtogenExitCode)' == '0'">true</ProtogenExists>
+    </PropertyGroup>
+  </Target>
+  
+  <Target Name="InstallProtogen" BeforeTargets="GenerateProtoContracts"
+          Condition="'$(ProtogenExists)' != 'true'">
+    <Message Text="Downloading protogen v$(ProtogenVersion)..." Importance="high"/>
+    <Exec Command="dotnet tool install --local protobuf-net.Protogen --version $(ProtogenVersion)"/>
+    <PropertyGroup>
+      <ProtogenExists>true</ProtogenExists>
+    </PropertyGroup>
+    
+    <Message Text="protogen installed successfully." Importance="high"/>
+  </Target>
+    
+  
+  <Target Name="GenerateProtoContracts" BeforeTargets="BeforeCompile"
+          Inputs="$(MSBuildProjectDirectory)\DataContracts\*.proto"
+          Outputs="$(MSBuildProjectDirectory)\ProtoAutogen\*.cs">
+    <Exec Command="dotnet tool run protogen --csharp_out=ProtoAutogen --proto_path=DataContracts +listset=yes *.proto"/>
+
+    <ItemGroup>
+      <Compile Include="ProtoAutogen\*.cs" KeepDuplicates="false"/>
+    </ItemGroup>
+
+  </Target>
+
     <ItemGroup>
     <ItemGroup>
       <ProjectReference Include="..\PixiEditor.Extensions.CommonApi.Diagnostics\PixiEditor.Extensions.CommonApi.Diagnostics.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
       <ProjectReference Include="..\PixiEditor.Extensions.CommonApi.Diagnostics\PixiEditor.Extensions.CommonApi.Diagnostics.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
     </ItemGroup>
     </ItemGroup>
   
   
+    <ItemGroup>
+<<<<<<< HEAD
+      <ProjectReference Include="..\PixiEditor.Extensions.CommonApi.Diagnostics\PixiEditor.Extensions.CommonApi.Diagnostics.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+    </ItemGroup>
+  
+=======
+      <PackageReference Include="protobuf-net" Version="3.2.30" />
+    </ItemGroup>
+  
+  <PropertyGroup>
+    <ProtogenVersion>3.2.42</ProtogenVersion>
+  </PropertyGroup>
+  
+  <Target Name="ProtogenCheck" BeforeTargets="GenerateProtoContracts">
+    <PropertyGroup>
+      <ProtogenExists>false</ProtogenExists>
+    </PropertyGroup>
+    <Exec Command="dotnet tool run protogen --version" IgnoreExitCode="true">
+      <Output TaskParameter="ExitCode" PropertyName="ProtogenExitCode"/>
+    </Exec>
+    <PropertyGroup>
+      <ProtogenExists Condition="'$(ProtogenExitCode)' == '0'">true</ProtogenExists>
+    </PropertyGroup>
+  </Target>
+  
+  <Target Name="InstallProtogen" BeforeTargets="GenerateProtoContracts"
+          Condition="'$(ProtogenExists)' != 'true'">
+    <Message Text="Downloading protogen v$(ProtogenVersion)..." Importance="high"/>
+    <Exec Command="dotnet tool install --local protobuf-net.Protogen --version $(ProtogenVersion)"/>
+    <PropertyGroup>
+      <ProtogenExists>true</ProtogenExists>
+    </PropertyGroup>
+    
+    <Message Text="protogen installed successfully." Importance="high"/>
+  </Target>
+    
+  
+  <Target Name="GenerateProtoContracts" BeforeTargets="BeforeCompile"
+          Inputs="$(MSBuildProjectDirectory)\DataContracts\*.proto"
+          Outputs="$(MSBuildProjectDirectory)\ProtoAutogen\*.cs">
+    <Exec Command="dotnet tool run protogen --csharp_out=ProtoAutogen --proto_path=DataContracts +listset=yes *.proto"/>
+
+    <ItemGroup>
+      <Compile Include="ProtoAutogen\*.cs" KeepDuplicates="false"/>
+    </ItemGroup>
+
+  </Target>
+
+>>>>>>> refs/heads/avalonia-rewrite
 </Project>
 </Project>

+ 38 - 0
src/PixiEditor.Extensions.CommonApi/ProtoAutogen/ExtensionPalette.cs

@@ -0,0 +1,38 @@
+// <auto-generated>
+//   This file was generated by a tool; you should avoid making direct changes.
+//   Consider using 'partial classes' to extend these types
+//   Input: ExtensionPalette.proto
+// </auto-generated>
+
+#region Designer generated code
+#pragma warning disable CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+namespace PixiEditor.Extensions.CommonApi.Palettes
+{
+
+    [global::ProtoBuf.ProtoContract()]
+    public partial class ExtensionPalette : global::ProtoBuf.IExtensible
+    {
+        private global::ProtoBuf.IExtension __pbn__extensionData;
+        global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
+            => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
+
+        [global::ProtoBuf.ProtoMember(1)]
+        [global::System.ComponentModel.DefaultValue("")]
+        public string Name { get; set; } = "";
+
+        [global::ProtoBuf.ProtoMember(2)]
+        public global::System.Collections.Generic.List<PaletteColor> Colors { get; set; } = new global::System.Collections.Generic.List<PaletteColor>();
+
+        [global::ProtoBuf.ProtoMember(3)]
+        public bool IsFavourite { get; set; }
+
+        [global::ProtoBuf.ProtoMember(4)]
+        [global::System.ComponentModel.DefaultValue("")]
+        public string FileName { get; set; } = "";
+
+    }
+
+}
+
+#pragma warning restore CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+#endregion

+ 37 - 0
src/PixiEditor.Extensions.CommonApi/ProtoAutogen/FetchPaletteListQuery.cs

@@ -0,0 +1,37 @@
+// <auto-generated>
+//   This file was generated by a tool; you should avoid making direct changes.
+//   Consider using 'partial classes' to extend these types
+//   Input: FetchPaletteListQuery.proto
+// </auto-generated>
+
+#region Designer generated code
+#pragma warning disable CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+namespace PixiEditor.Extensions.CommonApi.Palettes
+{
+
+    [global::ProtoBuf.ProtoContract()]
+    public partial class FetchPaletteListQuery : global::ProtoBuf.IExtensible
+    {
+        private global::ProtoBuf.IExtension __pbn__extensionData;
+        global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
+            => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
+
+        [global::ProtoBuf.ProtoMember(1)]
+        [global::System.ComponentModel.DefaultValue("")]
+        public string DataSourceName { get; set; } = "";
+
+        [global::ProtoBuf.ProtoMember(2)]
+        public int StartIndex { get; set; }
+
+        [global::ProtoBuf.ProtoMember(3)]
+        public int Items { get; set; }
+
+        [global::ProtoBuf.ProtoMember(4)]
+        public FilteringSettings Filtering { get; set; }
+
+    }
+
+}
+
+#pragma warning restore CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+#endregion

+ 49 - 0
src/PixiEditor.Extensions.CommonApi/ProtoAutogen/FilteringSettings.cs

@@ -0,0 +1,49 @@
+// <auto-generated>
+//   This file was generated by a tool; you should avoid making direct changes.
+//   Consider using 'partial classes' to extend these types
+//   Input: FilteringSettings.proto
+// </auto-generated>
+
+#region Designer generated code
+#pragma warning disable CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+namespace PixiEditor.Extensions.CommonApi.Palettes
+{
+
+    [global::ProtoBuf.ProtoContract()]
+    public partial class FilteringSettings : global::ProtoBuf.IExtensible
+    {
+        private global::ProtoBuf.IExtension __pbn__extensionData;
+        global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
+            => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
+
+        [global::ProtoBuf.ProtoMember(1)]
+        public ColorsNumberMode ColorsNumberMode { get; set; }
+
+        [global::ProtoBuf.ProtoMember(2)]
+        public int ColorsCount { get; set; }
+
+        [global::ProtoBuf.ProtoMember(3)]
+        [global::System.ComponentModel.DefaultValue("")]
+        public string Name { get; set; } = "";
+
+        [global::ProtoBuf.ProtoMember(4)]
+        public bool ShowOnlyFavourites { get; set; }
+
+        [global::ProtoBuf.ProtoMember(5)]
+        public global::System.Collections.Generic.List<string> Favourites { get; set; } = new global::System.Collections.Generic.List<string>();
+
+    }
+
+    [global::ProtoBuf.ProtoContract()]
+    public enum ColorsNumberMode
+    {
+        Any = 0,
+        Max = 1,
+        Min = 2,
+        Exact = 3,
+    }
+
+}
+
+#pragma warning restore CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+#endregion

+ 33 - 0
src/PixiEditor.Extensions.CommonApi/ProtoAutogen/PaletteColor.cs

@@ -0,0 +1,33 @@
+// <auto-generated>
+//   This file was generated by a tool; you should avoid making direct changes.
+//   Consider using 'partial classes' to extend these types
+//   Input: PaletteColor.proto
+// </auto-generated>
+
+#region Designer generated code
+#pragma warning disable CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+namespace PixiEditor.Extensions.CommonApi.Palettes
+{
+
+    [global::ProtoBuf.ProtoContract()]
+    public partial class PaletteColor : global::ProtoBuf.IExtensible
+    {
+        private global::ProtoBuf.IExtension __pbn__extensionData;
+        global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
+            => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
+
+        [global::ProtoBuf.ProtoMember(1, Name = @"R_Value")]
+        public uint RValue { get; set; }
+
+        [global::ProtoBuf.ProtoMember(2, Name = @"G_Value")]
+        public uint GValue { get; set; }
+
+        [global::ProtoBuf.ProtoMember(3, Name = @"B_Value")]
+        public uint BValue { get; set; }
+
+    }
+
+}
+
+#pragma warning restore CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+#endregion

+ 27 - 0
src/PixiEditor.Extensions.CommonApi/ProtoAutogen/PaletteListResult.cs

@@ -0,0 +1,27 @@
+// <auto-generated>
+//   This file was generated by a tool; you should avoid making direct changes.
+//   Consider using 'partial classes' to extend these types
+//   Input: PaletteListResult.proto
+// </auto-generated>
+
+#region Designer generated code
+#pragma warning disable CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+namespace PixiEditor.Extensions.CommonApi.Palettes
+{
+
+    [global::ProtoBuf.ProtoContract()]
+    public partial class PaletteListResult : global::ProtoBuf.IExtensible
+    {
+        private global::ProtoBuf.IExtension __pbn__extensionData;
+        global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
+            => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
+
+        [global::ProtoBuf.ProtoMember(1)]
+        public global::System.Collections.Generic.List<ExtensionPalette> Palettes { get; set; } = new global::System.Collections.Generic.List<ExtensionPalette>();
+
+    }
+
+}
+
+#pragma warning restore CS0612, CS0618, CS1591, CS3021, CS8981, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
+#endregion

+ 15 - 0
src/PixiEditor.Extensions.CommonApi/ProtoAutogen/README.md

@@ -0,0 +1,15 @@
+# DO NOT MODIFY .CS FILES IN THIS DIRECTORY
+
+They are autogenerated from the .proto files. If you want to make changes to them, 
+please modify the .proto files and build project.
+
+Protogen is installed on first build, but if you want to install it manually, you can do it by installing protobuf-net.Protogen nuget package.
+To install it globally on your machine go to https://www.nuget.org/packages/protobuf-net.Protogen and follow .NET CLI (Global) instructions.
+
+For example `dotnet tool install --global protobuf-net.Protogen --version 3.2.42`
+
+After installing protogen, you can run it by executing `protogen` in the root directory of PixiEditor.Extensions.CommonApi
+
+```bash
+protogen --csharp_out=ProtoAutogen --proto_path=DataContracts *.proto
+```

+ 4 - 4
src/PixiEditor.Extensions.CommonApi/UserPreferences/IPreferences.cs

@@ -14,7 +14,7 @@ public interface IPreferences
     /// </summary>
     /// </summary>
     /// <param name="name">The name of the setting</param>
     /// <param name="name">The name of the setting</param>
     /// <param name="action">The action that will be executed when the setting changes</param>
     /// <param name="action">The action that will be executed when the setting changes</param>
-    public void AddCallback(string name, Action<object> action);
+    public void AddCallback(string name, Action<string, object> action);
 
 
     /// <summary>
     /// <summary>
     /// Adds a callback that will be executed when the setting called <paramref name="name"/> changes.
     /// Adds a callback that will be executed when the setting called <paramref name="name"/> changes.
@@ -22,10 +22,10 @@ public interface IPreferences
     /// <typeparam name="T">The <see cref="Type"/> of the setting</typeparam>
     /// <typeparam name="T">The <see cref="Type"/> of the setting</typeparam>
     /// <param name="name">The name of the setting</param>
     /// <param name="name">The name of the setting</param>
     /// <param name="action">The action that will be executed when the setting changes</param>
     /// <param name="action">The action that will be executed when the setting changes</param>
-    public void AddCallback<T>(string name, Action<T> action);
+    public void AddCallback<T>(string name, Action<string, T> action);
 
 
-    public void RemoveCallback(string name, Action<object> action);
-    public void RemoveCallback<T>(string name, Action<T> action);
+    public void RemoveCallback(string name, Action<string, object> action);
+    public void RemoveCallback<T>(string name, Action<string, T> action);
 
 
     /// <summary>
     /// <summary>
     /// Initializes the preferences.
     /// Initializes the preferences.

+ 52 - 0
src/PixiEditor.Extensions.CommonApi/Utilities/PrefixedNameUtility.cs

@@ -0,0 +1,52 @@
+namespace PixiEditor.Extensions.CommonApi.Utilities;
+
+public static class PrefixedNameUtility
+{
+    /// <summary>
+    ///     Converts a preference name to a full name with the extension unique name. It is relative to PixiEditor, so
+    /// any preference without a prefix is a PixiEditor preference.
+    /// </summary>
+    /// <param name="uniqueName">Unique name of the extension.</param>
+    /// <param name="name">Name of the preference.</param>
+    /// <returns>Full name of the preference.</returns>
+    public static string ToPixiEditorRelativePreferenceName(string uniqueName, string name)
+    {
+        string[] splitted = name.Split(":");
+        
+        string finalName = $"{uniqueName}:{name}";
+        
+        if (splitted.Length == 2)
+        {
+            finalName = name;
+            
+            if(splitted[0].Equals("pixieditor", StringComparison.CurrentCultureIgnoreCase)) 
+            {
+                finalName = splitted[1];
+            }
+        }
+
+        return finalName;
+    }
+
+    /// <summary>
+    ///    It is a reverse of <see cref="ToPixiEditorRelativePreferenceName"/>. It converts a full preference name to a relative name.
+    /// Preferences owned by the extension will not have any prefix, while PixiEditor preferences will have "pixieditor:" prefix.
+    /// </summary>
+    /// <param name="extensionUniqueName">Unique name of the extension.</param>
+    /// <param name="preferenceName">Full name of the preference.</param>
+    /// <returns>Relative name of the preference.</returns>
+    public static string ToExtensionRelativeName(string extensionUniqueName, string preferenceName)
+    {
+        if (preferenceName.StartsWith(extensionUniqueName))
+        {
+            return preferenceName[(extensionUniqueName.Length + 1)..];
+        }
+
+        if(preferenceName.Split(":").Length == 1)
+        {
+            return $"pixieditor:{preferenceName}";
+        }
+
+        return preferenceName;
+    }
+}

+ 2 - 2
src/PixiEditor.Extensions.CommonApi/Windowing/IWindowProvider.cs

@@ -5,11 +5,11 @@ namespace PixiEditor.Extensions.CommonApi.Windowing;
 public interface IWindowProvider
 public interface IWindowProvider
 {
 {
     public IPopupWindow CreatePopupWindow(string title, object body);
     public IPopupWindow CreatePopupWindow(string title, object body);
-    public IPopupWindow GetWindow(WindowType type);
+    public IPopupWindow GetWindow(BuiltInWindowType type);
     public IPopupWindow GetWindow(string windowId);
     public IPopupWindow GetWindow(string windowId);
 }
 }
 
 
-public enum WindowType
+public enum BuiltInWindowType
 {
 {
     [Description("PalettesBrowser")]
     [Description("PalettesBrowser")]
     PalettesBrowser
     PalettesBrowser

+ 0 - 11
src/PixiEditor.Extensions.MSBuildLayoutCompiler/PixiEditor.Extensions.MSBuildLayoutCompiler.csproj

@@ -1,11 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-    <PropertyGroup>
-        <OutputType>Exe</OutputType>
-        <TargetFramework>net8.0</TargetFramework>
-        <ImplicitUsings>enable</ImplicitUsings>
-        <Nullable>enable</Nullable>
-        <RootNamespace>PixiEditor.Extensions.LayoutCompiler</RootNamespace>
-    </PropertyGroup>
-
-</Project>

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