Browse Source

Merge branch 'master' into group-layers

Krzysztof Krysiński 4 years ago
parent
commit
09c9c282a4
100 changed files with 1407 additions and 406 deletions
  1. 3 0
      .github/FUNDING.yml
  2. 72 14
      Custom.ruleset
  3. BIN
      PixiEditor.MSIX/Images/LargeTile.scale-100.png
  4. BIN
      PixiEditor.MSIX/Images/LargeTile.scale-125.png
  5. BIN
      PixiEditor.MSIX/Images/LargeTile.scale-150.png
  6. BIN
      PixiEditor.MSIX/Images/LargeTile.scale-200.png
  7. BIN
      PixiEditor.MSIX/Images/LargeTile.scale-400.png
  8. BIN
      PixiEditor.MSIX/Images/LockScreenLogo.scale-200.png
  9. BIN
      PixiEditor.MSIX/Images/SmallTile.scale-100.png
  10. BIN
      PixiEditor.MSIX/Images/SmallTile.scale-125.png
  11. BIN
      PixiEditor.MSIX/Images/SmallTile.scale-150.png
  12. BIN
      PixiEditor.MSIX/Images/SmallTile.scale-200.png
  13. BIN
      PixiEditor.MSIX/Images/SmallTile.scale-400.png
  14. BIN
      PixiEditor.MSIX/Images/SplashScreen.scale-100.png
  15. BIN
      PixiEditor.MSIX/Images/SplashScreen.scale-125.png
  16. BIN
      PixiEditor.MSIX/Images/SplashScreen.scale-150.png
  17. BIN
      PixiEditor.MSIX/Images/SplashScreen.scale-200.png
  18. BIN
      PixiEditor.MSIX/Images/SplashScreen.scale-400.png
  19. BIN
      PixiEditor.MSIX/Images/Square150x150Logo.scale-100.png
  20. BIN
      PixiEditor.MSIX/Images/Square150x150Logo.scale-125.png
  21. BIN
      PixiEditor.MSIX/Images/Square150x150Logo.scale-150.png
  22. BIN
      PixiEditor.MSIX/Images/Square150x150Logo.scale-200.png
  23. BIN
      PixiEditor.MSIX/Images/Square150x150Logo.scale-400.png
  24. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png
  25. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png
  26. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png
  27. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png
  28. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png
  29. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.altform-unplated_targetsize-16.png
  30. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.altform-unplated_targetsize-256.png
  31. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.altform-unplated_targetsize-32.png
  32. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.altform-unplated_targetsize-48.png
  33. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.scale-100.png
  34. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.scale-125.png
  35. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.scale-150.png
  36. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.scale-200.png
  37. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.scale-400.png
  38. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.targetsize-16.png
  39. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.targetsize-24.png
  40. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.targetsize-24_altform-unplated.png
  41. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.targetsize-256.png
  42. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.targetsize-32.png
  43. BIN
      PixiEditor.MSIX/Images/Square44x44Logo.targetsize-48.png
  44. BIN
      PixiEditor.MSIX/Images/StoreLogo.backup.png
  45. BIN
      PixiEditor.MSIX/Images/StoreLogo.scale-100.png
  46. BIN
      PixiEditor.MSIX/Images/StoreLogo.scale-125.png
  47. BIN
      PixiEditor.MSIX/Images/StoreLogo.scale-150.png
  48. BIN
      PixiEditor.MSIX/Images/StoreLogo.scale-200.png
  49. BIN
      PixiEditor.MSIX/Images/StoreLogo.scale-400.png
  50. BIN
      PixiEditor.MSIX/Images/Wide310x150Logo.scale-100.png
  51. BIN
      PixiEditor.MSIX/Images/Wide310x150Logo.scale-125.png
  52. BIN
      PixiEditor.MSIX/Images/Wide310x150Logo.scale-150.png
  53. BIN
      PixiEditor.MSIX/Images/Wide310x150Logo.scale-200.png
  54. BIN
      PixiEditor.MSIX/Images/Wide310x150Logo.scale-400.png
  55. 66 0
      PixiEditor.MSIX/Package.appxmanifest
  56. 129 0
      PixiEditor.MSIX/PixiEditor.MSIX.wapproj
  57. 1 0
      PixiEditor.UpdateInstaller/PixiEditor.UpdateInstaller.csproj
  58. 1 0
      PixiEditor.UpdateModule/PixiEditor.UpdateModule.csproj
  59. 120 68
      PixiEditor.sln
  60. 1 6
      PixiEditor/Exceptions/CorruptedFileException.cs
  61. 3 6
      PixiEditor/Helpers/AssemblyHelper.cs
  62. 1 1
      PixiEditor/Helpers/Converters/DoubleToIntConverter.cs
  63. 44 0
      PixiEditor/Helpers/Converters/FileExtensionToImageSourceConverter.cs
  64. 19 0
      PixiEditor/Helpers/Converters/FloorConverter.cs
  65. 20 0
      PixiEditor/Helpers/Converters/IntToPickerTypeConverter.cs
  66. 35 0
      PixiEditor/Helpers/Converters/KeyToStringConverter.cs
  67. 20 0
      PixiEditor/Helpers/Converters/NotNullToVisibiltyConverter.cs
  68. 26 0
      PixiEditor/Helpers/Converters/ThresholdVisibilityConverter.cs
  69. 17 0
      PixiEditor/Helpers/Extensions/EnumHelpers.cs
  70. 22 14
      PixiEditor/Helpers/Extensions/ParserHelpers.cs
  71. 27 0
      PixiEditor/Helpers/Extensions/StringHelpers.cs
  72. 82 0
      PixiEditor/Helpers/InputKeyHelpers.cs
  73. 1 1
      PixiEditor/Helpers/SelectionHelpers.cs
  74. 13 0
      PixiEditor/Helpers/StringExtensions.cs
  75. BIN
      PixiEditor/Images/JpgFile.png
  76. BIN
      PixiEditor/Images/PixiFile.png
  77. BIN
      PixiEditor/Images/PixiParserLogo.png
  78. BIN
      PixiEditor/Images/PngFile.png
  79. BIN
      PixiEditor/Images/UnknownFile.png
  80. 3 3
      PixiEditor/Models/Controllers/BitmapChangedEventArgs.cs
  81. 150 131
      PixiEditor/Models/Controllers/ClipboardController.cs
  82. 16 1
      PixiEditor/Models/Controllers/Shortcuts/Shortcut.cs
  83. 5 4
      PixiEditor/Models/Controllers/Shortcuts/ShortcutController.cs
  84. 43 0
      PixiEditor/Models/Controllers/Shortcuts/ShortcutGroup.cs
  85. 5 0
      PixiEditor/Models/DataHolders/BitmapPixelChanges.cs
  86. 1 1
      PixiEditor/Models/DataHolders/Document/Document.Constructors.cs
  87. 4 3
      PixiEditor/Models/DataHolders/Document/Document.IO.cs
  88. 105 1
      PixiEditor/Models/DataHolders/Document/Document.Layers.cs
  89. 59 0
      PixiEditor/Models/DataHolders/RecentlyOpenedCollection.cs
  90. 108 0
      PixiEditor/Models/DataHolders/RecentlyOpenedDocument.cs
  91. 2 1
      PixiEditor/Models/Dialogs/ConfirmationDialog.cs
  92. 16 2
      PixiEditor/Models/Dialogs/NoticeDialog.cs
  93. 14 0
      PixiEditor/Models/Enums/SelectionShape.cs
  94. 73 73
      PixiEditor/Models/IO/Importer.cs
  95. 8 13
      PixiEditor/Models/ImageManipulation/BitmapUtils.cs
  96. 14 14
      PixiEditor/Models/Layers/Layer.cs
  97. 4 4
      PixiEditor/Models/Processes/ProcessHelper.cs
  98. 47 31
      PixiEditor/Models/Tools/ShapeTool.cs
  99. 6 14
      PixiEditor/Models/Tools/Tool.cs
  100. 1 0
      PixiEditor/Models/Tools/ToolSettings/Toolbars/SelectToolToolbar.cs

+ 3 - 0
.github/FUNDING.yml

@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+open_collective: pixieditor

+ 72 - 14
Custom.ruleset

@@ -1,15 +1,73 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RuleSet Name="Name" Description="Description" ToolsVersion="16.0">
-  <Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
-    <Rule Id="CA1303" Action="None" />
-  </Rules>
-  <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
-    <Rule Id="SA0001" Action="None" />
-    <Rule Id="SA1000" Action="None" />
-    <Rule Id="SA1101" Action="None" />
-    <Rule Id="SA1201" Action="None" />
-    <Rule Id="SA1310" Action="None" />
-    <Rule Id="SA1413" Action="None" />
-    <Rule Id="SA1633" Action="None" />
-  </Rules>
+<?xml version="1.0" encoding="utf-8"?>
+<RuleSet Name="Name" Description="Description" ToolsVersion="16.0">
+  <Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.Features" RuleNamespace="Microsoft.CodeAnalysis.CSharp.Features">
+    <Rule Id="IDE0090" Action="None" />
+  </Rules>
+  <Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
+    <Rule Id="CA1303" Action="None" />
+  </Rules>
+  <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
+    <Rule Id="SA0001" Action="None" />
+    <Rule Id="SA1000" 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="SA1122" Action="None" />
+    <Rule Id="SA1124" Action="None" />
+    <Rule Id="SA1128" 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="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="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="SA1400" Action="None" />
+    <Rule Id="SA1401" Action="None" />
+    <Rule Id="SA1405" Action="None" />
+    <Rule Id="SA1406" Action="None" />
+    <Rule Id="SA1407" 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="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>
 </RuleSet>

BIN
PixiEditor.MSIX/Images/LargeTile.scale-100.png


BIN
PixiEditor.MSIX/Images/LargeTile.scale-125.png


BIN
PixiEditor.MSIX/Images/LargeTile.scale-150.png


BIN
PixiEditor.MSIX/Images/LargeTile.scale-200.png


BIN
PixiEditor.MSIX/Images/LargeTile.scale-400.png


BIN
PixiEditor.MSIX/Images/LockScreenLogo.scale-200.png


BIN
PixiEditor.MSIX/Images/SmallTile.scale-100.png


BIN
PixiEditor.MSIX/Images/SmallTile.scale-125.png


BIN
PixiEditor.MSIX/Images/SmallTile.scale-150.png


BIN
PixiEditor.MSIX/Images/SmallTile.scale-200.png


BIN
PixiEditor.MSIX/Images/SmallTile.scale-400.png


BIN
PixiEditor.MSIX/Images/SplashScreen.scale-100.png


BIN
PixiEditor.MSIX/Images/SplashScreen.scale-125.png


BIN
PixiEditor.MSIX/Images/SplashScreen.scale-150.png


BIN
PixiEditor.MSIX/Images/SplashScreen.scale-200.png


BIN
PixiEditor.MSIX/Images/SplashScreen.scale-400.png


BIN
PixiEditor.MSIX/Images/Square150x150Logo.scale-100.png


BIN
PixiEditor.MSIX/Images/Square150x150Logo.scale-125.png


BIN
PixiEditor.MSIX/Images/Square150x150Logo.scale-150.png


BIN
PixiEditor.MSIX/Images/Square150x150Logo.scale-200.png


BIN
PixiEditor.MSIX/Images/Square150x150Logo.scale-400.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.altform-unplated_targetsize-16.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.altform-unplated_targetsize-256.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.altform-unplated_targetsize-32.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.altform-unplated_targetsize-48.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.scale-100.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.scale-125.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.scale-150.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.scale-200.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.scale-400.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.targetsize-16.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.targetsize-24.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.targetsize-24_altform-unplated.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.targetsize-256.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.targetsize-32.png


BIN
PixiEditor.MSIX/Images/Square44x44Logo.targetsize-48.png


BIN
PixiEditor.MSIX/Images/StoreLogo.backup.png


BIN
PixiEditor.MSIX/Images/StoreLogo.scale-100.png


BIN
PixiEditor.MSIX/Images/StoreLogo.scale-125.png


BIN
PixiEditor.MSIX/Images/StoreLogo.scale-150.png


BIN
PixiEditor.MSIX/Images/StoreLogo.scale-200.png


BIN
PixiEditor.MSIX/Images/StoreLogo.scale-400.png


BIN
PixiEditor.MSIX/Images/Wide310x150Logo.scale-100.png


BIN
PixiEditor.MSIX/Images/Wide310x150Logo.scale-125.png


BIN
PixiEditor.MSIX/Images/Wide310x150Logo.scale-150.png


BIN
PixiEditor.MSIX/Images/Wide310x150Logo.scale-200.png


BIN
PixiEditor.MSIX/Images/Wide310x150Logo.scale-400.png


+ 66 - 0
PixiEditor.MSIX/Package.appxmanifest

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<Package
+  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
+  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
+  IgnorableNamespaces="uap rescap">
+
+  <Identity
+    Name="56069PixiEditorOrganizati.PixiEditor"
+    Publisher="CN=0AFA75AD-56A3-481D-B5E4-D3C6274DD38A"
+    Version="0.2.0.0" />
+
+  <Properties>
+    <DisplayName>PixiEditor</DisplayName>
+    <PublisherDisplayName>PixiEditor Organization</PublisherDisplayName>
+    <Logo>Images\StoreLogo.png</Logo>
+  </Properties>
+
+  <Dependencies>
+    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
+    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14393.0" MaxVersionTested="10.0.14393.0" />
+  </Dependencies>
+
+  <Resources>
+    <Resource Language="x-generate"/>
+  </Resources>
+
+  <Applications>
+    <Application Id="App"
+      Executable="$targetnametoken$.exe"
+      EntryPoint="$targetentrypoint$">
+      <uap:VisualElements
+        DisplayName="PixiEditor"
+        Description="PixiEditor is pixel-art editing software. Create beautiful sprites for your games, animations (coming soon!), and edit images. All packed in an eye-friendly dark theme."
+        BackgroundColor="transparent"
+        Square150x150Logo="Images\Square150x150Logo.png"
+        Square44x44Logo="Images\Square44x44Logo.png">
+        <uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png"  Square71x71Logo="Images\SmallTile.png" Square310x310Logo="Images\LargeTile.png" ShortName="PixiEditor">
+          <uap:ShowNameOnTiles>
+            <uap:ShowOn Tile="square150x150Logo"/>
+            <uap:ShowOn Tile="wide310x150Logo"/>
+            <uap:ShowOn Tile="square310x310Logo"/>
+          </uap:ShowNameOnTiles>
+        </uap:DefaultTile >
+        <uap:SplashScreen Image="Images\SplashScreen.png" />
+      </uap:VisualElements>
+      <Extensions>
+        <uap:Extension Category="windows.fileTypeAssociation">
+          <uap:FileTypeAssociation Name="pixieditor.pixifile">
+            <uap:SupportedFileTypes>
+              <uap:FileType>.pixi</uap:FileType>
+            </uap:SupportedFileTypes>
+            <uap:DisplayName>PixiEditor</uap:DisplayName>
+            <uap:InfoTip>A file used to save art made in PixiEditor</uap:InfoTip>
+            <uap:EditFlags OpenIsSafe="true"/>
+          </uap:FileTypeAssociation>
+        </uap:Extension>
+      </Extensions>
+    </Application>
+  </Applications>
+
+  <Capabilities>
+    <rescap:Capability Name="runFullTrust" />
+  </Capabilities>
+</Package>

+ 129 - 0
PixiEditor.MSIX/PixiEditor.MSIX.wapproj

@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Condition="'$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '15.0'">
+    <VisualStudioVersion>15.0</VisualStudioVersion>
+  </PropertyGroup>
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|x86">
+      <Configuration>Debug</Configuration>
+      <Platform>x86</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x86">
+      <Configuration>Release</Configuration>
+      <Platform>x86</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|ARM">
+      <Configuration>Debug</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|ARM">
+      <Configuration>Release</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|ARM64">
+      <Configuration>Debug</Configuration>
+      <Platform>ARM64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|ARM64">
+      <Configuration>Release</Configuration>
+      <Platform>ARM64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|AnyCPU">
+      <Configuration>Debug</Configuration>
+      <Platform>AnyCPU</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|AnyCPU">
+      <Configuration>Release</Configuration>
+      <Platform>AnyCPU</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup>
+    <WapProjPath Condition="'$(WapProjPath)'==''">$(MSBuildExtensionsPath)\Microsoft\DesktopBridge\</WapProjPath>
+  </PropertyGroup>
+  <Import Project="$(WapProjPath)\Microsoft.DesktopBridge.props" />
+  <PropertyGroup>
+    <ProjectGuid>1f97f972-f9e8-4f35-a8b5-3f71408d2230</ProjectGuid>
+    <TargetPlatformVersion>10.0.19041.0</TargetPlatformVersion>
+    <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
+    <DefaultLanguage>en-US</DefaultLanguage>
+    <AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
+    <EntryPointProjectUniqueName>..\PixiEditor\PixiEditor.csproj</EntryPointProjectUniqueName>
+    <PackageCertificateThumbprint>4C83B3D55F197ED681F813F8BEB48ACDED28FD6F</PackageCertificateThumbprint>
+    <PackageCertificateKeyFile>PixiEditor.MSIX_TemporaryKey.pfx</PackageCertificateKeyFile>
+  </PropertyGroup>
+  <ItemGroup>
+    <AppxManifest Include="Package.appxmanifest">
+      <SubType>Designer</SubType>
+    </AppxManifest>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Images\LargeTile.scale-100.png" />
+    <Content Include="Images\LargeTile.scale-125.png" />
+    <Content Include="Images\LargeTile.scale-150.png" />
+    <Content Include="Images\LargeTile.scale-200.png" />
+    <Content Include="Images\LargeTile.scale-400.png" />
+    <Content Include="Images\SmallTile.scale-100.png" />
+    <Content Include="Images\SmallTile.scale-125.png" />
+    <Content Include="Images\SmallTile.scale-150.png" />
+    <Content Include="Images\SmallTile.scale-200.png" />
+    <Content Include="Images\SmallTile.scale-400.png" />
+    <Content Include="Images\SplashScreen.scale-100.png" />
+    <Content Include="Images\SplashScreen.scale-125.png" />
+    <Content Include="Images\SplashScreen.scale-150.png" />
+    <Content Include="Images\SplashScreen.scale-200.png" />
+    <Content Include="Images\LockScreenLogo.scale-200.png" />
+    <Content Include="Images\SplashScreen.scale-400.png" />
+    <Content Include="Images\Square150x150Logo.scale-100.png" />
+    <Content Include="Images\Square150x150Logo.scale-125.png" />
+    <Content Include="Images\Square150x150Logo.scale-150.png" />
+    <Content Include="Images\Square150x150Logo.scale-200.png" />
+    <Content Include="Images\Square150x150Logo.scale-400.png" />
+    <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-16.png" />
+    <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-24.png" />
+    <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-256.png" />
+    <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-32.png" />
+    <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-48.png" />
+    <Content Include="Images\Square44x44Logo.altform-unplated_targetsize-16.png" />
+    <Content Include="Images\Square44x44Logo.altform-unplated_targetsize-256.png" />
+    <Content Include="Images\Square44x44Logo.altform-unplated_targetsize-32.png" />
+    <Content Include="Images\Square44x44Logo.altform-unplated_targetsize-48.png" />
+    <Content Include="Images\Square44x44Logo.scale-100.png" />
+    <Content Include="Images\Square44x44Logo.scale-125.png" />
+    <Content Include="Images\Square44x44Logo.scale-150.png" />
+    <Content Include="Images\Square44x44Logo.scale-200.png" />
+    <Content Include="Images\Square44x44Logo.scale-400.png" />
+    <Content Include="Images\Square44x44Logo.targetsize-16.png" />
+    <Content Include="Images\Square44x44Logo.targetsize-24.png" />
+    <Content Include="Images\Square44x44Logo.targetsize-24_altform-unplated.png" />
+    <Content Include="Images\Square44x44Logo.targetsize-256.png" />
+    <Content Include="Images\Square44x44Logo.targetsize-32.png" />
+    <Content Include="Images\Square44x44Logo.targetsize-48.png" />
+    <Content Include="Images\StoreLogo.scale-100.png" />
+    <Content Include="Images\StoreLogo.scale-125.png" />
+    <Content Include="Images\StoreLogo.scale-150.png" />
+    <Content Include="Images\StoreLogo.scale-200.png" />
+    <Content Include="Images\StoreLogo.scale-400.png" />
+    <Content Include="Images\Wide310x150Logo.scale-100.png" />
+    <Content Include="Images\Wide310x150Logo.scale-125.png" />
+    <Content Include="Images\Wide310x150Logo.scale-150.png" />
+    <Content Include="Images\Wide310x150Logo.scale-200.png" />
+    <Content Include="Images\Wide310x150Logo.scale-400.png" />
+  </ItemGroup>
+  <Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.19041.1" PrivateAssets="all" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\PixiEditor\PixiEditor.csproj">
+      <SkipGetTargetFrameworkProperties>True</SkipGetTargetFrameworkProperties>
+    </ProjectReference>
+  </ItemGroup>
+</Project>

+ 1 - 0
PixiEditor.UpdateInstaller/PixiEditor.UpdateInstaller.csproj

@@ -5,6 +5,7 @@
     <TargetFramework>net5.0-windows</TargetFramework>
     <TargetFramework>net5.0-windows</TargetFramework>
     <UseWPF>true</UseWPF>
     <UseWPF>true</UseWPF>
     <ApplicationManifest>app.manifest</ApplicationManifest>
     <ApplicationManifest>app.manifest</ApplicationManifest>
+    <Platforms>AnyCPU;x64;x86</Platforms>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 1 - 0
PixiEditor.UpdateModule/PixiEditor.UpdateModule.csproj

@@ -2,6 +2,7 @@
 
 
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>net5.0</TargetFramework>
     <TargetFramework>net5.0</TargetFramework>
+    <Platforms>AnyCPU;x64;x86</Platforms>
   </PropertyGroup>
   </PropertyGroup>
 
 
 </Project>
 </Project>

+ 120 - 68
PixiEditor.sln

@@ -18,100 +18,152 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildConfiguration", "Build
 		stylecop.json = stylecop.json
 		stylecop.json = stylecop.json
 	EndProjectSection
 	EndProjectSection
 EndProject
 EndProject
+Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "PixiEditor.MSIX", "PixiEditor.MSIX\PixiEditor.MSIX.wapproj", "{1F97F972-F9E8-4F35-A8B5-3F71408D2230}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
-		Debug|ARM = Debug|ARM
-		Debug|ARM64 = Debug|ARM64
 		Debug|x64 = Debug|x64
 		Debug|x64 = Debug|x64
 		Debug|x86 = Debug|x86
 		Debug|x86 = Debug|x86
+		MSIX Debug|Any CPU = MSIX Debug|Any CPU
+		MSIX Debug|x64 = MSIX Debug|x64
+		MSIX Debug|x86 = MSIX Debug|x86
+		MSIX|Any CPU = MSIX|Any CPU
+		MSIX|x64 = MSIX|x64
+		MSIX|x86 = MSIX|x86
 		Release|Any CPU = Release|Any CPU
 		Release|Any CPU = Release|Any CPU
-		Release|ARM = Release|ARM
-		Release|ARM64 = Release|ARM64
 		Release|x64 = Release|x64
 		Release|x64 = Release|x64
 		Release|x86 = Release|x86
 		Release|x86 = Release|x86
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|ARM.ActiveCfg = Debug|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|ARM.Build.0 = Debug|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|ARM64.ActiveCfg = Debug|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|ARM64.Build.0 = Debug|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x64.Build.0 = Debug|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x86.Build.0 = Debug|Any CPU
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x64.ActiveCfg = Debug|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x64.Build.0 = Debug|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x86.ActiveCfg = Debug|x86
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x86.Build.0 = Debug|x86
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|Any CPU.ActiveCfg = MSIX Debug|Any CPU
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|Any CPU.Build.0 = MSIX Debug|Any CPU
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x64.ActiveCfg = Debug|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x64.Build.0 = Debug|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x86.ActiveCfg = Debug|x86
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x86.Build.0 = Debug|x86
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|Any CPU.ActiveCfg = MSIX|Any CPU
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|Any CPU.Build.0 = MSIX|Any CPU
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x64.ActiveCfg = Release|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x64.Build.0 = Release|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x86.ActiveCfg = Release|x86
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x86.Build.0 = Release|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|Any CPU.Build.0 = Release|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|Any CPU.Build.0 = Release|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|ARM.ActiveCfg = Release|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|ARM.Build.0 = Release|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|ARM64.ActiveCfg = Release|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|ARM64.Build.0 = Release|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|x64.ActiveCfg = Release|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|x64.Build.0 = Release|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|x86.ActiveCfg = Release|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|x86.Build.0 = Release|Any CPU
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|x64.ActiveCfg = Release|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|x64.Build.0 = Release|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|x86.ActiveCfg = Release|x86
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|x86.Build.0 = Release|x86
 		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|ARM.ActiveCfg = Debug|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|ARM.Build.0 = Debug|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|ARM64.ActiveCfg = Debug|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|ARM64.Build.0 = Debug|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|x64.Build.0 = Debug|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|x86.Build.0 = Debug|Any CPU
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|x64.ActiveCfg = Debug|x64
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|x64.Build.0 = Debug|x64
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|x86.ActiveCfg = Debug|x86
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Debug|x86.Build.0 = Debug|x86
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX Debug|x64.ActiveCfg = Debug|x64
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX Debug|x64.Build.0 = Debug|x64
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX Debug|x86.ActiveCfg = Debug|x86
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX Debug|x86.Build.0 = Debug|x86
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX|Any CPU.ActiveCfg = Release|Any CPU
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX|Any CPU.Build.0 = Release|Any CPU
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX|x64.ActiveCfg = Release|x64
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX|x64.Build.0 = Release|x64
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX|x86.ActiveCfg = Release|x86
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.MSIX|x86.Build.0 = Release|x86
 		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|Any CPU.Build.0 = Release|Any CPU
 		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|Any CPU.Build.0 = Release|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|ARM.ActiveCfg = Release|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|ARM.Build.0 = Release|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|ARM64.ActiveCfg = Release|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|ARM64.Build.0 = Release|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|x64.ActiveCfg = Release|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|x64.Build.0 = Release|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|x86.ActiveCfg = Release|Any CPU
-		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|x86.Build.0 = Release|Any CPU
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|x64.ActiveCfg = Release|x64
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|x64.Build.0 = Release|x64
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|x86.ActiveCfg = Release|x86
+		{41B40602-2E8C-4B76-9BDB-B9FDE686ACCE}.Release|x86.Build.0 = Release|x86
 		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|ARM.ActiveCfg = Debug|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|ARM.Build.0 = Debug|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|ARM64.ActiveCfg = Debug|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|ARM64.Build.0 = Debug|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|x64.Build.0 = Debug|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|x86.Build.0 = Debug|Any CPU
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|x64.ActiveCfg = Debug|x64
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|x64.Build.0 = Debug|x64
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|x86.ActiveCfg = Debug|x86
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Debug|x86.Build.0 = Debug|x86
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX Debug|x64.ActiveCfg = Debug|x64
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX Debug|x64.Build.0 = Debug|x64
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX Debug|x86.ActiveCfg = Debug|x86
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX Debug|x86.Build.0 = Debug|x86
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX|Any CPU.ActiveCfg = Release|Any CPU
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX|Any CPU.Build.0 = Release|Any CPU
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX|x64.ActiveCfg = Release|x64
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX|x64.Build.0 = Release|x64
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX|x86.ActiveCfg = Release|x86
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.MSIX|x86.Build.0 = Release|x86
 		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|Any CPU.Build.0 = Release|Any CPU
 		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|Any CPU.Build.0 = Release|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|ARM.ActiveCfg = Release|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|ARM.Build.0 = Release|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|ARM64.ActiveCfg = Release|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|ARM64.Build.0 = Release|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|x64.ActiveCfg = Release|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|x64.Build.0 = Release|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|x86.ActiveCfg = Release|Any CPU
-		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|x86.Build.0 = Release|Any CPU
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|x64.ActiveCfg = Release|x64
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|x64.Build.0 = Release|x64
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|x86.ActiveCfg = Release|x86
+		{80BB2920-3DC0-406C-9E2B-30B08D5CC7A8}.Release|x86.Build.0 = Release|x86
 		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|ARM.ActiveCfg = Debug|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|ARM.Build.0 = Debug|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|ARM64.ActiveCfg = Debug|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|ARM64.Build.0 = Debug|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|x64.Build.0 = Debug|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|x86.Build.0 = Debug|Any CPU
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|x64.ActiveCfg = Debug|x64
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|x64.Build.0 = Debug|x64
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|x86.ActiveCfg = Debug|x86
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.Debug|x86.Build.0 = Debug|x86
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX Debug|x64.ActiveCfg = Debug|x64
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX Debug|x64.Build.0 = Debug|x64
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX Debug|x86.ActiveCfg = Debug|x86
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX Debug|x86.Build.0 = Debug|x86
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX|Any CPU.ActiveCfg = Release|Any CPU
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX|Any CPU.Build.0 = Release|Any CPU
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX|x64.ActiveCfg = Release|x64
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX|x64.Build.0 = Release|x64
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX|x86.ActiveCfg = Release|x86
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.MSIX|x86.Build.0 = Release|x86
 		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|Any CPU.Build.0 = Release|Any CPU
 		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|Any CPU.Build.0 = Release|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|ARM.ActiveCfg = Release|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|ARM.Build.0 = Release|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|ARM64.ActiveCfg = Release|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|ARM64.Build.0 = Release|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|x64.ActiveCfg = Release|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|x64.Build.0 = Release|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|x86.ActiveCfg = Release|Any CPU
-		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|x86.Build.0 = Release|Any CPU
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|x64.ActiveCfg = Release|x64
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|x64.Build.0 = Release|x64
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|x86.ActiveCfg = Release|x86
+		{5193C1C1-8362-40FD-802B-E097E8C88082}.Release|x86.Build.0 = Release|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Debug|x64.ActiveCfg = Debug|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Debug|x64.Build.0 = Debug|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Debug|x64.Deploy.0 = Debug|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Debug|x86.ActiveCfg = Debug|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Debug|x86.Build.0 = Debug|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Debug|x86.Deploy.0 = Debug|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX Debug|x64.ActiveCfg = Debug|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX Debug|x64.Build.0 = Debug|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX Debug|x64.Deploy.0 = Debug|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX Debug|x86.ActiveCfg = Debug|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX Debug|x86.Build.0 = Debug|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX Debug|x86.Deploy.0 = Debug|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX|Any CPU.ActiveCfg = Release|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX|Any CPU.Build.0 = Release|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX|Any CPU.Deploy.0 = Release|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX|x64.ActiveCfg = Release|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX|x64.Build.0 = Release|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX|x64.Deploy.0 = Release|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX|x86.ActiveCfg = Release|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX|x86.Build.0 = Release|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.MSIX|x86.Deploy.0 = Release|x86
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Release|Any CPU.Build.0 = Release|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Release|Any CPU.Deploy.0 = Release|Any CPU
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Release|x64.ActiveCfg = Release|x64
+		{1F97F972-F9E8-4F35-A8B5-3F71408D2230}.Release|x86.ActiveCfg = Release|x86
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE

+ 1 - 6
PixiEditor/Exceptions/CorruptedFileException.cs

@@ -1,12 +1,7 @@
 using System;
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 
 namespace PixiEditor.Exceptions
 namespace PixiEditor.Exceptions
 {
 {
-
     [Serializable]
     [Serializable]
     public class CorruptedFileException : Exception
     public class CorruptedFileException : Exception
     {
     {
@@ -32,4 +27,4 @@ namespace PixiEditor.Exceptions
         {
         {
         }
         }
     }
     }
-}
+}

+ 3 - 6
PixiEditor/Helpers/AssemblyHelper.cs

@@ -8,11 +8,8 @@ namespace PixiEditor.Helpers
 {
 {
     public static class AssemblyHelper
     public static class AssemblyHelper
     {
     {
-        public static string GetCurrentAssemblyVersion()
-        {
-            var assembly = Assembly.GetExecutingAssembly();
-            FileVersionInfo info = FileVersionInfo.GetVersionInfo(assembly.Location);
-            return info.FileVersion;
-        }
+        public static Version GetCurrentAssemblyVersion() => Assembly.GetExecutingAssembly().GetName().Version;
+
+        public static string GetCurrentAssemblyVersion(Func<Version, string> toString) => toString(GetCurrentAssemblyVersion());
     }
     }
 }
 }

+ 1 - 1
PixiEditor/Helpers/Converters/DoubleToIntConverter.cs

@@ -22,4 +22,4 @@ namespace PixiEditor.Helpers.Converters
             throw new NotImplementedException();
             throw new NotImplementedException();
         }
         }
     }
     }
-}
+}

+ 44 - 0
PixiEditor/Helpers/Converters/FileExtensionToImageSourceConverter.cs

@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class FileExtensionToImageSourceConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            string extension = (string)value;
+
+            if (extension == ".pixi")
+            {
+                return Join("PixiFile.png", parameter);
+            }
+            else if (extension == ".png")
+            {
+                return Join("PngFile.png", parameter);
+            }
+            else if (extension == ".jpg" || extension == ".jpeg")
+            {
+                return Join("JpgFile.png", parameter);
+            }
+
+            return Join("UnknownFile.png", parameter);
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+
+        private string Join(string path, object parameter)
+        {
+            return Path.Join((string)parameter, "Images", path);
+        }
+    }
+}

+ 19 - 0
PixiEditor/Helpers/Converters/FloorConverter.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    class FloorConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return Math.Floor((double)value);
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value;
+        }
+    }
+}

+ 20 - 0
PixiEditor/Helpers/Converters/IntToPickerTypeConverter.cs

@@ -0,0 +1,20 @@
+using ColorPicker.Models;
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class IntToPickerTypeConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return (PickerType)value;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return (int)value;
+        }
+    }
+}

+ 35 - 0
PixiEditor/Helpers/Converters/KeyToStringConverter.cs

@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using System.Windows.Input;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class KeyToStringConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value is Key key)
+            {
+                return InputKeyHelpers.GetCharFromKey(key);
+            }
+            else if (value is ModifierKeys)
+            {
+                return value.ToString();
+            }
+            else
+            {
+                return string.Empty;
+            }
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 20 - 0
PixiEditor/Helpers/Converters/NotNullToVisibiltyConverter.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    class NotNullToVisibiltyConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value != null ? Visibility.Visible : Visibility.Hidden;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 26 - 0
PixiEditor/Helpers/Converters/ThresholdVisibilityConverter.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    class ThresholdVisibilityConverter : IValueConverter
+    {
+        public double Threshold { get; set; } = 100;
+        public bool CheckIfLess { get; set; } = false;
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (CheckIfLess)
+                return (double)value < Threshold ? Visibility.Visible : Visibility.Hidden;
+            else
+                return (double)value >= Threshold ? Visibility.Visible : Visibility.Hidden;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 17 - 0
PixiEditor/Helpers/Extensions/EnumHelpers.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Helpers.Extensions
+{
+    public static class EnumHelpers
+    {
+        public static IEnumerable<T> GetFlags<T>(this T e)
+               where T : Enum
+        {
+            return Enum.GetValues(e.GetType()).Cast<T>().Where(x => e.HasFlag(x));
+        }
+    }
+}

+ 22 - 14
PixiEditor/Helpers/Extensions/ParserHelpers.cs

@@ -1,4 +1,6 @@
-using System;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.ImageManipulation;
+using PixiEditor.Models.Layers;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
@@ -7,6 +9,7 @@ using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Parser;
 using PixiEditor.Parser;
+using SDColor = System.Drawing.Color;
 
 
 namespace PixiEditor.Helpers.Extensions
 namespace PixiEditor.Helpers.Extensions
 {
 {
@@ -18,26 +21,32 @@ namespace PixiEditor.Helpers.Extensions
             {
             {
                 Layers = serializableDocument.ToLayers(),
                 Layers = serializableDocument.ToLayers(),
                 Swatches = new ObservableCollection<Color>(serializableDocument.Swatches.Select(x =>
                 Swatches = new ObservableCollection<Color>(serializableDocument.Swatches.Select(x =>
-                    Color.FromArgb(x.Item1, x.Item2, x.Item3, x.Item4)))
+                    Color.FromArgb(x.A, x.R, x.G, x.B)))
             };
             };
 
 
-            //document.LayerStructure = new LayerStructure(ToGroups(serializableDocument), document);
+            if (document.Layers.Count > 0)
+            {
+                document.SetMainActiveLayer(0);
+            }
 
 
             return document;
             return document;
         }
         }
 
 
         public static ObservableCollection<Layer> ToLayers(this Parser.SerializableDocument serializableDocument)
         public static ObservableCollection<Layer> ToLayers(this Parser.SerializableDocument serializableDocument)
         {
         {
-            ObservableCollection<Layer> layers = new();
-            for (int i = 0; i < serializableDocument.Layers.Length; i++)
+            ObservableCollection<Layer> layers = new ObservableCollection<Layer>();
+            for (int i = 0; i < serializableDocument.Layers.Count; i++)
             {
             {
                 Parser.SerializableLayer serLayer = serializableDocument.Layers[i];
                 Parser.SerializableLayer serLayer = serializableDocument.Layers[i];
-                Layer layer = new(serLayer.Name, BitmapUtils.BytesToWriteableBitmap(serLayer.Width, serLayer.Height, serLayer.BitmapBytes))
-                {
-                    IsVisible = serLayer.IsVisible,
-                    Offset = new Thickness(serLayer.OffsetX, serLayer.OffsetY, 0, 0),
-                    Opacity = serLayer.Opacity
-                };
+                Layer layer =
+                    new Layer(serLayer.Name, BitmapUtils.BytesToWriteableBitmap(serLayer.Width, serLayer.Height, serLayer.BitmapBytes))
+                    {
+                        IsVisible = serLayer.IsVisible,
+                        Offset = new Thickness(serLayer.OffsetX, serLayer.OffsetY, 0, 0),
+                        Opacity = serLayer.Opacity,
+                        MaxHeight = serializableDocument.Height,
+                        MaxWidth = serializableDocument.Width,
+                    };
                 layers.Add(layer);
                 layers.Add(layer);
             }
             }
 
 
@@ -77,9 +86,8 @@ namespace PixiEditor.Helpers.Extensions
             {
             {
                 Width = document.Width,
                 Width = document.Width,
                 Height = document.Height,
                 Height = document.Height,
-                //Groups = document.LayerStructure.Groups.Select(x => x.ToSerializable()).ToArray(),
-                Layers = document.Layers.Select(x => x.ToSerializable()).ToArray(),
-                Swatches = document.Swatches.Select(x => new Tuple<byte, byte, byte, byte>(x.A, x.R, x.G, x.B)).ToArray()
+                Layers = document.Layers.Select(x => x.ToSerializable()).ToList(),
+                Swatches = document.Swatches.Select(x => SDColor.FromArgb(x.A, x.R, x.G, x.B)).ToList()
             };
             };
 
 
             return serializable;
             return serializable;

+ 27 - 0
PixiEditor/Helpers/Extensions/StringHelpers.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Helpers.Extensions
+{
+    public static class StringHelpers
+    {
+        public static string AddSpacesBeforeUppercaseLetters(this string text)
+        {
+            if (string.IsNullOrWhiteSpace(text))
+                return "";
+
+            StringBuilder newText = new StringBuilder(text.Length * 2);
+            newText.Append(text[0]);
+            for (int i = 1; i < text.Length; i++)
+            {
+                if (char.IsUpper(text[i]) && text[i - 1] != ' ')
+                    newText.Append(' ');
+                newText.Append(text[i]);
+            }
+            return newText.ToString();
+        }
+    }
+}

+ 82 - 0
PixiEditor/Helpers/InputKeyHelpers.cs

@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace PixiEditor.Helpers
+{
+    public static class InputKeyHelpers
+    {
+        public static string GetCharFromKey(Key key)
+        {
+            int virtualKey = KeyInterop.VirtualKeyFromKey(key);
+            byte[] keyboardState = new byte[256];
+            GetKeyboardState(keyboardState);
+
+            uint scanCode = MapVirtualKeyW((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
+            StringBuilder stringBuilder = new (3);
+
+            int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
+
+            switch (result)
+            {
+                case 0:
+                    {
+                        return key.ToString();
+                    }
+
+                case -1:
+                    {
+                        return stringBuilder.ToString().ToUpper();
+                    }
+
+                default:
+                    {
+                        return stringBuilder[result - 1].ToString().ToUpper();
+                    }
+            }
+        }
+
+        private enum MapType : uint
+        {
+            /// <summary>
+            /// The uCode parameter is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the function returns 0.
+            /// </summary>
+            MAPVK_VK_TO_VSC = 0x0,
+
+            /// <summary>
+            /// The uCode parameter is a scan code and is translated into a virtual-key code that does not distinguish between left- and right-hand keys. If there is no translation, the function returns 0.
+            /// </summary>
+            MAPVK_VSC_TO_VK = 0x1,
+
+            /// <summary>
+            /// The uCode parameter is a virtual-key code and is translated into an unshifted character value in the low order word of the return value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation, the function returns 0.
+            /// </summary>
+            MAPVK_VK_TO_CHAR = 0x2,
+
+            /// <summary>
+            /// The uCode parameter is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand keys. If there is no translation, the function returns 0.
+            /// </summary>
+            MAPVK_VSC_TO_VK_EX = 0x3,
+        }
+
+        [DllImport("user32.dll")]
+        private static extern int ToUnicode(
+            uint wVirtKey,
+            uint wScanCode,
+            byte[] lpKeyState,
+            [Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)]
+            StringBuilder pwszBuff,
+            int cchBuff,
+            uint wFlags);
+
+        [DllImport("user32.dll")]
+        private static extern bool GetKeyboardState(byte[] lpKeyState);
+
+        [DllImport("user32.dll")]
+        private static extern uint MapVirtualKeyW(uint uCode, MapType uMapType);
+    }
+}

+ 1 - 1
PixiEditor/Helpers/SelectionHelpers.cs

@@ -27,7 +27,7 @@ namespace PixiEditor.Helpers
             {
             {
                 document.UndoManager.AddUndoChange(
                 document.UndoManager.AddUndoChange(
                     new Change(
                     new Change(
-                        SetSelectionProcess, new object[] { document, new List<Coordinates>(oldPoints) },
+                        SetSelectionProcess, new object[] { document, oldPoints is null ? new List<Coordinates>() : new List<Coordinates>(oldPoints) },
                         SetSelectionProcess, new object[] { document, new List<Coordinates>(document.ActiveSelection.SelectedPoints) }));
                         SetSelectionProcess, new object[] { document, new List<Coordinates>(document.ActiveSelection.SelectedPoints) }));
 #pragma warning restore SA1117 // Parameters should be on same line or separate lines
 #pragma warning restore SA1117 // Parameters should be on same line or separate lines
             }
             }

+ 13 - 0
PixiEditor/Helpers/StringExtensions.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Linq;
+
+namespace PixiEditor.Helpers
+{
+    public static class StringExtensions
+    {
+        public static string Reverse(this string s)
+        {
+            return new string(s.Reverse<char>().ToArray());
+        }
+    }
+}

BIN
PixiEditor/Images/JpgFile.png


BIN
PixiEditor/Images/PixiFile.png


BIN
PixiEditor/Images/PixiParserLogo.png


BIN
PixiEditor/Images/PngFile.png


BIN
PixiEditor/Images/UnknownFile.png


+ 3 - 3
PixiEditor/Models/Controllers/BitmapChangedEventArgs.cs

@@ -1,5 +1,5 @@
-using System;
-using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.DataHolders;
+using System;
 
 
 namespace PixiEditor.Models.Controllers
 namespace PixiEditor.Models.Controllers
 {
 {
@@ -16,6 +16,6 @@ namespace PixiEditor.Models.Controllers
 
 
         public BitmapPixelChanges OldPixelsValues { get; set; }
         public BitmapPixelChanges OldPixelsValues { get; set; }
 
 
-        public Guid ChangedLayerGuid{ get; set; }
+        public Guid ChangedLayerGuid { get; set; }
     }
     }
 }
 }

+ 150 - 131
PixiEditor/Models/Controllers/ClipboardController.cs

@@ -1,132 +1,151 @@
-using System.IO;
-using System.Linq;
-using System.Windows;
-using System.Windows.Media.Imaging;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.ImageManipulation;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Undo;
-using PixiEditor.ViewModels;
-
-namespace PixiEditor.Models.Controllers
-{
-    public static class ClipboardController
-    {
-        /// <summary>
-        ///     Copies selection to clipboard in PNG, Bitmap and DIB formats.
-        /// </summary>
-        /// <param name="layers">Layers where selection is.</param>
-        public static void CopyToClipboard(Layer[] layers, Coordinates[] selection, int originalImageWidth, int originalImageHeight)
-        {
-            Clipboard.Clear();
-            WriteableBitmap combinedBitmaps = BitmapUtils.CombineLayers(originalImageWidth, originalImageHeight, layers);
-            using (MemoryStream pngStream = new MemoryStream())
-            {
-                DataObject data = new DataObject();
-                BitmapSource croppedBmp = BitmapSelectionToBmpSource(combinedBitmaps, selection);
-                data.SetData(DataFormats.Bitmap, croppedBmp, true); // Bitmap, no transparency support
-
-                PngBitmapEncoder encoder = new PngBitmapEncoder();
-                encoder.Frames.Add(BitmapFrame.Create(croppedBmp));
-                encoder.Save(pngStream);
-                data.SetData("PNG", pngStream, false); // PNG, supports transparency
-
-                Clipboard.SetImage(croppedBmp); // DIB format
-                Clipboard.SetDataObject(data, true);
-            }
-        }
-
-        /// <summary>
-        ///     Pastes image from clipboard into new layer.
-        /// </summary>
-        public static void PasteFromClipboard()
-        {
-            WriteableBitmap image = GetImageFromClipboard();
-            if (image != null)
-            {
-                AddImageToLayers(image);
-                int latestLayerIndex = ViewModelMain.Current.BitmapManager.ActiveDocument.Layers.Count - 1;
-                ViewModelMain.Current.BitmapManager.ActiveDocument.UndoManager.AddUndoChange(
-                    new Change(RemoveLayerProcess, new object[] { latestLayerIndex }, AddLayerProcess, new object[] { image }));
-            }
-        }
-
-        private static void RemoveLayerProcess(object[] parameters)
-        {
-            if (parameters.Length == 0 || !(parameters[0] is int))
-            {
-                return;
-            }
-
-            ViewModelMain.Current.BitmapManager.ActiveDocument.RemoveLayer((int)parameters[0], true);
-        }
-
-        private static void AddLayerProcess(object[] parameters)
-        {
-            if (parameters.Length == 0 || !(parameters[0] is WriteableBitmap))
-            {
-                return;
-            }
-
-            AddImageToLayers((WriteableBitmap)parameters[0]);
-        }
-
-        /// <summary>
-        ///     Gets image from clipboard, supported PNG, Dib and Bitmap.
-        /// </summary>
-        /// <returns>WriteableBitmap.</returns>
-        public static WriteableBitmap GetImageFromClipboard()
-        {
-            DataObject dao = (DataObject)Clipboard.GetDataObject();
-            WriteableBitmap finalImage = null;
-            if (dao.GetDataPresent("PNG"))
-            {
-                using (MemoryStream pngStream = dao.GetData("PNG") as MemoryStream)
-                {
-                    if (pngStream != null)
-                    {
-                        PngBitmapDecoder decoder = new PngBitmapDecoder(pngStream, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
-                        finalImage = new WriteableBitmap(decoder.Frames[0].Clone());
-                    }
-                }
-            }
-            else if (dao.GetDataPresent(DataFormats.Dib))
-            {
-                finalImage = new WriteableBitmap(Clipboard.GetImage() !);
-            }
-            else if (dao.GetDataPresent(DataFormats.Bitmap))
-            {
-                finalImage = new WriteableBitmap((dao.GetData(DataFormats.Bitmap) as BitmapSource) !);
-            }
-
-            return finalImage;
-        }
-
-        public static bool IsImageInClipboard()
-        {
-            DataObject dao = (DataObject)Clipboard.GetDataObject();
-            if (dao == null)
-            {
-                return false;
-            }
-
-            return dao.GetDataPresent("PNG") || dao.GetDataPresent(DataFormats.Dib) ||
-                   dao.GetDataPresent(DataFormats.Bitmap);
-        }
-
-        public static BitmapSource BitmapSelectionToBmpSource(WriteableBitmap bitmap, Coordinates[] selection)
-        {
-            int offsetX = selection.Min(x => x.X);
-            int offsetY = selection.Min(x => x.Y);
-            int width = selection.Max(x => x.X) - offsetX + 1;
-            int height = selection.Max(x => x.Y) - offsetY + 1;
-            return bitmap.Crop(offsetX, offsetY, width, height);
-        }
-
-        private static void AddImageToLayers(WriteableBitmap image)
-        {
-            ViewModelMain.Current.BitmapManager.ActiveDocument.AddNewLayer("Image", image);
-        }
-    }
+using PixiEditor.Models.ImageManipulation;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Undo;
+using PixiEditor.ViewModels;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditor.Models.Controllers
+{
+    public static class ClipboardController
+    {
+        /// <summary>
+        ///     Copies selection to clipboard in PNG, Bitmap and DIB formats.
+        /// </summary>
+        /// <param name="layers">Layers where selection is.</param>
+        public static void CopyToClipboard(Layer[] layers, Coordinates[] selection, int originalImageWidth, int originalImageHeight)
+        {
+            Clipboard.Clear();
+            WriteableBitmap combinedBitmaps = BitmapUtils.CombineLayers(originalImageWidth, originalImageHeight, layers);
+            using (MemoryStream pngStream = new MemoryStream())
+            {
+                DataObject data = new DataObject();
+                BitmapSource croppedBmp = BitmapSelectionToBmpSource(combinedBitmaps, selection);
+                data.SetData(DataFormats.Bitmap, croppedBmp, true); // Bitmap, no transparency support
+
+                PngBitmapEncoder encoder = new PngBitmapEncoder();
+                encoder.Frames.Add(BitmapFrame.Create(croppedBmp));
+                encoder.Save(pngStream);
+                data.SetData("PNG", pngStream, false); // PNG, supports transparency
+
+                Clipboard.SetImage(croppedBmp); // DIB format
+                Clipboard.SetDataObject(data, true);
+            }
+        }
+
+        /// <summary>
+        ///     Pastes image from clipboard into new layer.
+        /// </summary>
+        public static void PasteFromClipboard()
+        {
+            WriteableBitmap image = GetImageFromClipboard();
+            if (image != null)
+            {
+                AddImageToLayers(image);
+                int latestLayerIndex = ViewModelMain.Current.BitmapManager.ActiveDocument.Layers.Count - 1;
+                ViewModelMain.Current.BitmapManager.ActiveDocument.UndoManager.AddUndoChange(
+                    new Change(RemoveLayerProcess, new object[] { latestLayerIndex }, AddLayerProcess, new object[] { image }));
+            }
+        }
+
+        private static void RemoveLayerProcess(object[] parameters)
+        {
+            if (parameters.Length == 0 || !(parameters[0] is int))
+            {
+                return;
+            }
+
+            ViewModelMain.Current.BitmapManager.ActiveDocument.RemoveLayer((int)parameters[0], true);
+        }
+
+        private static void AddLayerProcess(object[] parameters)
+        {
+            if (parameters.Length == 0 || !(parameters[0] is WriteableBitmap))
+            {
+                return;
+            }
+
+            AddImageToLayers((WriteableBitmap)parameters[0]);
+        }
+
+        /// <summary>
+        ///     Gets image from clipboard, supported PNG, Dib and Bitmap.
+        /// </summary>
+        /// <returns>WriteableBitmap.</returns>
+        public static WriteableBitmap GetImageFromClipboard()
+        {
+            DataObject dao = (DataObject)Clipboard.GetDataObject();
+            WriteableBitmap finalImage = null;
+            if (dao.GetDataPresent("PNG"))
+            {
+                using (MemoryStream pngStream = dao.GetData("PNG") as MemoryStream)
+                {
+                    if (pngStream != null)
+                    {
+                        PngBitmapDecoder decoder = new PngBitmapDecoder(pngStream, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
+                        finalImage = new WriteableBitmap(decoder.Frames[0].Clone());
+                    }
+                }
+            }
+            else if (dao.GetDataPresent(DataFormats.Dib))
+            {
+                finalImage = new WriteableBitmap(Clipboard.GetImage()!);
+            }
+            else if (dao.GetDataPresent(DataFormats.Bitmap))
+            {
+                finalImage = new WriteableBitmap((dao.GetData(DataFormats.Bitmap) as BitmapSource)!);
+            }
+
+            return finalImage;
+        }
+
+        public static bool IsImageInClipboard()
+        {
+            DataObject dao = (DataObject)Clipboard.GetDataObject();
+            if (dao == null)
+            {
+                return false;
+            }
+
+            return dao.GetDataPresent("PNG") || dao.GetDataPresent(DataFormats.Dib) ||
+                   dao.GetDataPresent(DataFormats.Bitmap);
+        }
+
+        public static BitmapSource BitmapSelectionToBmpSource(WriteableBitmap bitmap, Coordinates[] selection)
+        {
+            int offsetX = selection.Min(x => x.X);
+            int offsetY = selection.Min(x => x.Y);
+            int width = selection.Max(x => x.X) - offsetX + 1;
+            int height = selection.Max(x => x.Y) - offsetY + 1;
+            return bitmap.Crop(offsetX, offsetY, width, height);
+        }
+
+        private static void AddImageToLayers(WriteableBitmap image)
+        {
+            ViewModelMain.Current.BitmapManager.ActiveDocument.AddNewLayer("Image", image);
+        }
+
+        private static void RemoveLayerProcess(object[] parameters)
+        {
+            if (parameters.Length == 0 || !(parameters[0] is int))
+            {
+                return;
+            }
+
+            ViewModelMain.Current.BitmapManager.ActiveDocument.RemoveLayer((int)parameters[0]);
+        }
+
+        private static void AddLayerProcess(object[] parameters)
+        {
+            if (parameters.Length == 0 || !(parameters[0] is WriteableBitmap))
+            {
+                return;
+            }
+
+            AddImageToLayers((WriteableBitmap)parameters[0]);
+        }
+    }
 }
 }

+ 16 - 1
PixiEditor/Models/Controllers/Shortcuts/Shortcut.cs

@@ -1,4 +1,6 @@
-using System.Windows.Input;
+using System.Linq;
+using System.Windows.Input;
+using PixiEditor.Helpers.Extensions;
 
 
 namespace PixiEditor.Models.Controllers.Shortcuts
 namespace PixiEditor.Models.Controllers.Shortcuts
 {
 {
@@ -12,12 +14,25 @@ namespace PixiEditor.Models.Controllers.Shortcuts
             CommandParameter = commandParameter;
             CommandParameter = commandParameter;
         }
         }
 
 
+        public Shortcut(Key shortcutKey, ICommand command, string description, object commandParameter = null, ModifierKeys modifier = ModifierKeys.None)
+            : this(shortcutKey, command, commandParameter, modifier)
+        {
+            Description = description;
+        }
+
         public Key ShortcutKey { get; set; }
         public Key ShortcutKey { get; set; }
 
 
         public ModifierKeys Modifier { get; set; }
         public ModifierKeys Modifier { get; set; }
 
 
+        /// <summary>
+        /// Gets all <see cref="ModifierKeys"/> as an array.
+        /// </summary>
+        public ModifierKeys[] Modifiers { get => Modifier.GetFlags().Except(new ModifierKeys[] { ModifierKeys.None }).ToArray(); }
+
         public ICommand Command { get; set; }
         public ICommand Command { get; set; }
 
 
+        public string Description { get; set; }
+
         public object CommandParameter { get; set; }
         public object CommandParameter { get; set; }
 
 
         public void Execute()
         public void Execute()

+ 5 - 4
PixiEditor/Models/Controllers/Shortcuts/ShortcutController.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
 using System.Windows.Input;
 using System.Windows.Input;
 
 
@@ -6,14 +7,14 @@ namespace PixiEditor.Models.Controllers.Shortcuts
 {
 {
     public class ShortcutController
     public class ShortcutController
     {
     {
-        public ShortcutController()
+        public ShortcutController(params ShortcutGroup[] shortcutGroups)
         {
         {
-            Shortcuts = new List<Shortcut>();
+            ShortcutGroups = new ObservableCollection<ShortcutGroup>(shortcutGroups);
         }
         }
 
 
         public static bool BlockShortcutExecution { get; set; }
         public static bool BlockShortcutExecution { get; set; }
 
 
-        public List<Shortcut> Shortcuts { get; set; }
+        public ObservableCollection<ShortcutGroup> ShortcutGroups { get; init; }
 
 
         public Shortcut LastShortcut { get; private set; }
         public Shortcut LastShortcut { get; private set; }
 
 
@@ -21,7 +22,7 @@ namespace PixiEditor.Models.Controllers.Shortcuts
         {
         {
             if (!BlockShortcutExecution)
             if (!BlockShortcutExecution)
             {
             {
-                Shortcut[] shortcuts = Shortcuts.FindAll(x => x.ShortcutKey == key).ToArray();
+                Shortcut[] shortcuts = ShortcutGroups.SelectMany(x => x.Shortcuts).ToList().FindAll(x => x.ShortcutKey == key).ToArray();
                 if (shortcuts.Length < 1)
                 if (shortcuts.Length < 1)
                 {
                 {
                     return;
                     return;

+ 43 - 0
PixiEditor/Models/Controllers/Shortcuts/ShortcutGroup.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Models.Controllers.Shortcuts
+{
+    public class ShortcutGroup
+    {
+        /// <summary>
+        /// Gets or sets the shortcuts in the shortcuts group.
+        /// </summary>
+        public ObservableCollection<Shortcut> Shortcuts { get; set; }
+
+        /// <summary>
+        /// Gets or sets the name of the shortcut group.
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether gets or sets the shortcut group visible in the shortcut popup.
+        /// </summary>
+        public bool IsVisible { get; set; }
+
+        public ShortcutGroup(string name, params Shortcut[] shortcuts)
+        {
+            Name = name;
+            Shortcuts = new ObservableCollection<Shortcut>(shortcuts);
+            IsVisible = true;
+        }
+
+        /// <param name="name">The name of the group.</param>
+        /// <param name="shortcuts">The shortcuts that belong in the group.</param>
+        /// <param name="isVisible">Is the group visible in the shortcut popup.</param>
+        public ShortcutGroup(string name, bool isVisible = true, params Shortcut[] shortcuts)
+            : this(name, shortcuts)
+        {
+            IsVisible = isVisible;
+        }
+    }
+}

+ 5 - 0
PixiEditor/Models/DataHolders/BitmapPixelChanges.cs

@@ -35,6 +35,11 @@ namespace PixiEditor.Models.DataHolders
             Dictionary<Coordinates, Color> dict = new Dictionary<Coordinates, Color>();
             Dictionary<Coordinates, Color> dict = new Dictionary<Coordinates, Color>();
             foreach (Coordinates coordinate in coordinates)
             foreach (Coordinates coordinate in coordinates)
             {
             {
+                if (dict.ContainsKey(coordinate))
+                {
+                    continue;
+                }
+
                 dict.Add(coordinate, color);
                 dict.Add(coordinate, color);
             }
             }
 
 

+ 1 - 1
PixiEditor/Models/DataHolders/Document/Document.Constructors.cs

@@ -18,7 +18,7 @@ namespace PixiEditor.Models.DataHolders
             SetRelayCommands();
             SetRelayCommands();
             UndoManager = new UndoManager();
             UndoManager = new UndoManager();
             LayerStructure = new Layers.LayerStructure(this);
             LayerStructure = new Layers.LayerStructure(this);
-            XamlAccesibleViewModel = ViewModelMain.Current ?? null;
+            XamlAccesibleViewModel = ViewModelMain.Current;
             GeneratePreviewLayer();
             GeneratePreviewLayer();
             Layers.CollectionChanged += Layers_CollectionChanged;
             Layers.CollectionChanged += Layers_CollectionChanged;
             LayerStructure.Groups.CollectionChanged += Groups_CollectionChanged;
             LayerStructure.Groups.CollectionChanged += Groups_CollectionChanged;

+ 4 - 3
PixiEditor/Models/DataHolders/Document/Document.IO.cs

@@ -1,4 +1,5 @@
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
+using System.Linq;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Models.UserPreferences;
 
 
@@ -53,7 +54,7 @@ namespace PixiEditor.Models.DataHolders
 
 
         private void UpdateRecentlyOpened(string newPath)
         private void UpdateRecentlyOpened(string newPath)
         {
         {
-            ObservableCollection<string> recentlyOpened = XamlAccesibleViewModel.FileSubViewModel.RecentlyOpened;
+            RecentlyOpenedCollection recentlyOpened = XamlAccesibleViewModel.FileSubViewModel.RecentlyOpened;
 
 
             if (!recentlyOpened.Contains(newPath))
             if (!recentlyOpened.Contains(newPath))
             {
             {
@@ -65,7 +66,7 @@ namespace PixiEditor.Models.DataHolders
                 recentlyOpened.Move(index, 0);
                 recentlyOpened.Move(index, 0);
             }
             }
 
 
-            if (recentlyOpened.Count > IPreferences.Current.GetPreference("maxOpenedRecently", 10))
+            if (recentlyOpened.Count > IPreferences.Current.GetPreference("MaxOpenedRecently", 8))
             {
             {
                 for (int i = 4; i < recentlyOpened.Count; i++)
                 for (int i = 4; i < recentlyOpened.Count; i++)
                 {
                 {
@@ -73,7 +74,7 @@ namespace PixiEditor.Models.DataHolders
                 }
                 }
             }
             }
 
 
-            IPreferences.Current.UpdateLocalPreference("RecentlyOpened", recentlyOpened);
+            IPreferences.Current.UpdateLocalPreference("RecentlyOpened", recentlyOpened.Select(x => x.FilePath));
 
 
             XamlAccesibleViewModel.FileSubViewModel.HasRecent = true;
             XamlAccesibleViewModel.FileSubViewModel.HasRecent = true;
         }
         }

+ 105 - 1
PixiEditor/Models/DataHolders/Document/Document.Layers.cs

@@ -3,10 +3,14 @@ using System.Buffers;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
+using System.Text.RegularExpressions;
 using System.Windows;
 using System.Windows;
+using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
+using PixiEditor.Helpers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
+using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
@@ -17,6 +21,7 @@ namespace PixiEditor.Models.DataHolders
     {
     {
         public const string MainSelectedLayerColor = "#505056";
         public const string MainSelectedLayerColor = "#505056";
         public const string SecondarySelectedLayerColor = "#7D505056";
         public const string SecondarySelectedLayerColor = "#7D505056";
+        private static readonly Regex reversedLayerSuffixRegex = new (@"(?:\)([0-9]+)*\()? *([\s\S]+)", RegexOptions.Compiled);
         private Guid activeLayerGuid;
         private Guid activeLayerGuid;
         private LayerStructure layerStructure;
         private LayerStructure layerStructure;
 
 
@@ -195,11 +200,16 @@ namespace PixiEditor.Models.DataHolders
 
 
         public void AddNewLayer(string name, int width, int height, bool setAsActive = true)
         public void AddNewLayer(string name, int width, int height, bool setAsActive = true)
         {
         {
-            Layers.Add(new Layer(name, width, height)
+            Layer layer;
+
+            Layers.Add(layer = new Layer(name, width, height)
             {
             {
                 MaxHeight = Height,
                 MaxHeight = Height,
                 MaxWidth = Width
                 MaxWidth = Width
             });
             });
+
+            layer.Name = GetLayerSuffix(layer);
+
             if (setAsActive)
             if (setAsActive)
             {
             {
                 SetMainActiveLayer(Layers.Count - 1);
                 SetMainActiveLayer(Layers.Count - 1);
@@ -219,6 +229,31 @@ namespace PixiEditor.Models.DataHolders
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(Layers[^1].LayerGuid, LayerAction.Add));
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(Layers[^1].LayerGuid, LayerAction.Add));
         }
         }
 
 
+        /// <summary>
+        /// Duplicates the layer at the <paramref name="index"/>.
+        /// </summary>
+        /// <param name="index">The index of the layer to duplicate.</param>
+        /// <returns>The duplicate.</returns>
+        public Layer DuplicateLayer(int index)
+        {
+            Layer duplicate = Layers[index].Clone(true);
+
+            duplicate.Name = GetLayerSuffix(duplicate);
+
+            Layers.Insert(index + 1, duplicate);
+            SetMainActiveLayer(index + 1);
+
+            StorageBasedChange storageChange = new (this, new[] { duplicate }, false);
+            UndoManager.AddUndoChange(
+                storageChange.ToChange(
+                    RemoveLayerProcess,
+                    new object[] { duplicate.LayerGuid },
+                    RestoreLayersProcess,
+                    "Duplicate Layer"));
+
+            return duplicate;
+        }
+
         public void SetNextLayerAsActive(int lastLayerIndex)
         public void SetNextLayerAsActive(int lastLayerIndex)
         {
         {
             if (Layers.Count > 0)
             if (Layers.Count > 0)
@@ -470,6 +505,10 @@ namespace PixiEditor.Models.DataHolders
             LayerStructure.PostMoveReassignBounds(new GroupData(newGroup?.GroupGuid), layerGuid);
             LayerStructure.PostMoveReassignBounds(new GroupData(newGroup?.GroupGuid), layerGuid);
 
 
             RaisePropertyChanged(nameof(LayerStructure));
             RaisePropertyChanged(nameof(LayerStructure));
+
+        public Color GetColorAtPoint(int x, int y)
+        {
+            return BitmapUtils.GetColorAtPointCombined(x, y, Layers.ToArray());
         }
         }
 
 
         private void InjectRemoveActiveLayersUndo(object[] guidArgs, StorageBasedChange change)
         private void InjectRemoveActiveLayersUndo(object[] guidArgs, StorageBasedChange change)
@@ -708,6 +747,71 @@ namespace PixiEditor.Models.DataHolders
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// Get's the layers suffix, e.g. "New Layer" becomes "New Layer (1)".
+        /// </summary>
+        /// <returns>Name of the layer with suffix.</returns>
+        private string GetLayerSuffix(Layer layer)
+        {
+            Match match = reversedLayerSuffixRegex.Match(layer.Name.Reverse());
+
+            int? highestValue = GetHighestSuffix(layer, match.Groups[2].Value, reversedLayerSuffixRegex);
+
+            string actualName = match.Groups[2].Value.Reverse();
+
+            if (highestValue == null)
+            {
+                return actualName;
+            }
+
+            return actualName + $" ({highestValue + 1})";
+        }
+
+        private int? GetHighestSuffix(Layer except, string layerName, Regex regex)
+        {
+            int? highestValue = null;
+
+            foreach (Layer otherLayer in Layers)
+            {
+                if (otherLayer == except)
+                {
+                    continue;
+                }
+
+                Match otherMatch = regex.Match(otherLayer.Name.Reverse());
+
+                if (otherMatch.Groups[2].Value == layerName)
+                {
+                    SetHighest(otherMatch.Groups[1].Value.Reverse(), ref highestValue);
+                }
+            }
+
+            return highestValue;
+        }
+
+        /// <returns>Was the parse a sucess.</returns>
+        private bool SetHighest(string number, ref int? highest, int? defaultValue = 0)
+        {
+            bool sucess = int.TryParse(number, out int parsedNumber);
+
+            if (sucess)
+            {
+                if (highest == null || highest < parsedNumber)
+                {
+                    highest = parsedNumber;
+                }
+            }
+            else
+            {
+                if (highest == null)
+                {
+                    highest = defaultValue;
+                }
+            }
+
+            return sucess;
+        }
+
         private void RemoveLayersProcess(object[] parameters)
         private void RemoveLayersProcess(object[] parameters)
         {
         {
             if (parameters != null && parameters.Length > 0 && parameters[0] is IEnumerable<Guid> layerGuids)
             if (parameters != null && parameters.Length > 0 && parameters[0] is IEnumerable<Guid> layerGuids)

+ 59 - 0
PixiEditor/Models/DataHolders/RecentlyOpenedCollection.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Models.DataHolders
+{
+    public class RecentlyOpenedCollection : ObservableCollection<RecentlyOpenedDocument>
+    {
+        public RecentlyOpenedDocument this[string path]
+        {
+            get
+            {
+                return Get(path);
+            }
+        }
+
+        public RecentlyOpenedCollection()
+        {
+        }
+
+        public RecentlyOpenedCollection(IEnumerable<RecentlyOpenedDocument> documents)
+            : base(documents)
+        {
+        }
+
+        public void Add(string path)
+        {
+            if (string.IsNullOrWhiteSpace(path))
+            {
+                return;
+            }
+
+            Add(Create(path));
+        }
+
+        public bool Contains(string path) => Get(path) is not null;
+
+        public void Remove(string path) => Remove(Get(path));
+
+        public int IndexOf(string path) => IndexOf(Get(path));
+
+        public void Insert(int index, string path)
+        {
+            if (string.IsNullOrWhiteSpace(path))
+            {
+                return;
+            }
+
+            Insert(index, Create(path));
+        }
+
+        private static RecentlyOpenedDocument Create(string path) => new (path);
+
+        private RecentlyOpenedDocument Get(string path) => this.FirstOrDefault(x => x.FilePath == path);
+    }
+}

+ 108 - 0
PixiEditor/Models/DataHolders/RecentlyOpenedDocument.cs

@@ -0,0 +1,108 @@
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Windows.Media.Imaging;
+using PixiEditor.Helpers;
+using PixiEditor.Models.ImageManipulation;
+using PixiEditor.Models.IO;
+using PixiEditor.Models.Layers;
+using PixiEditor.Parser;
+
+namespace PixiEditor.Models.DataHolders
+{
+    [DebuggerDisplay("{FilePath}")]
+    public class RecentlyOpenedDocument : NotifyableObject
+    {
+        private bool corrupt;
+
+        private string filePath;
+
+        private WriteableBitmap previewBitmap;
+
+        public string FilePath
+        {
+            get => filePath;
+            set
+            {
+                SetProperty(ref filePath, value);
+                RaisePropertyChanged(nameof(FileName));
+                RaisePropertyChanged(nameof(FileExtension));
+                PreviewBitmap = null;
+            }
+        }
+
+        public bool Corrupt { get => corrupt; set => SetProperty(ref corrupt, value); }
+
+        public string FileName => Path.GetFileNameWithoutExtension(filePath);
+
+        public string FileExtension
+        {
+            get
+            {
+                if (Corrupt)
+                {
+                    return "Corrupt";
+                }
+
+                return Path.GetExtension(filePath).ToLower();
+            }
+        }
+
+        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+        public WriteableBitmap PreviewBitmap
+        {
+            get
+            {
+                if (previewBitmap == null)
+                {
+                    PreviewBitmap = LoadPreviewBitmap();
+                }
+
+                return previewBitmap;
+            }
+            private set => SetProperty(ref previewBitmap, value);
+        }
+
+        public RecentlyOpenedDocument(string path)
+        {
+            FilePath = path;
+        }
+
+        private WriteableBitmap LoadPreviewBitmap()
+        {
+            if (FileExtension == ".pixi")
+            {
+                SerializableDocument serializableDocument = null;
+
+                try
+                {
+                    serializableDocument = PixiParser.Deserialize(filePath);
+                }
+                catch
+                {
+                    corrupt = true;
+                    return null;
+                }
+
+                return BitmapUtils.GeneratePreviewBitmap(serializableDocument.Layers, serializableDocument.Width, serializableDocument.Height, 80, 50);
+            }
+            else if (FileExtension == ".png" || FileExtension == ".jpg" || FileExtension == ".jpeg")
+            {
+                WriteableBitmap bitmap = null;
+
+                try
+                {
+                    bitmap = Importer.ImportImage(FilePath);
+                }
+                catch
+                {
+                    corrupt = true;
+                }
+
+                return bitmap;
+            }
+
+            return null;
+        }
+    }
+}

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

@@ -9,7 +9,8 @@ namespace PixiEditor.Models.Dialogs
         {
         {
             ConfirmationPopup popup = new ConfirmationPopup
             ConfirmationPopup popup = new ConfirmationPopup
             {
             {
-                Body = message
+                Body = message,
+                Topmost = true
             };
             };
             if ((bool)popup.ShowDialog())
             if ((bool)popup.ShowDialog())
             {
             {

+ 16 - 2
PixiEditor/Models/Dialogs/NoticeDialog.cs

@@ -6,9 +6,23 @@ namespace PixiEditor.Models.Dialogs
     {
     {
         public static void Show(string message)
         public static void Show(string message)
         {
         {
-            NoticePopup popup = new NoticePopup
+            NoticePopup popup = new ()
             {
             {
-                Body = message
+                Body = message,
+                Title = string.Empty,
+                Topmost = true
+            };
+
+            popup.ShowDialog();
+        }
+
+        public static void Show(string message, string title)
+        {
+            NoticePopup popup = new ()
+            {
+                Body = message,
+                Title = title,
+                Topmost = true
             };
             };
 
 
             popup.ShowDialog();
             popup.ShowDialog();

+ 14 - 0
PixiEditor/Models/Enums/SelectionShape.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Models.Enums
+{
+    public enum SelectionShape
+    {
+        Rectangle,
+        Circle
+    }
+}

+ 73 - 73
PixiEditor/Models/IO/Importer.cs

@@ -1,13 +1,13 @@
 using System;
 using System;
-using System.IO;
-using System.Runtime.Serialization;
+using System.IO;
+using System.Runtime.Serialization;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
-using PixiEditor.Exceptions;
+using PixiEditor.Exceptions;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
-using PixiEditor.Helpers.Extensions;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Parser;
-
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Parser;
+
 namespace PixiEditor.Models.IO
 namespace PixiEditor.Models.IO
 {
 {
     public class Importer : NotifyableObject
     public class Importer : NotifyableObject
@@ -20,77 +20,77 @@ namespace PixiEditor.Models.IO
         /// <param name="height">New height of image.</param>
         /// <param name="height">New height of image.</param>
         /// <returns>WriteableBitmap of imported image.</returns>
         /// <returns>WriteableBitmap of imported image.</returns>
         public static WriteableBitmap ImportImage(string path, int width, int height)
         public static WriteableBitmap ImportImage(string path, int width, int height)
-        {
-            WriteableBitmap wbmp = ImportImage(path);
-            if (wbmp.PixelWidth != width || wbmp.PixelHeight != height)
-            {
-                return wbmp.Resize(width, height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
-            }
-
-            return wbmp;
-        }
-
+        {
+            WriteableBitmap wbmp = ImportImage(path);
+            if (wbmp.PixelWidth != width || wbmp.PixelHeight != height)
+            {
+                return wbmp.Resize(width, height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
+            }
+
+            return wbmp;
+        }
+
         /// <summary>
         /// <summary>
         ///     Imports image from path and resizes it to given dimensions.
         ///     Imports image from path and resizes it to given dimensions.
         /// </summary>
         /// </summary>
         /// <param name="path">Path of image.</param>
         /// <param name="path">Path of image.</param>
-        public static WriteableBitmap ImportImage(string path)
-        {
-            try
-            {
-                Uri uri = new Uri(path, UriKind.RelativeOrAbsolute);
-                BitmapImage bitmap = new BitmapImage();
-                bitmap.BeginInit();
-                bitmap.UriSource = uri;
-                bitmap.CacheOption = BitmapCacheOption.OnLoad;
-                bitmap.EndInit();
-
-                return BitmapFactory.ConvertToPbgra32Format(bitmap);
-            }
-            catch (NotSupportedException)
-            {
-                throw new CorruptedFileException();
-            }
-            catch (FileFormatException)
-            {
-                throw new CorruptedFileException();
-            }
-        }
-
-        public static Document ImportDocument(string path)
-        {
-            try
+        public static WriteableBitmap ImportImage(string path)
+        {
+            try
+            {
+                Uri uri = new Uri(path, UriKind.RelativeOrAbsolute);
+                BitmapImage bitmap = new BitmapImage();
+                bitmap.BeginInit();
+                bitmap.UriSource = uri;
+                bitmap.CacheOption = BitmapCacheOption.OnLoad;
+                bitmap.EndInit();
+
+                return BitmapFactory.ConvertToPbgra32Format(bitmap);
+            }
+            catch (NotSupportedException)
+            {
+                throw new CorruptedFileException();
+            }
+            catch (FileFormatException)
+            {
+                throw new CorruptedFileException();
+            }
+        }
+
+        public static Document ImportDocument(string path)
+        {
+            try
             {
             {
-                Document doc = PixiEditor.Parser.PixiParser.Deserialize(path).ToDocument();
-                doc.DocumentFilePath = path;
-                return doc;
-            }
-            catch (InvalidFileException)
-            {
-                throw new CorruptedFileException();
-            }
-        }
-
-        public static Document ImportOldDocument(string path)
-        {
-            try
-            {
-                using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
-
-                Document doc = PixiEditor.Parser.PixiParser.DeserializeOld(stream).ToDocument();
-                doc.DocumentFilePath = path;
+                Document doc = PixiEditor.Parser.PixiParser.Deserialize(path).ToDocument();
+                doc.DocumentFilePath = path;
                 return doc;
                 return doc;
-            }
-            catch (SerializationException)
-            {
-                throw new CorruptedFileException();
-            }
-        }
-
-        public static bool IsSupportedFile(string path)
-        {
-            path = path.ToLower();
-            return path.EndsWith(".pixi") || path.EndsWith(".png") || path.EndsWith(".jpg") || path.EndsWith(".jpeg");
-        }
+            }
+            catch (InvalidFileException)
+            {
+                throw new CorruptedFileException();
+            }
+        }
+
+        public static Document ImportOldDocument(string path)
+        {
+            try
+            {
+                using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
+
+                Document doc = PixiEditor.Parser.PixiParser.DeserializeOld(stream).ToDocument();
+                doc.DocumentFilePath = path;
+                return doc;
+            }
+            catch (SerializationException)
+            {
+                throw new CorruptedFileException();
+            }
+        }
+
+        public static bool IsSupportedFile(string path)
+        {
+            path = path.ToLower();
+            return path.EndsWith(".pixi") || path.EndsWith(".png") || path.EndsWith(".jpg") || path.EndsWith(".jpeg");
+        }
     }
     }
 }
 }

+ 8 - 13
PixiEditor/Models/ImageManipulation/BitmapUtils.cs

@@ -100,19 +100,14 @@ namespace PixiEditor.Models.ImageManipulation
         {
         {
             var opacityLayers = document.Layers.Where(x => x.IsVisible && x.Opacity > 0.8f);
             var opacityLayers = document.Layers.Where(x => x.IsVisible && x.Opacity > 0.8f);
 
 
-            return GeneratePreviewBitmap(opacityLayers, document.Width, document.Height, maxPreviewWidth, maxPreviewHeight);
-        }
-
-        public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<Layer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
-        {
             return GeneratePreviewBitmap(
             return GeneratePreviewBitmap(
-            layers.Select(x => x.LayerBitmap),
-            layers.Select(x => x.OffsetX),
-            layers.Select(x => x.OffsetY),
-            width,
-            height,
-            maxPreviewWidth,
-            maxPreviewHeight);
+                opacityLayers.Select(x => x.LayerBitmap),
+                opacityLayers.Select(x => x.OffsetX),
+                opacityLayers.Select(x => x.OffsetY),
+                document.Width,
+                document.Height,
+                maxPreviewWidth,
+                maxPreviewHeight);
         }
         }
 
 
         public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<SerializableLayer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
         public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<SerializableLayer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
@@ -225,4 +220,4 @@ namespace PixiEditor.Models.ImageManipulation
             return previewBitmap.Resize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
             return previewBitmap.Resize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
         }
         }
     }
     }
-}
+}

+ 14 - 14
PixiEditor/Models/Layers/Layer.cs

@@ -1,14 +1,14 @@
-using System;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Undo;
+using PixiEditor.ViewModels;
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Undo;
-using PixiEditor.ViewModels;
 
 
 namespace PixiEditor.Models.Layers
 namespace PixiEditor.Models.Layers
 {
 {
@@ -377,14 +377,14 @@ namespace PixiEditor.Models.Layers
 
 
             if (!(pixels.WasBuiltAsSingleColored && pixels.ChangedPixels.First().Value.A == 0))
             if (!(pixels.WasBuiltAsSingleColored && pixels.ChangedPixels.First().Value.A == 0))
             {
             {
-                if (newMaxX + 1 > Width || newMaxY + 1 > Height)
+                if ((newMaxX + 1 > Width && Width < MaxWidth) || (newMaxY + 1 > Height && Height < MaxHeight))
                 {
                 {
-                    IncreaseSizeToBottom(newMaxX, newMaxY);
+                    IncreaseSizeToBottomAndRight(newMaxX, newMaxY);
                 }
                 }
 
 
-                if (newMinX < 0 || newMinY < 0)
+                if ((newMinX < 0 && Width < MaxWidth) || (newMinY < 0 && Height < MaxHeight))
                 {
                 {
-                    IncreaseSizeToTop(newMinX, newMinY);
+                    IncreaseSizeToTopAndLeft(newMinX, newMinY);
                 }
                 }
             }
             }
 
 
@@ -503,7 +503,7 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
 
 
-        private void IncreaseSizeToBottom(int newMaxX, int newMaxY)
+        private void IncreaseSizeToBottomAndRight(int newMaxX, int newMaxY)
         {
         {
             if (MaxWidth - OffsetX < 0 || MaxHeight - OffsetY < 0)
             if (MaxWidth - OffsetX < 0 || MaxHeight - OffsetY < 0)
             {
             {
@@ -516,7 +516,7 @@ namespace PixiEditor.Models.Layers
             ResizeCanvas(0, 0, 0, 0, newMaxX, newMaxY);
             ResizeCanvas(0, 0, 0, 0, newMaxX, newMaxY);
         }
         }
 
 
-        private void IncreaseSizeToTop(int newMinX, int newMinY)
+        private void IncreaseSizeToTopAndLeft(int newMinX, int newMinY)
         {
         {
             newMinX = Math.Clamp(Math.Min(newMinX, Width), Math.Min(-OffsetX, OffsetX), 0);
             newMinX = Math.Clamp(Math.Min(newMinX, Width), Math.Min(-OffsetX, OffsetX), 0);
             newMinY = Math.Clamp(Math.Min(newMinY, Height), Math.Min(-OffsetY, OffsetY), 0);
             newMinY = Math.Clamp(Math.Min(newMinY, Height), Math.Min(-OffsetY, OffsetY), 0);
@@ -548,8 +548,8 @@ namespace PixiEditor.Models.Layers
         {
         {
             if (Width == 0 || Height == 0)
             if (Width == 0 || Height == 0)
             {
             {
-                int offsetX = pixels.ChangedPixels.Min(x => x.Key.X);
-                int offsetY = pixels.ChangedPixels.Min(x => x.Key.Y);
+                int offsetX = Math.Max(pixels.ChangedPixels.Min(x => x.Key.X), 0);
+                int offsetY = Math.Max(pixels.ChangedPixels.Min(x => x.Key.Y), 0);
                 Offset = new Thickness(offsetX, offsetY, 0, 0);
                 Offset = new Thickness(offsetX, offsetY, 0, 0);
             }
             }
         }
         }
@@ -581,4 +581,4 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
     }
     }
-}
+}

+ 4 - 4
PixiEditor/Models/Processes/ProcessHelper.cs

@@ -15,12 +15,12 @@ namespace PixiEditor.Models.Processes
                 proc.StartInfo.UseShellExecute = true;
                 proc.StartInfo.UseShellExecute = true;
                 proc.Start();
                 proc.Start();
             }
             }
-            catch (Win32Exception ex)
+            catch (Win32Exception)
             {
             {
-                throw ex;
+                throw;
             }
             }
-
+
             return proc;
             return proc;
         }
         }
     }
     }
-}
+}

+ 47 - 31
PixiEditor/Models/Tools/ShapeTool.cs

@@ -24,51 +24,67 @@ namespace PixiEditor.Models.Tools
         protected IEnumerable<Coordinates> GetThickShape(IEnumerable<Coordinates> shape, int thickness)
         protected IEnumerable<Coordinates> GetThickShape(IEnumerable<Coordinates> shape, int thickness)
         {
         {
             List<Coordinates> output = new List<Coordinates>();
             List<Coordinates> output = new List<Coordinates>();
-            foreach (Coordinates item in shape)
-            {
+            foreach (Coordinates item in shape)
+            {
                 output.AddRange(
                 output.AddRange(
                     CoordinatesCalculator.RectangleToCoordinates(
                     CoordinatesCalculator.RectangleToCoordinates(
-                        CoordinatesCalculator.CalculateThicknessCenter(item, thickness)));
-            }
-
+                        CoordinatesCalculator.CalculateThicknessCenter(item, thickness)));
+            }
+
             return output.Distinct();
             return output.Distinct();
         }
         }
-
-        protected DoubleCords CalculateCoordinatesForShapeRotation(
+
+        public static DoubleCords CalculateCoordinatesForShapeRotation(
             Coordinates startingCords,
             Coordinates startingCords,
             Coordinates secondCoordinates)
             Coordinates secondCoordinates)
         {
         {
             Coordinates currentCoordinates = secondCoordinates;
             Coordinates currentCoordinates = secondCoordinates;
 
 
-            if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
-            {
-                return new DoubleCords(
+            if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
+            {
+                return new DoubleCords(
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y),
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y),
-                    new Coordinates(startingCords.X, startingCords.Y));
-            }
-
-            if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
-            {
-                return new DoubleCords(
+                    new Coordinates(startingCords.X, startingCords.Y));
+            }
+
+            if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
+            {
+                return new DoubleCords(
                     new Coordinates(startingCords.X, startingCords.Y),
                     new Coordinates(startingCords.X, startingCords.Y),
-                    new Coordinates(currentCoordinates.X, currentCoordinates.Y));
-            }
-
-            if (startingCords.Y > currentCoordinates.Y)
-            {
-                return new DoubleCords(
+                    new Coordinates(currentCoordinates.X, currentCoordinates.Y));
+            }
+
+            if (startingCords.Y > currentCoordinates.Y)
+            {
+                return new DoubleCords(
                     new Coordinates(startingCords.X, currentCoordinates.Y),
                     new Coordinates(startingCords.X, currentCoordinates.Y),
-                    new Coordinates(currentCoordinates.X, startingCords.Y));
-            }
-
-            if (startingCords.X > currentCoordinates.X && startingCords.Y <= currentCoordinates.Y)
-            {
-                return new DoubleCords(
+                    new Coordinates(currentCoordinates.X, startingCords.Y));
+            }
+
+            if (startingCords.X > currentCoordinates.X && startingCords.Y <= currentCoordinates.Y)
+            {
+                return new DoubleCords(
                     new Coordinates(currentCoordinates.X, startingCords.Y),
                     new Coordinates(currentCoordinates.X, startingCords.Y),
-                    new Coordinates(startingCords.X, currentCoordinates.Y));
-            }
-
+                    new Coordinates(startingCords.X, currentCoordinates.Y));
+            }
+
             return new DoubleCords(startingCords, secondCoordinates);
             return new DoubleCords(startingCords, secondCoordinates);
         }
         }
+
+        // TODO: Add cache for lines 31, 32 (hopefully it would speed up calculation)
+        public abstract override LayerChange[] Use(Layer layer, Coordinates[] coordinates, Color color);
+
+        protected IEnumerable<Coordinates> GetThickShape(IEnumerable<Coordinates> shape, int thickness)
+        {
+            List<Coordinates> output = new List<Coordinates>();
+            foreach (Coordinates item in shape)
+            {
+                output.AddRange(
+                    CoordinatesCalculator.RectangleToCoordinates(
+                        CoordinatesCalculator.CalculateThicknessCenter(item, thickness)));
+            }
+
+            return output.Distinct();
+        }
     }
     }
 }
 }

+ 6 - 14
PixiEditor/Models/Tools/Tool.cs

@@ -1,6 +1,8 @@
 using System;
 using System;
+using System.Text;
 using System.Windows.Input;
 using System.Windows.Input;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
+using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools.ToolSettings;
 using PixiEditor.Models.Tools.ToolSettings;
@@ -10,24 +12,14 @@ namespace PixiEditor.Models.Tools
 {
 {
     public abstract class Tool : NotifyableObject
     public abstract class Tool : NotifyableObject
     {
     {
-        protected string name;
         private bool isActive;
         private bool isActive;
         private string actionDisplay = string.Empty;
         private string actionDisplay = string.Empty;
 
 
-        public string ToolName
-        {
-            get
-            {
-                if (string.IsNullOrWhiteSpace(name))
-                {
-                    return GetType().Name.Replace("Tool", string.Empty);
-                }
-
-                return name;
-            }
-        }
+        public virtual string ToolName => GetType().Name.Replace("Tool", string.Empty);
+
+        public virtual string DisplayName => ToolName.AddSpacesBeforeUppercaseLetters();
 
 
-        public string ImagePath => $"/Images/{ToolName}Image.png";
+        public virtual string ImagePath => $"/Images/{ToolName}Image.png";
 
 
         public bool HideHighlight { get; set; } = false;
         public bool HideHighlight { get; set; } = false;
 
 

+ 1 - 0
PixiEditor/Models/Tools/ToolSettings/Toolbars/SelectToolToolbar.cs

@@ -8,6 +8,7 @@ namespace PixiEditor.Models.Tools.ToolSettings.Toolbars
         public SelectToolToolbar()
         public SelectToolToolbar()
         {
         {
             Settings.Add(new EnumSetting<SelectionType>("SelectMode", "Selection type"));
             Settings.Add(new EnumSetting<SelectionType>("SelectMode", "Selection type"));
+            Settings.Add(new EnumSetting<SelectionShape>("SelectShape", "Selection shape"));
         }
         }
     }
     }
 }
 }

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