Browse Source

Merge pull request #94 from PixiEditor/master

Release 0.1.3.6, New icons, added auto updater compatibility and consent.
Krzysztof Krysiński 4 years ago
parent
commit
04e8752f3d
87 changed files with 2835 additions and 2173 deletions
  1. 2 1
      Custom.ruleset
  2. 37 7
      PixiEditor.UpdateModule/UpdateChecker.cs
  3. 20 2
      PixiEditor.UpdateModule/UpdateDownloader.cs
  4. 2 0
      PixiEditor/App.xaml
  5. 18 0
      PixiEditor/Helpers/AssemblyHelper.cs
  6. BIN
      PixiEditor/Images/BrightnessImage.png
  7. BIN
      PixiEditor/Images/BucketImage.png
  8. BIN
      PixiEditor/Images/CircleImage.png
  9. BIN
      PixiEditor/Images/ColorPickerImage.png
  10. BIN
      PixiEditor/Images/EraserImage.png
  11. BIN
      PixiEditor/Images/Eye-off.png
  12. BIN
      PixiEditor/Images/Eye.png
  13. BIN
      PixiEditor/Images/LineImage.png
  14. BIN
      PixiEditor/Images/MoveImage.png
  15. BIN
      PixiEditor/Images/MoveViewportImage.png
  16. BIN
      PixiEditor/Images/PenImage.png
  17. BIN
      PixiEditor/Images/RectangleImage.png
  18. BIN
      PixiEditor/Images/SelectImage.png
  19. BIN
      PixiEditor/Images/ZoomImage.png
  20. 1 1
      PixiEditor/Models/Tools/Tools/ColorPickerTool.cs
  21. 4 3
      PixiEditor/Models/Tools/Tools/MoveTool.cs
  22. 2 2
      PixiEditor/Models/Tools/Tools/MoveViewportTool.cs
  23. 7 7
      PixiEditor/Models/Tools/Tools/SelectTool.cs
  24. 2 2
      PixiEditor/Models/Tools/Tools/ZoomTool.cs
  25. 4 0
      PixiEditor/PixiEditor.csproj
  26. 2 2
      PixiEditor/Properties/AssemblyInfo.cs
  27. 182 0
      PixiEditor/Styles/DarkScrollBarStyle.xaml
  28. 21 0
      PixiEditor/Styles/ImageCheckBoxStyle.xaml
  29. 61 61
      PixiEditor/ViewModels/FeedbackDialogViewModel.cs
  30. 165 164
      PixiEditor/ViewModels/ImportFilePopupViewModel.cs
  31. 38 38
      PixiEditor/ViewModels/MenuButtonViewModel.cs
  32. 33 35
      PixiEditor/ViewModels/NewFileMenuViewModel.cs
  33. 114 115
      PixiEditor/ViewModels/SaveFilePopupViewModel.cs
  34. 65 0
      PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs
  35. 88 0
      PixiEditor/ViewModels/SubViewModels/Main/ColorsViewModel.cs
  36. 67 0
      PixiEditor/ViewModels/SubViewModels/Main/DocumentViewModel.cs
  37. 183 0
      PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs
  38. 140 0
      PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs
  39. 90 0
      PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs
  40. 57 0
      PixiEditor/ViewModels/SubViewModels/Main/SelectionViewModel.cs
  41. 94 0
      PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs
  42. 93 0
      PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs
  43. 115 0
      PixiEditor/ViewModels/SubViewModels/Main/UpdateViewModel.cs
  44. 64 0
      PixiEditor/ViewModels/SubViewModels/Main/ViewportViewModel.cs
  45. 12 0
      PixiEditor/ViewModels/SubViewModels/SubViewModel.cs
  46. 4 10
      PixiEditor/ViewModels/ViewModelBase.cs
  47. 94 886
      PixiEditor/ViewModels/ViewModelMain.cs
  48. 0 0
      PixiEditor/Views/Dialogs/ConfirmationPopup.xaml
  49. 57 60
      PixiEditor/Views/Dialogs/ConfirmationPopup.xaml.cs
  50. 4 1
      PixiEditor/Views/Dialogs/ImportFilePopup.xaml
  51. 7 4
      PixiEditor/Views/Dialogs/ImportFilePopup.xaml.cs
  52. 3 0
      PixiEditor/Views/Dialogs/NewFilePopup.xaml
  53. 7 5
      PixiEditor/Views/Dialogs/NewFilePopup.xaml.cs
  54. 1 0
      PixiEditor/Views/Dialogs/PopupTemplate.xaml
  55. 1 1
      PixiEditor/Views/Dialogs/PopupTemplate.xaml.cs
  56. 4 0
      PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml
  57. 67 66
      PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml.cs
  58. 4 1
      PixiEditor/Views/Dialogs/ResizeDocumentPopup.xaml
  59. 9 6
      PixiEditor/Views/Dialogs/ResizeDocumentPopup.xaml.cs
  60. 3 0
      PixiEditor/Views/Dialogs/SaveFilePopup.xaml
  61. 9 6
      PixiEditor/Views/Dialogs/SaveFilePopup.xaml.cs
  62. 57 59
      PixiEditor/Views/MainWindow.xaml
  63. 60 10
      PixiEditor/Views/MainWindow.xaml.cs
  64. 9 9
      PixiEditor/Views/UserControls/AnchorPointPicker.xaml
  65. 45 49
      PixiEditor/Views/UserControls/AnchorPointPicker.xaml.cs
  66. 24 24
      PixiEditor/Views/UserControls/EditableTextBlock.xaml
  67. 100 98
      PixiEditor/Views/UserControls/EditableTextBlock.xaml.cs
  68. 18 20
      PixiEditor/Views/UserControls/LayerItem.xaml
  69. 59 46
      PixiEditor/Views/UserControls/LayerItem.xaml.cs
  70. 3 1
      PixiEditor/Views/UserControls/MainDrawingPanel.xaml
  71. 78 76
      PixiEditor/Views/UserControls/MainDrawingPanel.xaml.cs
  72. 3 0
      PixiEditor/Views/UserControls/MenuButton.xaml
  73. 45 43
      PixiEditor/Views/UserControls/MenuButton.xaml.cs
  74. 2 0
      PixiEditor/Views/UserControls/NumberInput.xaml
  75. 65 72
      PixiEditor/Views/UserControls/NumberInput.xaml.cs
  76. 8 7
      PixiEditor/Views/UserControls/Rotatebox.xaml
  77. 79 72
      PixiEditor/Views/UserControls/Rotatebox.xaml.cs
  78. 2 1
      PixiEditor/Views/UserControls/SizeInput.xaml
  79. 75 71
      PixiEditor/Views/UserControls/SizeInput.xaml.cs
  80. 0 0
      PixiEditor/Views/UserControls/SizePicker.xaml
  81. 10 7
      PixiEditor/Views/UserControls/SizePicker.xaml.cs
  82. 1 1
      PixiEditorTests/ModelsTests/ToolsTests/ZoomToolTests.cs
  83. 1 0
      PixiEditorTests/PixiEditorTests.csproj
  84. 14 1
      PixiEditorTests/UpdateModuleTests/UpdateCheckerTests.cs
  85. 19 20
      PixiEditorTests/ViewModelsTests/ViewModelMainTests.cs
  86. BIN
      Screenshot.png
  87. 1 0
      incompatible.json

+ 2 - 1
Custom.ruleset

@@ -6,8 +6,9 @@
   <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
     <Rule Id="SA0001" 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" />
-    <Rule Id="SA1310" Action="None" />
   </Rules>
 </RuleSet>

+ 37 - 7
PixiEditor.UpdateModule/UpdateChecker.cs

@@ -1,4 +1,6 @@
-using System.Globalization;
+using System;
+using System.Globalization;
+using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Text.Json;
@@ -9,6 +11,7 @@ namespace PixiEditor.UpdateModule
     public class UpdateChecker
     {
         private const string ReleaseApiUrl = "https://api.github.com/repos/PixiEditor/PixiEditor/releases/latest";
+        private const string IncompatibleFileApiUrl = "https://raw.githubusercontent.com/PixiEditor/PixiEditor/{0}/incompatible.json";
 
         public UpdateChecker(string currentVersionTag)
         {
@@ -17,14 +20,14 @@ namespace PixiEditor.UpdateModule
 
         public ReleaseInfo LatestReleaseInfo { get; private set; }
 
-        private string CurrentVersionTag { get; }
+        public string CurrentVersionTag { get; }
 
         /// <summary>
         ///     Compares version strings and returns true if newVer > originalVer.
         /// </summary>
-        /// <param name="originalVer" />
-        /// <param name="newVer"></param>
-        /// <returns></returns>
+        /// <param name="originalVer">Version to compare.</param>
+        /// <param name="newVer">Version to compare with.</param>
+        /// <returns>True if semantic version is higher.</returns>
         public static bool VersionBigger(string originalVer, string newVer)
         {
             if (!ParseVersionString(originalVer, out float ver1))
@@ -42,7 +45,7 @@ namespace PixiEditor.UpdateModule
 
         public async Task<bool> CheckUpdateAvailable()
         {
-            LatestReleaseInfo = await GetLatestReleaseInfo_Async();
+            LatestReleaseInfo = await GetLatestReleaseInfoAsync();
             return CheckUpdateAvailable(LatestReleaseInfo);
         }
 
@@ -51,7 +54,34 @@ namespace PixiEditor.UpdateModule
             return latestRelease.WasDataFetchSuccessful && VersionBigger(CurrentVersionTag, latestRelease.TagName);
         }
 
-        private static async Task<ReleaseInfo> GetLatestReleaseInfo_Async()
+        public bool IsUpdateCompatible(string[] incompatibleVersions)
+        {
+            return !incompatibleVersions.Select(x => x.Trim()).Contains(CurrentVersionTag.Trim());
+        }
+
+        public async Task<bool> IsUpdateCompatible()
+        {
+            string[] incompatibleVersions = await GetUpdateIncompatibleVersionsAsync(LatestReleaseInfo.TagName);
+            return IsUpdateCompatible(incompatibleVersions);
+        }
+
+        public async Task<string[]> GetUpdateIncompatibleVersionsAsync(string tag)
+        {
+            using (HttpClient client = new HttpClient())
+            {
+                client.DefaultRequestHeaders.Add("User-Agent", "PixiEditor");
+                HttpResponseMessage response = await client.GetAsync(string.Format(IncompatibleFileApiUrl, tag));
+                if (response.StatusCode == HttpStatusCode.OK)
+                {
+                    string content = await response.Content.ReadAsStringAsync();
+                    return JsonSerializer.Deserialize<string[]>(content);
+                }
+            }
+
+            return Array.Empty<string>();
+        }
+
+        private static async Task<ReleaseInfo> GetLatestReleaseInfoAsync()
         {
             using (HttpClient client = new HttpClient())
             {

+ 20 - 2
PixiEditor.UpdateModule/UpdateDownloader.cs

@@ -29,6 +29,24 @@ namespace PixiEditor.UpdateModule
             }
         }
 
+        public static async Task DownloadInstaller(ReleaseInfo info)
+        {
+            Asset matchingAsset = GetMatchingAsset(info, "application/x-msdownload");
+
+            using (HttpClient client = new HttpClient())
+            {
+                client.DefaultRequestHeaders.Add("User-Agent", "PixiEditor");
+                client.DefaultRequestHeaders.Add("Accept", "application/octet-stream");
+                var response = await client.GetAsync(matchingAsset.Url);
+                if (response.StatusCode == HttpStatusCode.OK)
+                {
+                    byte[] bytes = await response.Content.ReadAsByteArrayAsync();
+                    CreateTempDirectory();
+                    File.WriteAllBytes(Path.Join(DownloadLocation, $"update-{info.TagName}.exe"), bytes);
+                }
+            }
+        }
+
         public static void CreateTempDirectory()
         {
             if (!Directory.Exists(DownloadLocation))
@@ -37,10 +55,10 @@ namespace PixiEditor.UpdateModule
             }
         }
 
-        private static Asset GetMatchingAsset(ReleaseInfo release)
+        private static Asset GetMatchingAsset(ReleaseInfo release, string assetType = "zip")
         {
             string arch = IntPtr.Size == 8 ? "x64" : "x86";
-            return release.Assets.First(x => x.ContentType.Contains("zip")
+            return release.Assets.First(x => x.ContentType.Contains(assetType)
             && x.Name.Contains(arch));
         }
     }

+ 2 - 0
PixiEditor/App.xaml

@@ -12,6 +12,8 @@
                 <ResourceDictionary Source="Styles/ComboBoxDarkStyle.xaml" />
                 <ResourceDictionary Source="Styles/AnchorPointToggleButtonStyle.xaml" />
                 <ResourceDictionary Source="Styles/DockingManagerStyle.xaml" />
+                <ResourceDictionary Source="Styles/DarkScrollBarStyle.xaml" />
+                <ResourceDictionary Source="Styles/ImageCheckBoxStyle.xaml" />
             </ResourceDictionary.MergedDictionaries>
         </ResourceDictionary>
     </Application.Resources>

+ 18 - 0
PixiEditor/Helpers/AssemblyHelper.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using System.Text;
+
+namespace PixiEditor.Helpers
+{
+    public static class AssemblyHelper
+    {
+        public static string GetCurrentAssemblyVersion()
+        {
+            var assembly = Assembly.GetExecutingAssembly();
+            FileVersionInfo info = FileVersionInfo.GetVersionInfo(assembly.Location);
+            return info.FileVersion;
+        }
+    }
+}

BIN
PixiEditor/Images/BrightnessImage.png


BIN
PixiEditor/Images/BucketImage.png


BIN
PixiEditor/Images/CircleImage.png


BIN
PixiEditor/Images/ColorPickerImage.png


BIN
PixiEditor/Images/EraserImage.png


BIN
PixiEditor/Images/Eye-off.png


BIN
PixiEditor/Images/Eye.png


BIN
PixiEditor/Images/LineImage.png


BIN
PixiEditor/Images/MoveImage.png


BIN
PixiEditor/Images/MoveViewportImage.png


BIN
PixiEditor/Images/PenImage.png


BIN
PixiEditor/Images/RectangleImage.png


BIN
PixiEditor/Images/SelectImage.png


BIN
PixiEditor/Images/ZoomImage.png


+ 1 - 1
PixiEditor/Models/Tools/Tools/ColorPickerTool.cs

@@ -17,7 +17,7 @@ namespace PixiEditor.Models.Tools.Tools
 
         public override void Use(Coordinates[] coordinates)
         {
-            ViewModelMain.Current.PrimaryColor = GetColorUnderMouse();
+            ViewModelMain.Current.ColorsSubViewModel.PrimaryColor = GetColorUnderMouse();
         }
 
         public Color GetColorUnderMouse()

+ 4 - 3
PixiEditor/Models/Tools/Tools/MoveTool.cs

@@ -88,9 +88,10 @@ namespace PixiEditor.Models.Tools.Tools
                 ResetSelectionValues(start);
 
                 // Move offset if no selection
-                if (ViewModelMain.Current.ActiveSelection != null && ViewModelMain.Current.ActiveSelection.SelectedPoints.Count > 0)
+                if (ViewModelMain.Current.SelectionSubViewModel.ActiveSelection != null && 
+                    ViewModelMain.Current.SelectionSubViewModel.ActiveSelection.SelectedPoints.Count > 0)
                 {
-                    currentSelection = ViewModelMain.Current.ActiveSelection.SelectedPoints.ToArray();
+                    currentSelection = ViewModelMain.Current.SelectionSubViewModel.ActiveSelection.SelectedPoints.ToArray();
                 }
                 else
                 {
@@ -143,7 +144,7 @@ namespace PixiEditor.Models.Tools.Tools
             currentSelection = TranslateSelection(end, out Coordinates[] previousSelection);
             if (updateViewModelSelection)
             {
-                ViewModelMain.Current.ActiveSelection.SetSelection(currentSelection, SelectionType.New);
+                ViewModelMain.Current.SelectionSubViewModel.ActiveSelection.SetSelection(currentSelection, SelectionType.New);
             }
 
             ClearSelectedPixels(layer, previousSelection);

+ 2 - 2
PixiEditor/Models/Tools/Tools/MoveViewportTool.cs

@@ -31,7 +31,7 @@ namespace PixiEditor.Models.Tools.Tools
             if (e.LeftButton == MouseButtonState.Pressed || e.MiddleButton == MouseButtonState.Pressed)
             {
                 var point = MousePositionConverter.GetCursorPosition();
-                ViewModelMain.Current.ViewportPosition = new System.Windows.Point(
+                ViewModelMain.Current.ViewportSubViewModel.ViewportPosition = new System.Windows.Point(
                     point.X - clickPoint.X,
                     point.Y - clickPoint.Y);
             }
@@ -41,7 +41,7 @@ namespace PixiEditor.Models.Tools.Tools
         {
             if (e.MiddleButton == MouseButtonState.Pressed)
             {
-                ViewModelMain.Current.SetActiveTool(ViewModelMain.Current.LastActionTool);
+                ViewModelMain.Current.ToolsSubViewModel.SetActiveTool(ViewModelMain.Current.ToolsSubViewModel.LastActionTool);
             }
         }
 

+ 7 - 7
PixiEditor/Models/Tools/Tools/SelectTool.cs

@@ -33,22 +33,22 @@ namespace PixiEditor.Models.Tools.Tools
             SelectionType = selectionType;
 
             oldSelection = null;
-            if (ViewModelMain.Current.ActiveSelection != null &&
-                ViewModelMain.Current.ActiveSelection.SelectedPoints != null)
+            if (ViewModelMain.Current.SelectionSubViewModel.ActiveSelection != null &&
+                ViewModelMain.Current.SelectionSubViewModel.ActiveSelection.SelectedPoints != null)
             {
-                oldSelection = ViewModelMain.Current.ActiveSelection;
+                oldSelection = ViewModelMain.Current.SelectionSubViewModel.ActiveSelection;
             }
         }
 
         public override void OnStoppedRecordingMouseUp(MouseEventArgs e)
         {
-            if (ViewModelMain.Current.ActiveSelection.SelectedPoints.Count() <= 1)
+            if (ViewModelMain.Current.SelectionSubViewModel.ActiveSelection.SelectedPoints.Count() <= 1)
             {
                 // If we have not selected multiple points, clear the selection
-                ViewModelMain.Current.ActiveSelection.Clear();
+                ViewModelMain.Current.SelectionSubViewModel.ActiveSelection.Clear();
             }
 
-            UndoManager.AddUndoChange(new Change("ActiveSelection", oldSelection, ViewModelMain.Current.ActiveSelection, "Select pixels"));
+            UndoManager.AddUndoChange(new Change("ActiveSelection", oldSelection, ViewModelMain.Current.SelectionSubViewModel.ActiveSelection, "Select pixels"));
         }
 
         public override void Use(Coordinates[] pixels)
@@ -85,7 +85,7 @@ namespace PixiEditor.Models.Tools.Tools
         private void Select(Coordinates[] pixels)
         {
             IEnumerable<Coordinates> selection = GetRectangleSelectionForPoints(pixels[^1], pixels[0]);
-            ViewModelMain.Current.ActiveSelection.SetSelection(selection, SelectionType);
+            ViewModelMain.Current.SelectionSubViewModel.ActiveSelection.SetSelection(selection, SelectionType);
         }
     }
 }

+ 2 - 2
PixiEditor/Models/Tools/Tools/ZoomTool.cs

@@ -28,7 +28,7 @@ namespace PixiEditor.Models.Tools.Tools
         public override void OnRecordingLeftMouseDown(MouseEventArgs e)
         {
             startingX = MousePositionConverter.GetCursorPosition().X;
-            ViewModelMain.Current.ZoomPercentage = 100; // This resest the value, so callback in MainDrawingPanel can fire again later
+            ViewModelMain.Current.ViewportSubViewModel.ZoomPercentage = 100; // This resest the value, so callback in MainDrawingPanel can fire again later
         }
 
         public override void OnMouseMove(MouseEventArgs e)
@@ -61,7 +61,7 @@ namespace PixiEditor.Models.Tools.Tools
 
         public void Zoom(double percentage)
         {
-            ViewModelMain.Current.ZoomPercentage = percentage;
+            ViewModelMain.Current.ViewportSubViewModel.ZoomPercentage = percentage;
         }
 
         public override void Use(Coordinates[] pixels)

+ 4 - 0
PixiEditor/PixiEditor.csproj

@@ -28,6 +28,8 @@
 
   <ItemGroup>
     <None Remove="Images\AnchorDot.png" />
+    <None Remove="Images\Eye-off.png" />
+    <None Remove="Images\Eye.png" />
     <None Remove="Images\MoveImage.png" />
     <None Remove="Images\MoveViewportImage.png" />
     <None Remove="Images\PixiEditorLogo.png" />
@@ -61,6 +63,8 @@
     <Resource Include="Images\CircleImage.png" />
     <Resource Include="Images\EraserImage.png" />
     <Resource Include="Images\BrightnessImage.png" />
+    <Resource Include="Images\Eye-off.png" />
+    <Resource Include="Images\Eye.png" />
     <Resource Include="Images\LineImage.png" />
     <Resource Include="Images\MoveImage.png" />
     <Resource Include="Images\MoveViewportImage.png" />

+ 2 - 2
PixiEditor/Properties/AssemblyInfo.cs

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

+ 182 - 0
PixiEditor/Styles/DarkScrollBarStyle.xaml

@@ -0,0 +1,182 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                    xmlns:local="clr-namespace:PixiEditor.Styles">
+    <SolidColorBrush x:Key="StandardBorderBrush" Color="#888" />
+    <SolidColorBrush x:Key="StandardBackgroundBrush" Color="Black" />
+    <SolidColorBrush x:Key="HoverBorderBrush" Color="#DDD" />
+    <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="Gray" />
+    <SolidColorBrush x:Key="SelectedForegroundBrush" Color="White" />
+    <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
+    <SolidColorBrush x:Key="GlyphBrush" Color="#444" />
+    <SolidColorBrush x:Key="NormalBrush" Color="#888" />
+    <SolidColorBrush x:Key="NormalBorderBrush" Color="#888" />
+    <SolidColorBrush x:Key="HorizontalNormalBrush" Color="#FF686868" />
+    <SolidColorBrush x:Key="HorizontalNormalBorderBrush" Color="#888" />
+
+    <LinearGradientBrush x:Key="ListBoxBackgroundBrush" StartPoint="0,0" EndPoint="1,0.001">
+        <GradientBrush.GradientStops>
+            <GradientStopCollection>
+                <GradientStop Color="White" Offset="0.0" />
+                <GradientStop Color="White" Offset="0.6" />
+                <GradientStop Color="#DDDDDD" Offset="1.2"/>
+            </GradientStopCollection>
+        </GradientBrush.GradientStops>
+    </LinearGradientBrush>
+    <LinearGradientBrush x:Key="StandardBrush" StartPoint="0,0" EndPoint="0,1">
+        <GradientBrush.GradientStops>
+            <GradientStopCollection>
+                <GradientStop Color="#FFF" Offset="0.0"/>
+                <GradientStop Color="#CCC" Offset="1.0"/>
+            </GradientStopCollection>
+        </GradientBrush.GradientStops>
+    </LinearGradientBrush>
+    <LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
+        <GradientBrush.GradientStops>
+            <GradientStopCollection>
+                <GradientStop Color="#BBB" Offset="0.0"/>
+                <GradientStop Color="#EEE" Offset="0.1"/>
+                <GradientStop Color="#EEE" Offset="0.9"/>
+                <GradientStop Color="#FFF" Offset="1.0"/>
+            </GradientStopCollection>
+        </GradientBrush.GradientStops>
+    </LinearGradientBrush>
+
+    <Style x:Key="ScrollBarLineButton" TargetType="{x:Type RepeatButton}">
+        <Setter Property="Visibility" Value="Hidden"/>
+        <Setter Property="SnapsToDevicePixels" Value="True"/>
+        <Setter Property="OverridesDefaultStyle" Value="true"/>
+        <Setter Property="Focusable" Value="false"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type RepeatButton}">
+                    <Border Name="Border" Margin="1" CornerRadius="2" Background="{StaticResource NormalBrush}" BorderBrush="{StaticResource NormalBorderBrush}" BorderThickness="1">
+                        <Path HorizontalAlignment="Center" VerticalAlignment="Center" Fill="{StaticResource GlyphBrush}" Data="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" />
+                    </Border>
+                    <ControlTemplate.Triggers>
+                        <Trigger Property="IsPressed" Value="true">
+                            <Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
+                        </Trigger>
+                        <Trigger Property="IsEnabled" Value="false">
+                            <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
+                        </Trigger>
+                    </ControlTemplate.Triggers>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+    <Style x:Key="ScrollBarPageButton" TargetType="{x:Type RepeatButton}">
+        <Setter Property="Visibility" Value="Hidden"/>
+        <Setter Property="SnapsToDevicePixels" Value="True"/>
+        <Setter Property="OverridesDefaultStyle" Value="true"/>
+        <Setter Property="IsTabStop" Value="false"/>
+        <Setter Property="Focusable" Value="false"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type RepeatButton}">
+                    <Border Background="Black" />
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
+        <Setter Property="SnapsToDevicePixels" Value="True"/>
+        <Setter Property="OverridesDefaultStyle" Value="true"/>
+        <Setter Property="IsTabStop" Value="false"/>
+        <Setter Property="Focusable" Value="false"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type Thumb}">
+                    <Border CornerRadius="4" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0"  Width="8" Margin="8,0,-2,0"/>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
+        <Grid>
+            <Grid.RowDefinitions>
+                <RowDefinition MaxHeight="0"/>
+                <RowDefinition Height="0.00001*"/>
+                <RowDefinition MaxHeight="0"/>
+            </Grid.RowDefinitions>
+            <Border Grid.RowSpan="3" CornerRadius="2" Background="Transparent" />
+            <RepeatButton Grid.Row="0" Style="{StaticResource ScrollBarLineButton}" Height="18" Command="ScrollBar.LineUpCommand" Content="M 0 4 L 8 4 L 4 0 Z" />
+            <Track Name="PART_Track" Grid.Row="1" IsDirectionReversed="true">
+                <Track.DecreaseRepeatButton>
+                    <RepeatButton Style="{StaticResource ScrollBarPageButton}" Command="ScrollBar.PageUpCommand" />
+                </Track.DecreaseRepeatButton>
+                <Track.Thumb>
+                    <Thumb Style="{StaticResource ScrollBarThumb}" Margin="1,0,1,0" Background="{StaticResource HorizontalNormalBrush}" BorderBrush="{StaticResource HorizontalNormalBorderBrush}" />
+                </Track.Thumb>
+                <Track.IncreaseRepeatButton>
+                    <RepeatButton Style="{StaticResource ScrollBarPageButton}" Command="ScrollBar.PageDownCommand" />
+                </Track.IncreaseRepeatButton>
+            </Track>
+            <RepeatButton Grid.Row="3" Style="{StaticResource ScrollBarLineButton}" Height="18" Command="ScrollBar.LineDownCommand" Content="M 0 0 L 4 4 L 8 0 Z"/>
+        </Grid>
+    </ControlTemplate>
+    <ControlTemplate x:Key="HorizontalScrollBar" TargetType="{x:Type ScrollBar}">
+        <Grid>
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition MaxWidth="18"/>
+                <ColumnDefinition Width="0.00001*"/>
+                <ColumnDefinition MaxWidth="18"/>
+            </Grid.ColumnDefinitions>
+            <Border Grid.ColumnSpan="3" CornerRadius="2" Background="#F0F0F0"/>
+            <RepeatButton Grid.Column="0"  Style="{StaticResource ScrollBarLineButton}" Width="18" Command="ScrollBar.LineLeftCommand" Content="M 4 0 L 4 8 L 0 4 Z" />
+            <Track Name="PART_Track" Grid.Column="1" IsDirectionReversed="False">
+                <Track.DecreaseRepeatButton>
+                    <RepeatButton Style="{StaticResource ScrollBarPageButton}" Command="ScrollBar.PageLeftCommand" />
+                </Track.DecreaseRepeatButton>
+                <Track.Thumb>
+                    <Thumb Style="{StaticResource ScrollBarThumb}" Margin="0,1,0,1" Background="{StaticResource NormalBrush}" BorderBrush="{StaticResource NormalBorderBrush}" />
+                </Track.Thumb>
+                <Track.IncreaseRepeatButton>
+                    <RepeatButton Style="{StaticResource ScrollBarPageButton}" Command="ScrollBar.PageRightCommand" />
+                </Track.IncreaseRepeatButton>
+            </Track>
+            <RepeatButton Grid.Column="3" Style="{StaticResource ScrollBarLineButton}" Width="18" Command="ScrollBar.LineRightCommand" Content="M 0 0 L 4 4 L 0 8 Z"/>
+        </Grid>
+    </ControlTemplate>
+    <Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
+        <Setter Property="SnapsToDevicePixels" Value="True"/>
+        <Setter Property="OverridesDefaultStyle" Value="true"/>
+        <Style.Triggers>
+            <Trigger Property="Orientation" Value="Horizontal">
+                <Setter Property="Width" Value="Auto"/>
+                <Setter Property="Height" Value="18" />
+                <Setter Property="Template" Value="{StaticResource HorizontalScrollBar}" />
+            </Trigger>
+            <Trigger Property="Orientation" Value="Vertical">
+                <Setter Property="Width" Value="18"/>
+                <Setter Property="Height" Value="Auto" />
+                <Setter Property="Template" Value="{StaticResource VerticalScrollBar}" />
+            </Trigger>
+        </Style.Triggers>
+    </Style>
+
+    <Style x:Key="FavsScrollViewer" TargetType="{x:Type ScrollViewer}">
+        <Setter Property="OverridesDefaultStyle" Value="True"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type ScrollViewer}">
+                    <Grid>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="Auto"/>
+                            <ColumnDefinition/>
+                        </Grid.ColumnDefinitions>
+                        <Grid.RowDefinitions>
+                            <RowDefinition/>
+                            <RowDefinition Height="Auto"/>
+                        </Grid.RowDefinitions>
+                        <ScrollContentPresenter Grid.Column="1"/>
+                        <ScrollBar Name="PART_VerticalScrollBar" Value="{TemplateBinding VerticalOffset}" Maximum="{TemplateBinding ScrollableHeight}" ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
+                        <ScrollBar Name="PART_HorizontalScrollBar" Orientation="Horizontal" Grid.Row="1" Grid.Column="1" Value="{TemplateBinding HorizontalOffset}" Maximum="{TemplateBinding ScrollableWidth}" ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
+                    </Grid>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+</ResourceDictionary>

+ 21 - 0
PixiEditor/Styles/ImageCheckBoxStyle.xaml

@@ -0,0 +1,21 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                    xmlns:local="clr-namespace:PixiEditor.Styles">
+    <Style TargetType="{x:Type CheckBox}" x:Key="ImageCheckBox">
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type CheckBox}">
+                    <StackPanel Orientation="Horizontal">
+                        <Image Cursor="Hand" x:Name="checkboxImage" Source="../Images/Eye-off.png" Width="36"/>
+                        <ContentPresenter/>
+                    </StackPanel>
+                    <ControlTemplate.Triggers>
+                        <Trigger Property="IsChecked" Value="True">
+                            <Setter TargetName="checkboxImage" Property="Source" Value="../Images/Eye.png"/>
+                        </Trigger>
+                    </ControlTemplate.Triggers>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+</ResourceDictionary>

+ 61 - 61
PixiEditor/ViewModels/FeedbackDialogViewModel.cs

@@ -1,64 +1,64 @@
-using System.Windows;
-using PixiEditor.Helpers;
-
-namespace PixiEditor.ViewModels
-{
-    internal class FeedbackDialogViewModel : ViewModelBase
-    {
-        private string emailBody;
+using System.Windows;
+using PixiEditor.Helpers;
 
-        private string mailFrom;
-
-        public FeedbackDialogViewModel()
-        {
-            CloseButtonCommand = new RelayCommand(CloseWindow);
-            SendButtonCommand = new RelayCommand(Send, CanSend);
-        }
-
-        public RelayCommand CloseButtonCommand { get; set; }
+namespace PixiEditor.ViewModels
+{
+    internal class FeedbackDialogViewModel : ViewModelBase
+    {
+        private string _emailBody;
 
-        public RelayCommand SendButtonCommand { get; set; }
-
-        public string MailFrom
-        {
-            get => mailFrom;
-            set
-            {
-                if (mailFrom != value)
-                {
-                    mailFrom = value;
-                    RaisePropertyChanged("MailFrom");
-                }
-            }
-        }
-
-        public string EmailBody
-        {
-            get => emailBody;
-            set
-            {
-                if (emailBody != value)
-                {
-                    emailBody = value;
-                    RaisePropertyChanged("EmailBody");
-                }
-            }
-        }
-
-        private void CloseWindow(object parameter)
-        {
-            ((Window)parameter).DialogResult = false;
-            CloseButton(parameter);
-        }
-
-        private void Send(object parameter)
-        {
-            CloseButton(parameter);
-        }
-
-        private bool CanSend(object property)
-        {
-            return !string.IsNullOrWhiteSpace(MailFrom);
-        }
-    }
+
+        private string _mailFrom;
+
+        public FeedbackDialogViewModel()
+        {
+            CloseButtonCommand = new RelayCommand(CloseWindow);
+            SendButtonCommand = new RelayCommand(Send, CanSend);
+        }
+
+        public RelayCommand CloseButtonCommand { get; set; }
+        public RelayCommand SendButtonCommand { get; set; }
+
+        public string MailFrom
+        {
+            get => _mailFrom;
+            set
+            {
+                if (_mailFrom != value)
+                {
+                    _mailFrom = value;
+                    RaisePropertyChanged("MailFrom");
+                }
+            }
+        }
+
+        public string EmailBody
+        {
+            get => _emailBody;
+            set
+            {
+                if (_emailBody != value)
+                {
+                    _emailBody = value;
+                    RaisePropertyChanged("EmailBody");
+                }
+            }
+        }
+
+        private void CloseWindow(object parameter)
+        {
+            ((Window) parameter).DialogResult = false;
+            CloseButton(parameter);
+        }
+
+        private void Send(object parameter)
+        {
+            CloseButton(parameter);
+        }
+
+        private bool CanSend(object property)
+        {
+            return !string.IsNullOrWhiteSpace(MailFrom);
+        }
+    }
 }

+ 165 - 164
PixiEditor/ViewModels/ImportFilePopupViewModel.cs

@@ -1,165 +1,166 @@
-using System;
-using System.IO;
-using System.Windows;
-using System.Windows.Media.Imaging;
-using Microsoft.Win32;
-using PixiEditor.Helpers;
-
-namespace PixiEditor.ViewModels
-{
-    internal class ImportFilePopupViewModel : ViewModelBase
-    {
-        private string filePath;
-
-        private int importHeight = 16;
-
-        private int importWidth = 16;
-
-        private string pathButtonBorder = "#f08080";
-
-        private bool pathIsCorrect;
-
-        public ImportFilePopupViewModel()
-        {
-            CloseButtonCommand = new RelayCommand(CloseWindow);
-            DragMoveCommand = new RelayCommand(MoveWindow);
-            ChoosePathCommand = new RelayCommand(ChoosePath);
-            OkCommand = new RelayCommand(OkButton, CanClickOk);
-        }
-
-        public RelayCommand CloseButtonCommand { get; set; }
-
-        public RelayCommand DragMoveCommand { get; set; }
-
-        public RelayCommand ChoosePathCommand { get; set; }
-
-        public RelayCommand OkCommand { get; set; }
-
-        public string PathButtonBorder
-        {
-            get => pathButtonBorder;
-            set
-            {
-                if (pathButtonBorder != value)
-                {
-                    pathButtonBorder = value;
-                    RaisePropertyChanged("PathButtonBorder");
-                }
-            }
-        }
-
-        public bool PathIsCorrect
-        {
-            get => pathIsCorrect;
-            set
-            {
-                if (pathIsCorrect != value)
-                {
-                    pathIsCorrect = value;
-                    RaisePropertyChanged("PathIsCorrect");
-                }
-            }
-        }
-
-        public string FilePath
-        {
-            get => filePath;
-            set
-            {
-                if (filePath != value)
-                {
-                    filePath = value;
-                    CheckForPath(value);
-                    RaisePropertyChanged("FilePath");
-                }
-            }
-        }
-
-        public int ImportWidth
-        {
-            get => importWidth;
-            set
-            {
-                if (importWidth != value)
-                {
-                    importWidth = value;
-                    RaisePropertyChanged("ImportWidth");
-                }
-            }
-        }
-
-        public int ImportHeight
-        {
-            get => importHeight;
-            set
-            {
-                if (importHeight != value)
-                {
-                    importHeight = value;
-                    RaisePropertyChanged("ImportHeight");
-                }
-            }
-        }
-
-        /// <summary>
-        ///     Command that handles Path choosing to save file.
-        /// </summary>
-        /// <param name="parameter"></param>
-        private void ChoosePath(object parameter)
-        {
-            OpenFileDialog path = new OpenFileDialog
-            {
-                Title = "Import path",
-                CheckPathExists = true,
-                Filter = "Image Files|*.png;*.jpeg;*.jpg"
-            };
-            if (path.ShowDialog() == true)
-            {
-                if (string.IsNullOrEmpty(path.FileName) == false)
-                {
-                    CheckForPath(path.FileName);
-                }
-                else
-                {
-                    PathButtonBorder = "#f08080";
-                    PathIsCorrect = false;
-                }
-            }
-        }
-
-        private void CheckForPath(string path)
-        {
-            if (File.Exists(path) && (path.EndsWith(".png") || path.EndsWith(".jpeg") || path.EndsWith(".jpg")))
-            {
-                PathButtonBorder = "#b8f080";
-                PathIsCorrect = true;
-                filePath = path;
-                BitmapImage bitmap = new BitmapImage(new Uri(path));
-                ImportHeight = bitmap.PixelHeight;
-                ImportWidth = bitmap.PixelWidth;
-            }
-        }
-
-        private void CloseWindow(object parameter)
-        {
-            ((Window)parameter).DialogResult = false;
-            CloseButton(parameter);
-        }
-
-        private void MoveWindow(object parameter)
-        {
-            DragMove(parameter);
-        }
-
-        private void OkButton(object parameter)
-        {
-            ((Window)parameter).DialogResult = true;
-            CloseButton(parameter);
-        }
-
-        private bool CanClickOk(object property)
-        {
-            return PathIsCorrect;
-        }
-    }
+using System;
+using System.IO;
+using System.Windows;
+using System.Windows.Media.Imaging;
+using Microsoft.Win32;
+using PixiEditor.Helpers;
+
+namespace PixiEditor.ViewModels
+{
+    internal class ImportFilePopupViewModel : ViewModelBase
+    {
+        private string _filePath;
+
+
+        private int _importHeight = 16;
+
+
+        private int _importWidth = 16;
+
+
+        private string _pathButtonBorder = "#f08080";
+
+
+        private bool _pathIsCorrect;
+
+        public ImportFilePopupViewModel()
+        {
+            CloseButtonCommand = new RelayCommand(CloseWindow);
+            DragMoveCommand = new RelayCommand(MoveWindow);
+            ChoosePathCommand = new RelayCommand(ChoosePath);
+            OkCommand = new RelayCommand(OkButton, CanClickOk);
+        }
+
+        public RelayCommand CloseButtonCommand { get; set; }
+        public RelayCommand DragMoveCommand { get; set; }
+        public RelayCommand ChoosePathCommand { get; set; }
+        public RelayCommand OkCommand { get; set; }
+
+        public string PathButtonBorder
+        {
+            get => _pathButtonBorder;
+            set
+            {
+                if (_pathButtonBorder != value)
+                {
+                    _pathButtonBorder = value;
+                    RaisePropertyChanged("PathButtonBorder");
+                }
+            }
+        }
+
+        public bool PathIsCorrect
+        {
+            get => _pathIsCorrect;
+            set
+            {
+                if (_pathIsCorrect != value)
+                {
+                    _pathIsCorrect = value;
+                    RaisePropertyChanged("PathIsCorrect");
+                }
+            }
+        }
+
+        public string FilePath
+        {
+            get => _filePath;
+            set
+            {
+                if (_filePath != value)
+                {
+                    _filePath = value;
+                    CheckForPath(value);
+                    RaisePropertyChanged("FilePath");
+                }
+            }
+        }
+
+        public int ImportWidth
+        {
+            get => _importWidth;
+            set
+            {
+                if (_importWidth != value)
+                {
+                    _importWidth = value;
+                    RaisePropertyChanged("ImportWidth");
+                }
+            }
+        }
+
+        public int ImportHeight
+        {
+            get => _importHeight;
+            set
+            {
+                if (_importHeight != value)
+                {
+                    _importHeight = value;
+                    RaisePropertyChanged("ImportHeight");
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Command that handles Path choosing to save file
+        /// </summary>
+        /// <param name="parameter"></param>
+        private void ChoosePath(object parameter)
+        {
+            OpenFileDialog path = new OpenFileDialog
+            {
+                Title = "Import path",
+                CheckPathExists = true,
+                Filter = "Image Files|*.png;*.jpeg;*.jpg"
+            };
+            if (path.ShowDialog() == true)
+            {
+                if (string.IsNullOrEmpty(path.FileName) == false)
+                {
+                    CheckForPath(path.FileName);
+                }
+                else
+                {
+                    PathButtonBorder = "#f08080";
+                    PathIsCorrect = false;
+                }
+            }
+        }
+
+        private void CheckForPath(string path)
+        {
+            if (File.Exists(path) && (path.EndsWith(".png") || path.EndsWith(".jpeg") || path.EndsWith(".jpg")))
+            {
+                PathButtonBorder = "#b8f080";
+                PathIsCorrect = true;
+                _filePath = path;
+                BitmapImage bitmap = new BitmapImage(new Uri(path));
+                ImportHeight = bitmap.PixelHeight;
+                ImportWidth = bitmap.PixelWidth;
+            }
+        }
+
+        private void CloseWindow(object parameter)
+        {
+            ((Window) parameter).DialogResult = false;
+            CloseButton(parameter);
+        }
+
+        private void MoveWindow(object parameter)
+        {
+            DragMove(parameter);
+        }
+
+        private void OkButton(object parameter)
+        {
+            ((Window) parameter).DialogResult = true;
+            CloseButton(parameter);
+        }
+
+        private bool CanClickOk(object property)
+        {
+            return PathIsCorrect;
+        }
+    }
 }

+ 38 - 38
PixiEditor/ViewModels/MenuButtonViewModel.cs

@@ -1,41 +1,41 @@
-using System.Windows;
-using PixiEditor.Helpers;
-
-namespace PixiEditor.ViewModels
-{
-    internal class MenuButtonViewModel : ViewModelBase
-    {
-        private Visibility listViewVisibility;
+using System.Windows;
+using PixiEditor.Helpers;
 
-        public MenuButtonViewModel()
-        {
-            OpenListViewCommand = new RelayCommand(OpenListView);
-            CloseListViewCommand = new RelayCommand(CloseListView);
-            ListViewVisibility = Visibility.Hidden;
-        }
-
-        public RelayCommand OpenListViewCommand { get; set; }
+namespace PixiEditor.ViewModels
+{
+    internal class MenuButtonViewModel : ViewModelBase
+    {
+        private Visibility listViewVisibility;
 
-        public RelayCommand CloseListViewCommand { get; set; }
-
-        public Visibility ListViewVisibility
-        {
-            get => listViewVisibility;
-            set
-            {
-                listViewVisibility = value;
-                RaisePropertyChanged("ListViewVisibility");
-            }
-        }
-
-        private void OpenListView(object parameter)
-        {
-            ListViewVisibility = ListViewVisibility == Visibility.Hidden ? Visibility.Visible : Visibility.Hidden;
-        }
-
-        private void CloseListView(object parameter)
-        {
-            ListViewVisibility = Visibility.Hidden;
-        }
-    }
+        public MenuButtonViewModel()
+        {
+            OpenListViewCommand = new RelayCommand(OpenListView);
+            CloseListViewCommand = new RelayCommand(CloseListView);
+            ListViewVisibility = Visibility.Hidden;
+        }
+
+        public RelayCommand OpenListViewCommand { get; set; }
+
+        public RelayCommand CloseListViewCommand { get; set; }
+
+        public Visibility ListViewVisibility
+        {
+            get => listViewVisibility;
+            set
+            {
+                listViewVisibility = value;
+                RaisePropertyChanged("ListViewVisibility");
+            }
+        }
+
+        private void OpenListView(object parameter)
+        {
+            ListViewVisibility = ListViewVisibility == Visibility.Hidden ? Visibility.Visible : Visibility.Hidden;
+        }
+
+        private void CloseListView(object parameter)
+        {
+            ListViewVisibility = Visibility.Hidden;
+        }
+    }
 }

+ 33 - 35
PixiEditor/ViewModels/NewFileMenuViewModel.cs

@@ -1,38 +1,36 @@
-using System.Windows;
-using PixiEditor.Helpers;
-
-namespace PixiEditor.ViewModels
-{
-    internal class NewFileMenuViewModel : ViewModelBase
-    {
-        public NewFileMenuViewModel()
-        {
-            OkCommand = new RelayCommand(OkButton);
-            CloseCommand = new RelayCommand(CloseWindow);
-            DragMoveCommand = new RelayCommand(MoveWindow);
-        }
-
-        public RelayCommand OkCommand { get; set; }
+using System.Windows;
+using PixiEditor.Helpers;
 
-        public RelayCommand CloseCommand { get; set; }
+namespace PixiEditor.ViewModels
+{
+    internal class NewFileMenuViewModel : ViewModelBase
+    {
+        public NewFileMenuViewModel()
+        {
+            OkCommand = new RelayCommand(OkButton);
+            CloseCommand = new RelayCommand(CloseWindow);
+            DragMoveCommand = new RelayCommand(MoveWindow);
+        }
 
-        public RelayCommand DragMoveCommand { get; set; }
-
-        private void OkButton(object parameter)
-        {
-            ((Window)parameter).DialogResult = true;
-            ((Window)parameter).Close();
-        }
-
-        private void CloseWindow(object parameter)
-        {
-            ((Window)parameter).DialogResult = false;
-            CloseButton(parameter);
-        }
-
-        private void MoveWindow(object parameter)
-        {
-            DragMove(parameter);
-        }
-    }
+        public RelayCommand OkCommand { get; set; }
+        public RelayCommand CloseCommand { get; set; }
+        public RelayCommand DragMoveCommand { get; set; }
+
+        private void OkButton(object parameter)
+        {
+            ((Window) parameter).DialogResult = true;
+            ((Window) parameter).Close();
+        }
+
+        private void CloseWindow(object parameter)
+        {
+            ((Window) parameter).DialogResult = false;
+            CloseButton(parameter);
+        }
+
+        private void MoveWindow(object parameter)
+        {
+            DragMove(parameter);
+        }
+    }
 }

+ 114 - 115
PixiEditor/ViewModels/SaveFilePopupViewModel.cs

@@ -1,121 +1,120 @@
-using System.Windows;
-using Microsoft.Win32;
-using PixiEditor.Helpers;
-
-namespace PixiEditor.ViewModels
-{
-    internal class SaveFilePopupViewModel : ViewModelBase
-    {
-        private string filePath;
+using System.Windows;
+using Microsoft.Win32;
+using PixiEditor.Helpers;
 
-        private string pathButtonBorder = "#f08080";
+namespace PixiEditor.ViewModels
+{
+    internal class SaveFilePopupViewModel : ViewModelBase
+    {
+        private string _filePath;
 
-        private bool pathIsCorrect;
-
-        public SaveFilePopupViewModel()
-        {
-            CloseButtonCommand = new RelayCommand(CloseWindow);
-            DragMoveCommand = new RelayCommand(MoveWindow);
-            ChoosePathCommand = new RelayCommand(ChoosePath);
-            OkCommand = new RelayCommand(OkButton, CanClickOk);
-        }
-
-        public RelayCommand CloseButtonCommand { get; set; }
 
-        public RelayCommand DragMoveCommand { get; set; }
+        private string _pathButtonBorder = "#f08080";
 
-        public RelayCommand ChoosePathCommand { get; set; }
 
-        public RelayCommand OkCommand { get; set; }
-
-        public string PathButtonBorder
-        {
-            get => pathButtonBorder;
-            set
-            {
-                if (pathButtonBorder != value)
-                {
-                    pathButtonBorder = value;
-                    RaisePropertyChanged("PathButtonBorder");
-                }
-            }
-        }
-
-        public bool PathIsCorrect
-        {
-            get => pathIsCorrect;
-            set
-            {
-                if (pathIsCorrect != value)
-                {
-                    pathIsCorrect = value;
-                    RaisePropertyChanged("PathIsCorrect");
-                }
-            }
-        }
-
-        public string FilePath
-        {
-            get => filePath;
-            set
-            {
-                if (filePath != value)
-                {
-                    filePath = value;
-                    RaisePropertyChanged("FilePath");
-                }
-            }
-        }
-
-        /// <summary>
-        ///     Command that handles Path choosing to save file.
-        /// </summary>
-        /// <param name="parameter"></param>
-        private void ChoosePath(object parameter)
-        {
-            SaveFileDialog path = new SaveFileDialog
-            {
-                Title = "Export path",
-                CheckPathExists = true,
-                DefaultExt = "PNG Image (.png) | *.png",
-                Filter = "PNG Image (.png) | *.png"
-            };
-            if (path.ShowDialog() == true)
-            {
-                if (string.IsNullOrEmpty(path.FileName) == false)
-                {
-                    PathButtonBorder = "#b8f080";
-                    PathIsCorrect = true;
-                    FilePath = path.FileName;
-                }
-                else
-                {
-                    PathButtonBorder = "#f08080";
-                    PathIsCorrect = false;
-                }
-            }
-        }
-
-        private void CloseWindow(object parameter)
-        {
-            ((Window)parameter).DialogResult = false;
-            CloseButton(parameter);
-        }
-
-        private void MoveWindow(object parameter)
-        {
-            DragMove(parameter);
-        }
-
-        private void OkButton(object parameter)
-        {
-            ((Window)parameter).DialogResult = true;
-            CloseButton(parameter);
-        }
-
-        private bool CanClickOk(object property)
-        {
-            return PathIsCorrect;
-        }
-    }
+        private bool _pathIsCorrect;
+
+        public SaveFilePopupViewModel()
+        {
+            CloseButtonCommand = new RelayCommand(CloseWindow);
+            DragMoveCommand = new RelayCommand(MoveWindow);
+            ChoosePathCommand = new RelayCommand(ChoosePath);
+            OkCommand = new RelayCommand(OkButton, CanClickOk);
+        }
+
+        public RelayCommand CloseButtonCommand { get; set; }
+        public RelayCommand DragMoveCommand { get; set; }
+        public RelayCommand ChoosePathCommand { get; set; }
+        public RelayCommand OkCommand { get; set; }
+
+        public string PathButtonBorder
+        {
+            get => _pathButtonBorder;
+            set
+            {
+                if (_pathButtonBorder != value)
+                {
+                    _pathButtonBorder = value;
+                    RaisePropertyChanged("PathButtonBorder");
+                }
+            }
+        }
+
+        public bool PathIsCorrect
+        {
+            get => _pathIsCorrect;
+            set
+            {
+                if (_pathIsCorrect != value)
+                {
+                    _pathIsCorrect = value;
+                    RaisePropertyChanged("PathIsCorrect");
+                }
+            }
+        }
+
+        public string FilePath
+        {
+            get => _filePath;
+            set
+            {
+                if (_filePath != value)
+                {
+                    _filePath = value;
+                    RaisePropertyChanged("FilePath");
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Command that handles Path choosing to save file
+        /// </summary>
+        /// <param name="parameter"></param>
+        private void ChoosePath(object parameter)
+        {
+            SaveFileDialog path = new SaveFileDialog
+            {
+                Title = "Export path",
+                CheckPathExists = true,
+                DefaultExt = "PNG Image (.png) | *.png",
+                Filter = "PNG Image (.png) | *.png"
+            };
+            if (path.ShowDialog() == true)
+            {
+                if (string.IsNullOrEmpty(path.FileName) == false)
+                {
+                    PathButtonBorder = "#b8f080";
+                    PathIsCorrect = true;
+                    FilePath = path.FileName;
+                }
+                else
+                {
+                    PathButtonBorder = "#f08080";
+                    PathIsCorrect = false;
+                }
+            }
+        }
+
+        private void CloseWindow(object parameter)
+        {
+            ((Window) parameter).DialogResult = false;
+            CloseButton(parameter);
+        }
+
+        private void MoveWindow(object parameter)
+        {
+            DragMove(parameter);
+        }
+
+        private void OkButton(object parameter)
+        {
+            ((Window) parameter).DialogResult = true;
+            CloseButton(parameter);
+        }
+
+        private bool CanClickOk(object property)
+        {
+            return PathIsCorrect;
+        }
+    }
 }

+ 65 - 0
PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Media;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class ClipboardViewModel : SubViewModel<ViewModelMain>
+    {
+        public RelayCommand CopyCommand { get; set; }
+
+        public RelayCommand DuplicateCommand { get; set; }
+
+        public RelayCommand CutCommand { get; set; }
+
+        public RelayCommand PasteCommand { get; set; }
+
+        public ClipboardViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            CopyCommand = new RelayCommand(Copy, Owner.SelectionSubViewModel.SelectionIsNotEmpty);
+            DuplicateCommand = new RelayCommand(Duplicate, Owner.SelectionSubViewModel.SelectionIsNotEmpty);
+            CutCommand = new RelayCommand(Cut, Owner.SelectionSubViewModel.SelectionIsNotEmpty);
+            PasteCommand = new RelayCommand(Paste, CanPaste);
+        }
+
+        public void Duplicate(object parameter)
+        {
+            Copy(null);
+            Paste(null);
+        }
+
+        public void Cut(object parameter)
+        {
+            Copy(null);
+            Owner.BitmapManager.ActiveLayer.SetPixels(
+                BitmapPixelChanges.FromSingleColoredArray(
+                    Owner.SelectionSubViewModel.ActiveSelection.SelectedPoints.ToArray(),
+                    Colors.Transparent));
+        }
+
+        public void Paste(object parameter)
+        {
+            ClipboardController.PasteFromClipboard();
+        }
+
+        private bool CanPaste(object property)
+        {
+            return Owner.DocumentIsNotNull(null) && ClipboardController.IsImageInClipboard();
+        }
+
+        private void Copy(object parameter)
+        {
+            ClipboardController.CopyToClipboard(
+                Owner.BitmapManager.ActiveDocument.Layers.ToArray(),
+                Owner.SelectionSubViewModel.ActiveSelection.SelectedPoints.ToArray(),
+                Owner.BitmapManager.ActiveDocument.Width,
+                Owner.BitmapManager.ActiveDocument.Height);
+        }
+    }
+}

+ 88 - 0
PixiEditor/ViewModels/SubViewModels/Main/ColorsViewModel.cs

@@ -0,0 +1,88 @@
+using System;
+using System.Windows.Media;
+using PixiEditor.Helpers;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class ColorsViewModel : SubViewModel<ViewModelMain>
+    {
+        public RelayCommand SwapColorsCommand { get; set; }
+
+        public RelayCommand SelectColorCommand { get; set; }
+
+        public RelayCommand RemoveSwatchCommand { get; set; }
+
+        private Color primaryColor = Colors.Black;
+
+        public Color PrimaryColor // Primary color, hooked with left mouse button
+        {
+            get => primaryColor;
+            set
+            {
+                if (primaryColor != value)
+                {
+                    primaryColor = value;
+                    Owner.BitmapManager.PrimaryColor = value;
+                    RaisePropertyChanged("PrimaryColor");
+                }
+            }
+        }
+
+        private Color secondaryColor = Colors.White;
+
+        public Color SecondaryColor
+        {
+            get => secondaryColor;
+            set
+            {
+                if (secondaryColor != value)
+                {
+                    secondaryColor = value;
+                    RaisePropertyChanged("SecondaryColor");
+                }
+            }
+        }
+
+        public ColorsViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            SelectColorCommand = new RelayCommand(SelectColor);
+            RemoveSwatchCommand = new RelayCommand(RemoveSwatch);
+            SwapColorsCommand = new RelayCommand(SwapColors);
+        }
+
+        public void SwapColors(object parameter)
+        {
+            var tmp = PrimaryColor;
+            PrimaryColor = SecondaryColor;
+            SecondaryColor = tmp;
+        }
+
+        public void AddSwatch(Color color)
+        {
+            if (!Owner.BitmapManager.ActiveDocument.Swatches.Contains(color))
+            {
+                Owner.BitmapManager.ActiveDocument.Swatches.Add(color);
+            }
+        }
+
+        private void RemoveSwatch(object parameter)
+        {
+            if (!(parameter is Color))
+            {
+                throw new ArgumentException();
+            }
+
+            Color color = (Color)parameter;
+            if (Owner.BitmapManager.ActiveDocument.Swatches.Contains(color))
+            {
+                Owner.BitmapManager.ActiveDocument.Swatches.Remove(color);
+            }
+        }
+
+        private void SelectColor(object parameter)
+        {
+            PrimaryColor = parameter as Color? ?? throw new ArgumentException();
+        }
+    }
+}

+ 67 - 0
PixiEditor/ViewModels/SubViewModels/Main/DocumentViewModel.cs

@@ -0,0 +1,67 @@
+using System.Linq;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Dialogs;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class DocumentViewModel : SubViewModel<ViewModelMain>
+    {
+        public const string ConfirmationDialogMessage = "Document was modified. Do you want to save changes?";
+
+        public bool UnsavedDocumentModified { get; set; }
+
+        public RelayCommand CenterContentCommand { get; set; }
+
+        public RelayCommand ClipCanvasCommand { get; set; }
+
+        public RelayCommand DeletePixelsCommand { get; set; }
+
+        public RelayCommand OpenResizePopupCommand { get; set; }
+
+        public DocumentViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            CenterContentCommand = new RelayCommand(CenterContent, Owner.DocumentIsNotNull);
+            ClipCanvasCommand = new RelayCommand(ClipCanvas, Owner.DocumentIsNotNull);
+            DeletePixelsCommand = new RelayCommand(DeletePixels, Owner.SelectionSubViewModel.SelectionIsNotEmpty);
+            OpenResizePopupCommand = new RelayCommand(OpenResizePopup, Owner.DocumentIsNotNull);
+        }
+
+        public void ClipCanvas(object parameter)
+        {
+            Owner.BitmapManager.ActiveDocument?.ClipCanvas();
+        }
+
+        private void DeletePixels(object parameter)
+        {
+            Owner.BitmapManager.BitmapOperations.DeletePixels(
+                new[] { Owner.BitmapManager.ActiveLayer },
+                Owner.SelectionSubViewModel.ActiveSelection.SelectedPoints.ToArray());
+        }
+
+        private void OpenResizePopup(object parameter)
+        {
+            bool isCanvasDialog = (string)parameter == "canvas";
+            ResizeDocumentDialog dialog = new ResizeDocumentDialog(
+                Owner.BitmapManager.ActiveDocument.Width,
+                Owner.BitmapManager.ActiveDocument.Height,
+                isCanvasDialog);
+            if (dialog.ShowDialog())
+            {
+                if (isCanvasDialog)
+                {
+                    Owner.BitmapManager.ActiveDocument.ResizeCanvas(dialog.Width, dialog.Height, dialog.ResizeAnchor);
+                }
+                else
+                {
+                    Owner.BitmapManager.ActiveDocument.Resize(dialog.Width, dialog.Height);
+                }
+            }
+        }
+
+        private void CenterContent(object property)
+        {
+            Owner.BitmapManager.ActiveDocument.CenterContent();
+        }
+    }
+}

+ 183 - 0
PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -0,0 +1,183 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media.Imaging;
+using Microsoft.Win32;
+using PixiEditor.Helpers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Dialogs;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.IO;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class FileViewModel : SubViewModel<ViewModelMain>
+    {
+        public RelayCommand OpenNewFilePopupCommand { get; set; }
+
+        public RelayCommand SaveDocumentCommand { get; set; }
+
+        public RelayCommand OpenFileCommand { get; set; }
+
+        public RelayCommand ExportFileCommand { get; set; } // Command that is used to save file
+
+        public FileViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            OpenNewFilePopupCommand = new RelayCommand(OpenNewFilePopup);
+            SaveDocumentCommand = new RelayCommand(SaveDocument, Owner.DocumentIsNotNull);
+            OpenFileCommand = new RelayCommand(Open);
+            ExportFileCommand = new RelayCommand(ExportFile, CanSave);
+            Owner.OnStartupEvent += Owner_OnStartupEvent;
+        }
+
+        /// <summary>
+        ///     Generates new Layer and sets it as active one.
+        /// </summary>
+        /// <param name="parameter">CommandParameter.</param>
+        public void OpenNewFilePopup(object parameter)
+        {
+            NewFileDialog newFile = new NewFileDialog();
+            if (newFile.ShowDialog())
+            {
+                NewDocument(newFile.Width, newFile.Height);
+            }
+        }
+
+        public void NewDocument(int width, int height, bool addBaseLayer = true)
+        {
+            Owner.BitmapManager.ActiveDocument = new Document(width, height);
+            if (addBaseLayer)
+            {
+                Owner.BitmapManager.AddNewLayer("Base Layer");
+            }
+
+            Owner.ResetProgramStateValues();
+        }
+
+        /// <summary>
+        ///     Opens file from path.
+        /// </summary>
+        /// <param name="path">Path to file.</param>
+        public void OpenFile(string path)
+        {
+            ImportFileDialog dialog = new ImportFileDialog();
+
+            if (path != null && File.Exists(path))
+            {
+                dialog.FilePath = path;
+            }
+
+            if (dialog.ShowDialog())
+            {
+                NewDocument(dialog.FileWidth, dialog.FileHeight, false);
+                Owner.BitmapManager.AddNewLayer("Image", Importer.ImportImage(dialog.FilePath, dialog.FileWidth, dialog.FileHeight));
+            }
+        }
+
+        public void SaveDocument(bool asNew)
+        {
+            SaveDocument(parameter: asNew ? "asnew" : null);
+        }
+
+        private void Owner_OnStartupEvent(object sender, System.EventArgs e)
+        {
+            var lastArg = Environment.GetCommandLineArgs().Last();
+            if (Importer.IsSupportedFile(lastArg) && File.Exists(lastArg))
+            {
+                Open(lastArg);
+            }
+            else
+            {
+                OpenNewFilePopup(null);
+            }
+        }
+
+        private void Open(string path)
+        {
+            if (Owner.DocumentSubViewModel.UnsavedDocumentModified)
+            {
+                var result = ConfirmationDialog.Show(DocumentViewModel.ConfirmationDialogMessage);
+                if (result == ConfirmationType.Yes)
+                {
+                    SaveDocument(null);
+                }
+                else if (result == ConfirmationType.Canceled)
+                {
+                    return;
+                }
+            }
+
+            Owner.ResetProgramStateValues();
+            if (path.EndsWith(".pixi"))
+            {
+                OpenDocument(path);
+            }
+            else
+            {
+                OpenFile(path);
+            }
+        }
+
+        private void Open(object property)
+        {
+            OpenFileDialog dialog = new OpenFileDialog
+            {
+                Filter = "All Files|*.*|PixiEditor Files | *.pixi|PNG Files|*.png",
+                DefaultExt = "pixi"
+            };
+            if ((bool)dialog.ShowDialog())
+            {
+                if (Importer.IsSupportedFile(dialog.FileName))
+                {
+                    Open(dialog.FileName);
+                }
+
+                Owner.ViewportSubViewModel.CenterViewport();
+            }
+        }
+
+        private void OpenDocument(string path)
+        {
+            Owner.BitmapManager.ActiveDocument = Importer.ImportDocument(path);
+            Exporter.SaveDocumentPath = path;
+            Owner.DocumentSubViewModel.UnsavedDocumentModified = false;
+        }
+
+        private void SaveDocument(object parameter)
+        {
+            bool paramIsAsNew = parameter != null && parameter.ToString()?.ToLower() == "asnew";
+            if (paramIsAsNew || Exporter.SaveDocumentPath == null)
+            {
+                var saved = Exporter.SaveAsEditableFileWithDialog(Owner.BitmapManager.ActiveDocument, !paramIsAsNew);
+                Owner.DocumentSubViewModel.UnsavedDocumentModified = Owner.DocumentSubViewModel.UnsavedDocumentModified && !saved;
+            }
+            else
+            {
+                Exporter.SaveAsEditableFile(Owner.BitmapManager.ActiveDocument, Exporter.SaveDocumentPath);
+                Owner.DocumentSubViewModel.UnsavedDocumentModified = false;
+            }
+        }
+
+        /// <summary>
+        ///     Generates export dialog or saves directly if save data is known.
+        /// </summary>
+        /// <param name="parameter">CommandProperty.</param>
+        private void ExportFile(object parameter)
+        {
+            WriteableBitmap bitmap = Owner.BitmapManager.GetCombinedLayersBitmap();
+            Exporter.Export(bitmap, new Size(bitmap.PixelWidth, bitmap.PixelHeight));
+        }
+
+        /// <summary>
+        ///     Returns true if file save is possible.
+        /// </summary>
+        /// <param name="property">CommandProperty.</param>
+        /// <returns>True if active document is not null.</returns>
+        private bool CanSave(object property)
+        {
+            return Owner.BitmapManager.ActiveDocument != null;
+        }
+    }
+}

+ 140 - 0
PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs

@@ -0,0 +1,140 @@
+using System;
+using System.Windows;
+using System.Windows.Input;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers.Shortcuts;
+using PixiEditor.Models.Position;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class IoViewModel : SubViewModel<ViewModelMain>
+    {
+        public RelayCommand MouseMoveCommand { get; set; }
+
+        public RelayCommand MouseDownCommand { get; set; }
+
+        public RelayCommand KeyDownCommand { get; set; }
+
+        public RelayCommand KeyUpCommand { get; set; }
+
+        private double mouseXonCanvas;
+
+        private double mouseYonCanvas;
+
+        public double MouseXOnCanvas // Mouse X coordinate relative to canvas
+        {
+            get => mouseXonCanvas;
+            set
+            {
+                mouseXonCanvas = value;
+                RaisePropertyChanged(nameof(MouseXOnCanvas));
+            }
+        }
+
+        public double MouseYOnCanvas // Mouse Y coordinate relative to canvas
+        {
+            get => mouseYonCanvas;
+            set
+            {
+                mouseYonCanvas = value;
+                RaisePropertyChanged(nameof(MouseYOnCanvas));
+            }
+        }
+
+        private bool restoreToolOnKeyUp = false;
+
+        public IoViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            MouseMoveCommand = new RelayCommand(MouseMove);
+            MouseDownCommand = new RelayCommand(MouseDown);
+            KeyDownCommand = new RelayCommand(KeyDown);
+            KeyUpCommand = new RelayCommand(KeyUp);
+        }
+
+        public void MouseHook_OnMouseUp(object sender, Point p, MouseButton button)
+        {
+            GlobalMouseHook.OnMouseUp -= MouseHook_OnMouseUp;
+            if (button == MouseButton.Left)
+            {
+                Owner.BitmapManager.MouseController.StopRecordingMouseMovementChanges();
+            }
+
+            Owner.BitmapManager.MouseController.MouseUp(new MouseEventArgs(
+                Mouse.PrimaryDevice,
+                (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+        }
+
+        public void KeyDown(object parameter)
+        {
+            KeyEventArgs args = (KeyEventArgs)parameter;
+            if (args.IsRepeat && !restoreToolOnKeyUp && Owner.ShortcutController.LastShortcut != null &&
+                Owner.ShortcutController.LastShortcut.Command == Owner.ToolsSubViewModel.SelectToolCommand)
+            {
+                restoreToolOnKeyUp = true;
+                ShortcutController.BlockShortcutExecution = true;
+            }
+
+            Owner.ShortcutController.KeyPressed(args.Key, Keyboard.Modifiers);
+        }
+
+        private void MouseDown(object parameter)
+        {
+            if (Owner.BitmapManager.ActiveDocument.Layers.Count == 0)
+            {
+                return;
+            }
+
+            if (Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                if (!Owner.BitmapManager.MouseController.IsRecordingChanges)
+                {
+                    bool clickedOnCanvas = MouseXOnCanvas >= 0 &&
+                        MouseXOnCanvas <= Owner.BitmapManager.ActiveDocument.Width &&
+                        MouseYOnCanvas >= 0 &&
+                        MouseYOnCanvas <= Owner.BitmapManager.ActiveDocument.Height;
+                    Owner.BitmapManager.MouseController.StartRecordingMouseMovementChanges(clickedOnCanvas);
+                    Owner.BitmapManager.MouseController.RecordMouseMovementChange(MousePositionConverter.CurrentCoordinates);
+                }
+            }
+
+            Owner.BitmapManager.MouseController.MouseDown(new MouseEventArgs(
+                Mouse.PrimaryDevice,
+                (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+
+            // Mouse down is guaranteed to only be raised from within this application, so by subscribing here we
+            // only listen for mouse up events that occurred as a result of a mouse down within this application.
+            // This seems better than maintaining a global listener indefinitely.
+            GlobalMouseHook.OnMouseUp += MouseHook_OnMouseUp;
+        }
+
+        /// <summary>
+        ///     Method connected with command, it executes tool "activity".
+        /// </summary>
+        /// <param name="parameter">CommandParameter.</param>
+        private void MouseMove(object parameter)
+        {
+            Coordinates cords = new Coordinates((int)MouseXOnCanvas, (int)MouseYOnCanvas);
+            MousePositionConverter.CurrentCoordinates = cords;
+
+            if (Owner.BitmapManager.MouseController.IsRecordingChanges && Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                Owner.BitmapManager.MouseController.RecordMouseMovementChange(cords);
+            }
+
+            Owner.BitmapManager.MouseController.MouseMoved(cords);
+        }
+
+        private void KeyUp(object parameter)
+        {
+            KeyEventArgs args = (KeyEventArgs)parameter;
+            if (restoreToolOnKeyUp && Owner.ShortcutController.LastShortcut != null &&
+                Owner.ShortcutController.LastShortcut.ShortcutKey == args.Key)
+            {
+                restoreToolOnKeyUp = false;
+                Owner.ToolsSubViewModel.SetActiveTool(Owner.ToolsSubViewModel.LastActionTool);
+                ShortcutController.BlockShortcutExecution = false;
+            }
+        }
+    }
+}

+ 90 - 0
PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -0,0 +1,90 @@
+using PixiEditor.Helpers;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class LayersViewModel : SubViewModel<ViewModelMain>
+    {
+        public RelayCommand SetActiveLayerCommand { get; set; }
+
+        public RelayCommand NewLayerCommand { get; set; }
+
+        public RelayCommand DeleteLayerCommand { get; set; }
+
+        public RelayCommand RenameLayerCommand { get; set; }
+
+        public RelayCommand MoveToBackCommand { get; set; }
+
+        public RelayCommand MoveToFrontCommand { get; set; }
+
+        public LayersViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            SetActiveLayerCommand = new RelayCommand(SetActiveLayer);
+            NewLayerCommand = new RelayCommand(NewLayer, CanCreateNewLayer);
+            DeleteLayerCommand = new RelayCommand(DeleteLayer, CanDeleteLayer);
+            MoveToBackCommand = new RelayCommand(MoveLayerToBack, CanMoveToBack);
+            MoveToFrontCommand = new RelayCommand(MoveLayerToFront, CanMoveToFront);
+            RenameLayerCommand = new RelayCommand(RenameLayer);
+        }
+
+        public void NewLayer(object parameter)
+        {
+            Owner.BitmapManager.AddNewLayer($"New Layer {Owner.BitmapManager.ActiveDocument.Layers.Count}");
+        }
+
+        public bool CanCreateNewLayer(object parameter)
+        {
+            return Owner.BitmapManager.ActiveDocument != null && Owner.BitmapManager.ActiveDocument.Layers.Count > 0;
+        }
+
+        public void SetActiveLayer(object parameter)
+        {
+            Owner.BitmapManager.SetActiveLayer((int)parameter);
+        }
+
+        public void DeleteLayer(object parameter)
+        {
+            Owner.BitmapManager.RemoveLayer((int)parameter);
+        }
+
+        public bool CanDeleteLayer(object property)
+        {
+            return Owner.BitmapManager.ActiveDocument != null && Owner.BitmapManager.ActiveDocument.Layers.Count > 1;
+        }
+
+        public void RenameLayer(object parameter)
+        {
+            Owner.BitmapManager.ActiveDocument.Layers[(int)parameter].IsRenaming = true;
+        }
+
+        public void MoveLayerToFront(object parameter)
+        {
+            int oldIndex = (int)parameter;
+            Owner.BitmapManager.ActiveDocument.Layers.Move(oldIndex, oldIndex + 1);
+            if (Owner.BitmapManager.ActiveDocument.ActiveLayerIndex == oldIndex)
+            {
+                Owner.BitmapManager.SetActiveLayer(oldIndex + 1);
+            }
+        }
+
+        public void MoveLayerToBack(object parameter)
+        {
+            int oldIndex = (int)parameter;
+            Owner.BitmapManager.ActiveDocument.Layers.Move(oldIndex, oldIndex - 1);
+            if (Owner.BitmapManager.ActiveDocument.ActiveLayerIndex == oldIndex)
+            {
+                Owner.BitmapManager.SetActiveLayer(oldIndex - 1);
+            }
+        }
+
+        public bool CanMoveToFront(object property)
+        {
+            return Owner.DocumentIsNotNull(null) && Owner.BitmapManager.ActiveDocument.Layers.Count - 1 > (int)property;
+        }
+
+        public bool CanMoveToBack(object property)
+        {
+            return (int)property > 0;
+        }
+    }
+}

+ 57 - 0
PixiEditor/ViewModels/SubViewModels/Main/SelectionViewModel.cs

@@ -0,0 +1,57 @@
+using System;
+using PixiEditor.Helpers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools.Tools;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class SelectionViewModel : SubViewModel<ViewModelMain>
+    {
+        public RelayCommand DeselectCommand { get; set; }
+
+        public RelayCommand SelectAllCommand { get; set; }
+
+        private Selection selection;
+
+        public Selection ActiveSelection
+        {
+            get => selection;
+            set
+            {
+                selection = value;
+                RaisePropertyChanged("ActiveSelection");
+            }
+        }
+
+        public SelectionViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            DeselectCommand = new RelayCommand(Deselect, SelectionIsNotEmpty);
+            SelectAllCommand = new RelayCommand(SelectAll, CanSelectAll);
+            ActiveSelection = new Selection(Array.Empty<Coordinates>());
+        }
+
+        public void SelectAll(object parameter)
+        {
+            SelectTool select = new SelectTool();
+            ActiveSelection.SetSelection(select.GetAllSelection(), SelectionType.New);
+        }
+
+        public void Deselect(object parameter)
+        {
+            ActiveSelection?.Clear();
+        }
+
+        public bool SelectionIsNotEmpty(object property)
+        {
+            return ActiveSelection?.SelectedPoints != null && ActiveSelection.SelectedPoints.Count > 0;
+        }
+
+        private bool CanSelectAll(object property)
+        {
+            return Owner.BitmapManager.ActiveDocument != null && Owner.BitmapManager.ActiveDocument.Layers.Count > 0;
+        }
+    }
+}

+ 94 - 0
PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -0,0 +1,94 @@
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows.Input;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Tools;
+using PixiEditor.Models.Tools.Tools;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class ToolsViewModel : SubViewModel<ViewModelMain>
+    {
+        private Cursor toolCursor;
+
+        public RelayCommand SelectToolCommand { get; set; } // Command that handles tool switching.
+
+        public RelayCommand ChangeToolSizeCommand { get; set; }
+
+        public Tool LastActionTool { get; private set; }
+
+        public ObservableCollection<Tool> ToolSet { get; set; }
+
+        public Cursor ToolCursor
+        {
+            get => toolCursor;
+            set
+            {
+                toolCursor = value;
+                RaisePropertyChanged("ToolCursor");
+            }
+        }
+
+        public ToolsViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            SelectToolCommand = new RelayCommand(SetTool, Owner.DocumentIsNotNull);
+            ChangeToolSizeCommand = new RelayCommand(ChangeToolSize);
+
+            ToolSet = new ObservableCollection<Tool>
+            {
+                new MoveViewportTool(), new MoveTool(), new PenTool(), new SelectTool(), new FloodFill(), new LineTool(),
+                new CircleTool(), new RectangleTool(), new EraserTool(), new ColorPickerTool(), new BrightnessTool(),
+                new ZoomTool()
+            };
+            SetActiveTool(ToolType.Move);
+        }
+
+        public void SetActiveTool(ToolType tool)
+        {
+            Tool foundTool = ToolSet.First(x => x.ToolType == tool);
+            SetActiveTool(foundTool);
+        }
+
+        public void SetActiveTool(Tool tool)
+        {
+            Tool activeTool = ToolSet.FirstOrDefault(x => x.IsActive);
+            if (activeTool != null)
+            {
+                activeTool.IsActive = false;
+            }
+
+            tool.IsActive = true;
+            LastActionTool = Owner.BitmapManager.SelectedTool;
+            Owner.BitmapManager.SetActiveTool(tool);
+            SetToolCursor(tool.ToolType);
+        }
+
+        public void SetTool(object parameter)
+        {
+            SetActiveTool((ToolType)parameter);
+        }
+
+        private void ChangeToolSize(object parameter)
+        {
+            int increment = (int)parameter;
+            int newSize = Owner.BitmapManager.ToolSize + increment;
+            if (newSize > 0)
+            {
+                Owner.BitmapManager.ToolSize = newSize;
+            }
+        }
+
+        private void SetToolCursor(ToolType tool)
+        {
+            if (tool != ToolType.None)
+            {
+                ToolCursor = Owner.BitmapManager.SelectedTool.Cursor;
+            }
+            else
+            {
+                ToolCursor = Cursors.Arrow;
+            }
+        }
+    }
+}

+ 93 - 0
PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs

@@ -0,0 +1,93 @@
+using System;
+using System.Linq;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Tools;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class UndoViewModel : SubViewModel<ViewModelMain>
+    {
+        public RelayCommand UndoCommand { get; set; }
+
+        public RelayCommand RedoCommand { get; set; }
+
+        private LayerChange[] undoChanges;
+
+        public LayerChange[] UndoChanges // This acts like UndoManager process, but it was implemented before process system, so it can be transformed into it
+        {
+            get => undoChanges;
+            set
+            {
+                undoChanges = value;
+                for (int i = 0; i < value.Length; i++)
+                {
+                    Owner.BitmapManager.ActiveDocument.Layers[value[i].LayerIndex].SetPixels(value[i].PixelChanges);
+                }
+            }
+        }
+
+        public UndoViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            UndoCommand = new RelayCommand(Undo, CanUndo);
+            RedoCommand = new RelayCommand(Redo, CanRedo);
+        }
+
+        public void TriggerNewUndoChange(Tool toolUsed)
+        {
+            if (BitmapManager.IsOperationTool(toolUsed)
+                && ((BitmapOperationTool)toolUsed).UseDefaultUndoMethod)
+            {
+                Tuple<LayerChange, LayerChange>[] changes = Owner.ChangesController.PopChanges();
+                if (changes != null && changes.Length > 0)
+                {
+                    LayerChange[] newValues = changes.Select(x => x.Item1).ToArray();
+                    LayerChange[] oldValues = changes.Select(x => x.Item2).ToArray();
+                    UndoManager.AddUndoChange(new Change("UndoChanges", oldValues, newValues, root: this));
+                    toolUsed.AfterAddedUndo();
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Redo last action.
+        /// </summary>
+        /// <param name="parameter">CommandProperty.</param>
+        public void Redo(object parameter)
+        {
+            UndoManager.Redo();
+        }
+
+        /// <summary>
+        ///     Undo last action.
+        /// </summary>
+        /// <param name="parameter">CommandParameter.</param>
+        public void Undo(object parameter)
+        {
+            Owner.SelectionSubViewModel.Deselect(null);
+            UndoManager.Undo();
+        }
+
+        /// <summary>
+        ///     Returns true if undo can be done.
+        /// </summary>
+        /// <param name="property">CommandParameter.</param>
+        /// <returns>True if can undo.</returns>
+        private bool CanUndo(object property)
+        {
+            return UndoManager.CanUndo;
+        }
+
+        /// <summary>
+        ///     Returns true if redo can be done.
+        /// </summary>
+        /// <param name="property">CommandProperty.</param>
+        /// <returns>True if can redo.</returns>
+        private bool CanRedo(object property)
+        {
+            return UndoManager.CanRedo;
+        }
+    }
+}

+ 115 - 0
PixiEditor/ViewModels/SubViewModels/Main/UpdateViewModel.cs

@@ -0,0 +1,115 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Threading.Tasks;
+using System.Windows;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Processes;
+using PixiEditor.UpdateModule;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class UpdateViewModel : SubViewModel<ViewModelMain>
+    {
+        private bool updateReadyToInstall = false;
+
+        public UpdateChecker UpdateChecker { get; set; }
+
+        public RelayCommand RestartApplicationCommand { get; set; }
+
+        private string versionText;
+
+        public string VersionText
+        {
+            get => versionText;
+            set
+            {
+                versionText = value;
+                RaisePropertyChanged(nameof(VersionText));
+            }
+        }
+
+        public bool UpdateReadyToInstall
+        {
+            get => updateReadyToInstall;
+            set
+            {
+                updateReadyToInstall = value;
+                RaisePropertyChanged(nameof(UpdateReadyToInstall));
+                if (value)
+                {
+                    VersionText = $"to install update (current {UpdateChecker.CurrentVersionTag})"; // Button shows "Restart" before this text
+                }
+            }
+        }
+
+        public UpdateViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            Owner.OnStartupEvent += Owner_OnStartupEvent;
+            RestartApplicationCommand = new RelayCommand(RestartApplication);
+            InitUpdateChecker();
+        }
+
+        public async Task<bool> CheckForUpdate()
+        {
+            return await Task.Run(async () =>
+            {
+                bool updateAvailable = await UpdateChecker.CheckUpdateAvailable();
+                bool updateCompatible = await UpdateChecker.IsUpdateCompatible();
+                bool updateFileDoesNotExists = !File.Exists(
+                    Path.Join(UpdateDownloader.DownloadLocation, $"update-{UpdateChecker.LatestReleaseInfo.TagName}.zip"));
+                bool updateExeDoesNotExists = !File.Exists(
+                    Path.Join(UpdateDownloader.DownloadLocation, $"update-{UpdateChecker.LatestReleaseInfo.TagName}.exe"));
+                if (updateAvailable && updateFileDoesNotExists && updateExeDoesNotExists)
+                {
+                    VersionText = "Downloading update...";
+                    if (updateCompatible)
+                    {
+                        await UpdateDownloader.DownloadReleaseZip(UpdateChecker.LatestReleaseInfo);
+                    }
+                    else
+                    {
+                        await UpdateDownloader.DownloadInstaller(UpdateChecker.LatestReleaseInfo);
+                    }
+                    
+                    UpdateReadyToInstall = true;
+                    return true;
+                }
+
+                return false;
+            });
+        }
+
+        private async void Owner_OnStartupEvent(object sender, EventArgs e)
+        {
+            await CheckForUpdate();
+        }
+
+        private void RestartApplication(object parameter)
+        {
+            try
+            {
+                ProcessHelper.RunAsAdmin(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "PixiEditor.UpdateInstaller.exe"));
+                Application.Current.Shutdown();
+            }
+            catch (Win32Exception)
+            {
+                MessageBox.Show(
+                    "Couldn't update without administrator rights.",
+                    "Insufficient permissions",
+                    MessageBoxButton.OK,
+                    MessageBoxImage.Error);
+            }
+        }
+
+        private void InitUpdateChecker()
+        {
+            string version = AssemblyHelper.GetCurrentAssemblyVersion();
+            UpdateChecker = new UpdateChecker(version);
+            VersionText = $"Version {version}";
+        }
+    }
+}

+ 64 - 0
PixiEditor/ViewModels/SubViewModels/Main/ViewportViewModel.cs

@@ -0,0 +1,64 @@
+using System.Windows;
+using PixiEditor.Helpers;
+
+namespace PixiEditor.ViewModels.SubViewModels.Main
+{
+    public class ViewportViewModel : SubViewModel<ViewModelMain>
+    {
+        public RelayCommand ZoomCommand { get; set; }
+
+        private double zoomPercentage = 100;
+
+        public double ZoomPercentage
+        {
+            get => zoomPercentage;
+            set
+            {
+                zoomPercentage = value;
+                RaisePropertyChanged(nameof(ZoomPercentage));
+            }
+        }
+
+        private Point viewPortPosition;
+
+        public Point ViewportPosition
+        {
+            get => viewPortPosition;
+            set
+            {
+                viewPortPosition = value;
+                RaisePropertyChanged(nameof(ViewportPosition));
+            }
+        }
+
+        private bool recenterZoombox;
+
+        public bool RecenterZoombox
+        {
+            get => recenterZoombox;
+            set
+            {
+                recenterZoombox = value;
+                RaisePropertyChanged(nameof(RecenterZoombox));
+            }
+        }
+
+        public ViewportViewModel(ViewModelMain owner)
+            : base(owner)
+        {
+            ZoomCommand = new RelayCommand(ZoomViewport);
+        }
+
+        public void CenterViewport()
+        {
+            RecenterZoombox = !RecenterZoombox; // It's a trick to trigger change in UserControl
+        }
+
+        private void ZoomViewport(object parameter)
+        {
+            double zoom = (int)parameter;
+            ZoomPercentage = zoom;
+            ZoomPercentage = 100;
+        }
+    }
+}

+ 12 - 0
PixiEditor/ViewModels/SubViewModels/SubViewModel.cs

@@ -0,0 +1,12 @@
+namespace PixiEditor.ViewModels.SubViewModels
+{
+    public class SubViewModel<T> : ViewModelBase where T: ViewModelBase
+    {
+        public T Owner { get; }
+
+        public SubViewModel(T owner)
+        {
+            Owner = owner;
+        }        
+    }
+}

+ 4 - 10
PixiEditor/ViewModels/ViewModelBase.cs

@@ -7,28 +7,22 @@ namespace PixiEditor.ViewModels
 {
     public class ViewModelBase : INotifyPropertyChanged
     {
-        public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
+        public event PropertyChangedEventHandler PropertyChanged = delegate { };
 
         protected void RaisePropertyChanged(string property)
         {
-            if (property != null)
-            {
-                PropertyChanged(this, new PropertyChangedEventArgs(property));
-            }
+            if (property != null) PropertyChanged(this, new PropertyChangedEventArgs(property));
         }
 
         protected void CloseButton(object parameter)
         {
-            ((Window)parameter).Close();
+            ((Window) parameter).Close();
         }
 
         protected void DragMove(object parameter)
         {
             Window popup = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
-            if (Mouse.LeftButton == MouseButtonState.Pressed)
-            {
-                popup.DragMove();
-            }
+            if (Mouse.LeftButton == MouseButtonState.Pressed) popup.DragMove();
         }
     }
 }

+ 94 - 886
PixiEditor/ViewModels/ViewModelMain.cs

@@ -1,17 +1,9 @@
 using System;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using Microsoft.Win32;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers.Shortcuts;
@@ -21,44 +13,52 @@ using PixiEditor.Models.Enums;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.Position;
-using PixiEditor.Models.Processes;
 using PixiEditor.Models.Tools;
-using PixiEditor.Models.Tools.Tools;
-using PixiEditor.UpdateModule;
+using PixiEditor.ViewModels.SubViewModels.Main;
 
 namespace PixiEditor.ViewModels
 {
     public class ViewModelMain : ViewModelBase
     {
-        private const string ConfirmationDialogMessage = "Document was modified. Do you want to save changes?";
+        public static ViewModelMain Current { get; set; }
+
+        public Action CloseAction { get; set; }
+
+        public event EventHandler OnStartupEvent;
+
+        public RelayCommand OnStartupCommand { get; set; }
+
+        public RelayCommand CloseWindowCommand { get; set; }
 
-        private Color primaryColor = Colors.Black;
+        public RelayCommand OpenHyperlinkCommand { get; set; }
 
-        private bool recenterZoombox;
+        public FileViewModel FileSubViewModel { get; set; }
 
-        private Color secondaryColor = Colors.White;
+        public UpdateViewModel UpdateSubViewModel { get; set; }
 
-        private Selection selection;
+        public ToolsViewModel ToolsSubViewModel { get; set; }
 
-        private Cursor toolCursor;
+        public IoViewModel IoSubViewModel { get; set; }
 
-        private LayerChange[] undoChanges;
+        public LayersViewModel LayersSubViewModel { get; set; }
 
-        private bool unsavedDocumentModified;
+        public ClipboardViewModel ClipboardSubViewModel { get; set; }
 
-        private double mouseXonCanvas;
+        public UndoViewModel UndoSubViewModel { get; set; }
 
-        private double mouseYonCanvas;
+        public SelectionViewModel SelectionSubViewModel { get; set; }
 
-        private string versionText;
+        public ViewportViewModel ViewportSubViewModel { get; set; }
 
-        private double zoomPercentage = 100;
+        public ColorsViewModel ColorsSubViewModel { get; set; }
 
-        private Point viewPortPosition;
+        public DocumentViewModel DocumentSubViewModel { get; set; }
 
-        private bool updateReadyToInstall = false;
+        public BitmapManager BitmapManager { get; set; }
 
-        private bool restoreToolOnKeyUp = false;
+        public PixelChangesController ChangesController { get; set; }
+
+        public ShortcutController ShortcutController { get; set; }
 
         public ViewModelMain()
         {
@@ -66,555 +66,73 @@ namespace PixiEditor.ViewModels
             BitmapManager.BitmapOperations.BitmapChanged += BitmapUtility_BitmapChanged;
             BitmapManager.MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
             BitmapManager.DocumentChanged += BitmapManager_DocumentChanged;
+
+            SelectionSubViewModel = new SelectionViewModel(this);
+
             ChangesController = new PixelChangesController();
-            SelectToolCommand = new RelayCommand(SetTool, DocumentIsNotNull);
-            OpenNewFilePopupCommand = new RelayCommand(OpenNewFilePopup);
-            MouseMoveCommand = new RelayCommand(MouseMove);
-            MouseDownCommand = new RelayCommand(MouseDown);
-            ExportFileCommand = new RelayCommand(ExportFile, CanSave);
-            UndoCommand = new RelayCommand(Undo, CanUndo);
-            RedoCommand = new RelayCommand(Redo, CanRedo);
-            OpenFileCommand = new RelayCommand(Open);
-            SetActiveLayerCommand = new RelayCommand(SetActiveLayer);
-            NewLayerCommand = new RelayCommand(NewLayer, CanCreateNewLayer);
-            DeleteLayerCommand = new RelayCommand(DeleteLayer, CanDeleteLayer);
-            MoveToBackCommand = new RelayCommand(MoveLayerToBack, CanMoveToBack);
-            MoveToFrontCommand = new RelayCommand(MoveLayerToFront, CanMoveToFront);
-            SwapColorsCommand = new RelayCommand(SwapColors);
-            KeyDownCommand = new RelayCommand(KeyDown);
-            KeyUpCommand = new RelayCommand(KeyUp);
-            RenameLayerCommand = new RelayCommand(RenameLayer);
-            DeselectCommand = new RelayCommand(Deselect, SelectionIsNotEmpty);
-            SelectAllCommand = new RelayCommand(SelectAll, CanSelectAll);
-            CopyCommand = new RelayCommand(Copy, SelectionIsNotEmpty);
-            DuplicateCommand = new RelayCommand(Duplicate, SelectionIsNotEmpty);
-            CutCommand = new RelayCommand(Cut, SelectionIsNotEmpty);
-            PasteCommand = new RelayCommand(Paste, CanPaste);
-            ClipCanvasCommand = new RelayCommand(ClipCanvas, DocumentIsNotNull);
-            DeletePixelsCommand = new RelayCommand(DeletePixels, SelectionIsNotEmpty);
-            OpenResizePopupCommand = new RelayCommand(OpenResizePopup, DocumentIsNotNull);
-            SelectColorCommand = new RelayCommand(SelectColor);
-            RemoveSwatchCommand = new RelayCommand(RemoveSwatch);
-            SaveDocumentCommand = new RelayCommand(SaveDocument, DocumentIsNotNull);
             OnStartupCommand = new RelayCommand(OnStartup);
             CloseWindowCommand = new RelayCommand(CloseWindow);
-            CenterContentCommand = new RelayCommand(CenterContent, DocumentIsNotNull);
             OpenHyperlinkCommand = new RelayCommand(OpenHyperlink);
-            ZoomCommand = new RelayCommand(ZoomViewport);
-            ChangeToolSizeCommand = new RelayCommand(ChangeToolSize);
-            RestartApplicationCommand = new RelayCommand(RestartApplication);
-            ToolSet = new ObservableCollection<Tool>
-            {
-                new MoveViewportTool(), new MoveTool(), new PenTool(), new SelectTool(), new FloodFill(), new LineTool(),
-                new CircleTool(), new RectangleTool(), new EraserTool(), new ColorPickerTool(), new BrightnessTool(),
-                new ZoomTool()
-            };
+
+            FileSubViewModel = new FileViewModel(this);
+            UpdateSubViewModel = new UpdateViewModel(this);
+            ToolsSubViewModel = new ToolsViewModel(this);
+            IoSubViewModel = new IoViewModel(this);
+            LayersSubViewModel = new LayersViewModel(this);
+            ClipboardSubViewModel = new ClipboardViewModel(this);
+            UndoSubViewModel = new UndoViewModel(this);
+            ViewportSubViewModel = new ViewportViewModel(this);
+            ColorsSubViewModel = new ColorsViewModel(this);
+            DocumentSubViewModel = new DocumentViewModel(this);
+
             ShortcutController = new ShortcutController
             {
                 Shortcuts = new List<Shortcut>
                 {
                     // Tools
-                    new Shortcut(Key.B, SelectToolCommand, ToolType.Pen),
-                    new Shortcut(Key.E, SelectToolCommand, ToolType.Eraser),
-                    new Shortcut(Key.O, SelectToolCommand, ToolType.ColorPicker),
-                    new Shortcut(Key.R, SelectToolCommand, ToolType.Rectangle),
-                    new Shortcut(Key.C, SelectToolCommand, ToolType.Circle),
-                    new Shortcut(Key.L, SelectToolCommand, ToolType.Line),
-                    new Shortcut(Key.G, SelectToolCommand, ToolType.Bucket),
-                    new Shortcut(Key.U, SelectToolCommand, ToolType.Brightness),
-                    new Shortcut(Key.V, SelectToolCommand, ToolType.Move),
-                    new Shortcut(Key.M, SelectToolCommand, ToolType.Select),
-                    new Shortcut(Key.Z, SelectToolCommand, ToolType.Zoom),
-                    new Shortcut(Key.H, SelectToolCommand, ToolType.MoveViewport),
-                    new Shortcut(Key.OemPlus, ZoomCommand, 115),
-                    new Shortcut(Key.OemMinus, ZoomCommand, 85),
-                    new Shortcut(Key.OemOpenBrackets, ChangeToolSizeCommand, -1),
-                    new Shortcut(Key.OemCloseBrackets, ChangeToolSizeCommand, 1),
+                    new Shortcut(Key.B, ToolsSubViewModel.SelectToolCommand, ToolType.Pen),
+                    new Shortcut(Key.E, ToolsSubViewModel.SelectToolCommand, ToolType.Eraser),
+                    new Shortcut(Key.O, ToolsSubViewModel.SelectToolCommand, ToolType.ColorPicker),
+                    new Shortcut(Key.R, ToolsSubViewModel.SelectToolCommand, ToolType.Rectangle),
+                    new Shortcut(Key.C, ToolsSubViewModel.SelectToolCommand, ToolType.Circle),
+                    new Shortcut(Key.L, ToolsSubViewModel.SelectToolCommand, ToolType.Line),
+                    new Shortcut(Key.G, ToolsSubViewModel.SelectToolCommand, ToolType.Bucket),
+                    new Shortcut(Key.U, ToolsSubViewModel.SelectToolCommand, ToolType.Brightness),
+                    new Shortcut(Key.V, ToolsSubViewModel.SelectToolCommand, ToolType.Move),
+                    new Shortcut(Key.M, ToolsSubViewModel.SelectToolCommand, ToolType.Select),
+                    new Shortcut(Key.Z, ToolsSubViewModel.SelectToolCommand, ToolType.Zoom),
+                    new Shortcut(Key.H, ToolsSubViewModel.SelectToolCommand, ToolType.MoveViewport),
+                    new Shortcut(Key.OemPlus, ViewportSubViewModel.ZoomCommand, 115),
+                    new Shortcut(Key.OemMinus, ViewportSubViewModel.ZoomCommand, 85),
+                    new Shortcut(Key.OemOpenBrackets, ToolsSubViewModel.ChangeToolSizeCommand, -1),
+                    new Shortcut(Key.OemCloseBrackets, ToolsSubViewModel.ChangeToolSizeCommand, 1),
 
                     // Editor
-                    new Shortcut(Key.X, SwapColorsCommand),
-                    new Shortcut(Key.Y, RedoCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.Z, UndoCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.D, DeselectCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.A, SelectAllCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.C, CopyCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.V, PasteCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.J, DuplicateCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.X, CutCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.Delete, DeletePixelsCommand),
-                    new Shortcut(Key.I, OpenResizePopupCommand, modifier: ModifierKeys.Control | ModifierKeys.Shift),
-                    new Shortcut(Key.C, OpenResizePopupCommand, "canvas", ModifierKeys.Control | ModifierKeys.Shift),
+                    new Shortcut(Key.X, ColorsSubViewModel.SwapColorsCommand),
+                    new Shortcut(Key.Y, UndoSubViewModel.RedoCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.Z, UndoSubViewModel.UndoCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.D, SelectionSubViewModel.DeselectCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.A, SelectionSubViewModel.SelectAllCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.C, ClipboardSubViewModel.CopyCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.V, ClipboardSubViewModel.PasteCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.J, ClipboardSubViewModel.DuplicateCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.X, ClipboardSubViewModel.CutCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.Delete, DocumentSubViewModel.DeletePixelsCommand),
+                    new Shortcut(Key.I, DocumentSubViewModel.OpenResizePopupCommand, modifier: ModifierKeys.Control | ModifierKeys.Shift),
+                    new Shortcut(Key.C, DocumentSubViewModel.OpenResizePopupCommand, "canvas", ModifierKeys.Control | ModifierKeys.Shift),
                     new Shortcut(Key.F11, SystemCommands.MaximizeWindowCommand),
 
                     // File
-                    new Shortcut(Key.O, OpenFileCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.S, ExportFileCommand, modifier: ModifierKeys.Control | ModifierKeys.Shift | ModifierKeys.Alt),
-                    new Shortcut(Key.S, SaveDocumentCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.S, SaveDocumentCommand, "AsNew", ModifierKeys.Control | ModifierKeys.Shift),
-                    new Shortcut(Key.N, OpenNewFilePopupCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.O, FileSubViewModel.OpenFileCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.S, FileSubViewModel.ExportFileCommand, modifier: ModifierKeys.Control | ModifierKeys.Shift | ModifierKeys.Alt),
+                    new Shortcut(Key.S, FileSubViewModel.SaveDocumentCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.S, FileSubViewModel.SaveDocumentCommand, "AsNew", ModifierKeys.Control | ModifierKeys.Shift),
+                    new Shortcut(Key.N, FileSubViewModel.OpenNewFilePopupCommand, modifier: ModifierKeys.Control)
                 }
             };
             UndoManager.SetMainRoot(this);
-            SetActiveTool(ToolType.Move);
-            BitmapManager.PrimaryColor = PrimaryColor;
-            ActiveSelection = new Selection(Array.Empty<Coordinates>());
+            BitmapManager.PrimaryColor = ColorsSubViewModel.PrimaryColor;
             Current = this;
-            InitUpdateChecker();
-        }
-
-        public static ViewModelMain Current { get; set; }
-
-        public Action CloseAction { get; set; }
-
-        public RelayCommand SelectToolCommand { get; set; } // Command that handles tool switching
-
-        public RelayCommand OpenNewFilePopupCommand { get; set; } // Command that generates draw area
-
-        public RelayCommand MouseMoveCommand { get; set; } // Command that is used to draw
-
-        public RelayCommand MouseDownCommand { get; set; }
-
-        public RelayCommand KeyDownCommand { get; set; }
-
-        public RelayCommand KeyUpCommand { get; set; }
-
-        public RelayCommand ExportFileCommand { get; set; } // Command that is used to save file
-
-        public RelayCommand UndoCommand { get; set; }
-
-        public RelayCommand RedoCommand { get; set; }
-
-        public RelayCommand OpenFileCommand { get; set; }
-
-        public RelayCommand SetActiveLayerCommand { get; set; }
-
-        public RelayCommand NewLayerCommand { get; set; }
-
-        public RelayCommand DeleteLayerCommand { get; set; }
-
-        public RelayCommand RenameLayerCommand { get; set; }
-
-        public RelayCommand MoveToBackCommand { get; set; }
-
-        public RelayCommand MoveToFrontCommand { get; set; }
-
-        public RelayCommand SwapColorsCommand { get; set; }
-
-        public RelayCommand DeselectCommand { get; set; }
-
-        public RelayCommand SelectAllCommand { get; set; }
-
-        public RelayCommand CopyCommand { get; set; }
-
-        public RelayCommand DuplicateCommand { get; set; }
-
-        public RelayCommand CutCommand { get; set; }
-
-        public RelayCommand PasteCommand { get; set; }
-
-        public RelayCommand ClipCanvasCommand { get; set; }
-
-        public RelayCommand DeletePixelsCommand { get; set; }
-
-        public RelayCommand OpenResizePopupCommand { get; set; }
-
-        public RelayCommand SelectColorCommand { get; set; }
-
-        public RelayCommand RemoveSwatchCommand { get; set; }
-
-        public RelayCommand SaveDocumentCommand { get; set; }
-
-        public RelayCommand OnStartupCommand { get; set; }
-
-        public RelayCommand CloseWindowCommand { get; set; }
-
-        public RelayCommand CenterContentCommand { get; set; }
-
-        public RelayCommand OpenHyperlinkCommand { get; set; }
-
-        public RelayCommand ZoomCommand { get; set; }
-
-        public RelayCommand ChangeToolSizeCommand { get; set; }
-
-        public RelayCommand RestartApplicationCommand { get; set; }
-
-        public double MouseXOnCanvas // Mouse X coordinate relative to canvas
-        {
-            get => mouseXonCanvas;
-            set
-            {
-                mouseXonCanvas = value;
-                RaisePropertyChanged("MouseXOnCanvas");
-            }
-        }
-
-        public double MouseYOnCanvas // Mouse Y coordinate relative to canvas
-        {
-            get => mouseYonCanvas;
-            set
-            {
-                mouseYonCanvas = value;
-                RaisePropertyChanged("MouseYOnCanvas");
-            }
-        }
-
-        public string VersionText
-        {
-            get => versionText;
-            set
-            {
-                versionText = value;
-                RaisePropertyChanged(nameof(VersionText));
-            }
-        }
-
-        public bool RecenterZoombox
-        {
-            get => recenterZoombox;
-            set
-            {
-                recenterZoombox = value;
-                RaisePropertyChanged("RecenterZoombox");
-            }
-        }
-
-        public Color PrimaryColor // Primary color, hooked with left mouse button
-        {
-            get => primaryColor;
-            set
-            {
-                if (primaryColor != value)
-                {
-                    primaryColor = value;
-                    BitmapManager.PrimaryColor = value;
-                    RaisePropertyChanged("PrimaryColor");
-                }
-            }
-        }
-
-        public Color SecondaryColor
-        {
-            get => secondaryColor;
-            set
-            {
-                if (secondaryColor != value)
-                {
-                    secondaryColor = value;
-                    RaisePropertyChanged("SecondaryColor");
-                }
-            }
-        }
-
-        public ObservableCollection<Tool> ToolSet { get; set; }
-
-        public LayerChange[] UndoChanges // This acts like UndoManager process, but it was implemented before process system, so it can be transformed into it
-        {
-            get => undoChanges;
-            set
-            {
-                undoChanges = value;
-                for (int i = 0; i < value.Length; i++)
-                {
-                    BitmapManager.ActiveDocument.Layers[value[i].LayerIndex].SetPixels(value[i].PixelChanges);
-                }
-            }
-        }
-
-        public Cursor ToolCursor
-        {
-            get => toolCursor;
-            set
-            {
-                toolCursor = value;
-                RaisePropertyChanged("ToolCursor");
-            }
-        }
-
-        public double ZoomPercentage
-        {
-            get
-            {
-                return zoomPercentage;
-            }
-
-            set
-            {
-                zoomPercentage = value;
-                RaisePropertyChanged(nameof(ZoomPercentage));
-            }
-        }
-
-        public Point ViewportPosition
-        {
-            get => viewPortPosition;
-            set
-            {
-                viewPortPosition = value;
-                RaisePropertyChanged(nameof(ViewportPosition));
-            }
-        }
-
-        public bool UpdateReadyToInstall
-        {
-            get => updateReadyToInstall;
-            set
-            {
-                updateReadyToInstall = value;
-                RaisePropertyChanged(nameof(UpdateReadyToInstall));
-            }
-        }
-
-        public BitmapManager BitmapManager { get; set; }
-
-        public PixelChangesController ChangesController { get; set; }
-
-        public ShortcutController ShortcutController { get; set; }
-
-        public Selection ActiveSelection
-        {
-            get => selection;
-            set
-            {
-                selection = value;
-                RaisePropertyChanged("ActiveSelection");
-            }
-        }
-
-        public Tool LastActionTool { get; private set; }
-
-        public UpdateChecker UpdateChecker { get; set; }
-
-        public async Task<bool> CheckForUpdate()
-        {
-            return await Task.Run(async () =>
-            {
-                bool updateAvailable = await UpdateChecker.CheckUpdateAvailable();
-                bool updateFileDoesNotExists = !File.Exists(
-                    Path.Join(UpdateDownloader.DownloadLocation, $"update-{UpdateChecker.LatestReleaseInfo.TagName}.zip"));
-                if (updateAvailable && updateFileDoesNotExists)
-                {
-                    VersionText = "Downloading update...";
-                    await UpdateDownloader.DownloadReleaseZip(UpdateChecker.LatestReleaseInfo);
-                    VersionText = "to install update"; // Button shows "Restart" before this text
-                    UpdateReadyToInstall = true;
-                    return true;
-                }
-
-                return false;
-            });
-        }
-
-        public void AddSwatch(Color color)
-        {
-            if (!BitmapManager.ActiveDocument.Swatches.Contains(color))
-            {
-                BitmapManager.ActiveDocument.Swatches.Add(color);
-            }
-        }
-
-        public void ClipCanvas(object parameter)
-        {
-            BitmapManager.ActiveDocument?.ClipCanvas();
-        }
-
-        public void Duplicate(object parameter)
-        {
-            Copy(null);
-            Paste(null);
-        }
-
-        public void Cut(object parameter)
-        {
-            Copy(null);
-            BitmapManager.ActiveLayer.SetPixels(
-                BitmapPixelChanges.FromSingleColoredArray(ActiveSelection.SelectedPoints.ToArray(), Colors.Transparent));
-        }
-
-        public void Paste(object parameter)
-        {
-            ClipboardController.PasteFromClipboard();
-        }
-
-        public void SelectAll(object parameter)
-        {
-            SelectTool select = new SelectTool();
-            ActiveSelection.SetSelection(select.GetAllSelection(), SelectionType.New);
-        }
-
-        public bool DocumentIsNotNull(object property)
-        {
-            return BitmapManager.ActiveDocument != null;
-        }
-
-        public void Deselect(object parameter)
-        {
-            ActiveSelection?.Clear();
-        }
-
-        public void SetTool(object parameter)
-        {
-            SetActiveTool((ToolType)parameter);
-        }
-
-        public void RenameLayer(object parameter)
-        {
-            BitmapManager.ActiveDocument.Layers[(int)parameter].IsRenaming = true;
-        }
-
-        public void KeyDown(object parameter)
-        {
-            KeyEventArgs args = (KeyEventArgs)parameter;
-            if (args.IsRepeat && !restoreToolOnKeyUp && ShortcutController.LastShortcut != null && ShortcutController.LastShortcut.Command == SelectToolCommand)
-            {
-                restoreToolOnKeyUp = true;
-                ShortcutController.BlockShortcutExecution = true;
-            }
-
-            ShortcutController.KeyPressed(args.Key, Keyboard.Modifiers);
-        }
-
-        public void TriggerNewUndoChange(Tool toolUsed)
-        {
-            if (BitmapManager.IsOperationTool(toolUsed)
-                && ((BitmapOperationTool)toolUsed).UseDefaultUndoMethod)
-            {
-                Tuple<LayerChange, LayerChange>[] changes = ChangesController.PopChanges();
-                if (changes != null && changes.Length > 0)
-                {
-                    LayerChange[] newValues = changes.Select(x => x.Item1).ToArray();
-                    LayerChange[] oldValues = changes.Select(x => x.Item2).ToArray();
-                    UndoManager.AddUndoChange(new Change("UndoChanges", oldValues, newValues));
-                    toolUsed.AfterAddedUndo();
-                }
-            }
-        }
-
-        public void SwapColors(object parameter)
-        {
-            var tmp = PrimaryColor;
-            PrimaryColor = SecondaryColor;
-            SecondaryColor = tmp;
-        }
-
-        public void MoveLayerToFront(object parameter)
-        {
-            int oldIndex = (int)parameter;
-            BitmapManager.ActiveDocument.Layers.Move(oldIndex, oldIndex + 1);
-            if (BitmapManager.ActiveDocument.ActiveLayerIndex == oldIndex)
-            {
-                BitmapManager.SetActiveLayer(oldIndex + 1);
-            }
-        }
-
-        public void MoveLayerToBack(object parameter)
-        {
-            int oldIndex = (int)parameter;
-            BitmapManager.ActiveDocument.Layers.Move(oldIndex, oldIndex - 1);
-            if (BitmapManager.ActiveDocument.ActiveLayerIndex == oldIndex)
-            {
-                BitmapManager.SetActiveLayer(oldIndex - 1);
-            }
-        }
-
-        public bool CanMoveToFront(object property)
-        {
-            return DocumentIsNotNull(null) && BitmapManager.ActiveDocument.Layers.Count - 1 > (int)property;
-        }
-
-        public bool CanMoveToBack(object property)
-        {
-            return (int)property > 0;
-        }
-
-        public void SetActiveLayer(object parameter)
-        {
-            BitmapManager.SetActiveLayer((int)parameter);
-        }
-
-        public void DeleteLayer(object parameter)
-        {
-            BitmapManager.RemoveLayer((int)parameter);
-        }
-
-        public bool CanDeleteLayer(object property)
-        {
-            return BitmapManager.ActiveDocument != null && BitmapManager.ActiveDocument.Layers.Count > 1;
-        }
-
-        public void SetActiveTool(ToolType tool)
-        {
-            Tool foundTool = ToolSet.First(x => x.ToolType == tool);
-            SetActiveTool(foundTool);
-        }
-
-        public void SetActiveTool(Tool tool)
-        {
-            Tool activeTool = ToolSet.FirstOrDefault(x => x.IsActive);
-            if (activeTool != null)
-            {
-                activeTool.IsActive = false;
-            }
-
-            tool.IsActive = true;
-            LastActionTool = BitmapManager.SelectedTool;
-            BitmapManager.SetActiveTool(tool);
-            SetToolCursor(tool.ToolType);
-        }
-
-        // this is public for testing.
-        public void MouseHook_OnMouseUp(object sender, Point p, MouseButton button)
-        {
-            GlobalMouseHook.OnMouseUp -= MouseHook_OnMouseUp;
-            if (button == MouseButton.Left)
-            {
-                BitmapManager.MouseController.StopRecordingMouseMovementChanges();
-            }
-
-            BitmapManager.MouseController.MouseUp(new MouseEventArgs(
-                Mouse.PrimaryDevice,
-                (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
-        }
-
-        /// <summary>
-        ///     Generates new Layer and sets it as active one.
-        /// </summary>
-        public void OpenNewFilePopup(object parameter)
-        {
-            NewFileDialog newFile = new NewFileDialog();
-            if (newFile.ShowDialog())
-            {
-                NewDocument(newFile.Width, newFile.Height);
-            }
-        }
-
-        /// <summary>
-        ///     Opens file from path.
-        /// </summary>
-        public void OpenFile(string path)
-        {
-            ImportFileDialog dialog = new ImportFileDialog();
-
-            if (path != null && File.Exists(path))
-            {
-                dialog.FilePath = path;
-            }
-
-            if (dialog.ShowDialog())
-            {
-                NewDocument(dialog.FileWidth, dialog.FileHeight, false);
-                BitmapManager.AddNewLayer("Image", Importer.ImportImage(dialog.FilePath, dialog.FileWidth, dialog.FileHeight));
-            }
-        }
-
-        public void NewDocument(int width, int height, bool addBaseLayer = true)
-        {
-            BitmapManager.ActiveDocument = new Document(width, height);
-            if (addBaseLayer)
-            {
-                BitmapManager.AddNewLayer("Base Layer");
-            }
-
-            ResetProgramStateValues();
         }
 
         /// <summary>
@@ -625,75 +143,15 @@ namespace PixiEditor.ViewModels
             BitmapManager.PreviewLayer = null;
             UndoManager.UndoStack.Clear();
             UndoManager.RedoStack.Clear();
-            ActiveSelection = new Selection(Array.Empty<Coordinates>());
-            RecenterZoombox = !RecenterZoombox;
+            SelectionSubViewModel.ActiveSelection = new Selection(Array.Empty<Coordinates>());
+            ViewportSubViewModel.CenterViewport();
             Exporter.SaveDocumentPath = null;
-            unsavedDocumentModified = false;
+            DocumentSubViewModel.UnsavedDocumentModified = false;
         }
 
-        public void NewLayer(object parameter)
-        {
-            BitmapManager.AddNewLayer($"New Layer {BitmapManager.ActiveDocument.Layers.Count}");
-        }
-
-        public bool CanCreateNewLayer(object parameter)
-        {
-            return BitmapManager.ActiveDocument != null && BitmapManager.ActiveDocument.Layers.Count > 0;
-        }
-
-        /// <summary>
-        ///     Undo last action.
-        /// </summary>
-        public void Undo(object parameter)
-        {
-            Deselect(null);
-            UndoManager.Undo();
-        }
-
-        /// <summary>
-        ///     Redo last action.
-        /// </summary>
-        public void Redo(object parameter)
-        {
-            UndoManager.Redo();
-        }
-
-        private void RestartApplication(object parameter)
-        {
-            try
-            {
-                ProcessHelper.RunAsAdmin(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "PixiEditor.UpdateInstaller.exe"));
-                Application.Current.Shutdown();
-            }
-            catch (Win32Exception)
-            {
-                MessageBox.Show("Couldn't update without administrator rights.", "Insufficient permissions", MessageBoxButton.OK, MessageBoxImage.Error);
-            }
-        }
-
-        private void InitUpdateChecker()
-        {
-            var assembly = Assembly.GetExecutingAssembly();
-            FileVersionInfo info = FileVersionInfo.GetVersionInfo(assembly.Location);
-            UpdateChecker = new UpdateChecker(info.FileVersion);
-            VersionText = $"Version {info.FileVersion}";
-        }
-
-        private void ZoomViewport(object parameter)
-        {
-            double zoom = (int)parameter;
-            ZoomPercentage = zoom;
-            ZoomPercentage = 100;
-        }
-
-        private void ChangeToolSize(object parameter)
+        public bool DocumentIsNotNull(object property)
         {
-            int increment = (int)parameter;
-            int newSize = BitmapManager.ToolSize + increment;
-            if (newSize > 0)
-            {
-                BitmapManager.ToolSize = newSize;
-            }
+            return BitmapManager.ActiveDocument != null;
         }
 
         private void OpenHyperlink(object parameter)
@@ -703,7 +161,7 @@ namespace PixiEditor.ViewModels
                 return;
             }
 
-            string url = (string)parameter;
+            var url = (string)parameter;
             var processInfo = new ProcessStartInfo()
             {
                 FileName = url,
@@ -712,11 +170,6 @@ namespace PixiEditor.ViewModels
             Process.Start(processInfo);
         }
 
-        private void CenterContent(object property)
-        {
-            BitmapManager.ActiveDocument.CenterContent();
-        }
-
         private void CloseWindow(object property)
         {
             if (!(property is CancelEventArgs))
@@ -726,13 +179,13 @@ namespace PixiEditor.ViewModels
 
             ((CancelEventArgs)property).Cancel = true;
 
-            ConfirmationType result = ConfirmationType.No;
-            if (unsavedDocumentModified)
+            var result = ConfirmationType.No;
+            if (DocumentSubViewModel.UnsavedDocumentModified)
             {
-                result = ConfirmationDialog.Show(ConfirmationDialogMessage);
+                result = ConfirmationDialog.Show(DocumentViewModel.ConfirmationDialogMessage);
                 if (result == ConfirmationType.Yes)
                 {
-                    SaveDocument(null);
+                    FileSubViewModel.SaveDocument(false);
                 }
             }
 
@@ -742,19 +195,9 @@ namespace PixiEditor.ViewModels
             }
         }
 
-        private async void OnStartup(object parameter)
+        private void OnStartup(object parameter)
         {
-            var lastArg = Environment.GetCommandLineArgs().Last();
-            if (Importer.IsSupportedFile(lastArg) && File.Exists(lastArg))
-            {
-                Open(lastArg);
-            }
-            else
-            {
-                OpenNewFilePopup(null);
-            }
-
-            await CheckForUpdate();
+            OnStartupEvent?.Invoke(this, EventArgs.Empty);
         }
 
         private void BitmapManager_DocumentChanged(object sender, DocumentChangedEventArgs e)
@@ -762,263 +205,28 @@ namespace PixiEditor.ViewModels
             e.NewDocument.DocumentSizeChanged += ActiveDocument_DocumentSizeChanged;
         }
 
-        private void Open(object property)
-        {
-            OpenFileDialog dialog = new OpenFileDialog
-            {
-                Filter = "All Files|*.*|PixiEditor Files | *.pixi|PNG Files|*.png",
-                DefaultExt = "pixi"
-            };
-            if ((bool)dialog.ShowDialog())
-            {
-                if (Importer.IsSupportedFile(dialog.FileName))
-                {
-                    Open(dialog.FileName);
-                }
-
-                RecenterZoombox = !RecenterZoombox;
-            }
-        }
-
-        private void Open(string path)
-        {
-            if (unsavedDocumentModified)
-            {
-                var result = ConfirmationDialog.Show(ConfirmationDialogMessage);
-                if (result == ConfirmationType.Yes)
-                {
-                    SaveDocument(null);
-                }
-                else if (result == ConfirmationType.Canceled)
-                {
-                    return;
-                }
-            }
-
-            ResetProgramStateValues();
-            if (path.EndsWith(".pixi"))
-            {
-                OpenDocument(path);
-            }
-            else
-            {
-                OpenFile(path);
-            }
-        }
-
-        private void OpenDocument(string path)
-        {
-            BitmapManager.ActiveDocument = Importer.ImportDocument(path);
-            Exporter.SaveDocumentPath = path;
-            unsavedDocumentModified = false;
-        }
-
-        private void SaveDocument(object parameter)
-        {
-            bool paramIsAsNew = parameter != null && parameter.ToString()?.ToLower() == "asnew";
-            if (paramIsAsNew || Exporter.SaveDocumentPath == null)
-            {
-                var saved = Exporter.SaveAsEditableFileWithDialog(BitmapManager.ActiveDocument, !paramIsAsNew);
-                unsavedDocumentModified = unsavedDocumentModified && !saved;
-            }
-            else
-            {
-                Exporter.SaveAsEditableFile(BitmapManager.ActiveDocument, Exporter.SaveDocumentPath);
-                unsavedDocumentModified = false;
-            }
-        }
-
-        private void RemoveSwatch(object parameter)
-        {
-            if (!(parameter is Color))
-            {
-                throw new ArgumentException();
-            }
-
-            Color color = (Color)parameter;
-            if (BitmapManager.ActiveDocument.Swatches.Contains(color))
-            {
-                BitmapManager.ActiveDocument.Swatches.Remove(color);
-            }
-        }
-
-        private void SelectColor(object parameter)
-        {
-            PrimaryColor = parameter as Color? ?? throw new ArgumentException();
-        }
-
         private void ActiveDocument_DocumentSizeChanged(object sender, DocumentSizeChangedEventArgs e)
         {
-            ActiveSelection = new Selection(Array.Empty<Coordinates>());
-            RecenterZoombox = !RecenterZoombox;
-            unsavedDocumentModified = true;
-        }
-
-        private void OpenResizePopup(object parameter)
-        {
-            bool isCanvasDialog = (string)parameter == "canvas";
-            ResizeDocumentDialog dialog = new ResizeDocumentDialog(BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height, isCanvasDialog);
-            if (dialog.ShowDialog())
-            {
-                if (isCanvasDialog)
-                {
-                    BitmapManager.ActiveDocument.ResizeCanvas(dialog.Width, dialog.Height, dialog.ResizeAnchor);
-                }
-                else
-                {
-                    BitmapManager.ActiveDocument.Resize(dialog.Width, dialog.Height);
-                }
-            }
-        }
-
-        private void DeletePixels(object parameter)
-        {
-            BitmapManager.BitmapOperations.DeletePixels(
-                new[] { BitmapManager.ActiveLayer },
-                ActiveSelection.SelectedPoints.ToArray());
-        }
-
-        private bool CanPaste(object property)
-        {
-            return DocumentIsNotNull(null) && ClipboardController.IsImageInClipboard();
-        }
-
-        private void Copy(object parameter)
-        {
-            ClipboardController.CopyToClipboard(
-                BitmapManager.ActiveDocument.Layers.ToArray(),
-                ActiveSelection.SelectedPoints.ToArray(),
-                BitmapManager.ActiveDocument.Width,
-                BitmapManager.ActiveDocument.Height);
-        }
-
-        private bool CanSelectAll(object property)
-        {
-            return BitmapManager.ActiveDocument != null && BitmapManager.ActiveDocument.Layers.Count > 0;
-        }
-
-        private bool SelectionIsNotEmpty(object property)
-        {
-            return ActiveSelection?.SelectedPoints != null && ActiveSelection.SelectedPoints.Count > 0;
-        }
-
-        private void KeyUp(object parameter)
-        {
-            KeyEventArgs args = (KeyEventArgs)parameter;
-            if (restoreToolOnKeyUp && ShortcutController.LastShortcut != null && ShortcutController.LastShortcut.ShortcutKey == args.Key)
-            {
-                restoreToolOnKeyUp = false;
-                SetActiveTool(LastActionTool);
-                ShortcutController.BlockShortcutExecution = false;
-            }
+            SelectionSubViewModel.ActiveSelection = new Selection(Array.Empty<Coordinates>());
+            ViewportSubViewModel.CenterViewport();
+            DocumentSubViewModel.UnsavedDocumentModified = true;
         }
 
         private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
         {
-            TriggerNewUndoChange(BitmapManager.SelectedTool);
+            UndoSubViewModel.TriggerNewUndoChange(BitmapManager.SelectedTool);
         }
 
         private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
         {
             ChangesController.AddChanges(
-                new LayerChange(
-                    e.PixelsChanged,
-                    e.ChangedLayerIndex),
+                new LayerChange(e.PixelsChanged, e.ChangedLayerIndex),
                 new LayerChange(e.OldPixelsValues, e.ChangedLayerIndex));
-            unsavedDocumentModified = true;
+            DocumentSubViewModel.UnsavedDocumentModified = true;
             if (BitmapManager.IsOperationTool())
             {
-                AddSwatch(PrimaryColor);
-            }
-        }
-
-        private void SetToolCursor(ToolType tool)
-        {
-            if (tool != ToolType.None)
-            {
-                ToolCursor = BitmapManager.SelectedTool.Cursor;
-            }
-            else
-            {
-                ToolCursor = Cursors.Arrow;
+                ColorsSubViewModel.AddSwatch(ColorsSubViewModel.PrimaryColor);
             }
         }
-
-        private void MouseDown(object parameter)
-        {
-            if (BitmapManager.ActiveDocument.Layers.Count == 0)
-            {
-                return;
-            }
-
-            if (Mouse.LeftButton == MouseButtonState.Pressed)
-            {
-                if (!BitmapManager.MouseController.IsRecordingChanges)
-                {
-                    bool clickedOnCanvas = MouseXOnCanvas >= 0 && MouseXOnCanvas <= BitmapManager.ActiveDocument.Width &&
-                        MouseYOnCanvas >= 0 && MouseYOnCanvas <= BitmapManager.ActiveDocument.Height;
-                    BitmapManager.MouseController.StartRecordingMouseMovementChanges(clickedOnCanvas);
-                    BitmapManager.MouseController.RecordMouseMovementChange(MousePositionConverter.CurrentCoordinates);
-                }
-            }
-
-            BitmapManager.MouseController.MouseDown(new MouseEventArgs(
-                Mouse.PrimaryDevice,
-                (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
-
-            // Mouse down is guaranteed to only be raised from within this application, so by subscribing here we
-            // only listen for mouse up events that occurred as a result of a mouse down within this application.
-            // This seems better than maintaining a global listener indefinitely.
-            GlobalMouseHook.OnMouseUp += MouseHook_OnMouseUp;
-        }
-
-        /// <summary>
-        ///     Method connected with command, it executes tool "activity".
-        /// </summary>
-        private void MouseMove(object parameter)
-        {
-            Coordinates cords = new Coordinates((int)MouseXOnCanvas, (int)MouseYOnCanvas);
-            MousePositionConverter.CurrentCoordinates = cords;
-
-            if (BitmapManager.MouseController.IsRecordingChanges && Mouse.LeftButton == MouseButtonState.Pressed)
-            {
-                BitmapManager.MouseController.RecordMouseMovementChange(cords);
-            }
-
-            BitmapManager.MouseController.MouseMoved(cords);
-        }
-
-        /// <summary>
-        ///     Returns true if undo can be done.
-        /// </summary>
-        private bool CanUndo(object property)
-        {
-            return UndoManager.CanUndo;
-        }
-
-        /// <summary>
-        ///     Returns true if redo can be done.
-        /// </summary>
-        private bool CanRedo(object property)
-        {
-            return UndoManager.CanRedo;
-        }
-
-        /// <summary>
-        ///     Generates export dialog or saves directly if save data is known.
-        /// </summary>
-        private void ExportFile(object parameter)
-        {
-            WriteableBitmap bitmap = BitmapManager.GetCombinedLayersBitmap();
-            Exporter.Export(bitmap, new Size(bitmap.PixelWidth, bitmap.PixelHeight));
-        }
-
-        /// <summary>
-        ///     Returns true if file save is possible.
-        /// </summary>
-        private bool CanSave(object property)
-        {
-            return BitmapManager.ActiveDocument != null;
-        }
     }
 }

+ 0 - 0
PixiEditor/Views/ConfirmationPopup.xaml → PixiEditor/Views/Dialogs/ConfirmationPopup.xaml


+ 57 - 60
PixiEditor/Views/ConfirmationPopup.xaml.cs → PixiEditor/Views/Dialogs/ConfirmationPopup.xaml.cs

@@ -1,61 +1,58 @@
-using System.Windows;
-using PixiEditor.Helpers;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for ConfirmationPopup.xaml.
-    /// </summary>
-    public partial class ConfirmationPopup : Window
-    {
-        // Using a DependencyProperty as the backing store for SaveChanges.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty SaveChangesProperty =
-            DependencyProperty.Register(
-                "SaveChanges",
-                typeof(bool),
-                typeof(ConfirmationPopup),
-                new PropertyMetadata(true));
-
-        // Using a DependencyProperty as the backing store for Body.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty BodyProperty =
-            DependencyProperty.Register("Body", typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(string.Empty));
-
-        public ConfirmationPopup()
-        {
-            InitializeComponent();
-            CancelCommand = new RelayCommand(Cancel);
-            SetResultAndCloseCommand = new RelayCommand(SetResultAndClose);
-            DataContext = this;
-        }
-
-        public RelayCommand CancelCommand { get; set; }
-
-        public RelayCommand SetResultAndCloseCommand { get; set; }
-
-        public bool Result
-        {
-            get => (bool)GetValue(SaveChangesProperty);
-            set => SetValue(SaveChangesProperty, value);
-        }
-
-        public string Body
-        {
-            get => (string)GetValue(BodyProperty);
-            set => SetValue(BodyProperty, value);
-        }
-
-        private void SetResultAndClose(object property)
-        {
-            bool result = (bool)property;
-            Result = result;
-            DialogResult = true;
-            Close();
-        }
-
-        private void Cancel(object property)
-        {
-            DialogResult = false;
-            Close();
-        }
-    }
+using System.Windows;
+using PixiEditor.Helpers;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for ConfirmationPopup.xaml
+    /// </summary>
+    public partial class ConfirmationPopup : Window
+    {
+        // Using a DependencyProperty as the backing store for SaveChanges.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty SaveChangesProperty =
+            DependencyProperty.Register("SaveChanges", typeof(bool), typeof(ConfirmationPopup),
+                new PropertyMetadata(true));
+
+        // Using a DependencyProperty as the backing store for Body.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty BodyProperty =
+            DependencyProperty.Register("Body", typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(""));
+
+        public ConfirmationPopup()
+        {
+            InitializeComponent();
+            CancelCommand = new RelayCommand(Cancel);
+            SetResultAndCloseCommand = new RelayCommand(SetResultAndClose);
+            DataContext = this;
+        }
+
+        public RelayCommand CancelCommand { get; set; }
+        public RelayCommand SetResultAndCloseCommand { get; set; }
+
+        public bool Result
+        {
+            get => (bool) GetValue(SaveChangesProperty);
+            set => SetValue(SaveChangesProperty, value);
+        }
+
+
+        public string Body
+        {
+            get => (string) GetValue(BodyProperty);
+            set => SetValue(BodyProperty, value);
+        }
+
+        private void SetResultAndClose(object property)
+        {
+            bool result = (bool) property;
+            Result = result;
+            DialogResult = true;
+            Close();
+        }
+
+        private void Cancel(object property)
+        {
+            DialogResult = false;
+            Close();
+        }
+    }
 }

+ 4 - 1
PixiEditor/Views/ImportFilePopup.xaml → PixiEditor/Views/Dialogs/ImportFilePopup.xaml

@@ -6,12 +6,15 @@
         xmlns:local="clr-namespace:PixiEditor.Views"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
+        xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
+        xmlns:helpers="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d"
         Title="ImportFilePopup" Topmost="True" ShowInTaskbar="False" Height="350" Width="300" WindowStyle="None"
         ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Name="importFilePopup"
         DataContext="{DynamicResource ImportFilePopupViewModel}">
     <Window.Resources>
         <vm:ImportFilePopupViewModel x:Key="ImportFilePopupViewModel" />
+        <helpers:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
     <Border BorderBrush="Black" BorderThickness="1">
         <Grid Background="{StaticResource AccentColor}">
@@ -33,7 +36,7 @@
             <StackPanel Grid.Row="1">
                 <Label Height="40" Width="120" VerticalAlignment="Top" Content="Open" Foreground="Snow"
                        HorizontalContentAlignment="Center" FontSize="24" Margin="0,10,0,0" />
-                <Button BorderThickness="1" Foreground="Snow" Height="40" Width="160" Margin="0,30,0,0"
+                <Button Grid.Row="1" BorderThickness="1" Foreground="Snow" Height="40" Width="160" Margin="0,30,0,0"
                         Content="File Path" Background="#303030" BorderBrush="{Binding PathButtonBorder}"
                         Command="{Binding ChoosePathCommand}" />
                 <StackPanel Background="{StaticResource MainColor}" Height="120" Width="225" Margin="0,30,0,0">

+ 7 - 4
PixiEditor/Views/ImportFilePopup.xaml.cs → PixiEditor/Views/Dialogs/ImportFilePopup.xaml.cs

@@ -4,7 +4,7 @@ using PixiEditor.ViewModels;
 namespace PixiEditor.Views
 {
     /// <summary>
-    ///     Interaction logic for ImportFilePopup.xaml.
+    ///     Interaction logic for ImportFilePopup.xaml
     /// </summary>
     public partial class ImportFilePopup : Window
     {
@@ -15,19 +15,22 @@ namespace PixiEditor.Views
             InitializeComponent();
             DataContext = dc;
         }
-
+
+
         public int ImportHeight
         {
             get => dc.ImportHeight;
             set => dc.ImportWidth = value;
         }
-
+
+
         public int ImportWidth
         {
             get => dc.ImportWidth;
             set => dc.ImportWidth = value;
         }
-
+
+
         public string FilePath
         {
             get => dc.FilePath;

+ 3 - 0
PixiEditor/Views/NewFilePopup.xaml → PixiEditor/Views/Dialogs/NewFilePopup.xaml

@@ -6,12 +6,15 @@
         xmlns:local="clr-namespace:PixiEditor.Views"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
+        xmlns:helpers="clr-namespace:PixiEditor.Helpers.Behaviours"
+        xmlns:converters="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d"
         d:DesignHeight="600" Topmost="True" ShowInTaskbar="False" d:DesignWidth="450"
         DataContext="{DynamicResource NewFileMenuViewModel}" WindowStyle="None" WindowStartupLocation="CenterScreen"
         ResizeMode="NoResize" Height="600" Width="450" Name="newFilePopup">
     <Window.Resources>
         <vm:NewFileMenuViewModel x:Key="NewFileMenuViewModel" />
+        <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
     <Border BorderBrush="Black" BorderThickness="1">
         <Grid Background="{StaticResource AccentColor}">

+ 7 - 5
PixiEditor/Views/NewFilePopup.xaml.cs → PixiEditor/Views/Dialogs/NewFilePopup.xaml.cs

@@ -3,7 +3,7 @@
 namespace PixiEditor.Views
 {
     /// <summary>
-    ///     Interaction logic for NewFilePopup.xaml.
+    ///     Interaction logic for NewFilePopup.xaml
     /// </summary>
     public partial class NewFilePopup : Window
     {
@@ -19,16 +19,18 @@ namespace PixiEditor.Views
         {
             InitializeComponent();
         }
-
+
+
         public int FileHeight
         {
-            get => (int)GetValue(FileHeightProperty);
+            get => (int) GetValue(FileHeightProperty);
             set => SetValue(FileHeightProperty, value);
         }
-
+
+
         public int FileWidth
         {
-            get => (int)GetValue(FileWidthProperty);
+            get => (int) GetValue(FileWidthProperty);
             set => SetValue(FileWidthProperty, value);
         }
     }

+ 1 - 0
PixiEditor/Views/PopupTemplate.xaml → PixiEditor/Views/Dialogs/PopupTemplate.xaml

@@ -3,6 +3,7 @@
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:local="clr-namespace:PixiEditor.Views"
         mc:Ignorable="d"
         Title="ResizeDocumentPopup" WindowStartupLocation="CenterScreen" Height="200" Width="400" WindowStyle="None">
 

+ 1 - 1
PixiEditor/Views/PopupTemplate.xaml.cs → PixiEditor/Views/Dialogs/PopupTemplate.xaml.cs

@@ -4,7 +4,7 @@ using System.Windows.Input;
 namespace PixiEditor.Views
 {
     /// <summary>
-    ///     Interaction logic for PopupTemplate.xaml.
+    ///     Interaction logic for PopupTemplate.xaml
     /// </summary>
     public partial class PopupTemplate : Window
     {

+ 4 - 0
PixiEditor/Views/ResizeCanvasPopup.xaml → PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml

@@ -4,11 +4,15 @@
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:PixiEditor.Views"
+        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
+        xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
+        xmlns:converters="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d" Name="window"
         Title="ResizeCanvasPopup" Topmost="True" ShowInTaskbar="False" WindowStartupLocation="CenterScreen"
         Height="390" Width="400" WindowStyle="None">
 
     <Window.Resources>
+        <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
 
     <WindowChrome.WindowChrome>

+ 67 - 66
PixiEditor/Views/ResizeCanvasPopup.xaml.cs → PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml.cs

@@ -1,67 +1,68 @@
-using System.Windows;
-using System.Windows.Input;
-using PixiEditor.Models.Enums;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for ResizeCanvasPopup.xaml.
-    /// </summary>
-    public partial class ResizeCanvasPopup : Window
-    {
-        // Using a DependencyProperty as the backing store for SelectedAnchorPoint.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty SelectedAnchorPointProperty =
-            DependencyProperty.Register(
-                "SelectedAnchorPoint",
-                typeof(AnchorPoint),
-                typeof(ResizeCanvasPopup),
-                new PropertyMetadata(AnchorPoint.Top | AnchorPoint.Left));
-
-        // Using a DependencyProperty as the backing store for NewHeight.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty NewHeightProperty =
-            DependencyProperty.Register("NewHeight", typeof(int), typeof(ResizeCanvasPopup), new PropertyMetadata(0));
-
-        // Using a DependencyProperty as the backing store for NewWidth.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty NewWidthProperty =
-            DependencyProperty.Register("NewWidth", typeof(int), typeof(ResizeCanvasPopup), new PropertyMetadata(0));
-
-        public ResizeCanvasPopup()
-        {
-            InitializeComponent();
-        }
-
-        public AnchorPoint SelectedAnchorPoint
-        {
-            get => (AnchorPoint)GetValue(SelectedAnchorPointProperty);
-            set => SetValue(SelectedAnchorPointProperty, value);
-        }
-
-        public int NewHeight
-        {
-            get => (int)GetValue(NewHeightProperty);
-            set => SetValue(NewHeightProperty, value);
-        }
-
-        public int NewWidth
-        {
-            get => (int)GetValue(NewWidthProperty);
-            set => SetValue(NewWidthProperty, value);
-        }
-
-        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
-        {
-            e.CanExecute = true;
-        }
-
-        private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
-        {
-            SystemCommands.CloseWindow(this);
-        }
-
-        private void Button_Click(object sender, RoutedEventArgs e)
-        {
-            DialogResult = true;
-            Close();
-        }
-    }
+using System.Windows;
+using System.Windows.Input;
+using PixiEditor.Models.Enums;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for ResizeCanvasPopup.xaml
+    /// </summary>
+    public partial class ResizeCanvasPopup : Window
+    {
+        // Using a DependencyProperty as the backing store for SelectedAnchorPoint.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty SelectedAnchorPointProperty =
+            DependencyProperty.Register("SelectedAnchorPoint", typeof(AnchorPoint), typeof(ResizeCanvasPopup),
+                new PropertyMetadata(AnchorPoint.Top | AnchorPoint.Left));
+
+        // Using a DependencyProperty as the backing store for NewHeight.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty NewHeightProperty =
+            DependencyProperty.Register("NewHeight", typeof(int), typeof(ResizeCanvasPopup), new PropertyMetadata(0));
+
+        // Using a DependencyProperty as the backing store for NewWidth.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty NewWidthProperty =
+            DependencyProperty.Register("NewWidth", typeof(int), typeof(ResizeCanvasPopup), new PropertyMetadata(0));
+
+        public ResizeCanvasPopup()
+        {
+            InitializeComponent();
+        }
+
+
+        public AnchorPoint SelectedAnchorPoint
+        {
+            get => (AnchorPoint) GetValue(SelectedAnchorPointProperty);
+            set => SetValue(SelectedAnchorPointProperty, value);
+        }
+
+
+        public int NewHeight
+        {
+            get => (int) GetValue(NewHeightProperty);
+            set => SetValue(NewHeightProperty, value);
+        }
+
+
+        public int NewWidth
+        {
+            get => (int) GetValue(NewWidthProperty);
+            set => SetValue(NewWidthProperty, value);
+        }
+
+
+        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
+        {
+            e.CanExecute = true;
+        }
+
+        private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
+        {
+            SystemCommands.CloseWindow(this);
+        }
+
+        private void Button_Click(object sender, RoutedEventArgs e)
+        {
+            DialogResult = true;
+            Close();
+        }
+    }
 }

+ 4 - 1
PixiEditor/Views/ResizeDocumentPopup.xaml → PixiEditor/Views/Dialogs/ResizeDocumentPopup.xaml

@@ -3,12 +3,15 @@
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:PixiEditor.Views"
+        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
+        xmlns:local="clr-namespace:PixiEditor.Views" xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
+        xmlns:converters="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d" Name="window"
         Title="ResizeDocumentPopup" Topmost="True" ShowInTaskbar="False" WindowStartupLocation="CenterScreen"
         Height="300" Width="400" WindowStyle="None">
 
     <Window.Resources>
+        <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
 
     <WindowChrome.WindowChrome>

+ 9 - 6
PixiEditor/Views/ResizeDocumentPopup.xaml.cs → PixiEditor/Views/Dialogs/ResizeDocumentPopup.xaml.cs

@@ -4,7 +4,7 @@ using System.Windows.Input;
 namespace PixiEditor.Views
 {
     /// <summary>
-    ///     Interaction logic for ResizeDocumentPopup.xaml.
+    ///     Interaction logic for ResizeDocumentPopup.xaml
     /// </summary>
     public partial class ResizeDocumentPopup : Window
     {
@@ -21,19 +21,22 @@ namespace PixiEditor.Views
             InitializeComponent();
             DataContext = this;
         }
-
+
+
         public int NewHeight
         {
-            get => (int)GetValue(NewHeightProperty);
+            get => (int) GetValue(NewHeightProperty);
             set => SetValue(NewHeightProperty, value);
         }
-
+
+
         public int NewWidth
         {
-            get => (int)GetValue(NewWidthProperty);
+            get => (int) GetValue(NewWidthProperty);
             set => SetValue(NewWidthProperty, value);
         }
-
+
+
         private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
         {
             e.CanExecute = true;

+ 3 - 0
PixiEditor/Views/SaveFilePopup.xaml → PixiEditor/Views/Dialogs/SaveFilePopup.xaml

@@ -5,10 +5,13 @@
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:PixiEditor.Views"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
+        xmlns:vm="clr-namespace:PixiEditor.ViewModels"
+        xmlns:helpers="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:helpers1="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d"
         Title="SaveFilePopup" Height="300" Width="400" WindowStyle="None" ResizeMode="NoResize"
         WindowStartupLocation="CenterScreen" Name="saveFilePopup">
     <Window.Resources>
+        <helpers1:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
     <Border BorderBrush="Black" BorderThickness="1">
         <Grid Background="{StaticResource AccentColor}">

+ 9 - 6
PixiEditor/Views/SaveFilePopup.xaml.cs → PixiEditor/Views/Dialogs/SaveFilePopup.xaml.cs

@@ -4,14 +4,15 @@ using PixiEditor.ViewModels;
 namespace PixiEditor.Views
 {
     /// <summary>
-    ///     Interaction logic for SaveFilePopup.xaml.
+    ///     Interaction logic for SaveFilePopup.xaml
     /// </summary>
     public partial class SaveFilePopup : Window
     {
         // Using a DependencyProperty as the backing store for SaveHeight.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty SaveHeightProperty =
             DependencyProperty.Register("SaveHeight", typeof(int), typeof(SaveFilePopup), new PropertyMetadata(32));
-
+
+
         // Using a DependencyProperty as the backing store for SaveWidth.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty SaveWidthProperty =
             DependencyProperty.Register("SaveWidth", typeof(int), typeof(SaveFilePopup), new PropertyMetadata(32));
@@ -23,16 +24,18 @@ namespace PixiEditor.Views
             InitializeComponent();
             DataContext = dataContext;
         }
-
+
+
         public int SaveWidth
         {
-            get => (int)GetValue(SaveWidthProperty);
+            get => (int) GetValue(SaveWidthProperty);
             set => SetValue(SaveWidthProperty, value);
         }
-
+
+
         public int SaveHeight
         {
-            get => (int)GetValue(SaveHeightProperty);
+            get => (int) GetValue(SaveHeightProperty);
             set => SetValue(SaveHeightProperty, value);
         }
 

+ 57 - 59
PixiEditor/Views/MainWindow.xaml

@@ -42,10 +42,10 @@
 
     <i:Interaction.Triggers>
         <i:EventTrigger EventName="KeyDown">
-            <cmd:EventToCommand Command="{Binding KeyDownCommand}" PassEventArgsToCommand="True" />
+            <cmd:EventToCommand Command="{Binding IoSubViewModel.KeyDownCommand}" PassEventArgsToCommand="True" />
         </i:EventTrigger>
         <i:EventTrigger EventName="KeyUp">
-            <cmd:EventToCommand Command="{Binding KeyUpCommand}" PassEventArgsToCommand="True"/>
+            <cmd:EventToCommand Command="{Binding IoSubViewModel.KeyUpCommand}" PassEventArgsToCommand="True"/>
         </i:EventTrigger>
         <i:EventTrigger EventName="ContentRendered">
             <i:InvokeCommandAction Command="{Binding OnStartupCommand}" />
@@ -76,52 +76,52 @@
                     <Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource menuItemStyle}" />
                 </Menu.Resources>
                 <MenuItem Header="_File">
-                    <MenuItem InputGestureText="CTRL+N" Header="_New" Command="{Binding OpenNewFilePopupCommand}" />
-                    <MenuItem Header="_Open" InputGestureText="Ctrl+O" Command="{Binding OpenFileCommand}" />
-                    <MenuItem Header="_Save" InputGestureText="Ctrl+S" Command="{Binding SaveDocumentCommand}" />
+                    <MenuItem InputGestureText="CTRL+N" Header="_New" Command="{Binding FileSubViewModel.OpenNewFilePopupCommand}" />
+                    <MenuItem Header="_Open" InputGestureText="Ctrl+O" Command="{Binding FileSubViewModel.OpenFileCommand}" />
+                    <MenuItem Header="_Save" InputGestureText="Ctrl+S" Command="{Binding FileSubViewModel.SaveDocumentCommand}" />
                     <MenuItem Header="_Save As..." InputGestureText="Ctrl+Shift+S"
-                              Command="{Binding SaveDocumentCommand}" CommandParameter="AsNew" />
-                    <MenuItem Header="_Export" InputGestureText="Ctrl+Shift+Alt+S" Command="{Binding ExportFileCommand}" />
+                              Command="{Binding FileSubViewModel.SaveDocumentCommand}" CommandParameter="AsNew" />
+                    <MenuItem Header="_Export" InputGestureText="Ctrl+Shift+Alt+S" Command="{Binding FileSubViewModel.ExportFileCommand}" />
                     <Separator />
                     <MenuItem Header="_Exit" Command="{x:Static SystemCommands.CloseWindowCommand}" />
                 </MenuItem>
                 <MenuItem Header="_Edit">
-                    <MenuItem Header="_Undo" InputGestureText="Ctrl+Z" Command="{Binding UndoCommand}" />
-                    <MenuItem Header="_Redo" InputGestureText="Ctrl+Y" Command="{Binding RedoCommand}" />
+                    <MenuItem Header="_Undo" InputGestureText="Ctrl+Z" Command="{Binding UndoSubViewModel.UndoCommand}" />
+                    <MenuItem Header="_Redo" InputGestureText="Ctrl+Y" Command="{Binding UndoSubViewModel.RedoCommand}" />
                     <Separator />
-                    <MenuItem Header="_Cut" Command="{Binding CutCommand}" InputGestureText="Ctrl+X" />
-                    <MenuItem Header="_Copy" Command="{Binding CopyCommand}" InputGestureText="Ctrl+C" />
-                    <MenuItem Header="_Paste" Command="{Binding PasteCommand}" InputGestureText="Ctrl+V" />
-                    <MenuItem Header="_Duplicate" Command="{Binding DuplicateCommand}" InputGestureText="Ctrl+D" />
+                    <MenuItem Header="_Cut" Command="{Binding ClipboardSubViewModel.CutCommand}" InputGestureText="Ctrl+X" />
+                    <MenuItem Header="_Copy" Command="{Binding ClipboardSubViewModel.CopyCommand}" InputGestureText="Ctrl+C" />
+                    <MenuItem Header="_Paste" Command="{Binding ClipboardSubViewModel.PasteCommand}" InputGestureText="Ctrl+V" />
+                    <MenuItem Header="_Duplicate" Command="{Binding ClipboardSubViewModel.DuplicateCommand}" InputGestureText="Ctrl+J" />
                     <Separator />
-                    <MenuItem Header="_Delete Selected" Command="{Binding DeletePixelsCommand}"
+                    <MenuItem Header="_Delete Selected" Command="{Binding DocumentSubViewModel.DeletePixelsCommand}"
                               InputGestureText="Delete" />
                 </MenuItem>
                 <MenuItem Header="_Select">
-                    <MenuItem Header="_Select All" Command="{Binding SelectAllCommand}" InputGestureText="Ctrl+A" />
-                    <MenuItem Header="_Deselect" Command="{Binding DeselectCommand}" InputGestureText="Ctrl+D" />
+                    <MenuItem Header="_Select All" Command="{Binding SelectionSubViewModel.SelectAllCommand}" InputGestureText="Ctrl+A" />
+                    <MenuItem Header="_Deselect" Command="{Binding SelectionSubViewModel.DeselectCommand}" InputGestureText="Ctrl+D" />
                 </MenuItem>
                 <MenuItem Header="_Document">
-                    <MenuItem Header="Resize Document..." Command="{Binding OpenResizePopupCommand}"
+                    <MenuItem Header="Resize Document..." Command="{Binding DocumentSubViewModel.OpenResizePopupCommand}"
                               InputGestureText="Ctrl+Shift+I" />
-                    <MenuItem Header="Resize Canvas..." Command="{Binding OpenResizePopupCommand}"
+                    <MenuItem Header="Resize Canvas..." Command="{Binding DocumentSubViewModel.OpenResizePopupCommand}"
                               CommandParameter="canvas" InputGestureText="Ctrl+Shift+C" />
-                    <MenuItem Header="Clip Canvas" Command="{Binding ClipCanvasCommand}" />
+                    <MenuItem Header="Clip Canvas" Command="{Binding DocumentSubViewModel.ClipCanvasCommand}" />
                     <Separator/>
-                    <MenuItem Header="Center Content" Command="{Binding CenterContentCommand}" />
+                    <MenuItem Header="Center Content" Command="{Binding DocumentSubViewModel.CenterContentCommand}" />
                 </MenuItem>
                 <MenuItem Header="_Help">
                     <MenuItem Header="Documentation" Command="{Binding OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/flabbet/PixiEditor/wiki"/>
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor/wiki"/>
                     <MenuItem Header="Repository" Command="{Binding OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/flabbet/PixiEditor"/>
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor"/>
                     <MenuItem Header="Shortcuts" Command="{Binding OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/flabbet/PixiEditor/wiki/Shortcuts"/>
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor/wiki/Shortcuts"/>
                     <Separator/>
                     <MenuItem Header="License" Command="{Binding OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/flabbet/PixiEditor/blob/master/LICENSE"/>
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor/blob/master/LICENSE"/>
                     <MenuItem Header="Third Party Licenses" Command="{Binding OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/flabbet/PixiEditor/wiki/Third-party-licenses"/>
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor/wiki/Third-party-licenses"/>
                 </MenuItem>
             </Menu>
             <StackPanel DockPanel.Dock="Right" VerticalAlignment="Top" Orientation="Horizontal"
@@ -162,26 +162,23 @@
         </StackPanel>
         <Grid Grid.Column="1" Grid.Row="2" Background="#303030" Margin="0,7,5,0">
             <Grid>
-                <vws:MainDrawingPanel ZoomPercentage="{Binding ZoomPercentage, Mode=TwoWay}" Center="{Binding RecenterZoombox, Mode=TwoWay}" x:Name="DrawingPanel"
-                                      CenterOnStart="True" Cursor="{Binding ToolCursor}" 
-                                      MiddleMouseClickedCommand="{Binding SelectToolCommand}" 
+                <vws:MainDrawingPanel ZoomPercentage="{Binding ViewportSubViewModel.ZoomPercentage, Mode=TwoWay}" Center="{Binding ViewportSubViewModel.RecenterZoombox, Mode=TwoWay}" x:Name="DrawingPanel"
+                                      CenterOnStart="True" Cursor="{Binding ToolsSubViewModel.ToolCursor}" 
+                                      MiddleMouseClickedCommand="{Binding ToolsSubViewModel.SelectToolCommand}" 
                                       MiddleMouseClickedCommandParameter="{x:Static tools:ToolType.MoveViewport}"
-                                      ViewportPosition="{Binding ViewportPosition, Mode=TwoWay}">
+                                      ViewportPosition="{Binding ViewportSubViewModel.ViewportPosition, Mode=TwoWay}">
                     <i:Interaction.Triggers>
                         <i:EventTrigger EventName="MouseMove">
-                            <i:InvokeCommandAction Command="{Binding MouseMoveCommand}" />
-                        </i:EventTrigger>
-                        <i:EventTrigger EventName="MouseUp">
-                            <i:InvokeCommandAction Command="{Binding MouseUpCommand}" />
+                            <i:InvokeCommandAction Command="{Binding IoSubViewModel.MouseMoveCommand}" />
                         </i:EventTrigger>
                         <i:EventTrigger EventName="MouseDown">
-                            <i:InvokeCommandAction Command="{Binding MouseDownCommand}"/>
+                            <i:InvokeCommandAction Command="{Binding IoSubViewModel.MouseDownCommand}"/>
                         </i:EventTrigger>
                     </i:Interaction.Triggers>
                     <i:Interaction.Behaviors>
                         <behaviors:MouseBehaviour RelativeTo="{Binding ElementName=DrawingPanel, Path=Item}"
-                                                  MouseX="{Binding MouseXOnCanvas, Mode=OneWayToSource}"
-                                                  MouseY="{Binding MouseYOnCanvas, Mode=OneWayToSource}" />
+                                                  MouseX="{Binding IoSubViewModel.MouseXOnCanvas, Mode=OneWayToSource}"
+                                                  MouseY="{Binding IoSubViewModel.MouseYOnCanvas, Mode=OneWayToSource}" />
                     </i:Interaction.Behaviors>
                     <vws:MainDrawingPanel.Item>
                         <Canvas Width="{Binding BitmapManager.ActiveDocument.Width}"
@@ -212,11 +209,11 @@
                                     </DataTemplate>
                                 </ItemsControl.ItemTemplate>
                             </ItemsControl>
-                            <Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding ActiveSelection.SelectionLayer.LayerBitmap}"
+                            <Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding SelectionSubViewModel.ActiveSelection.SelectionLayer.LayerBitmap}"
                                    RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform"
-                                   Width="{Binding ActiveSelection.SelectionLayer.Width}"
-                                   Height="{Binding ActiveSelection.SelectionLayer.Height}" 
-                                   Margin="{Binding ActiveSelection.SelectionLayer.Offset}" />
+                                   Width="{Binding SelectionSubViewModel.ActiveSelection.SelectionLayer.Width}"
+                                   Height="{Binding SelectionSubViewModel.ActiveSelection.SelectionLayer.Height}" 
+                                   Margin="{Binding SelectionSubViewModel.ActiveSelection.SelectionLayer.Offset}" />
                         </Canvas>
                     </vws:MainDrawingPanel.Item>
                 </vws:MainDrawingPanel>
@@ -226,13 +223,13 @@
         <StackPanel Orientation="Vertical" Cursor="Arrow" Grid.Row="2" Grid.Column="0" Margin="0,7,5,0"
                     Background="{StaticResource AccentColor}" Grid.RowSpan="2">
 
-            <ItemsControl ItemsSource="{Binding ToolSet}">
+            <ItemsControl ItemsSource="{Binding ToolsSubViewModel.ToolSet}">
                 <ItemsControl.ItemTemplate>
                     <DataTemplate>
                         <Button BorderBrush="White"
                                 BorderThickness="{Binding IsActive, Converter={StaticResource BoolToIntConverter}}"
                                 Style="{StaticResource ToolButtonStyle}"
-                                Command="{Binding Path=DataContext.SelectToolCommand,
+                                Command="{Binding Path=DataContext.ToolsSubViewModel.SelectToolCommand,
                             RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                                 CommandParameter="{Binding ToolType}" ToolTip="{Binding Tooltip}">
                             <Button.Background>
@@ -252,8 +249,8 @@
             </Grid.RowDefinitions>
             <StackPanel Grid.Row="2" Orientation="Vertical" ZIndex="15">
             </StackPanel>
-            <colorpicker:StandardColorPicker Grid.Row="0" SelectedColor="{Binding PrimaryColor, Mode=TwoWay}"
-                             SecondaryColor="{Binding SecondaryColor, Mode=TwoWay}" />
+            <colorpicker:StandardColorPicker Grid.Row="0" SelectedColor="{Binding ColorsSubViewModel.PrimaryColor, Mode=TwoWay}"
+                             SecondaryColor="{Binding ColorsSubViewModel.SecondaryColor, Mode=TwoWay}" />
             <avalondock:DockingManager Foreground="White" Background="{StaticResource AccentColor}" BorderThickness="0"
                                        Grid.Row="1">
                 <avalondock:LayoutRoot x:Name="LayoutRoot">
@@ -263,7 +260,7 @@
                                                          CanClose="False" CanAutoHide="False"
                                                          CanDockAsTabbedDocument="False" CanFloat="True">
                                 <StackPanel Orientation="Vertical">
-                                    <Button Command="{Binding NewLayerCommand}" Height="30" Content="New Layer"
+                                    <Button Command="{Binding LayersSubViewModel.NewLayerCommand}" Height="30" Content="New Layer"
                                             HorizontalAlignment="Stretch" Margin="5"
                                             Style="{StaticResource DarkRoundButton}" />
                                     <StackPanel Orientation="Horizontal" Margin="10,0">
@@ -284,27 +281,27 @@
                                         <ItemsControl.ItemTemplate>
                                             <DataTemplate>
                                                 <vws:LayerItem LayerIndex="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
-                            Path=(ItemsControl.AlternationIndex)}" SetActiveLayerCommand="{Binding Path=DataContext.SetActiveLayerCommand, ElementName=mainWindow}"
+                            Path=(ItemsControl.AlternationIndex)}" SetActiveLayerCommand="{Binding Path=DataContext.LayersSubViewModel.SetActiveLayerCommand, ElementName=mainWindow}"
                                                                LayerName="{Binding Name, Mode=TwoWay}" IsActive="{Binding IsActive, Mode=TwoWay}"
                                                                IsRenaming="{Binding IsRenaming, Mode=TwoWay}"
-                                                               MoveToBackCommand="{Binding DataContext.MoveToBackCommand, ElementName=mainWindow}"
-                                                               MoveToFrontCommand="{Binding DataContext.MoveToFrontCommand, ElementName=mainWindow}">
+                                                               MoveToBackCommand="{Binding DataContext.LayersSubViewModel.MoveToBackCommand, ElementName=mainWindow}"
+                                                               MoveToFrontCommand="{Binding DataContext.LayersSubViewModel.MoveToFrontCommand, ElementName=mainWindow}">
                                                     <vws:LayerItem.ContextMenu>
                                                         <ContextMenu>
                                                             <MenuItem Header="Delete"
-                                                                              Command="{Binding DeleteLayerCommand, Source={StaticResource ViewModelMain}}"
+                                                                              Command="{Binding LayersSubViewModel.DeleteLayerCommand, Source={StaticResource ViewModelMain}}"
                                                                               CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
                             Path=(ItemsControl.AlternationIndex)}" />
                                                             <MenuItem Header="Rename"
-                                                                              Command="{Binding RenameLayerCommand, Source={StaticResource ViewModelMain}}"
+                                                                              Command="{Binding LayersSubViewModel.RenameLayerCommand, Source={StaticResource ViewModelMain}}"
                                                                               CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
                             Path=(ItemsControl.AlternationIndex)}" />
                                                             <MenuItem Header="Move to front"
-                                                                              Command="{Binding MoveToFrontCommand, Source={StaticResource ViewModelMain}}"
+                                                                              Command="{Binding LayersSubViewModel.MoveToFrontCommand, Source={StaticResource ViewModelMain}}"
                                                                               CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
                             Path=(ItemsControl.AlternationIndex)}" />
                                                             <MenuItem Header="Move to back"
-                                                                              Command="{Binding MoveToBackCommand, Source={StaticResource ViewModelMain}}"
+                                                                              Command="{Binding LayersSubViewModel.MoveToBackCommand, Source={StaticResource ViewModelMain}}"
                                                                               CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
                             Path=(ItemsControl.AlternationIndex)}" />
                                                         </ContextMenu>
@@ -361,14 +358,14 @@
                                                         <i:EventTrigger EventName="MouseDown">
                                                             <i:InvokeCommandAction
                                                                 Command="{Binding
-                                                                    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.SelectColorCommand}"
+                                                                    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ColorsSubViewModel.SelectColorCommand}"
                                                                 CommandParameter="{Binding}" />
                                                         </i:EventTrigger>
                                                     </i:Interaction.Triggers>
                                                     <Grid.ContextMenu>
                                                         <ContextMenu>
                                                             <MenuItem Header="Remove" Foreground="White"
-                                                                      Command="{Binding RemoveSwatchCommand, Source={StaticResource ViewModelMain}}"
+                                                                      Command="{Binding ColorsSubViewModel.RemoveSwatchCommand, Source={StaticResource ViewModelMain}}"
                                                                       CommandParameter="{Binding}" />
                                                         </ContextMenu>
                                                     </Grid.ContextMenu>
@@ -389,17 +386,18 @@
         <DockPanel Grid.Row="3" Grid.Column="1">
             <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
                 <TextBlock Text="X:" Foreground="White" FontSize="16"/>
-                <TextBlock Margin="4,0,10,0" Text="{Binding MouseXOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
+                <TextBlock Margin="4,0,10,0" Text="{Binding IoSubViewModel.MouseXOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
                 <TextBlock Text="Y:" Foreground="White" FontSize="16"/>
-                <TextBlock Margin="4,0,10,0" Text="{Binding MouseYOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
+                <TextBlock Margin="4,0,10,0" Text="{Binding IoSubViewModel.MouseYOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
             </StackPanel>
         </DockPanel>
         <StackPanel Margin="10,0,0,0" VerticalAlignment="Center" Grid.Row="3"
                        Grid.Column="3" Orientation="Horizontal">
             <Button Style="{StaticResource BaseDarkButton}" 
-                    Visibility="{Binding UpdateReadyToInstall, Converter={StaticResource BoolToVisibilityConverter}}" FontSize="14" Height="20" Command="{Binding RestartApplicationCommand}">Restart</Button>
+                    Visibility="{Binding UpdateSubViewModel.UpdateReadyToInstall, Converter={StaticResource BoolToVisibilityConverter}}" FontSize="14" Height="20" 
+                    Command="{Binding UpdateSubViewModel.RestartApplicationCommand}">Restart</Button>
             <TextBlock VerticalAlignment="Center" Padding="10" HorizontalAlignment="Right"
-                       Foreground="White" FontSize="14"  Text="{Binding VersionText}" />
+                       Foreground="White" FontSize="14"  Text="{Binding UpdateSubViewModel.VersionText}" />
         </StackPanel>
     </Grid>
-</Window>
+</Window>

+ 60 - 10
PixiEditor/Views/MainWindow.xaml.cs

@@ -1,8 +1,12 @@
 using System;
 using System.ComponentModel;
+using System.Diagnostics;
 using System.IO;
+using System.Reflection;
 using System.Windows;
 using System.Windows.Input;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Processes;
 using PixiEditor.UpdateModule;
 using PixiEditor.ViewModels;
@@ -14,7 +18,7 @@ namespace PixiEditor
     /// </summary>
     public partial class MainWindow : Window
     {
-        private ViewModelMain viewModel;
+        private readonly ViewModelMain viewModel;
 
         public MainWindow()
         {
@@ -68,20 +72,66 @@ namespace PixiEditor
         {
             string dir = AppDomain.CurrentDomain.BaseDirectory;
             UpdateDownloader.CreateTempDirectory();
-            bool updateFileExists = Directory.GetFiles(UpdateDownloader.DownloadLocation, "update-*.zip").Length > 0;
+            bool updateZipExists = Directory.GetFiles(UpdateDownloader.DownloadLocation, "update-*.zip").Length > 0;
+            string[] updateExeFiles = Directory.GetFiles(UpdateDownloader.DownloadLocation, "update-*.exe");
+            bool updateExeExists = updateExeFiles.Length > 0;
+
             string updaterPath = Path.Join(dir, "PixiEditor.UpdateInstaller.exe");
-            if (updateFileExists && File.Exists(updaterPath))
+
+            if (updateZipExists || updateExeExists)
             {
-                try
-                {
-                    ProcessHelper.RunAsAdmin(updaterPath);
-                    Close();
-                }
-                catch (Win32Exception)
+                ViewModelMain.Current.UpdateSubViewModel.UpdateReadyToInstall = true;
+                var result = ConfirmationDialog.Show("Update is ready to install. Do you want to install it now?");
+                if (result == Models.Enums.ConfirmationType.Yes)
                 {
-                    MessageBox.Show("Couldn't update without administrator rights.", "Insufficient permissions", MessageBoxButton.OK, MessageBoxImage.Error);
+                    if (updateZipExists && File.Exists(updaterPath))
+                    {
+                        InstallHeadless(updaterPath);
+                    }
+                    else if (updateExeExists)
+                    {
+                        OpenExeInstaller(updateExeFiles[0]);
+                    }
                 }
             }
         }
+
+        private void InstallHeadless(string updaterPath)
+        {
+            try
+            {
+                ProcessHelper.RunAsAdmin(updaterPath);
+                Close();
+            }
+            catch (Win32Exception)
+            {
+                MessageBox.Show(
+                    "Couldn't update without administrator rights.",
+                    "Insufficient permissions",
+                    MessageBoxButton.OK,
+                    MessageBoxImage.Error);
+            }
+        }
+
+        private void OpenExeInstaller(string updateExeFile)
+        {
+            bool alreadyUpdated = AssemblyHelper.GetCurrentAssemblyVersion() ==
+                    updateExeFile.Split('-')[1].Split(".exe")[0];
+
+            if (!alreadyUpdated)
+            {
+                RestartToUpdate(updateExeFile);
+            }
+            else
+            {
+                File.Delete(updateExeFile);
+            }
+        }
+
+        private void RestartToUpdate(string updateExeFile)
+        {
+            Process.Start(updateExeFile);
+            Close();
+        }
     }
 }

+ 9 - 9
PixiEditor/Views/AnchorPointPicker.xaml → PixiEditor/Views/UserControls/AnchorPointPicker.xaml

@@ -20,63 +20,63 @@
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" ToolTip="Top left" Grid.Row="0"
                       Grid.Column="0" BorderBrush="Black">
             <ToggleButton.Background>
-                <ImageBrush ImageSource="../Images/AnchorDot.png" />
+                <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="0" ToolTip="Top center"
                       Grid.Column="1" BorderBrush="Black">
             <ToggleButton.Background>
-                <ImageBrush ImageSource="../Images/AnchorDot.png" />
+                <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" ToolTip="Top right" Grid.Row="0"
                       Grid.Column="2" BorderBrush="Black">
             <ToggleButton.Background>
-                <ImageBrush ImageSource="../Images/AnchorDot.png" />
+                <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="1" ToolTip="Middle left"
                       Grid.Column="0" BorderBrush="Black">
             <ToggleButton.Background>
-                <ImageBrush ImageSource="../Images/AnchorDot.png" />
+                <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="1" Grid.Column="1"
                       ToolTip="Middle center" BorderBrush="Black">
             <ToggleButton.Background>
-                <ImageBrush ImageSource="../Images/AnchorDot.png" />
+                <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="1" Grid.Column="2"
                       ToolTip="Middle right" BorderBrush="Black">
             <ToggleButton.Background>
-                <ImageBrush ImageSource="../Images/AnchorDot.png" />
+                <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="2" Grid.Column="0"
                       ToolTip="Bottom left" BorderBrush="Black">
             <ToggleButton.Background>
-                <ImageBrush ImageSource="../Images/AnchorDot.png" />
+                <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="2" Grid.Column="1"
                       ToolTip="Bottom center" BorderBrush="Black">
             <ToggleButton.Background>
-                <ImageBrush ImageSource="../Images/AnchorDot.png" />
+                <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
         <ToggleButton Checked="ToggleButton_Checked" Click="ToggleButton_Click" Margin="0.25"
                       Style="{DynamicResource AnchorPointToggleButtonStyle}" Grid.Row="2" Grid.Column="2"
                       ToolTip="Bottom right" BorderBrush="Black">
             <ToggleButton.Background>
-                <ImageBrush ImageSource="../Images/AnchorDot.png" />
+                <ImageBrush ImageSource="../../Images/AnchorDot.png" />
             </ToggleButton.Background>
         </ToggleButton>
     </Grid>

+ 45 - 49
PixiEditor/Views/AnchorPointPicker.xaml.cs → PixiEditor/Views/UserControls/AnchorPointPicker.xaml.cs

@@ -1,50 +1,46 @@
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Controls.Primitives;
-using PixiEditor.Models.Enums;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for AnchorPointPicker.xaml.
-    /// </summary>
-    public partial class AnchorPointPicker : UserControl
-    {
-        // Using a DependencyProperty as the backing store for AnchorPoint.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty AnchorPointProperty =
-            DependencyProperty.Register("AnchorPoint", typeof(AnchorPoint), typeof(AnchorPointPicker), new PropertyMetadata());
-
-        private ToggleButton selectedToggleButton;
-
-        public AnchorPointPicker()
-        {
-            InitializeComponent();
-        }
-
-        public AnchorPoint AnchorPoint
-        {
-            get => (AnchorPoint)GetValue(AnchorPointProperty);
-            set => SetValue(AnchorPointProperty, value);
-        }
-
-        private void ToggleButton_Checked(object sender, RoutedEventArgs e)
-        {
-            ToggleButton btn = (ToggleButton)sender;
-            AnchorPoint = (AnchorPoint)(1 << (Grid.GetRow(btn) + 3)) | (AnchorPoint)(1 << Grid.GetColumn(btn));
-            if (selectedToggleButton != null)
-            {
-                selectedToggleButton.IsChecked = false;
-            }
-
-            selectedToggleButton = btn;
-        }
-
-        private void ToggleButton_Click(object sender, RoutedEventArgs e)
-        {
-            if ((sender as ToggleButton).IsChecked.Value)
-            {
-                e.Handled = true;
-            }
-        }
-    }
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using PixiEditor.Models.Enums;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for AnchorPointPicker.xaml
+    /// </summary>
+    public partial class AnchorPointPicker : UserControl
+    {
+        // Using a DependencyProperty as the backing store for AnchorPoint.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty AnchorPointProperty =
+            DependencyProperty.Register("AnchorPoint", typeof(AnchorPoint), typeof(AnchorPointPicker),
+                new PropertyMetadata());
+
+
+        private ToggleButton _selectedToggleButton;
+
+        public AnchorPointPicker()
+        {
+            InitializeComponent();
+        }
+
+        public AnchorPoint AnchorPoint
+        {
+            get => (AnchorPoint) GetValue(AnchorPointProperty);
+            set => SetValue(AnchorPointProperty, value);
+        }
+
+        private void ToggleButton_Checked(object sender, RoutedEventArgs e)
+        {
+            ToggleButton btn = (ToggleButton) sender;
+            AnchorPoint = (AnchorPoint) (1 << (Grid.GetRow(btn) + 3)) | (AnchorPoint) (1 << Grid.GetColumn(btn));
+            if (_selectedToggleButton != null) _selectedToggleButton.IsChecked = false;
+            _selectedToggleButton = btn;
+        }
+
+        private void ToggleButton_Click(object sender, RoutedEventArgs e)
+        {
+            if ((sender as ToggleButton).IsChecked.Value)
+                e.Handled = true;
+        }
+    }
 }

+ 24 - 24
PixiEditor/Views/EditableTextBlock.xaml → PixiEditor/Views/UserControls/EditableTextBlock.xaml

@@ -1,25 +1,25 @@
-<UserControl x:Class="PixiEditor.Views.EditableTextBlock"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
-             mc:Ignorable="d"
-             d:DesignHeight="60" d:DesignWidth="100">
-    <UserControl.Resources>
-        <converters:OppositeVisibilityConverter x:Key="OppositeVisibilityConverter" />
-    </UserControl.Resources>
-    <Grid>
-        <TextBlock Foreground="Snow" MouseDown="TextBlock_MouseDown"
-                   Visibility="{Binding Path=TextBlockVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
-                   Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
-        <TextBox Style="{StaticResource DarkTextBoxStyle}"
-                 LostFocus="TextBox_LostFocus"
-                 Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
-                 KeyDown="TextBox_KeyDown"
-                 LostKeyboardFocus="TextBox_LostKeyboardFocus"
-                 Visibility="{Binding Path=TextBlockVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, 
-            Converter={StaticResource OppositeVisibilityConverter}}"
-                 Name="textBox" />
-    </Grid>
+<UserControl x:Class="PixiEditor.Views.EditableTextBlock"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+             mc:Ignorable="d"
+             d:DesignHeight="60" d:DesignWidth="100">
+    <UserControl.Resources>
+        <converters:OppositeVisibilityConverter x:Key="OppositeVisibilityConverter" />
+    </UserControl.Resources>
+    <Grid>
+        <TextBlock Foreground="Snow" MouseDown="TextBlock_MouseDown"
+                   Visibility="{Binding Path=TextBlockVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
+                   Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
+        <TextBox Style="{StaticResource DarkTextBoxStyle}"
+                 LostFocus="TextBox_LostFocus"
+                 Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
+                 KeyDown="TextBox_KeyDown"
+                 LostKeyboardFocus="textBox_LostKeyboardFocus"
+                 Visibility="{Binding Path=TextBlockVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, 
+            Converter={StaticResource OppositeVisibilityConverter}}"
+                 Name="textBox" />
+    </Grid>
 </UserControl>

+ 100 - 98
PixiEditor/Views/EditableTextBlock.xaml.cs → PixiEditor/Views/UserControls/EditableTextBlock.xaml.cs

@@ -1,99 +1,101 @@
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using PixiEditor.Models.Controllers.Shortcuts;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for EditableTextBlock.xaml.
-    /// </summary>
-    public partial class EditableTextBlock : UserControl
-    {
-        // Using a DependencyProperty as the backing store for TextBlockVisibility.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty TextBlockVisibilityProperty =
-            DependencyProperty.Register("TextBlockVisibility", typeof(Visibility), typeof(EditableTextBlock), new PropertyMetadata(Visibility.Visible));
-
-        // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty TextProperty =
-            DependencyProperty.Register("Text", typeof(string), typeof(EditableTextBlock), new PropertyMetadata(default(string)));
-
-        // Using a DependencyProperty as the backing store for EnableEditing.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty EnableEditingProperty =
-            DependencyProperty.Register("IsEditing", typeof(bool), typeof(EditableTextBlock), new PropertyMetadata(OnIsEditingChanged));
-
-        public EditableTextBlock()
-        {
-            InitializeComponent();
-        }
-
-        public Visibility TextBlockVisibility
-        {
-            get => (Visibility)GetValue(TextBlockVisibilityProperty);
-            set => SetValue(TextBlockVisibilityProperty, value);
-        }
-
-        public bool IsEditing
-        {
-            get => (bool)GetValue(EnableEditingProperty);
-            set => SetValue(EnableEditingProperty, value);
-        }
-
-        public string Text
-        {
-            get => (string)GetValue(TextProperty);
-            set => SetValue(TextProperty, value);
-        }
-
-        public void EnableEditing()
-        {
-            ShortcutController.BlockShortcutExecution = true;
-            TextBlockVisibility = Visibility.Hidden;
-            IsEditing = true;
-            textBox.Focus();
-            textBox.SelectAll();
-        }
-
-        private static void OnIsEditingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            if ((bool)e.NewValue)
-            {
-                EditableTextBlock tb = (EditableTextBlock)d;
-                tb.EnableEditing();
-            }
-        }
-
-        private void DisableEditing()
-        {
-            TextBlockVisibility = Visibility.Visible;
-            ShortcutController.BlockShortcutExecution = false;
-            IsEditing = false;
-        }
-
-        private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
-        {
-            if (e.ChangedButton == MouseButton.Left && e.ClickCount == 2)
-            {
-                EnableEditing();
-            }
-        }
-
-        private void TextBox_KeyDown(object sender, KeyEventArgs e)
-        {
-            if (e.Key == Key.Enter)
-            {
-                DisableEditing();
-            }
-        }
-
-        private void TextBox_LostFocus(object sender, RoutedEventArgs e)
-        {
-            DisableEditing();
-        }
-
-        private void TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
-        {
-            DisableEditing();
-        }
-    }
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Controllers.Shortcuts;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for EditableTextBlock.xaml
+    /// </summary>
+    public partial class EditableTextBlock : UserControl
+    {
+        // Using a DependencyProperty as the backing store for TextBlockVisibility.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty TextBlockVisibilityProperty =
+            DependencyProperty.Register("TextBlockVisibility", typeof(Visibility), typeof(EditableTextBlock),
+                new PropertyMetadata(Visibility.Visible));
+
+
+        // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty TextProperty =
+            DependencyProperty.Register("Text", typeof(string), typeof(EditableTextBlock),
+                new PropertyMetadata(default(string)));
+
+        // Using a DependencyProperty as the backing store for EnableEditing.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty EnableEditingProperty =
+            DependencyProperty.Register("IsEditing", typeof(bool), typeof(EditableTextBlock),
+                new PropertyMetadata(OnIsEditingChanged));
+
+        public EditableTextBlock()
+        {
+            InitializeComponent();
+        }
+
+        public Visibility TextBlockVisibility
+        {
+            get => (Visibility) GetValue(TextBlockVisibilityProperty);
+            set => SetValue(TextBlockVisibilityProperty, value);
+        }
+
+
+        public bool IsEditing
+        {
+            get => (bool) GetValue(EnableEditingProperty);
+            set => SetValue(EnableEditingProperty, value);
+        }
+
+
+        public string Text
+        {
+            get => (string) GetValue(TextProperty);
+            set => SetValue(TextProperty, value);
+        }
+
+        private static void OnIsEditingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            if ((bool) e.NewValue)
+            {
+                EditableTextBlock tb = (EditableTextBlock) d;
+                tb.EnableEditing();
+            }
+        }
+
+        public void EnableEditing()
+        {
+            ShortcutController.BlockShortcutExecution = true;
+            TextBlockVisibility = Visibility.Hidden;
+            IsEditing = true;
+            textBox.Focus();
+            textBox.SelectAll();
+        }
+
+        private void DisableEditing()
+        {
+            TextBlockVisibility = Visibility.Visible;
+            ShortcutController.BlockShortcutExecution = false;
+            IsEditing = false;
+        }
+
+
+        private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+            if (e.ChangedButton == MouseButton.Left && e.ClickCount == 2) EnableEditing();
+        }
+
+        private void TextBox_KeyDown(object sender, KeyEventArgs e)
+        {
+            if (e.Key == Key.Enter) DisableEditing();
+        }
+
+        private void TextBox_LostFocus(object sender, RoutedEventArgs e)
+        {
+            DisableEditing();
+        }
+
+        private void textBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
+        {
+            DisableEditing();
+        }
+    }
 }

+ 18 - 20
PixiEditor/Views/LayerItem.xaml → PixiEditor/Views/UserControls/LayerItem.xaml

@@ -1,12 +1,12 @@
 <UserControl x:Class="PixiEditor.Views.LayerItem"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
              xmlns:local="clr-namespace:PixiEditor.Views"
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
-             mc:Ignorable="d"
+             mc:Ignorable="d" 
              d:DesignHeight="60" d:DesignWidth="250" Name="uc" MouseLeave="LayerItem_OnMouseLeave" MouseEnter="LayerItem_OnMouseEnter">
     <UserControl.Resources>
         <converters:BoolToColorConverter x:Key="BoolToColorConverter" />
@@ -17,34 +17,32 @@
             <i:EventTrigger EventName="MouseDown">
                 <i:InvokeCommandAction Command="{Binding ElementName=uc, 
                             Path=SetActiveLayerCommand}"
-                                       CommandParameter="{Binding Path=LayerIndex, ElementName=uc}" />
+                                       CommandParameter="{Binding Path=LayerIndex, ElementName=uc}"/>
             </i:EventTrigger>
         </i:Interaction.Triggers>
         <Grid>
             <Grid.ColumnDefinitions>
-                <ColumnDefinition Width="35" />
-                <ColumnDefinition Width="199*" />
-                <ColumnDefinition Width="35" />
+                <ColumnDefinition Width="35"/>
+                <ColumnDefinition Width="199*"/>
+                <ColumnDefinition Width="35"/>
             </Grid.ColumnDefinitions>
-            <CheckBox VerticalAlignment="Center"
+            <CheckBox Style="{StaticResource ImageCheckBox}" VerticalAlignment="Center"
                       IsThreeState="False" HorizontalAlignment="Center"
                       IsChecked="{Binding Path=IsVisible, Mode=TwoWay}" Grid.Column="0" Height="16" />
             <local:EditableTextBlock
-                IsEditing="{Binding IsRenaming, ElementName=uc, Mode=TwoWay}" Grid.Column="1" FontSize="16" HorizontalAlignment="Center"
-                VerticalAlignment="Center"
-                Text="{Binding LayerName, ElementName=uc, Mode=TwoWay}" />
-            <StackPanel Visibility="{Binding Path=ControlButtonsVisible, ElementName=uc}"
-                        Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Width="15"
+                    IsEditing="{Binding IsRenaming, ElementName=uc, Mode=TwoWay}" Grid.Column="1" FontSize="16" HorizontalAlignment="Center"
+                    VerticalAlignment="Center"
+                    Text="{Binding LayerName, ElementName=uc, Mode=TwoWay}" />
+            <StackPanel Visibility="{Binding Path=ControlButtonsVisible, ElementName=uc}" 
+                        Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Width="15" 
                         Grid.Column="2">
-                <Button CommandParameter="{Binding LayerIndex, ElementName=uc}" Command="{Binding Path=MoveToFrontCommand, ElementName=uc}" Background="Transparent"
-                        Style="{StaticResource OpacityButtonStyle}" Foreground="White" HorizontalAlignment="Center" BorderThickness="0">
-                    <TextBlock Text="&#9650;" />
+                <Button CommandParameter="{Binding LayerIndex, ElementName=uc}" Command="{Binding Path=MoveToFrontCommand, ElementName=uc}" Background="Transparent" Style="{StaticResource OpacityButtonStyle}" Foreground="White" HorizontalAlignment="Center" BorderThickness="0">
+                    <TextBlock Text="&#9650;"/>
                 </Button>
-                <Button CommandParameter="{Binding LayerIndex, ElementName=uc}" Command="{Binding Path=MoveToBackCommand, ElementName=uc}" Background="Transparent"
-                        HorizontalAlignment="Center" Style="{StaticResource OpacityButtonStyle}" Foreground="White" BorderThickness="0">
-                    <TextBlock Text="&#9660;" />
+                <Button CommandParameter="{Binding LayerIndex, ElementName=uc}" Command="{Binding Path=MoveToBackCommand, ElementName=uc}" Background="Transparent" HorizontalAlignment="Center" Style="{StaticResource OpacityButtonStyle}" Foreground="White" BorderThickness="0">
+                    <TextBlock Text="&#9660;"/>
                 </Button>
             </StackPanel>
         </Grid>
     </Border>
-</UserControl>
+</UserControl>

+ 59 - 46
PixiEditor/Views/LayerItem.xaml.cs → PixiEditor/Views/UserControls/LayerItem.xaml.cs

@@ -1,93 +1,105 @@
-using System.Windows;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
 using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
 using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
 using PixiEditor.Helpers;
 
 namespace PixiEditor.Views
 {
     /// <summary>
-    ///     Interaction logic for LayerItem.xaml.
+    /// Interaction logic for LayerItem.xaml
     /// </summary>
     public partial class LayerItem : UserControl
     {
-        public static readonly DependencyProperty IsRenamingProperty = DependencyProperty.Register(
-            "IsRenaming", typeof(bool), typeof(LayerItem), new PropertyMetadata(default(bool)));
-
-        public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(
-            "IsActive", typeof(bool), typeof(LayerItem), new PropertyMetadata(default(bool)));
-
-        public static readonly DependencyProperty SetActiveLayerCommandProperty = DependencyProperty.Register(
-            "SetActiveLayerCommand", typeof(RelayCommand), typeof(LayerItem), new PropertyMetadata(default(RelayCommand)));
-
-        public static readonly DependencyProperty LayerIndexProperty = DependencyProperty.Register(
-            "LayerIndex", typeof(int), typeof(LayerItem), new PropertyMetadata(default(int)));
-
-        public static readonly DependencyProperty LayerNameProperty = DependencyProperty.Register(
-            "LayerName", typeof(string), typeof(LayerItem), new PropertyMetadata(default(string)));
-
-        public static readonly DependencyProperty ControlButtonsVisibleProperty = DependencyProperty.Register(
-            "ControlButtonsVisible", typeof(Visibility), typeof(LayerItem), new PropertyMetadata(Visibility.Hidden));
-
-        // Using a DependencyProperty as the backing store for MoveToBackCommand.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MoveToBackCommandProperty =
-            DependencyProperty.Register("MoveToBackCommand", typeof(RelayCommand), typeof(LayerItem), new PropertyMetadata(default(RelayCommand)));
-
-        public static readonly DependencyProperty MoveToFrontCommandProperty = DependencyProperty.Register(
-            "MoveToFrontCommand", typeof(RelayCommand), typeof(LayerItem), new PropertyMetadata(default(RelayCommand)));
-
         public LayerItem()
         {
             InitializeComponent();
         }
 
+        public static readonly DependencyProperty IsRenamingProperty = DependencyProperty.Register(
+            "IsRenaming", typeof(bool), typeof(LayerItem), new PropertyMetadata(default(bool)));
+
         public bool IsRenaming
         {
-            get => (bool)GetValue(IsRenamingProperty);
-            set => SetValue(IsRenamingProperty, value);
+            get { return (bool) GetValue(IsRenamingProperty); }
+            set { SetValue(IsRenamingProperty, value); }
         }
 
+        public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(
+            "IsActive", typeof(bool), typeof(LayerItem), new PropertyMetadata(default(bool)));
+
         public bool IsActive
         {
-            get => (bool)GetValue(IsActiveProperty);
-            set => SetValue(IsActiveProperty, value);
+            get { return (bool) GetValue(IsActiveProperty); }
+            set { SetValue(IsActiveProperty, value); }
         }
 
+        public static readonly DependencyProperty SetActiveLayerCommandProperty = DependencyProperty.Register(
+            "SetActiveLayerCommand", typeof(RelayCommand), typeof(LayerItem), new PropertyMetadata(default(RelayCommand)));
+
         public RelayCommand SetActiveLayerCommand
         {
-            get => (RelayCommand)GetValue(SetActiveLayerCommandProperty);
-            set => SetValue(SetActiveLayerCommandProperty, value);
+            get { return (RelayCommand) GetValue(SetActiveLayerCommandProperty); }
+            set { SetValue(SetActiveLayerCommandProperty, value); }
         }
 
+        public static readonly DependencyProperty LayerIndexProperty = DependencyProperty.Register(
+            "LayerIndex", typeof(int), typeof(LayerItem), new PropertyMetadata(default(int)));
+
         public int LayerIndex
         {
-            get => (int)GetValue(LayerIndexProperty);
-            set => SetValue(LayerIndexProperty, value);
+            get { return (int) GetValue(LayerIndexProperty); }
+            set { SetValue(LayerIndexProperty, value); }
         }
 
+        public static readonly DependencyProperty LayerNameProperty = DependencyProperty.Register(
+            "LayerName", typeof(string), typeof(LayerItem), new PropertyMetadata(default(string)));
+
         public string LayerName
         {
-            get => (string)GetValue(LayerNameProperty);
-            set => SetValue(LayerNameProperty, value);
+            get { return (string) GetValue(LayerNameProperty); }
+            set { SetValue(LayerNameProperty, value); }
         }
 
+        public static readonly DependencyProperty ControlButtonsVisibleProperty = DependencyProperty.Register(
+            "ControlButtonsVisible", typeof(Visibility), typeof(LayerItem), new PropertyMetadata(System.Windows.Visibility.Hidden));
+
         public Visibility ControlButtonsVisible
         {
-            get => (Visibility)GetValue(ControlButtonsVisibleProperty);
-            set => SetValue(ControlButtonsVisibleProperty, value);
+            get { return (Visibility) GetValue(ControlButtonsVisibleProperty); }
+            set { SetValue(ControlButtonsVisibleProperty, value); }
         }
-
+
+
+
         public RelayCommand MoveToBackCommand
         {
-            get => (RelayCommand)GetValue(MoveToBackCommandProperty);
-            set => SetValue(MoveToBackCommandProperty, value);
+            get { return (RelayCommand)GetValue(MoveToBackCommandProperty); }
+            set { SetValue(MoveToBackCommandProperty, value); }
         }
 
+        // Using a DependencyProperty as the backing store for MoveToBackCommand.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MoveToBackCommandProperty =
+            DependencyProperty.Register("MoveToBackCommand", typeof(RelayCommand), typeof(LayerItem), new PropertyMetadata(default(RelayCommand)));
+
+        public static readonly DependencyProperty MoveToFrontCommandProperty = DependencyProperty.Register(
+            "MoveToFrontCommand", typeof(RelayCommand), typeof(LayerItem), new PropertyMetadata(default(RelayCommand)));
+
         public RelayCommand MoveToFrontCommand
         {
-            get => (RelayCommand)GetValue(MoveToFrontCommandProperty);
-            set => SetValue(MoveToFrontCommandProperty, value);
+            get { return (RelayCommand) GetValue(MoveToFrontCommandProperty); }
+            set { SetValue(MoveToFrontCommandProperty, value); }
         }
-
+
+
         private void LayerItem_OnMouseEnter(object sender, MouseEventArgs e)
         {
             ControlButtonsVisible = Visibility.Visible;
@@ -96,6 +108,7 @@ namespace PixiEditor.Views
         private void LayerItem_OnMouseLeave(object sender, MouseEventArgs e)
         {
             ControlButtonsVisible = Visibility.Hidden;
+
         }
     }
-}
+}

+ 3 - 1
PixiEditor/Views/MainDrawingPanel.xaml → PixiEditor/Views/UserControls/MainDrawingPanel.xaml

@@ -3,9 +3,11 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:local="clr-namespace:PixiEditor.Views"
              xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
+             xmlns:helpers="clr-namespace:PixiEditor.Helpers"
              xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
-             mc:Ignorable="d" PreviewMouseDown="MainDrawingPanel_MouseDown" PreviewMouseUp="MainDrawingPanel_PreviewMouseUp"
+             mc:Ignorable="d" PreviewMouseDown="mainDrawingPanel_MouseDown" PreviewMouseUp="mainDrawingPanel_PreviewMouseUp"
              d:DesignHeight="450" d:DesignWidth="800" x:Name="mainDrawingPanel" PreviewMouseWheel="Zoombox_MouseWheel">
     <xctk:Zoombox PreviewMouseDown="Zoombox_PreviewMouseDown" Cursor="{Binding Cursor}" Name="Zoombox" KeepContentInBounds="True"
                   Loaded="Zoombox_Loaded" IsAnimated="False" MouseDown="Zoombox_MouseDown"

+ 78 - 76
PixiEditor/Views/MainDrawingPanel.xaml.cs → PixiEditor/Views/UserControls/MainDrawingPanel.xaml.cs

@@ -1,22 +1,25 @@
-using System;
+using PixiEditor.ViewModels;
+using System;
 using System.Windows;
+using System.Windows.Automation;
 using System.Windows.Controls;
 using System.Windows.Input;
 using PixiEditor.Models.Tools.Tools;
-using PixiEditor.ViewModels;
 using Xceed.Wpf.Toolkit.Core.Input;
 using Xceed.Wpf.Toolkit.Zoombox;
+using PixiEditor.Models.Position;
 
 namespace PixiEditor.Views
 {
     /// <summary>
-    ///     Interaction logic for MainDrawingPanel.xaml.
+    ///     Interaction logic for MainDrawingPanel.xaml
     /// </summary>
     public partial class MainDrawingPanel : UserControl
     {
         // Using a DependencyProperty as the backing store for Center.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty CenterProperty =
-            DependencyProperty.Register("Center", typeof(bool), typeof(MainDrawingPanel), new PropertyMetadata(true, OnCenterChanged));
+            DependencyProperty.Register("Center", typeof(bool), typeof(MainDrawingPanel),
+                new PropertyMetadata(true, OnCenterChanged));
 
         // Using a DependencyProperty as the backing store for MouseX.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty MouseXProperty =
@@ -28,11 +31,13 @@ namespace PixiEditor.Views
 
         // Using a DependencyProperty as the backing store for MouseMoveCommand.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty MouseMoveCommandProperty =
-            DependencyProperty.Register("MouseMoveCommand", typeof(ICommand), typeof(MainDrawingPanel), new PropertyMetadata(null));
+            DependencyProperty.Register("MouseMoveCommand", typeof(ICommand), typeof(MainDrawingPanel),
+                new PropertyMetadata(null));
 
         // Using a DependencyProperty as the backing store for CenterOnStart.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty CenterOnStartProperty =
-            DependencyProperty.Register("CenterOnStart", typeof(bool), typeof(MainDrawingPanel), new PropertyMetadata(false));
+            DependencyProperty.Register("CenterOnStart", typeof(bool), typeof(MainDrawingPanel),
+                new PropertyMetadata(false));
 
         // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty ItemProperty =
@@ -40,76 +45,65 @@ namespace PixiEditor.Views
 
         // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty IsUsingZoomToolProperty =
-            DependencyProperty.Register("IsUsingZoomTool", typeof(bool), typeof(MainDrawingPanel), new PropertyMetadata(false));
-
-        // Using a DependencyProperty as the backing store for ZoomPercentage.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty ZoomPercentageProperty =
-            DependencyProperty.Register("ZoomPercentage", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(0.0, ZoomPercentegeChanged));
-
-        // Using a DependencyProperty as the backing store for ViewportPosition.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty ViewportPositionProperty =
-            DependencyProperty.Register("ViewportPosition", typeof(Point), typeof(MainDrawingPanel), new PropertyMetadata(default(Point), ViewportPosCallback));
-
-        // Using a DependencyProperty as the backing store for MiddleMouseClickedCommand.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MiddleMouseClickedCommandProperty =
-            DependencyProperty.Register("MiddleMouseClickedCommand", typeof(ICommand), typeof(MainDrawingPanel), new PropertyMetadata(default(ICommand)));
+            DependencyProperty.Register("IsUsingZoomTool", typeof(bool), typeof(MainDrawingPanel), new PropertyMetadata(false));
+
 
-        // Using a DependencyProperty as the backing store for MiddleMouseClickedCommandParameter.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MiddleMouseClickedCommandParameterProperty =
-            DependencyProperty.Register("MiddleMouseClickedCommandParameter", typeof(object), typeof(MainDrawingPanel), new PropertyMetadata(default(object)));
-
-        public MainDrawingPanel()
-        {
-            InitializeComponent();
-            Zoombox.ZoomToSelectionModifiers = new KeyModifierCollection() { KeyModifier.RightAlt };
-        }
-
-        public double ClickScale { get; set; }
-
-        public Point ClickPosition { get; set; }
-
         public double ZoomPercentage
         {
             get { return (double)GetValue(ZoomPercentageProperty); }
             set { SetValue(ZoomPercentageProperty, value); }
         }
-
+
+        // Using a DependencyProperty as the backing store for ZoomPercentage.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ZoomPercentageProperty =
+            DependencyProperty.Register("ZoomPercentage", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(0.0, ZoomPercentegeChanged));
+
+
+
         public Point ViewportPosition
         {
             get { return (Point)GetValue(ViewportPositionProperty); }
             set { SetValue(ViewportPositionProperty, value); }
         }
 
+        // Using a DependencyProperty as the backing store for ViewportPosition.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ViewportPositionProperty =
+            DependencyProperty.Register("ViewportPosition", typeof(Point),
+                typeof(MainDrawingPanel), new PropertyMetadata(default(Point), ViewportPosCallback));
+
         public bool Center
         {
-            get => (bool)GetValue(CenterProperty);
+            get => (bool) GetValue(CenterProperty);
             set => SetValue(CenterProperty, value);
         }
 
         public double MouseX
         {
-            get => (double)GetValue(MouseXProperty);
+            get => (double) GetValue(MouseXProperty);
             set => SetValue(MouseXProperty, value);
         }
 
         public double MouseY
         {
-            get => (double)GetValue(MouseYProperty);
+            get => (double) GetValue(MouseYProperty);
             set => SetValue(MouseYProperty, value);
         }
-
+
+
         public ICommand MouseMoveCommand
         {
-            get => (ICommand)GetValue(MouseMoveCommandProperty);
+            get => (ICommand) GetValue(MouseMoveCommandProperty);
             set => SetValue(MouseMoveCommandProperty, value);
         }
-
+
+
         public bool CenterOnStart
         {
-            get => (bool)GetValue(CenterOnStartProperty);
+            get => (bool) GetValue(CenterOnStartProperty);
             set => SetValue(CenterOnStartProperty, value);
         }
-
+
+
         public object Item
         {
             get => GetValue(ItemProperty);
@@ -118,7 +112,7 @@ namespace PixiEditor.Views
 
         public bool IsUsingZoomTool
         {
-            get => (bool)GetValue(IsUsingZoomToolProperty);
+            get => (bool) GetValue(IsUsingZoomToolProperty);
             set => SetValue(IsUsingZoomToolProperty, value);
         }
 
@@ -127,25 +121,46 @@ namespace PixiEditor.Views
             get { return (ICommand)GetValue(MiddleMouseClickedCommandProperty); }
             set { SetValue(MiddleMouseClickedCommandProperty, value); }
         }
-
+
+        // Using a DependencyProperty as the backing store for MiddleMouseClickedCommand.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MiddleMouseClickedCommandProperty =
+            DependencyProperty.Register("MiddleMouseClickedCommand", typeof(ICommand), typeof(MainDrawingPanel), new PropertyMetadata(default(ICommand)));
+
+
+
         public object MiddleMouseClickedCommandParameter
         {
             get { return (object)GetValue(MiddleMouseClickedCommandParameterProperty); }
             set { SetValue(MiddleMouseClickedCommandParameterProperty, value); }
-        }
-
+        }
+
+        // Using a DependencyProperty as the backing store for MiddleMouseClickedCommandParameter.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MiddleMouseClickedCommandParameterProperty =
+            DependencyProperty.Register("MiddleMouseClickedCommandParameter", typeof(object), typeof(MainDrawingPanel), 
+                new PropertyMetadata(default(object)));
+
+
+
         private static void ZoomPercentegeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
             MainDrawingPanel panel = (MainDrawingPanel)d;
             double percentage = (double)e.NewValue;
-            if (percentage == 100)
+            if(percentage == 100)
             {
                 panel.SetClickValues();
             }
-
             panel.Zoombox.ZoomTo(panel.ClickScale * ((double)e.NewValue / 100.0));
         }
 
+        public double ClickScale;
+        public Point ClickPosition;
+
+        public MainDrawingPanel()
+        {
+            InitializeComponent();
+            Zoombox.ZoomToSelectionModifiers = new KeyModifierCollection() { KeyModifier.RightAlt };
+        }
+
         private static void ViewportPosCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
             MainDrawingPanel panel = (MainDrawingPanel)d;
@@ -153,23 +168,15 @@ namespace PixiEditor.Views
             {
                 panel.Zoombox.Position = default;
                 return;
-            }
-
+            }           
             TranslateZoombox(panel, (Point)e.NewValue);
         }
 
         private static void TranslateZoombox(MainDrawingPanel panel, Point vector)
         {
-            var newPos = new Point(
-                panel.ClickPosition.X + vector.X,
+            var newPos = new Point(panel.ClickPosition.X + vector.X,
                 panel.ClickPosition.Y + vector.Y);
             panel.Zoombox.Position = newPos;
-        }
-
-        private static void OnCenterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            MainDrawingPanel panel = (MainDrawingPanel)d;
-            panel.Zoombox.CenterContent();
         }
 
         private void Zoombox_CurrentViewChanged(object sender, ZoomboxViewChangedEventArgs e)
@@ -188,11 +195,7 @@ namespace PixiEditor.Views
 
         private void SetClickValues()
         {
-            if (!IsUsingZoomTool)
-            {
-                return;
-            }
-
+            if (!IsUsingZoomTool) return;
             ClickScale = Zoombox.Scale;
             SetZoomOrigin();
         }
@@ -200,22 +203,21 @@ namespace PixiEditor.Views
         private void SetZoomOrigin()
         {
             var item = (FrameworkElement)Item;
-            if (item == null)
-            {
-                return;
-            }
-
+            if (item == null) return;
             var mousePos = Mouse.GetPosition(item);
             Zoombox.ZoomOrigin = new Point(Math.Clamp(mousePos.X / item.Width, 0, 1), Math.Clamp(mousePos.Y / item.Height, 0, 1));
         }
 
+        private static void OnCenterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            MainDrawingPanel panel = (MainDrawingPanel) d;
+            panel.Zoombox.CenterContent();
+        }
+
+
         private void Zoombox_Loaded(object sender, RoutedEventArgs e)
         {
-            if (CenterOnStart)
-            {
-                ((Zoombox)sender).CenterContent();
-            }
-
+            if (CenterOnStart) ((Zoombox) sender).CenterContent();
             ClickScale = Zoombox.Scale;
         }
 
@@ -224,7 +226,7 @@ namespace PixiEditor.Views
             SetZoomOrigin();
         }
 
-        private void MainDrawingPanel_MouseDown(object sender, MouseButtonEventArgs e)
+        private void mainDrawingPanel_MouseDown(object sender, MouseButtonEventArgs e)
         {
             IsUsingZoomTool = ViewModelMain.Current.BitmapManager.SelectedTool is ZoomTool;
             Mouse.Capture((IInputElement)sender, CaptureMode.SubTree);
@@ -232,14 +234,14 @@ namespace PixiEditor.Views
             SetClickValues();
         }
 
-        private void MainDrawingPanel_PreviewMouseUp(object sender, MouseButtonEventArgs e)
+        private void mainDrawingPanel_PreviewMouseUp(object sender, MouseButtonEventArgs e)
         {
             ((IInputElement)sender).ReleaseMouseCapture();
         }
 
         private void Zoombox_MouseDown(object sender, MouseButtonEventArgs e)
         {
-            if (e.MiddleButton == MouseButtonState.Pressed &&
+            if (e.MiddleButton == MouseButtonState.Pressed && 
                 MiddleMouseClickedCommand.CanExecute(MiddleMouseClickedCommandParameter))
             {
                 MiddleMouseClickedCommand.Execute(MiddleMouseClickedCommandParameter);

+ 3 - 0
PixiEditor/Views/MenuButton.xaml → PixiEditor/Views/UserControls/MenuButton.xaml

@@ -3,7 +3,10 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:local="clr-namespace:PixiEditor.Views"
              xmlns:vm="clr-namespace:PixiEditor.ViewModels"
+             xmlns:helpers="clr-namespace:PixiEditor.Helpers"
+             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
              mc:Ignorable="d"
              d:DesignHeight="40" d:DesignWidth="80" x:Name="menuButton"
              DataContext="{DynamicResource MenuButtonViewModel}">

+ 45 - 43
PixiEditor/Views/MenuButton.xaml.cs → PixiEditor/Views/UserControls/MenuButton.xaml.cs

@@ -1,44 +1,46 @@
-using System.Windows;
-using System.Windows.Controls;
-using PixiEditor.ViewModels;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for MenuButton.xaml.
-    /// </summary>
-    public partial class MenuButton : UserControl
-    {
-        public static readonly DependencyProperty MenuButtonTextProperty =
-            DependencyProperty.Register("Text", typeof(string), typeof(MenuButton), new UIPropertyMetadata(string.Empty));
-
-        // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty ItemProperty =
-            DependencyProperty.Register("Item", typeof(object), typeof(MenuButton), new PropertyMetadata(null));
-
-        private readonly MenuButtonViewModel dc = new MenuButtonViewModel();
-
-        public MenuButton()
-        {
-            InitializeComponent();
-            DataContext = dc;
-        }
-
-        public string Text
-        {
-            get => (string)GetValue(MenuButtonTextProperty);
-            set => SetValue(MenuButtonTextProperty, value);
-        }
-
-        public object Item
-        {
-            get => GetValue(ItemProperty);
-            set => SetValue(ItemProperty, value);
-        }
-
-        protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
-        {
-            dc.CloseListViewCommand.Execute(null);
-        }
-    }
+using System.Windows;
+using System.Windows.Controls;
+using PixiEditor.ViewModels;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for MenuButton.xaml
+    /// </summary>
+    public partial class MenuButton : UserControl
+    {
+        public static readonly DependencyProperty MenuButtonTextProperty =
+            DependencyProperty.Register("Text", typeof(string), typeof(MenuButton),
+                new UIPropertyMetadata(string.Empty));
+
+        // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ItemProperty =
+            DependencyProperty.Register("Item", typeof(object), typeof(MenuButton), new PropertyMetadata(null));
+
+        private readonly MenuButtonViewModel dc = new MenuButtonViewModel();
+
+        public MenuButton()
+        {
+            InitializeComponent();
+            DataContext = dc;
+        }
+
+        public string Text
+        {
+            get => (string) GetValue(MenuButtonTextProperty);
+            set => SetValue(MenuButtonTextProperty, value);
+        }
+
+
+        public object Item
+        {
+            get => GetValue(ItemProperty);
+            set => SetValue(ItemProperty, value);
+        }
+
+        protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
+        {
+            dc.CloseListViewCommand.Execute(null);
+        }
+    }
 }

+ 2 - 0
PixiEditor/Views/NumberInput.xaml → PixiEditor/Views/UserControls/NumberInput.xaml

@@ -3,8 +3,10 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:local="clr-namespace:PixiEditor.Views"
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
              xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
+             xmlns:ui="clr-namespace:PixiEditor.Helpers.UI"
              mc:Ignorable="d"
              d:DesignHeight="20" d:DesignWidth="40" x:Name="numberInput">
     <Grid>

+ 65 - 72
PixiEditor/Views/NumberInput.xaml.cs → PixiEditor/Views/UserControls/NumberInput.xaml.cs

@@ -1,73 +1,66 @@
-using System;
-using System.Text.RegularExpressions;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for NumerInput.xaml.
-    /// </summary>
-    public partial class NumberInput : UserControl
-    {
-        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty ValueProperty =
-            DependencyProperty.Register(
-                "Value",
-                typeof(float),
-                typeof(NumberInput),
-                new PropertyMetadata(0f, OnValueChanged));
-
-        // Using a DependencyProperty as the backing store for Min.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MinProperty =
-            DependencyProperty.Register(
-                "Min",
-                typeof(float),
-                typeof(NumberInput),
-                new PropertyMetadata(float.NegativeInfinity));
-
-        // Using a DependencyProperty as the backing store for Max.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MaxProperty =
-            DependencyProperty.Register(
-                "Max",
-                typeof(float),
-                typeof(NumberInput),
-                new PropertyMetadata(float.PositiveInfinity));
-
-        public NumberInput()
-        {
-            InitializeComponent();
-        }
-
-        public float Value
-        {
-            get => (float)GetValue(ValueProperty);
-            set => SetValue(ValueProperty, value);
-        }
-
-        public float Min
-        {
-            get => (float)GetValue(MinProperty);
-            set => SetValue(MinProperty, value);
-        }
-
-        public float Max
-        {
-            get => (float)GetValue(MaxProperty);
-            set => SetValue(MaxProperty, value);
-        }
-
-        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            NumberInput input = (NumberInput)d;
-            input.Value = Math.Clamp((float)e.NewValue, input.Min, input.Max);
-        }
-
-        private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
-        {
-            Regex regex = new Regex("^[.][0-9]+$|^[0-9]*[.]{0,1}[0-9]*$");
-            e.Handled = !regex.IsMatch((sender as TextBox).Text.Insert((sender as TextBox).SelectionStart, e.Text));
-        }
-    }
+using System;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for NumerInput.xaml
+    /// </summary>
+    public partial class NumberInput : UserControl
+    {
+        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ValueProperty =
+            DependencyProperty.Register("Value", typeof(float), typeof(NumberInput),
+                new PropertyMetadata(0f, OnValueChanged));
+
+        // Using a DependencyProperty as the backing store for Min.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MinProperty =
+            DependencyProperty.Register("Min", typeof(float), typeof(NumberInput),
+                new PropertyMetadata(float.NegativeInfinity));
+
+        // Using a DependencyProperty as the backing store for Max.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MaxProperty =
+            DependencyProperty.Register("Max", typeof(float), typeof(NumberInput),
+                new PropertyMetadata(float.PositiveInfinity));
+
+
+        public NumberInput()
+        {
+            InitializeComponent();
+        }
+
+        public float Value
+        {
+            get => (float) GetValue(ValueProperty);
+            set => SetValue(ValueProperty, value);
+        }
+
+        public float Min
+        {
+            get => (float) GetValue(MinProperty);
+            set => SetValue(MinProperty, value);
+        }
+
+
+        public float Max
+        {
+            get => (float) GetValue(MaxProperty);
+            set => SetValue(MaxProperty, value);
+        }
+
+        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            NumberInput input = (NumberInput) d;
+            input.Value = Math.Clamp((float) e.NewValue, input.Min, input.Max);
+        }
+
+        private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
+        {
+            Regex regex = new Regex("^[.][0-9]+$|^[0-9]*[.]{0,1}[0-9]*$");
+            e.Handled = !regex.IsMatch((sender as TextBox).Text.Insert((sender as TextBox).SelectionStart, e.Text));
+        }
+    }
 }

+ 8 - 7
PixiEditor/Views/Rotatebox.xaml → PixiEditor/Views/UserControls/Rotatebox.xaml

@@ -1,15 +1,16 @@
 <UserControl x:Class="PixiEditor.Views.Rotatebox"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-             mc:Ignorable="d"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:PixiEditor.Views"
+             mc:Ignorable="d" 
              d:DesignHeight="100" d:DesignWidth="160" x:Name="uc">
     <StackPanel Orientation="Vertical" RenderTransformOrigin="0.5, 0.5">
-        <Image Name="knob" Source="../Images/AnchorDot.png" RenderTransformOrigin="0.5,0.5" Width="20" Height="20" />
-        <Border Width="120" Height="60" BorderThickness="0.3" BorderBrush="DeepSkyBlue" CornerRadius="1" />
+    <Image Name="knob" Source="../Images/AnchorDot.png" RenderTransformOrigin="0.5,0.5" Width="20" Height="20"/>
+        <Border Width="120" Height="60" BorderThickness="0.3" BorderBrush="DeepSkyBlue" CornerRadius="1"/>
         <StackPanel.RenderTransform>
-            <RotateTransform Angle="{Binding Path=Angle, ElementName=uc}" />
+            <RotateTransform Angle="{Binding Path=Angle, ElementName=uc}"/>
         </StackPanel.RenderTransform>
     </StackPanel>
-</UserControl>
+</UserControl>

+ 79 - 72
PixiEditor/Views/Rotatebox.xaml.cs → PixiEditor/Views/UserControls/Rotatebox.xaml.cs

@@ -1,72 +1,79 @@
-using System;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for Rotatebox.xaml.
-    /// </summary>
-    public partial class Rotatebox : UserControl
-    {
-        // Using a DependencyProperty backing store for Angle.
-        public static readonly DependencyProperty AngleProperty =
-            DependencyProperty.Register("Angle", typeof(double), typeof(Rotatebox), new UIPropertyMetadata(0.0));
-
-        private readonly float offset = 90;
-
-        private double height;
-        private double width;
-
-        public Rotatebox()
-        {
-            InitializeComponent();
-            MouseLeftButtonDown += OnMouseLeftButtonDown;
-            MouseUp += OnMouseUp;
-            MouseMove += OnMouseMove;
-        }
-
-        public double Angle
-        {
-            get => (double)GetValue(AngleProperty);
-            set => SetValue(AngleProperty, value);
-        }
-
-        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
-        {
-            Mouse.Capture(this);
-            width = ActualWidth;
-            height = ActualHeight;
-        }
-
-        private void OnMouseUp(object sender, MouseButtonEventArgs e)
-        {
-            Mouse.Capture(null);
-        }
-
-        private void OnMouseMove(object sender, MouseEventArgs e)
-        {
-            if (Equals(Mouse.Captured, this))
-            {
-                // Get the current mouse position relative to the control
-                Point currentLocation = Mouse.GetPosition(this);
-
-                // We want to rotate around the center of the knob, not the top corner
-                Point knobCenter = new Point(width / 2, height / 2);
-
-                // Calculate an angle
-                double radians = Math.Atan((currentLocation.Y - knobCenter.Y) /
-                                        (currentLocation.X - knobCenter.X));
-                Angle = (radians * 180 / Math.PI) + offset;
-
-                // Apply a 180 degree shift when X is negative so that we can rotate
-                // all of the way around
-                if (currentLocation.X - knobCenter.X < 0)
-                {
-                    Angle += 180;
-                }
-            }
-        }
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    /// Interaction logic for Rotatebox.xaml
+    /// </summary>
+    public partial class Rotatebox : UserControl
+    {
+        private double _height = 0, _width = 0;
+        private float _offset = 90;
+
+        public Rotatebox()
+        {
+            InitializeComponent();
+            MouseLeftButtonDown += OnMouseLeftButtonDown;
+            MouseUp += OnMouseUp;
+            MouseMove += OnMouseMove;
+        }
+
+        // Using a DependencyProperty backing store for Angle.
+        public static readonly DependencyProperty AngleProperty =
+            DependencyProperty.Register("Angle", typeof(double), typeof(Rotatebox), new UIPropertyMetadata(0.0));
+
+        public double Angle
+        {
+            get { return (double)GetValue(AngleProperty); }
+            set { SetValue(AngleProperty, value); }
+        }
+
+
+        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+        {
+            Mouse.Capture(this);
+            _width = ActualWidth;
+            _height = ActualHeight;
+        }
+
+        private void OnMouseUp(object sender, MouseButtonEventArgs e)
+        {
+            Mouse.Capture(null);
+        }
+
+        private void OnMouseMove(object sender, MouseEventArgs e)
+        {
+            if (Equals(Mouse.Captured, this))
+            {
+                // Get the current mouse position relative to the control
+                Point currentLocation = Mouse.GetPosition(this);
+
+                // We want to rotate around the center of the knob, not the top corner
+                Point knobCenter = new Point(_width / 2, _height/ 2);
+
+                // Calculate an angle
+                double radians = Math.Atan((currentLocation.Y - knobCenter.Y) /
+                                           (currentLocation.X - knobCenter.X));
+                Angle = radians * 180 / Math.PI + _offset;
+
+                // Apply a 180 degree shift when X is negative so that we can rotate
+                // all of the way around
+                if (currentLocation.X - knobCenter.X < 0)
+                {
+                    Angle += 180;
+                }
+            }
+        }
+    }
+}

+ 2 - 1
PixiEditor/Views/SizeInput.xaml → PixiEditor/Views/UserControls/SizeInput.xaml

@@ -3,12 +3,13 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:local="clr-namespace:PixiEditor.Views"
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
              xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
              xmlns:converters="clr-namespace:PixiEditor.Helpers"
              xmlns:validators="clr-namespace:PixiEditor.Helpers.Validators"
              mc:Ignorable="d"
-             d:DesignHeight="30" d:DesignWidth="160" Name="uc" LayoutUpdated="Uc_LayoutUpdated">
+             d:DesignHeight="30" d:DesignWidth="160" Name="uc" LayoutUpdated="uc_LayoutUpdated">
     <UserControl.Resources>
         <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </UserControl.Resources>

+ 75 - 71
PixiEditor/Views/SizeInput.xaml.cs → PixiEditor/Views/UserControls/SizeInput.xaml.cs

@@ -1,72 +1,76 @@
-using System;
-using System.Windows;
-using System.Windows.Controls;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for SizeInput.xaml.
-    /// </summary>
-    public partial class SizeInput : UserControl
-    {
-        // Using a DependencyProperty as the backing store for Size.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty SizeProperty =
-            DependencyProperty.Register("Size", typeof(int), typeof(SizeInput), new PropertyMetadata(1));
-
-        // Using a DependencyProperty as the backing store for PreserveAspectRatio.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty PreserveAspectRatioProperty =
-            DependencyProperty.Register("PreserveAspectRatio", typeof(bool), typeof(SizeInput), new PropertyMetadata(false));
-
-        // Using a DependencyProperty as the backing store for AspectRatioValue.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty AspectRatioValueProperty =
-            DependencyProperty.Register("AspectRatioValue", typeof(int), typeof(SizeInput), new PropertyMetadata(1, AspectRatioValChanged));
-
-        private int loadedAspectRatioSize = -1;
-
-        private int loadedSize = -1;
-
-        public SizeInput()
-        {
-            InitializeComponent();
-        }
-
-        public int Size
-        {
-            get => (int)GetValue(SizeProperty);
-            set => SetValue(SizeProperty, value);
-        }
-
-        public bool PreserveAspectRatio
-        {
-            get => (bool)GetValue(PreserveAspectRatioProperty);
-            set => SetValue(PreserveAspectRatioProperty, value);
-        }
-
-        public int AspectRatioValue
-        {
-            get => (int)GetValue(AspectRatioValueProperty);
-            set => SetValue(AspectRatioValueProperty, value);
-        }
-
-        private static void AspectRatioValChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            SizeInput input = (SizeInput)d;
-
-            if (input.PreserveAspectRatio && input.loadedSize != -1)
-            {
-                int newVal = (int)e.NewValue;
-                float ratio = newVal / Math.Clamp(input.loadedAspectRatioSize, 1f, float.MaxValue);
-                input.Size = (int)(input.loadedSize * ratio);
-            }
-        }
-
-        private void Uc_LayoutUpdated(object sender, EventArgs e)
-        {
-            if (loadedSize == -1)
-            {
-                loadedSize = Size;
-                loadedAspectRatioSize = AspectRatioValue;
-            }
-        }
-    }
+using System;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for SizeInput.xaml
+    /// </summary>
+    public partial class SizeInput : UserControl
+    {
+        // Using a DependencyProperty as the backing store for Size.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty SizeProperty =
+            DependencyProperty.Register("Size", typeof(int), typeof(SizeInput), new PropertyMetadata(1));
+
+        // Using a DependencyProperty as the backing store for PreserveAspectRatio.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty PreserveAspectRatioProperty =
+            DependencyProperty.Register("PreserveAspectRatio", typeof(bool), typeof(SizeInput),
+                new PropertyMetadata(false));
+
+        // Using a DependencyProperty as the backing store for AspectRatioValue.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty AspectRatioValueProperty =
+            DependencyProperty.Register("AspectRatioValue", typeof(int), typeof(SizeInput),
+                new PropertyMetadata(1, AspectRatioValChanged));
+
+        private int _loadedAspectRatioSize = -1;
+
+        private int _loadedSize = -1;
+
+        public SizeInput()
+        {
+            InitializeComponent();
+        }
+
+
+        public int Size
+        {
+            get => (int) GetValue(SizeProperty);
+            set => SetValue(SizeProperty, value);
+        }
+
+        public bool PreserveAspectRatio
+        {
+            get => (bool) GetValue(PreserveAspectRatioProperty);
+            set => SetValue(PreserveAspectRatioProperty, value);
+        }
+
+
+        public int AspectRatioValue
+        {
+            get => (int) GetValue(AspectRatioValueProperty);
+            set => SetValue(AspectRatioValueProperty, value);
+        }
+
+        private static void AspectRatioValChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            SizeInput input = (SizeInput) d;
+
+            if (input.PreserveAspectRatio && input._loadedSize != -1)
+            {
+                int newVal = (int) e.NewValue;
+                float ratio = newVal / Math.Clamp(input._loadedAspectRatioSize, 1f, float.MaxValue);
+                input.Size = (int) (input._loadedSize * ratio);
+            }
+        }
+
+        private void uc_LayoutUpdated(object sender, EventArgs e)
+        {
+            if (_loadedSize == -1)
+            {
+                _loadedSize = Size;
+                _loadedAspectRatioSize = AspectRatioValue;
+            }
+        }
+    }
 }

+ 0 - 0
PixiEditor/Views/SizePicker.xaml → PixiEditor/Views/UserControls/SizePicker.xaml


+ 10 - 7
PixiEditor/Views/SizePicker.xaml.cs → PixiEditor/Views/UserControls/SizePicker.xaml.cs

@@ -4,7 +4,7 @@ using System.Windows.Controls;
 namespace PixiEditor.Views
 {
     /// <summary>
-    ///     Interaction logic for SizePicker.xaml.
+    ///     Interaction logic for SizePicker.xaml
     /// </summary>
     public partial class SizePicker : UserControl
     {
@@ -24,22 +24,25 @@ namespace PixiEditor.Views
         {
             InitializeComponent();
         }
-
+
+
         public bool EditingEnabled
         {
-            get => (bool)GetValue(EditingEnabledProperty);
+            get => (bool) GetValue(EditingEnabledProperty);
             set => SetValue(EditingEnabledProperty, value);
         }
-
+
+
         public int ChosenWidth
         {
-            get => (int)GetValue(ChosenWidthProperty);
+            get => (int) GetValue(ChosenWidthProperty);
             set => SetValue(ChosenWidthProperty, value);
         }
-
+
+
         public int ChosenHeight
         {
-            get => (int)GetValue(ChosenHeightProperty);
+            get => (int) GetValue(ChosenHeightProperty);
             set => SetValue(ChosenHeightProperty, value);
         }
     }

+ 1 - 1
PixiEditorTests/ModelsTests/ToolsTests/ZoomToolTests.cs

@@ -14,7 +14,7 @@ namespace PixiEditorTests.ModelsTests.ToolsTests
             ZoomTool zoomTool = new ZoomTool();
             double zoom = 110;
             zoomTool.Zoom(zoom);
-            Assert.Equal(zoom, vm.ZoomPercentage);
+            Assert.Equal(zoom, vm.ViewportSubViewModel.ZoomPercentage);
         }
     }
 }

+ 1 - 0
PixiEditorTests/PixiEditorTests.csproj

@@ -18,6 +18,7 @@
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
+    <PackageReference Include="Moq" Version="4.15.1" />
     <PackageReference Include="OpenCover" Version="4.7.922" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">

+ 14 - 1
PixiEditorTests/UpdateModuleTests/UpdateCheckerTests.cs

@@ -1,4 +1,6 @@
-using PixiEditor.UpdateModule;
+using Moq;
+using PixiEditor.UpdateModule;
+using System.Threading.Tasks;
 using Xunit;
 
 namespace PixiEditorTests.UpdateModuleTests
@@ -28,5 +30,16 @@ namespace PixiEditorTests.UpdateModuleTests
         {
             Assert.True(UpdateChecker.VersionBigger(currentVersion, newVersion) == expectedValue);
         }
+
+        [Theory]
+        [InlineData("0.1.3.5", new string[] { "" }, true)]
+        [InlineData("0.1.3.5", new string[] { "0.1.3.5" }, false)]
+        [InlineData("0.1.0.0", new string[] { "0.1.3.5", "0.4.2.1" }, true)]
+        [InlineData("0.1.2.2", new string[] { " 0.1.2.2 " }, false)]
+        public void TestThatIsUpdateCompatibleChecksVersionCorrectly(string version, string[] incompatibleVersions, bool expectedResult)
+        {
+            UpdateChecker checker = new UpdateChecker(version);
+            Assert.Equal(expectedResult, checker.IsUpdateCompatible(incompatibleVersions));
+        }
     }
 }

+ 19 - 20
PixiEditorTests/ViewModelsTests/ViewModelMainTests.cs

@@ -29,16 +29,15 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         public void TestThatSwapColorsCommandSwapsColors()
         {
-            ViewModelMain viewModel = new ViewModelMain
-            {
-                PrimaryColor = Colors.Black,
-                SecondaryColor = Colors.White
-            };
+            ViewModelMain viewModel = new ViewModelMain();
+
+            viewModel.ColorsSubViewModel.PrimaryColor = Colors.Black;
+            viewModel.ColorsSubViewModel.SecondaryColor = Colors.White;
 
-            viewModel.SwapColorsCommand.Execute(null);
+            viewModel.ColorsSubViewModel.SwapColorsCommand.Execute(null);
 
-            Assert.Equal(Colors.White, viewModel.PrimaryColor);
-            Assert.Equal(Colors.Black, viewModel.SecondaryColor);
+            Assert.Equal(Colors.White, viewModel.ColorsSubViewModel.PrimaryColor);
+            Assert.Equal(Colors.Black, viewModel.ColorsSubViewModel.SecondaryColor);
         }
 
         [StaFact]
@@ -46,7 +45,7 @@ namespace PixiEditorTests.ViewModelsTests
         {
             ViewModelMain viewModel = new ViewModelMain();
 
-            viewModel.NewDocument(5, 5);
+            viewModel.FileSubViewModel.NewDocument(5, 5);
 
             Assert.NotNull(viewModel.BitmapManager.ActiveDocument);
             Assert.Single(viewModel.BitmapManager.ActiveDocument.Layers);
@@ -59,10 +58,10 @@ namespace PixiEditorTests.ViewModelsTests
 
             Assert.Equal(new Coordinates(0, 0), MousePositionConverter.CurrentCoordinates);
 
-            viewModel.MouseXOnCanvas = 5;
-            viewModel.MouseYOnCanvas = 5;
+            viewModel.IoSubViewModel.MouseXOnCanvas = 5;
+            viewModel.IoSubViewModel.MouseYOnCanvas = 5;
 
-            viewModel.MouseMoveCommand.Execute(null);
+            viewModel.IoSubViewModel.MouseMoveCommand.Execute(null);
 
             Assert.Equal(new Coordinates(5, 5), MousePositionConverter.CurrentCoordinates);
         }
@@ -74,7 +73,7 @@ namespace PixiEditorTests.ViewModelsTests
 
             Assert.Equal(ToolType.Move, viewModel.BitmapManager.SelectedTool.ToolType);
 
-            viewModel.SelectToolCommand.Execute(ToolType.Line);
+            viewModel.ToolsSubViewModel.SelectToolCommand.Execute(ToolType.Line);
 
             Assert.Equal(ToolType.Line, viewModel.BitmapManager.SelectedTool.ToolType);
         }
@@ -88,7 +87,7 @@ namespace PixiEditorTests.ViewModelsTests
 
             Assert.True(viewModel.BitmapManager.MouseController.IsRecordingChanges);
 
-            viewModel.MouseHook_OnMouseUp(default, default, default);
+            viewModel.IoSubViewModel.MouseHook_OnMouseUp(default, default, default);
 
             Assert.False(viewModel.BitmapManager.MouseController.IsRecordingChanges);
         }
@@ -102,7 +101,7 @@ namespace PixiEditorTests.ViewModelsTests
 
             Assert.Empty(viewModel.BitmapManager.ActiveDocument.Layers);
 
-            viewModel.NewLayerCommand.Execute(null);
+            viewModel.LayersSubViewModel.NewLayerCommand.Execute(null);
 
             Assert.Single(viewModel.BitmapManager.ActiveDocument.Layers);
         }
@@ -117,7 +116,7 @@ namespace PixiEditorTests.ViewModelsTests
 
             Exporter.SaveDocumentPath = fileName;
 
-            viewModel.SaveDocumentCommand.Execute(null);
+            viewModel.FileSubViewModel.SaveDocumentCommand.Execute(null);
 
             Assert.True(File.Exists(fileName));
 
@@ -130,8 +129,8 @@ namespace PixiEditorTests.ViewModelsTests
             ViewModelMain viewModel = new ViewModelMain();
             viewModel.BitmapManager.ActiveDocument = new Document(1, 1);
 
-            viewModel.AddSwatch(Colors.Green);
-            viewModel.AddSwatch(Colors.Green);
+            viewModel.ColorsSubViewModel.AddSwatch(Colors.Green);
+            viewModel.ColorsSubViewModel.AddSwatch(Colors.Green);
 
             Assert.Single(viewModel.BitmapManager.ActiveDocument.Swatches);
             Assert.Equal(Colors.Green, viewModel.BitmapManager.ActiveDocument.Swatches[0]);
@@ -153,11 +152,11 @@ namespace PixiEditorTests.ViewModelsTests
             };
             viewModel.BitmapManager.AddNewLayer("layer");
 
-            viewModel.SelectAllCommand.Execute(null);
+            viewModel.SelectionSubViewModel.SelectAllCommand.Execute(null);
 
             Assert.Equal(
                 viewModel.BitmapManager.ActiveDocument.Width * viewModel.BitmapManager.ActiveDocument.Height,
-                viewModel.ActiveSelection.SelectedPoints.Count);
+                viewModel.SelectionSubViewModel.ActiveSelection.SelectedPoints.Count);
         }
 
         [StaFact]

BIN
Screenshot.png


+ 1 - 0
incompatible.json

@@ -0,0 +1 @@
+[]