Browse Source

Merge pull request #196 from PixiEditor/zoombox

Implement Zoombox
Egor Mozgovoy 4 years ago
parent
commit
abb74ff3be
67 changed files with 1659 additions and 1057 deletions
  1. 4 4
      PixiEditor.sln
  2. 1 1
      PixiEditor/Helpers/Converters/EmptyStringToVisibilityConverter.cs
  3. 45 0
      PixiEditor/Helpers/Converters/FileExtensionToColorConverter.cs
  4. 0 44
      PixiEditor/Helpers/Converters/FileExtensionToImageSourceConverter.cs
  5. 4 3
      PixiEditor/Helpers/Converters/IsSpecifiedTypeConverter.cs
  6. 30 0
      PixiEditor/Helpers/Converters/NotNullToVisibilityConverter.cs
  7. 35 0
      PixiEditor/Helpers/Converters/NullToVisibilityConverter.cs
  8. 2 2
      PixiEditor/Helpers/Converters/ZoomToViewportConverter.cs
  9. 13 0
      PixiEditor/Helpers/ExecutionTrigger.cs
  10. BIN
      PixiEditor/Images/ChevronDown.png
  11. BIN
      PixiEditor/Images/JpgFile.png
  12. BIN
      PixiEditor/Images/PixiFile.png
  13. BIN
      PixiEditor/Images/PngFile.png
  14. BIN
      PixiEditor/Images/penMode.png
  15. 24 65
      PixiEditor/Models/DataHolders/Document/Document.cs
  16. 5 2
      PixiEditor/Models/DataHolders/RecentlyOpenedDocument.cs
  17. 5 27
      PixiEditor/Models/Tools/Tools/MoveViewportTool.cs
  18. 3 53
      PixiEditor/Models/Tools/Tools/ZoomTool.cs
  19. 4 7
      PixiEditor/PixiEditor.csproj
  20. 2 2
      PixiEditor/Styles/DockingManagerStyle.xaml
  21. 23 1
      PixiEditor/Styles/Titlebar.xaml
  22. 1 0
      PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs
  23. 47 0
      PixiEditor/ViewModels/SubViewModels/Main/StylusViewModel.cs
  24. 3 6
      PixiEditor/ViewModels/SubViewModels/Main/ViewportViewModel.cs
  25. 4 5
      PixiEditor/ViewModels/ViewModelMain.cs
  26. 1 1
      PixiEditor/Views/Dialogs/ConfirmationPopup.xaml
  27. 94 14
      PixiEditor/Views/Dialogs/HelloTherePopup.xaml
  28. 31 32
      PixiEditor/Views/Dialogs/ImportFilePopup.xaml
  29. 11 0
      PixiEditor/Views/Dialogs/ImportFilePopup.xaml.cs
  30. 28 29
      PixiEditor/Views/Dialogs/NewFilePopup.xaml
  31. 12 1
      PixiEditor/Views/Dialogs/NewFilePopup.xaml.cs
  32. 2 2
      PixiEditor/Views/Dialogs/NoticePopup.xaml
  33. 2 2
      PixiEditor/Views/Dialogs/PopupTemplate.xaml
  34. 1 1
      PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml
  35. 1 1
      PixiEditor/Views/Dialogs/ResizeDocumentPopup.xaml
  36. 30 27
      PixiEditor/Views/Dialogs/SaveFilePopup.xaml
  37. 13 2
      PixiEditor/Views/Dialogs/SaveFilePopup.xaml.cs
  38. 1 1
      PixiEditor/Views/Dialogs/SettingsWindow.xaml
  39. 3 3
      PixiEditor/Views/Dialogs/ShortcutPopup.xaml
  40. 47 11
      PixiEditor/Views/MainWindow.xaml
  41. 41 0
      PixiEditor/Views/UserControls/AvalonDockWindows/ReferenceLayerWindow.xaml
  42. 82 0
      PixiEditor/Views/UserControls/AvalonDockWindows/ReferenceLayerWindow.xaml.cs
  43. 5 5
      PixiEditor/Views/UserControls/DiscordRPPreview.xaml
  44. 26 18
      PixiEditor/Views/UserControls/DrawingViewPort.xaml
  45. 125 60
      PixiEditor/Views/UserControls/DrawingViewPort.xaml.cs
  46. 1 1
      PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml
  47. 37 37
      PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml.cs
  48. 1 1
      PixiEditor/Views/UserControls/Layers/LayerItem.xaml
  49. 15 15
      PixiEditor/Views/UserControls/Layers/LayerItem.xaml.cs
  50. 2 2
      PixiEditor/Views/UserControls/Layers/LayerStructureItemContainer.xaml
  51. 1 1
      PixiEditor/Views/UserControls/Layers/LayerStructureItemContainer.xaml.cs
  52. 6 5
      PixiEditor/Views/UserControls/Layers/LayersManager.xaml
  53. 251 250
      PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs
  54. 1 1
      PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml
  55. 1 1
      PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml.cs
  56. 93 0
      PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml
  57. 70 0
      PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml.cs
  58. 0 22
      PixiEditor/Views/UserControls/MainDrawingPanel.xaml
  59. 0 258
      PixiEditor/Views/UserControls/MainDrawingPanel.xaml.cs
  60. 1 1
      PixiEditor/Views/UserControls/NumberInput.xaml
  61. 14 4
      PixiEditor/Views/UserControls/PrependTextBlock.xaml
  62. 12 0
      PixiEditor/Views/UserControls/PrependTextBlock.xaml.cs
  63. 2 2
      PixiEditor/Views/UserControls/PreviewWindow.xaml
  64. 1 1
      PixiEditor/Views/UserControls/SizeInput.xaml
  65. 20 0
      PixiEditor/Views/UserControls/Zoombox.xaml
  66. 319 0
      PixiEditor/Views/UserControls/Zoombox.xaml.cs
  67. 0 23
      PixiEditorTests/ModelsTests/ToolsTests/ZoomToolTests.cs

+ 4 - 4
PixiEditor.sln

@@ -44,14 +44,14 @@ Global
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x86.Build.0 = Debug|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Debug|x86.Build.0 = Debug|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|Any CPU.ActiveCfg = MSIX Debug|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|Any CPU.ActiveCfg = MSIX Debug|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|Any CPU.Build.0 = MSIX Debug|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|Any CPU.Build.0 = MSIX Debug|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x64.ActiveCfg = Debug|x64
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x64.Build.0 = Debug|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x64.ActiveCfg = MSIX Debug|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x64.Build.0 = MSIX Debug|x64
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x86.ActiveCfg = Debug|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x86.ActiveCfg = Debug|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x86.Build.0 = Debug|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX Debug|x86.Build.0 = Debug|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|Any CPU.ActiveCfg = MSIX|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|Any CPU.ActiveCfg = MSIX|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|Any CPU.Build.0 = MSIX|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|Any CPU.Build.0 = MSIX|Any CPU
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x64.ActiveCfg = Release|x64
-		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x64.Build.0 = Release|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x64.ActiveCfg = MSIX|x64
+		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x64.Build.0 = MSIX|x64
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x86.ActiveCfg = Release|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x86.ActiveCfg = Release|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x86.Build.0 = Release|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.MSIX|x86.Build.0 = Release|x86
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{2CCDDE79-06CB-4771-AF85-7B25313EBA30}.Release|Any CPU.ActiveCfg = Release|Any CPU

+ 1 - 1
PixiEditor/Helpers/Converters/EmptyStringToVisibiltyConverter.cs → PixiEditor/Helpers/Converters/EmptyStringToVisibilityConverter.cs

@@ -9,7 +9,7 @@ using System.Windows.Data;
 
 
 namespace PixiEditor.Helpers.Converters
 namespace PixiEditor.Helpers.Converters
 {
 {
-    class EmptyStringToVisibiltyConverter : IValueConverter
+    public class EmptyStringToVisibilityConverter : IValueConverter
     {
     {
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
         {

+ 45 - 0
PixiEditor/Helpers/Converters/FileExtensionToColorConverter.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using System.Windows.Media;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class FileExtensionToColorConverter : IValueConverter
+    {
+        private static readonly SolidColorBrush PixiBrush = ColorBrush(226, 1, 45);
+
+        private static readonly SolidColorBrush PngBrush = ColorBrush(56, 108, 254);
+
+        private static readonly SolidColorBrush JpgBrush = ColorBrush(36, 179, 66);
+
+        private static readonly SolidColorBrush UnknownBrush = ColorBrush(100, 100, 100);
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            string extension = (string)value;
+
+            if (extension == ".pixi")
+            {
+                return PixiBrush;
+            }
+            else if (extension == ".png")
+            {
+                return PngBrush;
+            }
+            else if (extension is ".jpg" or ".jpeg")
+            {
+                return JpgBrush;
+            }
+
+            return UnknownBrush;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+
+        private static SolidColorBrush ColorBrush(byte r, byte g, byte b) => new SolidColorBrush(Color.FromRgb(r, g, b));
+    }
+}

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

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

+ 4 - 3
PixiEditor/Helpers/Converters/NotNullToVisibiltyConverter.cs → PixiEditor/Helpers/Converters/IsSpecifiedTypeConverter.cs

@@ -1,15 +1,16 @@
 using System;
 using System;
 using System.Globalization;
 using System.Globalization;
-using System.Windows;
 using System.Windows.Data;
 using System.Windows.Data;
 
 
 namespace PixiEditor.Helpers.Converters
 namespace PixiEditor.Helpers.Converters
 {
 {
-    class NotNullToVisibiltyConverter : IValueConverter
+    [ValueConversion(typeof(object), typeof(bool))]
+    public class IsSpecifiedTypeConverter : IValueConverter
     {
     {
+        public Type SpecifiedType { get; set; }
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
         {
-            return value != null ? Visibility.Visible : Visibility.Hidden;
+            return value != null && value.GetType() == SpecifiedType;
         }
         }
 
 
         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

+ 30 - 0
PixiEditor/Helpers/Converters/NotNullToVisibilityConverter.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    [ValueConversion(typeof(object), typeof(Visibility))]
+    public class NotNullToVisibilityConverter : IValueConverter
+    {
+        public bool Inverted { get; set; }
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            bool isNull = value != null;
+
+            if (Inverted)
+            {
+                isNull = !isNull;
+            }
+
+            return isNull ? Visibility.Visible : Visibility.Collapsed;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

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

@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Markup;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class NullToVisibilityConverter : MarkupExtension, IValueConverter
+    {
+        private static NullToVisibilityConverter converter;
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value != null ? Visibility.Collapsed : Visibility.Visible;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override object ProvideValue(IServiceProvider serviceProvider)
+        {
+            if(converter == null)
+            {
+                converter = new NullToVisibilityConverter();
+            }
+            return converter;
+        }
+    }
+}

+ 2 - 2
PixiEditor/Helpers/Converters/ZoomToViewportConverter.cs

@@ -9,7 +9,7 @@ namespace PixiEditor.Helpers.Converters
     {
     {
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
         {
-            if(value is double scale)
+            if (value is double scale)
             {
             {
                 double newSize = Math.Clamp((double)parameter / scale, 1, 9999);
                 double newSize = Math.Clamp((double)parameter / scale, 1, 9999);
                 return new Rect(0, 0, newSize, newSize);
                 return new Rect(0, 0, newSize, newSize);
@@ -23,4 +23,4 @@ namespace PixiEditor.Helpers.Converters
             throw new NotImplementedException();
             throw new NotImplementedException();
         }
         }
     }
     }
-}
+}

+ 13 - 0
PixiEditor/Helpers/ExecutionTrigger.cs

@@ -0,0 +1,13 @@
+using System;
+
+namespace PixiEditor.Helpers
+{
+    public class ExecutionTrigger<T>
+    {
+        public event EventHandler<T> Triggered;
+        public void Execute(object sender, T args)
+        {
+            Triggered?.Invoke(sender, args);
+        }
+    }
+}

BIN
PixiEditor/Images/ChevronDown.png


BIN
PixiEditor/Images/JpgFile.png


BIN
PixiEditor/Images/PixiFile.png


BIN
PixiEditor/Images/PngFile.png


BIN
PixiEditor/Images/penMode.png


+ 24 - 65
PixiEditor/Models/DataHolders/Document/Document.cs

@@ -1,36 +1,28 @@
-using System;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Undo;
+using PixiEditor.ViewModels;
+using System;
 using System.Buffers;
 using System.Buffers;
-using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
 using System.Windows;
 using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
-using PixiEditor.Helpers;
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.Enums;
-using PixiEditor.Models.ImageManipulation;
-using PixiEditor.Models.IO;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Undo;
-using PixiEditor.ViewModels;
 
 
 namespace PixiEditor.Models.DataHolders
 namespace PixiEditor.Models.DataHolders
 {
 {
     [DebuggerDisplay("'{Name, nq}' {width}x{height} {Layers.Count} Layer(s)")]
     [DebuggerDisplay("'{Name, nq}' {width}x{height} {Layers.Count} Layer(s)")]
     public partial class Document : NotifyableObject
     public partial class Document : NotifyableObject
     {
     {
-        private int height;
-        private int width;
 
 
         private ViewModelMain xamlAccesibleViewModel = null;
         private ViewModelMain xamlAccesibleViewModel = null;
-
         public ViewModelMain XamlAccesibleViewModel // Used to access ViewModelMain, without changing DataContext in XAML
         public ViewModelMain XamlAccesibleViewModel // Used to access ViewModelMain, without changing DataContext in XAML
         {
         {
             get => xamlAccesibleViewModel;
             get => xamlAccesibleViewModel;
@@ -41,12 +33,21 @@ namespace PixiEditor.Models.DataHolders
             }
             }
         }
         }
 
 
+        private Layer referenceLayer;
+
+        public Layer ReferenceLayer
+        {
+            get => referenceLayer;
+            set => SetProperty(ref referenceLayer, value);
+        }
+
         public string Name
         public string Name
         {
         {
             get => (string.IsNullOrEmpty(DocumentFilePath) ? "Untitled" : Path.GetFileName(DocumentFilePath))
             get => (string.IsNullOrEmpty(DocumentFilePath) ? "Untitled" : Path.GetFileName(DocumentFilePath))
                 + (!ChangesSaved ? " *" : string.Empty);
                 + (!ChangesSaved ? " *" : string.Empty);
         }
         }
 
 
+        private int width;
         public int Width
         public int Width
         {
         {
             get => width;
             get => width;
@@ -57,6 +58,7 @@ namespace PixiEditor.Models.DataHolders
             }
             }
         }
         }
 
 
+        private int height;
         public int Height
         public int Height
         {
         {
             get => height;
             get => height;
@@ -80,9 +82,6 @@ namespace PixiEditor.Models.DataHolders
         }
         }
 
 
         private double mouseXonCanvas;
         private double mouseXonCanvas;
-
-        private double mouseYonCanvas;
-
         public double MouseXOnCanvas // Mouse X coordinate relative to canvas
         public double MouseXOnCanvas // Mouse X coordinate relative to canvas
         {
         {
             get => mouseXonCanvas;
             get => mouseXonCanvas;
@@ -93,6 +92,7 @@ namespace PixiEditor.Models.DataHolders
             }
             }
         }
         }
 
 
+        private double mouseYonCanvas;
         public double MouseYOnCanvas // Mouse Y coordinate relative to canvas
         public double MouseYOnCanvas // Mouse Y coordinate relative to canvas
         {
         {
             get => mouseYonCanvas;
             get => mouseYonCanvas;
@@ -103,59 +103,18 @@ namespace PixiEditor.Models.DataHolders
             }
             }
         }
         }
 
 
-        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 = true;
-
-        public bool RecenterZoombox
-        {
-            get => recenterZoombox;
-            set
-            {
-                recenterZoombox = value;
-                RaisePropertyChanged(nameof(RecenterZoombox));
-            }
-        }
+        public ExecutionTrigger<Size> CenterViewportTrigger { get; } = new();
+        public ExecutionTrigger<double> ZoomViewportTrigger { get; } = new();
 
 
         public UndoManager UndoManager { get; set; }
         public UndoManager UndoManager { get; set; }
 
 
+        public ObservableCollection<Color> Swatches { get; set; } = new ObservableCollection<Color>();
+
         public void RaisePropertyChange(string name)
         public void RaisePropertyChange(string name)
         {
         {
             RaisePropertyChanged(name);
             RaisePropertyChanged(name);
         }
         }
 
 
-        public void CenterViewport()
-        {
-            RecenterZoombox = false; // It's a trick to trigger change in UserControl
-            RecenterZoombox = true;
-            ViewportPosition = default;
-            ZoomPercentage = default;
-        }
-
-        public ObservableCollection<Color> Swatches { get; set; } = new ObservableCollection<Color>();
-
         /// <summary>
         /// <summary>
         ///     Resizes canvas, so it fits exactly the size of drawn content, without any transparent pixels outside.
         ///     Resizes canvas, so it fits exactly the size of drawn content, without any transparent pixels outside.
         /// </summary>
         /// </summary>
@@ -321,4 +280,4 @@ namespace PixiEditor.Models.DataHolders
                 new Coordinates(biggestX, biggestY));
                 new Coordinates(biggestX, biggestY));
         }
         }
     }
     }
-}
+}

+ 5 - 2
PixiEditor/Models/DataHolders/RecentlyOpenedDocument.cs

@@ -41,10 +41,13 @@ namespace PixiEditor.Models.DataHolders
             {
             {
                 if (Corrupt)
                 if (Corrupt)
                 {
                 {
-                    return "Corrupt";
+                    return "? (Corrupt)";
                 }
                 }
 
 
-                return Path.GetExtension(filePath).ToLower();
+                string extension = Path.GetExtension(filePath).ToLower();
+                return extension is not (".pixi" or ".png" or ".jpg" or ".jpeg")
+                    ? $"? ({extension})"
+                    : extension;
             }
             }
         }
         }
 
 

+ 5 - 27
PixiEditor/Models/Tools/Tools/MoveViewportTool.cs

@@ -1,16 +1,13 @@
-using System.Collections.Generic;
-using System.Drawing;
-using System.Windows.Input;
-using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using PixiEditor.ViewModels.SubViewModels.Main;
+using System.Collections.Generic;
+using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
     public class MoveViewportTool : ReadonlyTool
     public class MoveViewportTool : ReadonlyTool
-    {
-        private Point clickPoint;
-
+    {
         private BitmapManager BitmapManager { get; }
         private BitmapManager BitmapManager { get; }
 
 
         private ToolsViewModel ToolsViewModel { get; }
         private ToolsViewModel ToolsViewModel { get; }
@@ -26,25 +23,6 @@ namespace PixiEditor.Models.Tools.Tools
             ToolsViewModel = toolsViewModel;
             ToolsViewModel = toolsViewModel;
         }
         }
 
 
-        public override void OnMouseDown(MouseEventArgs e)
-        {
-            if (e.LeftButton == MouseButtonState.Pressed || e.MiddleButton == MouseButtonState.Pressed)
-            {
-                clickPoint = MousePositionConverter.GetCursorPosition();
-            }
-        }
-
-        public override void OnMouseMove(MouseEventArgs e)
-        {
-            if (e.LeftButton == MouseButtonState.Pressed || e.MiddleButton == MouseButtonState.Pressed)
-            {
-                var point = MousePositionConverter.GetCursorPosition();
-                BitmapManager.ActiveDocument.ViewportPosition = new System.Windows.Point(
-                    point.X - clickPoint.X,
-                    point.Y - clickPoint.Y);
-            }
-        }
-
         public override void OnMouseUp(MouseEventArgs e)
         public override void OnMouseUp(MouseEventArgs e)
         {
         {
             if (e.MiddleButton == MouseButtonState.Pressed)
             if (e.MiddleButton == MouseButtonState.Pressed)
@@ -57,4 +35,4 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
         }
         }
     }
     }
-}
+}

+ 3 - 53
PixiEditor/Models/Tools/Tools/ZoomTool.cs

@@ -1,21 +1,12 @@
-using System;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Position;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Windows;
 using System.Windows.Input;
 using System.Windows.Input;
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.Position;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
     public class ZoomTool : ReadonlyTool
     public class ZoomTool : ReadonlyTool
     {
     {
-        public const float ZoomSensitivityMultiplier = 30f;
-
-        private double startingX;
-
-        private double workAreaWidth = SystemParameters.WorkArea.Width;
-        private double pixelsPerZoomMultiplier;
-
         private BitmapManager BitmapManager { get; }
         private BitmapManager BitmapManager { get; }
 
 
         public ZoomTool(BitmapManager bitmapManager)
         public ZoomTool(BitmapManager bitmapManager)
@@ -24,8 +15,6 @@ namespace PixiEditor.Models.Tools.Tools
             CanStartOutsideCanvas = true;
             CanStartOutsideCanvas = true;
             ActionDisplay = "Click and move to zoom. Click to zoom in, hold alt and click to zoom out.";
             ActionDisplay = "Click and move to zoom. Click to zoom in, hold alt and click to zoom out.";
             Tooltip = "Zooms viewport (Z). Click to zoom in, hold alt and click to zoom out.";
             Tooltip = "Zooms viewport (Z). Click to zoom in, hold alt and click to zoom out.";
-            pixelsPerZoomMultiplier = workAreaWidth / ZoomSensitivityMultiplier;
-
             BitmapManager = bitmapManager;
             BitmapManager = bitmapManager;
         }
         }
 
 
@@ -43,49 +32,10 @@ namespace PixiEditor.Models.Tools.Tools
             {
             {
                 ActionDisplay = "Click and move to zoom. Click to zoom in, hold alt and click to zoom out.";
                 ActionDisplay = "Click and move to zoom. Click to zoom in, hold alt and click to zoom out.";
             }
             }
-        }
-
-        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
-        {
-            startingX = MousePositionConverter.GetCursorPosition().X;
-            BitmapManager.ActiveDocument.ZoomPercentage = 100; // This resest the value, so callback in MainDrawingPanel can fire again later
-        }
-
-        public override void OnMouseMove(MouseEventArgs e)
-        {
-            if (e.LeftButton == MouseButtonState.Pressed)
-            {
-                double xPos = MousePositionConverter.GetCursorPosition().X;
-
-                double rawPercentDifference = (xPos - startingX) / pixelsPerZoomMultiplier; // negative - zoom out, positive - zoom in, linear
-                double finalPercentDifference = Math.Pow(2, rawPercentDifference) * 100.0; // less than 100 - zoom out, greater than 100 - zoom in
-                Zoom(finalPercentDifference);
-            }
-        }
-
-        public override void OnStoppedRecordingMouseUp(MouseEventArgs e)
-        {
-            if (e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released &&
-                startingX == MousePositionConverter.GetCursorPosition().X)
-            {
-                if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt))
-                {
-                    Zoom(85);
-                }
-                else
-                {
-                    Zoom(115);
-                }
-            }
-        }
-
-        public void Zoom(double percentage)
-        {
-            BitmapManager.ActiveDocument.ZoomPercentage = percentage;
         }
         }
 
 
         public override void Use(List<Coordinates> pixels)
         public override void Use(List<Coordinates> pixels)
         {
         {
         }
         }
     }
     }
-}
+}

+ 4 - 7
PixiEditor/PixiEditor.csproj

@@ -115,20 +115,19 @@
   <ItemGroup>
   <ItemGroup>
     <None Remove="Images\AnchorDot.png" />
     <None Remove="Images\AnchorDot.png" />
     <None Remove="Images\CheckerTile.png" />
     <None Remove="Images\CheckerTile.png" />
+    <None Remove="Images\ChevronDown.png" />
     <None Remove="Images\DiagonalRed.png" />
     <None Remove="Images\DiagonalRed.png" />
     <None Remove="Images\Eye-off.png" />
     <None Remove="Images\Eye-off.png" />
     <None Remove="Images\Eye.png" />
     <None Remove="Images\Eye.png" />
     <None Remove="Images\Folder-add.png" />
     <None Remove="Images\Folder-add.png" />
     <None Remove="Images\Folder.png" />
     <None Remove="Images\Folder.png" />
     <None Remove="Images\Layer-add.png" />
     <None Remove="Images\Layer-add.png" />
-    <None Remove="Images\JpgFile.png" />
     <None Remove="Images\MoveImage.png" />
     <None Remove="Images\MoveImage.png" />
     <None Remove="Images\MoveViewportImage.png" />
     <None Remove="Images\MoveViewportImage.png" />
+    <None Remove="Images\penMode.png" />
     <None Remove="Images\PixiBotLogo.png" />
     <None Remove="Images\PixiBotLogo.png" />
     <None Remove="Images\PixiEditorLogo.png" />
     <None Remove="Images\PixiEditorLogo.png" />
-    <None Remove="Images\PixiFile.png" />
     <None Remove="Images\PixiParserLogo.png" />
     <None Remove="Images\PixiParserLogo.png" />
-    <None Remove="Images\PngFile.png" />
     <None Remove="Images\SelectImage.png" />
     <None Remove="Images\SelectImage.png" />
     <None Remove="Images\Trash.png" />
     <None Remove="Images\Trash.png" />
     <None Remove="Images\UnknownFile.png" />
     <None Remove="Images\UnknownFile.png" />
@@ -149,7 +148,6 @@
       <Version>1.0.2</Version>
       <Version>1.0.2</Version>
       <NoWarn>NU1701</NoWarn>
       <NoWarn>NU1701</NoWarn>
     </PackageReference>
     </PackageReference>
-    <PackageReference Include="Extended.Wpf.Toolkit" Version="3.8.2" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
     <PackageReference Include="MvvmLightLibs" Version="5.4.1.1">
     <PackageReference Include="MvvmLightLibs" Version="5.4.1.1">
       <NoWarn>NU1701</NoWarn>
       <NoWarn>NU1701</NoWarn>
@@ -164,6 +162,7 @@
   <ItemGroup>
   <ItemGroup>
     <Resource Include="Images\AnchorDot.png" />
     <Resource Include="Images\AnchorDot.png" />
     <Resource Include="Images\CheckerTile.png" />
     <Resource Include="Images\CheckerTile.png" />
+    <Resource Include="Images\ChevronDown.png" />
     <Resource Include="Images\DiagonalRed.png" />
     <Resource Include="Images\DiagonalRed.png" />
     <Resource Include="Images\FloodFillImage.png" />
     <Resource Include="Images\FloodFillImage.png" />
     <Resource Include="Images\CircleImage.png" />
     <Resource Include="Images\CircleImage.png" />
@@ -174,17 +173,15 @@
     <Resource Include="Images\Folder-add.png" />
     <Resource Include="Images\Folder-add.png" />
     <Resource Include="Images\Folder.png" />
     <Resource Include="Images\Folder.png" />
     <Resource Include="Images\Layer-add.png" />
     <Resource Include="Images\Layer-add.png" />
-    <Resource Include="Images\JpgFile.png" />
     <Resource Include="Images\LineImage.png" />
     <Resource Include="Images\LineImage.png" />
     <Resource Include="Images\MoveImage.png" />
     <Resource Include="Images\MoveImage.png" />
     <Resource Include="Images\MoveViewportImage.png" />
     <Resource Include="Images\MoveViewportImage.png" />
     <Resource Include="Images\PenImage.png" />
     <Resource Include="Images\PenImage.png" />
     <Resource Include="Images\ColorPickerImage.png" />
     <Resource Include="Images\ColorPickerImage.png" />
+    <Resource Include="Images\penMode.png" />
     <Resource Include="Images\PixiBotLogo.png" />
     <Resource Include="Images\PixiBotLogo.png" />
     <Resource Include="Images\PixiEditorLogo.png" />
     <Resource Include="Images\PixiEditorLogo.png" />
-    <Resource Include="Images\PixiFile.png" />
     <Resource Include="Images\PixiParserLogo.png" />
     <Resource Include="Images\PixiParserLogo.png" />
-    <Resource Include="Images\PngFile.png" />
     <Resource Include="Images\RectangleImage.png" />
     <Resource Include="Images\RectangleImage.png" />
     <Resource Include="Images\SelectImage.png" />
     <Resource Include="Images\SelectImage.png" />
     <Resource Include="Images\transparentbg.png" />
     <Resource Include="Images\transparentbg.png" />

+ 2 - 2
PixiEditor/Styles/DockingManagerStyle.xaml

@@ -1,5 +1,5 @@
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-                    xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
+                    xmlns:xcad="https://github.com/Dirkster99/AvalonDock"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 
 
     <Style TargetType="xcad:DockingManager">
     <Style TargetType="xcad:DockingManager">
@@ -7,4 +7,4 @@
         <Setter Property="Background" Value="{StaticResource AccentColor}" />
         <Setter Property="Background" Value="{StaticResource AccentColor}" />
     </Style>
     </Style>
 
 
-</ResourceDictionary>
+</ResourceDictionary>

+ 23 - 1
PixiEditor/Styles/Titlebar.xaml

@@ -39,8 +39,30 @@
         <Setter Property="Content" Value="&#xE923;" />
         <Setter Property="Content" Value="&#xE923;" />
     </Style>
     </Style>
 
 
-    <Style x:Key="CloseButtonStyle" TargetType="Button" BasedOn="{StaticResource CaptionButtonStyle}">
+    <Style x:Key="CloseButtonStyle" TargetType="Button">
         <Setter Property="Content" Value="&#xE106;" />
         <Setter Property="Content" Value="&#xE106;" />
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Grid x:Name="LayoutRoot" Background="Transparent" Width="44" Height="35">
+                        <TextBlock x:Name="txt" Text="{TemplateBinding Content}" FontFamily="Segoe MDL2 Assets"
+                                   FontSize="10"
+                                   Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"
+                                   RenderOptions.ClearTypeHint="Auto" TextOptions.TextRenderingMode="Aliased"
+                                   TextOptions.TextFormattingMode="Display" />
+                    </Grid>
+                    <ControlTemplate.Triggers>
+                        <Trigger Property="IsMouseOver" Value="True">
+                            <Setter TargetName="LayoutRoot" Property="Background">
+                                <Setter.Value>
+                                    <SolidColorBrush Color="#CD1A2B"/>
+                                </Setter.Value>
+                            </Setter>
+                        </Trigger>
+                    </ControlTemplate.Triggers>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
     </Style>
     </Style>
 
 
     <Style x:Key="CaptionToggleButton" TargetType="ToggleButton">
     <Style x:Key="CaptionToggleButton" TargetType="ToggleButton">

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

@@ -2,6 +2,7 @@
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Views.UserControls;
 using PixiEditor.Views.UserControls;
+using PixiEditor.Views.UserControls.Layers;
 using System;
 using System;
 using System.Linq;
 using System.Linq;
 using System.Windows.Input;
 using System.Windows.Input;

+ 47 - 0
PixiEditor/ViewModels/SubViewModels/Main/StylusViewModel.cs

@@ -2,13 +2,39 @@
 using System.Windows.Input;
 using System.Windows.Input;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
+using PixiEditor.Models.UserPreferences;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
 {
     public class StylusViewModel : SubViewModel<ViewModelMain>
     public class StylusViewModel : SubViewModel<ViewModelMain>
     {
     {
+        private bool isPenModeEnabled;
+        private bool useTouchGestures;
+
         public bool ToolSetByStylus { get; set; }
         public bool ToolSetByStylus { get; set; }
 
 
+        /// <summary>
+        /// Gets or sets a value indicating whether touch gestures are enabled even when the MoveViewportTool and ZoomTool are not selected.
+        /// </summary>
+        public bool IsPenModeEnabled
+        {
+            get => isPenModeEnabled;
+            set
+            {
+                if (SetProperty(ref isPenModeEnabled, value))
+                {
+                    IPreferences.Current.UpdateLocalPreference(nameof(IsPenModeEnabled), value);
+                    UpdateUseTouchGesture();
+                }
+            }
+        }
+
+        public bool UseTouchGestures
+        {
+            get => useTouchGestures;
+            set => SetProperty(ref useTouchGestures, value);
+        }
+
         private Tool PreviousTool { get; set; }
         private Tool PreviousTool { get; set; }
 
 
         public StylusViewModel()
         public StylusViewModel()
@@ -27,6 +53,10 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             {
             {
                 throw new System.Exception($"{nameof(StylusViewModel)} already has an owner");
                 throw new System.Exception($"{nameof(StylusViewModel)} already has an owner");
             }
             }
+            else if (owner is null)
+            {
+                return;
+            }
 
 
             Owner = owner;
             Owner = owner;
 
 
@@ -36,6 +66,23 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             mw.PreviewStylusButtonDown += Mw_StylusButtonDown;
             mw.PreviewStylusButtonDown += Mw_StylusButtonDown;
             mw.PreviewStylusButtonUp += Mw_StylusButtonUp;
             mw.PreviewStylusButtonUp += Mw_StylusButtonUp;
             mw.PreviewStylusSystemGesture += Mw_PreviewStylusSystemGesture;
             mw.PreviewStylusSystemGesture += Mw_PreviewStylusSystemGesture;
+
+            isPenModeEnabled = IPreferences.Current.GetLocalPreference<bool>(nameof(IsPenModeEnabled));
+            Owner.BitmapManager.AddPropertyChangedCallback(nameof(Owner.BitmapManager.SelectedTool), UpdateUseTouchGesture);
+
+            UpdateUseTouchGesture();
+        }
+
+        private void UpdateUseTouchGesture()
+        {
+            if (Owner.BitmapManager.SelectedTool is not (MoveViewportTool or ZoomTool))
+            {
+                UseTouchGestures = IsPenModeEnabled;
+            }
+            else
+            {
+                UseTouchGestures = true;
+            }
         }
         }
 
 
         private void Mw_PreviewStylusSystemGesture(object sender, StylusSystemGestureEventArgs e)
         private void Mw_PreviewStylusSystemGesture(object sender, StylusSystemGestureEventArgs e)

+ 3 - 6
PixiEditor/ViewModels/SubViewModels/Main/ViewportViewModel.cs

@@ -1,6 +1,4 @@
-using System;
-using System.Windows;
-using PixiEditor.Helpers;
+using PixiEditor.Helpers;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
 {
@@ -37,8 +35,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         private void ZoomViewport(object parameter)
         private void ZoomViewport(object parameter)
         {
         {
             double zoom = (int)parameter;
             double zoom = (int)parameter;
-            Owner.BitmapManager.ActiveDocument.ZoomPercentage = zoom;
-            Owner.BitmapManager.ActiveDocument.ZoomPercentage = 100;
+            Owner.BitmapManager.ActiveDocument.ZoomViewportTrigger.Execute(this, zoom);
         }
         }
     }
     }
-}
+}

+ 4 - 5
PixiEditor/ViewModels/ViewModelMain.cs

@@ -5,6 +5,7 @@ using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
 using System.Windows.Input;
 using System.Windows.Input;
+using System.Windows.Threading;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
@@ -186,8 +187,8 @@ namespace PixiEditor.ViewModels
                         CreateToolShortcut<SelectTool>(Key.M, "Select Select Tool"),
                         CreateToolShortcut<SelectTool>(Key.M, "Select Select Tool"),
                         CreateToolShortcut<ZoomTool>(Key.Z, "Select Zoom Tool"),
                         CreateToolShortcut<ZoomTool>(Key.Z, "Select Zoom Tool"),
                         CreateToolShortcut<MoveViewportTool>(Key.H, "Select Viewport Move Tool"),
                         CreateToolShortcut<MoveViewportTool>(Key.H, "Select Viewport Move Tool"),
-                        new Shortcut(Key.OemPlus, ViewportSubViewModel.ZoomCommand, "Zoom in", 115),
-                        new Shortcut(Key.OemMinus, ViewportSubViewModel.ZoomCommand, "Zoom out", 85),
+                        new Shortcut(Key.OemPlus, ViewportSubViewModel.ZoomCommand, "Zoom in", 1),
+                        new Shortcut(Key.OemMinus, ViewportSubViewModel.ZoomCommand, "Zoom out", -1),
                         new Shortcut(Key.OemOpenBrackets, ToolsSubViewModel.ChangeToolSizeCommand, "Decrease Tool Size", -1),
                         new Shortcut(Key.OemOpenBrackets, ToolsSubViewModel.ChangeToolSizeCommand, "Decrease Tool Size", -1),
                         new Shortcut(Key.OemCloseBrackets, ToolsSubViewModel.ChangeToolSizeCommand, "Increase Tool Size", 1)),
                         new Shortcut(Key.OemCloseBrackets, ToolsSubViewModel.ChangeToolSizeCommand, "Increase Tool Size", 1)),
                     new ShortcutGroup(
                     new ShortcutGroup(
@@ -241,8 +242,6 @@ namespace PixiEditor.ViewModels
             {
             {
                 document.PreviewLayer = null;
                 document.PreviewLayer = null;
             }
             }
-
-            BitmapManager.ActiveDocument?.CenterViewport();
         }
         }
 
 
         public bool DocumentIsNotNull(object property)
         public bool DocumentIsNotNull(object property)
@@ -347,8 +346,8 @@ namespace PixiEditor.ViewModels
         private void ActiveDocument_DocumentSizeChanged(object sender, DocumentSizeChangedEventArgs e)
         private void ActiveDocument_DocumentSizeChanged(object sender, DocumentSizeChangedEventArgs e)
         {
         {
             BitmapManager.ActiveDocument.ActiveSelection = new Selection(Array.Empty<Coordinates>());
             BitmapManager.ActiveDocument.ActiveSelection = new Selection(Array.Empty<Coordinates>());
-            BitmapManager.ActiveDocument.CenterViewport();
             BitmapManager.ActiveDocument.ChangesSaved = false;
             BitmapManager.ActiveDocument.ChangesSaved = false;
+            BitmapManager.ActiveDocument.CenterViewportTrigger.Execute(this, new Size(BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height));
         }
         }
 
 
         private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
         private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)

+ 1 - 1
PixiEditor/Views/Dialogs/ConfirmationPopup.xaml

@@ -9,7 +9,7 @@
         WindowStyle="None">
         WindowStyle="None">
 
 
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="32"
+        <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
     </WindowChrome.WindowChrome>
     </WindowChrome.WindowChrome>
 
 

+ 94 - 14
PixiEditor/Views/Dialogs/HelloTherePopup.xaml

@@ -11,8 +11,8 @@
         WindowStyle="None" WindowStartupLocation="CenterScreen">
         WindowStyle="None" WindowStartupLocation="CenterScreen">
 
 
     <Window.Resources>
     <Window.Resources>
-        <converters:EqualityBoolToVisibilityConverter x:Key="EqualBoolToVisibilty"/>
-        <converters:FileExtensionToImageSourceConverter x:Key="FileExtensionToImageSource"/>
+        <converters:EqualityBoolToVisibilityConverter x:Key="EqualityBoolToVisibilityConverter"/>
+        <converters:FileExtensionToColorConverter x:Key="FileExtensionToColorConverter"/>
 
 
         <Style TargetType="TextBlock">
         <Style TargetType="TextBlock">
             <Setter Property="Foreground" Value="White"/>
             <Setter Property="Foreground" Value="White"/>
@@ -28,7 +28,7 @@
     </Window.Resources>
     </Window.Resources>
 
 
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="35"
+        <WindowChrome CaptionHeight="35"  GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}"/>
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}"/>
     </WindowChrome.WindowChrome>
     </WindowChrome.WindowChrome>
 
 
@@ -76,7 +76,7 @@
                     <TextBlock Margin="0,12.5,0,0" Foreground="LightGray" HorizontalAlignment="Center">
                     <TextBlock Margin="0,12.5,0,0" Foreground="LightGray" HorizontalAlignment="Center">
                         <TextBlock.Visibility>
                         <TextBlock.Visibility>
                             <Binding Path="RecentlyOpened.Count"
                             <Binding Path="RecentlyOpened.Count"
-                                     Converter="{StaticResource EqualBoolToVisibilty}">
+                                     Converter="{StaticResource EqualityBoolToVisibilityConverter}">
                                 <Binding.ConverterParameter>
                                 <Binding.ConverterParameter>
                                     <sys:Int32/>
                                     <sys:Int32/>
                                 </Binding.ConverterParameter>
                                 </Binding.ConverterParameter>
@@ -88,24 +88,104 @@
                         <ItemsControl.ItemTemplate>
                         <ItemsControl.ItemTemplate>
                             <DataTemplate DataType="{x:Type dataHolders:RecentlyOpenedDocument}">
                             <DataTemplate DataType="{x:Type dataHolders:RecentlyOpenedDocument}">
                                 <Grid>
                                 <Grid>
-                                    <StackPanel Margin="8,5,8,0" ToolTip="{Binding FilePath}">
+                                    <StackPanel Margin="8,5,8,0">
                                         <Button Margin="0,10,0,0" HorizontalAlignment="Center"
                                         <Button Margin="0,10,0,0" HorizontalAlignment="Center"
                                                 Width="100" Height="100"
                                                 Width="100" Height="100"
                                                 Command="{Binding DataContext.OpenRecentCommand, RelativeSource={RelativeSource AncestorType=uc:AlignableWrapPanel}}"
                                                 Command="{Binding DataContext.OpenRecentCommand, RelativeSource={RelativeSource AncestorType=uc:AlignableWrapPanel}}"
                                                 CommandParameter="{Binding FilePath}"
                                                 CommandParameter="{Binding FilePath}"
-                                                Style="{StaticResource DarkRoundButton}">
-                                            <Image Source="{Binding PreviewBitmap}" Margin="20"/>
+                                                Style="{StaticResource DarkRoundButton}"
+                                                x:Name="fileButton">
+                                            <Grid Width="100" Height="100">
+                                                <Image Source="{Binding PreviewBitmap}" Margin="20"/>
+                                                <Border Grid.Row="1" Height="8" Width="8" x:Name="extensionBorder" Margin="5"
+                                                        Background="{Binding FileExtension, Converter={StaticResource FileExtensionToColorConverter}}" 
+                                                        VerticalAlignment="Bottom" HorizontalAlignment="Right">
+                                                    <Border.Style>
+                                                        <Style TargetType="Border">
+                                                            <Style.Triggers>
+                                                                <Trigger Property="IsMouseOver" Value="False">
+                                                                    <Setter Property="CornerRadius" Value="2"/>
+                                                                </Trigger>
+                                                                <DataTrigger Binding="{Binding IsMouseOver, ElementName=fileButton}" Value="True">
+                                                                    <DataTrigger.EnterActions>
+                                                                        <BeginStoryboard Name="open">
+                                                                            <Storyboard BeginTime="0:0:.1">
+                                                                                <DoubleAnimation Storyboard.TargetProperty="Height" By="8" To="25" BeginTime="0:0:.1" Duration="0:0:.3">
+                                                                                    <DoubleAnimation.EasingFunction>
+                                                                                        <ExponentialEase/>
+                                                                                    </DoubleAnimation.EasingFunction>
+                                                                                </DoubleAnimation>
+                                                                                <DoubleAnimation Storyboard.TargetProperty="Width" By="8" To="100" Duration="0:0:.1">
+                                                                                    <DoubleAnimation.EasingFunction>
+                                                                                        <ExponentialEase/>
+                                                                                    </DoubleAnimation.EasingFunction>
+                                                                                </DoubleAnimation>
+                                                                                <ThicknessAnimation Storyboard.TargetProperty="Margin" By="5" To="0" BeginTime="0:0:.1" Duration="0:0:.25">
+                                                                                    <ThicknessAnimation.EasingFunction>
+                                                                                        <ExponentialEase/>
+                                                                                    </ThicknessAnimation.EasingFunction>
+                                                                                </ThicknessAnimation>
+                                                                            </Storyboard>
+                                                                        </BeginStoryboard>
+                                                                    </DataTrigger.EnterActions>
+                                                                    <DataTrigger.ExitActions>
+                                                                        <BeginStoryboard Name="close">
+                                                                            <Storyboard>
+                                                                                <DoubleAnimation Storyboard.TargetProperty="Height" By="25" To="8"  Duration="0:0:.2">
+                                                                                    <DoubleAnimation.EasingFunction>
+                                                                                        <ExponentialEase/>
+                                                                                    </DoubleAnimation.EasingFunction>
+                                                                                </DoubleAnimation>
+                                                                                <DoubleAnimation Storyboard.TargetProperty="Width" By="100" To="8" BeginTime="0:0:.2" Duration="0:0:.1">
+                                                                                    <DoubleAnimation.EasingFunction>
+                                                                                        <ExponentialEase/>
+                                                                                    </DoubleAnimation.EasingFunction>
+                                                                                </DoubleAnimation>
+                                                                                <ThicknessAnimation Storyboard.TargetProperty="Margin" By="0" To="5" Duration="0:0:.1">
+                                                                                    <ThicknessAnimation.EasingFunction>
+                                                                                        <ExponentialEase/>
+                                                                                    </ThicknessAnimation.EasingFunction>
+                                                                                </ThicknessAnimation>
+                                                                            </Storyboard>
+                                                                        </BeginStoryboard>
+                                                                    </DataTrigger.ExitActions>
+                                                                    <Setter Property="CornerRadius" Value="0,0,4,4"/>
+                                                                </DataTrigger>
+                                                            </Style.Triggers>
+                                                        </Style>
+                                                    </Border.Style>
+                                                    <TextBlock x:Name="extension" Text="{Binding FileExtension}" FontSize="15" TextAlignment="Center" VerticalAlignment="Center" Opacity="0">
+                                                        <TextBlock.Style>
+                                                            <Style TargetType="TextBlock">
+                                                                <Style.Triggers>
+                                                                    <DataTrigger Binding="{Binding IsMouseOver, ElementName=fileButton}" Value="True">
+                                                                        <DataTrigger.EnterActions>
+                                                                            <BeginStoryboard Name="start">
+                                                                                <Storyboard BeginTime="0:0:.2">
+                                                                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" By="0" To="1" Duration="0:0:.4">
+                                                                                        <DoubleAnimation.EasingFunction>
+                                                                                            <PowerEase/>
+                                                                                        </DoubleAnimation.EasingFunction>
+                                                                                    </DoubleAnimation>
+                                                                                </Storyboard>
+                                                                            </BeginStoryboard>
+                                                                        </DataTrigger.EnterActions>
+                                                                        <DataTrigger.ExitActions>
+                                                                            <RemoveStoryboard BeginStoryboardName="start"/>
+                                                                        </DataTrigger.ExitActions>
+                                                                    </DataTrigger>
+                                                                </Style.Triggers>
+                                                            </Style>
+                                                        </TextBlock.Style>
+                                                    </TextBlock>
+                                                </Border>
+                                            </Grid>
                                         </Button>
                                         </Button>
 
 
-                                        <TextBlock Text="{Binding FileName}" Width="110" TextAlignment="Center" TextTrimming="CharacterEllipsis"
+                                        <TextBlock Text="{Binding FileName}" ToolTip="{Binding FilePath}"
+                                                   Width="110" TextAlignment="Center" TextTrimming="CharacterEllipsis"
                                                    FontSize="18" Margin="10,10,10,2" HorizontalAlignment="Center" Foreground="White"/>
                                                    FontSize="18" Margin="10,10,10,2" HorizontalAlignment="Center" Foreground="White"/>
                                     </StackPanel>
                                     </StackPanel>
-                                    <Image Source="{Binding FileExtension, Converter={StaticResource FileExtensionToImageSource}, ConverterParameter=../..}" Width="33"
-                                           ToolTip="{Binding FileExtension}">
-                                        <Image.RenderTransform>
-                                            <TranslateTransform X="38" Y="23"/>
-                                        </Image.RenderTransform>
-                                    </Image>
                                 </Grid>
                                 </Grid>
                             </DataTemplate>
                             </DataTemplate>
                         </ItemsControl.ItemTemplate>
                         </ItemsControl.ItemTemplate>

+ 31 - 32
PixiEditor/Views/Dialogs/ImportFilePopup.xaml

@@ -8,49 +8,48 @@
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
         xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
         xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
         xmlns:helpers="clr-namespace:PixiEditor.Helpers"
         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"
+        mc:Ignorable="d" BorderBrush="Black" BorderThickness="1"
+        Title="ImportFilePopup" Topmost="True" ShowInTaskbar="False" Height="350" Width="300" WindowStyle="None" WindowStartupLocation="CenterScreen" Name="importFilePopup" MinHeight="350" MinWidth="300"
         DataContext="{DynamicResource ImportFilePopupViewModel}">
         DataContext="{DynamicResource ImportFilePopupViewModel}">
     <Window.Resources>
     <Window.Resources>
         <vm:ImportFilePopupViewModel x:Key="ImportFilePopupViewModel" />
         <vm:ImportFilePopupViewModel x:Key="ImportFilePopupViewModel" />
         <helpers:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
         <helpers:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
     </Window.Resources>
-    <Border BorderBrush="Black" BorderThickness="1">
-        <Grid Background="{StaticResource AccentColor}" Focusable="True">
-            <Grid.RowDefinitions>
-                <RowDefinition Height="32" />
-                <RowDefinition Height="67*" />
-            </Grid.RowDefinitions>
-            <i:Interaction.Behaviors>
-                <behaviors:ClearFocusOnClickBehavior/>
-            </i:Interaction.Behaviors>
-            <Grid Grid.Row="0" Background="#FF2C2C2C">
-                <i:Interaction.Triggers>
-                    <i:EventTrigger EventName="MouseDown">
-                        <i:InvokeCommandAction Command="{Binding DragMoveCommand}" />
-                    </i:EventTrigger>
-                </i:Interaction.Triggers>
-                <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Style="{StaticResource CloseButtonStyle}"
-                        WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Close"
-                        Command="{Binding CloseButtonCommand}"
-                        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
-            </Grid>
-            <StackPanel Grid.Row="1">
-                <Label Height="40" Width="120" VerticalAlignment="Top" Content="Open" Foreground="Snow"
+    <WindowChrome.WindowChrome>
+        <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
+                      ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
+    </WindowChrome.WindowChrome>
+
+    <Window.CommandBindings>
+        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute"
+                        Executed="CommandBinding_Executed_Close" />
+    </Window.CommandBindings>
+
+    <Grid Background="{StaticResource AccentColor}">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="35" />
+            <RowDefinition />
+        </Grid.RowDefinitions>
+
+        <DockPanel Grid.Row="0" Background="{StaticResource MainColor}">
+            <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Style="{StaticResource CloseButtonStyle}"
+                    WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Close"
+                    Command="{x:Static SystemCommands.CloseWindowCommand}" />
+        </DockPanel>
+        <StackPanel Grid.Row="1">
+            <Label Height="40" Width="120" VerticalAlignment="Top" Content="Open" Foreground="Snow"
                        HorizontalContentAlignment="Center" FontSize="24" Margin="0,10,0,0" />
                        HorizontalContentAlignment="Center" FontSize="24" Margin="0,10,0,0" />
-                <Button Grid.Row="1" 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}"
                         Content="File Path" Background="#303030" BorderBrush="{Binding PathButtonBorder}"
                         Command="{Binding ChoosePathCommand}" />
                         Command="{Binding ChoosePathCommand}" />
-                <StackPanel Background="{StaticResource MainColor}" Height="120" Width="225" Margin="0,30,0,0">
-                    <local:SizePicker EditingEnabled="{Binding PathIsCorrect}"
+            <StackPanel Background="{StaticResource MainColor}" Height="120" Width="225" Margin="0,30,0,0">
+                <local:SizePicker EditingEnabled="{Binding PathIsCorrect}"
                                       ChosenWidth="{Binding ImportWidth, Mode=TwoWay}"
                                       ChosenWidth="{Binding ImportWidth, Mode=TwoWay}"
                                       ChosenHeight="{Binding ImportHeight,Mode=TwoWay}" />
                                       ChosenHeight="{Binding ImportHeight,Mode=TwoWay}" />
-                </StackPanel>
             </StackPanel>
             </StackPanel>
-            <Button Grid.Row="1" Height="30" Width="60" VerticalAlignment="Bottom" HorizontalAlignment="Right"
+        </StackPanel>
+        <Button Grid.Row="1" Height="30" Width="60" VerticalAlignment="Bottom" HorizontalAlignment="Right"
                     Margin="10" Style="{StaticResource DarkRoundButton}" Content="OK" Command="{Binding OkCommand}"
                     Margin="10" Style="{StaticResource DarkRoundButton}" Content="OK" Command="{Binding OkCommand}"
                     CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
                     CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
-        </Grid>
-    </Border>
+    </Grid>
 </Window>
 </Window>

+ 11 - 0
PixiEditor/Views/Dialogs/ImportFilePopup.xaml.cs

@@ -1,4 +1,5 @@
 using System.Windows;
 using System.Windows;
+using System.Windows.Input;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
 
 
 namespace PixiEditor.Views
 namespace PixiEditor.Views
@@ -35,6 +36,16 @@ namespace PixiEditor.Views
         {
         {
             get => dc.FilePath;
             get => dc.FilePath;
             set => dc.FilePath = value;
             set => dc.FilePath = value;
+        }
+
+        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
+        {
+            e.CanExecute = true;
         }
         }
+
+        private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
+        {
+            SystemCommands.CloseWindow(this);
+        }
     }
     }
 }
 }

+ 28 - 29
PixiEditor/Views/Dialogs/NewFilePopup.xaml

@@ -10,44 +10,43 @@
         xmlns:converters="clr-namespace:PixiEditor.Helpers"
         xmlns:converters="clr-namespace:PixiEditor.Helpers"
         mc:Ignorable="d"
         mc:Ignorable="d"
         d:DesignHeight="600" Topmost="True" ShowInTaskbar="False" d:DesignWidth="450"
         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">
+        DataContext="{DynamicResource NewFileMenuViewModel}" WindowStyle="None" WindowStartupLocation="CenterScreen" MinHeight="300" MinWidth="400" Height="600" Width="450" Name="newFilePopup" BorderBrush="Black" BorderThickness="1">
     <Window.Resources>
     <Window.Resources>
         <vm:NewFileMenuViewModel x:Key="NewFileMenuViewModel" />
         <vm:NewFileMenuViewModel x:Key="NewFileMenuViewModel" />
         <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
         <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
     </Window.Resources>
-    <Border BorderBrush="Black" BorderThickness="1">
-        <Grid Background="{StaticResource AccentColor}" Focusable="True">
-            <Grid.RowDefinitions>
-                <RowDefinition Height="32*" />
-                <RowDefinition Height="577*" />
-            </Grid.RowDefinitions>
-            <i:Interaction.Behaviors>
-                <helpers:ClearFocusOnClickBehavior/>
-            </i:Interaction.Behaviors>
-            <Grid Grid.Row="0" Background="{StaticResource MainColor}">
-                <i:Interaction.Triggers>
-                    <i:EventTrigger EventName="MouseDown">
-                        <i:InvokeCommandAction Command="{Binding DragMoveCommand}" />
-                    </i:EventTrigger>
-                </i:Interaction.Triggers>
-                <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Style="{StaticResource CloseButtonStyle}"
-                        WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Close"
-                        Command="{Binding CloseCommand}"
-                        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
-            </Grid>
-            <Label Content="New File" Grid.Row="1" Margin="0,10,0,0" HorizontalAlignment="Center"
+    <WindowChrome.WindowChrome>
+        <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
+                      ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
+    </WindowChrome.WindowChrome>
+
+    <Window.CommandBindings>
+        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute"
+                        Executed="CommandBinding_Executed_Close" />
+    </Window.CommandBindings>
+
+    <Grid Background="{StaticResource AccentColor}">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="35" />
+            <RowDefinition />
+        </Grid.RowDefinitions>
+
+        <DockPanel Grid.Row="0" Background="{StaticResource MainColor}">
+            <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Style="{StaticResource CloseButtonStyle}"
+                    WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Close"
+                    Command="{x:Static SystemCommands.CloseWindowCommand}" />
+        </DockPanel>
+        <Label Content="New File" Grid.Row="1" Margin="0,10,0,0" HorizontalAlignment="Center"
                    VerticalAlignment="Top" Foreground="White" FontSize="24" />
                    VerticalAlignment="Top" Foreground="White" FontSize="24" />
-            <StackPanel HorizontalAlignment="Center" Margin="0,60,0,0" Background="{StaticResource MainColor}"
+        <StackPanel HorizontalAlignment="Center" Margin="0,60,0,0" Background="{StaticResource MainColor}"
                         VerticalAlignment="Top" Grid.Row="1" Width="350" Height="150">
                         VerticalAlignment="Top" Grid.Row="1" Width="350" Height="150">
-                <local:SizePicker Margin="0,20" HorizontalAlignment="Center" Height="110"
+            <local:SizePicker Margin="0,20" HorizontalAlignment="Center" Height="110"
                                   ChosenHeight="{Binding FileHeight,Mode=TwoWay, ElementName=newFilePopup}"
                                   ChosenHeight="{Binding FileHeight,Mode=TwoWay, ElementName=newFilePopup}"
                                   ChosenWidth="{Binding FileWidth,Mode=TwoWay, ElementName=newFilePopup}" />
                                   ChosenWidth="{Binding FileWidth,Mode=TwoWay, ElementName=newFilePopup}" />
-            </StackPanel>
-            <Button VerticalAlignment="Bottom" HorizontalAlignment="Right" FontSize="20" Height="30" Width="60"
+        </StackPanel>
+        <Button VerticalAlignment="Bottom" HorizontalAlignment="Right" FontSize="20" Height="30" Width="60"
                     Style="{StaticResource DarkRoundButton}" Content="OK" Margin="0,0,10,10" Grid.Row="1"
                     Style="{StaticResource DarkRoundButton}" Content="OK" Margin="0,0,10,10" Grid.Row="1"
                     Command="{Binding OkCommand}"
                     Command="{Binding OkCommand}"
                     CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
                     CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
-        </Grid>
-    </Border>
+    </Grid>
 </Window>
 </Window>

+ 12 - 1
PixiEditor/Views/Dialogs/NewFilePopup.xaml.cs

@@ -1,5 +1,6 @@
 using System.Windows;
 using System.Windows;
-
+using System.Windows.Input;
+
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
@@ -31,6 +32,16 @@ namespace PixiEditor.Views
         {
         {
             get => (int)GetValue(FileWidthProperty);
             get => (int)GetValue(FileWidthProperty);
             set => SetValue(FileWidthProperty, value);
             set => SetValue(FileWidthProperty, value);
+        }
+
+        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
+        {
+            e.CanExecute = true;
+        }
+
+        private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
+        {
+            SystemCommands.CloseWindow(this);
         }
         }
     }
     }
 }
 }

+ 2 - 2
PixiEditor/Views/Dialogs/NoticePopup.xaml

@@ -4,12 +4,12 @@
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:system="clr-namespace:System;assembly=System.Runtime" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:system="clr-namespace:System;assembly=System.Runtime" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
-        mc:Ignorable="d"
+        mc:Ignorable="d" WindowStyle="None"
         Title="NoticePopup" Height="200" Width="500"
         Title="NoticePopup" Height="200" Width="500"
         x:Name="popup">
         x:Name="popup">
 
 
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="32"
+        <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
     </WindowChrome.WindowChrome>
     </WindowChrome.WindowChrome>
 
 

+ 2 - 2
PixiEditor/Views/Dialogs/PopupTemplate.xaml

@@ -4,11 +4,11 @@
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:PixiEditor.Views"
         xmlns:local="clr-namespace:PixiEditor.Views"
-        mc:Ignorable="d"
+        mc:Ignorable="d" BorderBrush="Black" BorderThickness="1"
         Title="ResizeDocumentPopup" WindowStartupLocation="CenterScreen" Height="200" Width="400" WindowStyle="None">
         Title="ResizeDocumentPopup" WindowStartupLocation="CenterScreen" Height="200" Width="400" WindowStyle="None">
 
 
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="32"
+        <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
     </WindowChrome.WindowChrome>
     </WindowChrome.WindowChrome>
 
 

+ 1 - 1
PixiEditor/Views/Dialogs/ResizeCanvasPopup.xaml

@@ -16,7 +16,7 @@
     </Window.Resources>
     </Window.Resources>
 
 
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="32"
+        <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
     </WindowChrome.WindowChrome>
     </WindowChrome.WindowChrome>
 
 

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

@@ -15,7 +15,7 @@
     </Window.Resources>
     </Window.Resources>
 
 
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="32"
+        <WindowChrome CaptionHeight="32" GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
     </WindowChrome.WindowChrome>
     </WindowChrome.WindowChrome>
 
 

+ 30 - 27
PixiEditor/Views/Dialogs/SaveFilePopup.xaml

@@ -7,34 +7,38 @@
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
         xmlns:helpers="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:helpers1="clr-namespace:PixiEditor.Helpers"
         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"
+        mc:Ignorable="d" BorderBrush="Black" BorderThickness="1"
+        Title="SaveFilePopup" Height="300" Width="400" WindowStyle="None" MinHeight="300" MinWidth="400"
         WindowStartupLocation="CenterScreen" Name="saveFilePopup">
         WindowStartupLocation="CenterScreen" Name="saveFilePopup">
     <Window.Resources>
     <Window.Resources>
         <helpers1:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
         <helpers1:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </Window.Resources>
     </Window.Resources>
-    <Border BorderBrush="Black" BorderThickness="1">
-        <Grid Background="{StaticResource AccentColor}" Focusable="True">
-            <Grid.RowDefinitions>
-                <RowDefinition Height="32" />
-                <RowDefinition Height="229*" />
-            </Grid.RowDefinitions>
-            <i:Interaction.Behaviors>
-                <helpers:ClearFocusOnClickBehavior/>
-            </i:Interaction.Behaviors>
-            
-            <Grid Grid.Row="0" Background="{StaticResource MainColor}">
-                <i:Interaction.Triggers>
-                    <i:EventTrigger EventName="MouseDown">
-                        <i:InvokeCommandAction Command="{Binding DragMoveCommand}" />
-                    </i:EventTrigger>
-                </i:Interaction.Triggers>
-                <Button Style="{StaticResource CloseButtonStyle}" VerticalAlignment="Top" HorizontalAlignment="Right"
-                        BorderThickness="0"
-                        Command="{Binding CloseButtonCommand}"
-                        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
-            </Grid>
-            <TextBlock Grid.Row="1" Foreground="Snow" VerticalAlignment="Top" HorizontalAlignment="Center"
+    <WindowChrome.WindowChrome>
+        <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
+                      ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
+    </WindowChrome.WindowChrome>
+
+    <Window.CommandBindings>
+        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute"
+                        Executed="CommandBinding_Executed_Close" />
+    </Window.CommandBindings>
+
+    <Grid Background="{StaticResource AccentColor}" Focusable="True">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="35" />
+            <RowDefinition />
+        </Grid.RowDefinitions>
+        <i:Interaction.Behaviors>
+            <helpers:ClearFocusOnClickBehavior/>
+        </i:Interaction.Behaviors>
+
+        <DockPanel Grid.Row="0" Background="{StaticResource MainColor}">
+            <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Style="{StaticResource CloseButtonStyle}"
+                    WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Close"
+                    Command="{x:Static SystemCommands.CloseWindowCommand}" />
+        </DockPanel>
+        
+        <TextBlock Grid.Row="1" Foreground="Snow" VerticalAlignment="Top" HorizontalAlignment="Center"
                        Text="File settings" TextAlignment="Center" Margin="0,10,0,0" FontSize="24" />
                        Text="File settings" TextAlignment="Center" Margin="0,10,0,0" FontSize="24" />
             <StackPanel Orientation="Vertical" Grid.Row="1" Margin="0,50,0,0">
             <StackPanel Orientation="Vertical" Grid.Row="1" Margin="0,50,0,0">
                 <local:SizePicker Width="250" Height="120"
                 <local:SizePicker Width="250" Height="120"
@@ -44,9 +48,8 @@
                         Background="{StaticResource MainColor}" BorderBrush="{Binding PathButtonBorder}"
                         Background="{StaticResource MainColor}" BorderBrush="{Binding PathButtonBorder}"
                         Command="{Binding ChoosePathCommand}" />
                         Command="{Binding ChoosePathCommand}" />
             </StackPanel>
             </StackPanel>
-            <Button Grid.Row="1" Height="30" Width="60" VerticalAlignment="Bottom" HorizontalAlignment="Right"
+        <Button Grid.Row="1" Height="30" Width="60" VerticalAlignment="Bottom" HorizontalAlignment="Right"
                     Margin="10" Style="{StaticResource DarkRoundButton}" Content="OK" Command="{Binding OkCommand}"
                     Margin="10" Style="{StaticResource DarkRoundButton}" Content="OK" Command="{Binding OkCommand}"
                     CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
                     CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
-        </Grid>
-    </Border>
+    </Grid>
 </Window>
 </Window>

+ 13 - 2
PixiEditor/Views/Dialogs/SaveFilePopup.xaml.cs

@@ -1,6 +1,7 @@
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
 using System.Windows;
 using System.Windows;
-
+using System.Windows.Input;
+
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
@@ -15,7 +16,17 @@ namespace PixiEditor.Views
         public static readonly DependencyProperty SaveWidthProperty =
         public static readonly DependencyProperty SaveWidthProperty =
             DependencyProperty.Register("SaveWidth", typeof(int), typeof(SaveFilePopup), new PropertyMetadata(32));
             DependencyProperty.Register("SaveWidth", typeof(int), typeof(SaveFilePopup), new PropertyMetadata(32));
 
 
-        private readonly SaveFilePopupViewModel dataContext = new SaveFilePopupViewModel();
+        private readonly SaveFilePopupViewModel dataContext = new SaveFilePopupViewModel();
+
+        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
+        {
+            e.CanExecute = true;
+        }
+
+        private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
+        {
+            SystemCommands.CloseWindow(this);
+        }
 
 
         public SaveFilePopup()
         public SaveFilePopup()
         {
         {

+ 1 - 1
PixiEditor/Views/Dialogs/SettingsWindow.xaml

@@ -16,7 +16,7 @@
         <BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
         <BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
     </Window.Resources>
     </Window.Resources>
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="32"
+        <WindowChrome CaptionHeight="32"  GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
     </WindowChrome.WindowChrome>
     </WindowChrome.WindowChrome>
 
 

+ 3 - 3
PixiEditor/Views/Dialogs/ShortcutPopup.xaml

@@ -12,7 +12,7 @@
         MinHeight="400" MinWidth="350" Topmost="{Binding IsTopmost}">
         MinHeight="400" MinWidth="350" Topmost="{Binding IsTopmost}">
     <Window.Resources>
     <Window.Resources>
         <converters:KeyToStringConverter x:Key="KeyToStringConverter"/>
         <converters:KeyToStringConverter x:Key="KeyToStringConverter"/>
-        <BoolToVisibilityConverter x:Key="BoolToVisibilty"/>
+        <BoolToVisibilityConverter x:Key="BoolToVisibility"/>
         
         
         <Style TargetType="Border" x:Key="KeyBorder">
         <Style TargetType="Border" x:Key="KeyBorder">
             <Setter Property="BorderThickness" Value="1"/>
             <Setter Property="BorderThickness" Value="1"/>
@@ -42,7 +42,7 @@
     </Window.Resources>
     </Window.Resources>
 
 
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="35"
+        <WindowChrome CaptionHeight="35"  GlassFrameThickness="0.1"
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}"/>
                       ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}"/>
     </WindowChrome.WindowChrome>
     </WindowChrome.WindowChrome>
 
 
@@ -79,7 +79,7 @@
                 <ItemsControl ItemsSource="{Binding Controller.ShortcutGroups}" Background="Transparent">
                 <ItemsControl ItemsSource="{Binding Controller.ShortcutGroups}" Background="Transparent">
                     <ItemsControl.ItemTemplate>
                     <ItemsControl.ItemTemplate>
                         <DataTemplate DataType="{x:Type shortcuts:ShortcutGroup}">
                         <DataTemplate DataType="{x:Type shortcuts:ShortcutGroup}">
-                            <StackPanel Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilty}}">
+                            <StackPanel Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibility}}">
                                 <TextBlock Text="{Binding Name}" Foreground="White" FontSize="18" FontWeight="Medium" Margin="10,8,0,0"/>
                                 <TextBlock Text="{Binding Name}" Foreground="White" FontSize="18" FontWeight="Medium" Margin="10,8,0,0"/>
                                 <ItemsControl ItemsSource="{Binding Shortcuts}">
                                 <ItemsControl ItemsSource="{Binding Shortcuts}">
                                     <ItemsControl.ItemTemplate>
                                     <ItemsControl.ItemTemplate>

+ 47 - 11
PixiEditor/Views/MainWindow.xaml

@@ -4,6 +4,7 @@
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:PixiEditor"
         xmlns:local="clr-namespace:PixiEditor"
+        xmlns:tools="clr-namespace:PixiEditor.Models.Tools.Tools"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
         xmlns:dataHolders="clr-namespace:PixiEditor.Models.DataHolders"
         xmlns:dataHolders="clr-namespace:PixiEditor.Models.DataHolders"
         xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
         xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
@@ -12,12 +13,12 @@
         xmlns:cmd="http://www.galasoft.ch/mvvmlight" 
         xmlns:cmd="http://www.galasoft.ch/mvvmlight" 
         xmlns:avalondock="https://github.com/Dirkster99/AvalonDock"
         xmlns:avalondock="https://github.com/Dirkster99/AvalonDock"
         xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" xmlns:usercontrols="clr-namespace:PixiEditor.Views.UserControls" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
         xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" xmlns:usercontrols="clr-namespace:PixiEditor.Views.UserControls" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
-        xmlns:avalonDockTheme="clr-namespace:PixiEditor.Styles.AvalonDock" d:DataContext="{d:DesignInstance Type=vm:ViewModelMain}"
+        xmlns:avalonDockTheme="clr-namespace:PixiEditor.Styles.AvalonDock" xmlns:layerUserControls="clr-namespace:PixiEditor.Views.UserControls.Layers" d:DataContext="{d:DesignInstance Type=vm:ViewModelMain}"
         mc:Ignorable="d" WindowStyle="None" Initialized="MainWindow_Initialized"
         mc:Ignorable="d" WindowStyle="None" Initialized="MainWindow_Initialized"
         Title="PixiEditor" Name="mainWindow" Height="1000" Width="1600" Background="{StaticResource MainColor}"
         Title="PixiEditor" Name="mainWindow" Height="1000" Width="1600" Background="{StaticResource MainColor}"
         WindowStartupLocation="CenterScreen" WindowState="Maximized">
         WindowStartupLocation="CenterScreen" WindowState="Maximized">
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="35" x:Name="windowsChrome"/>
+        <WindowChrome CaptionHeight="35" GlassFrameThickness="0.1" x:Name="windowsChrome"/>
     </WindowChrome.WindowChrome>
     </WindowChrome.WindowChrome>
 
 
     <Window.Resources>
     <Window.Resources>
@@ -27,6 +28,8 @@
             <converters:NotNullToBoolConverter x:Key="NotNullToBoolConverter" />
             <converters:NotNullToBoolConverter x:Key="NotNullToBoolConverter" />
             <converters:LayersToStructuredLayersConverter x:Key="LayersToStructuredLayersConverter"/>
             <converters:LayersToStructuredLayersConverter x:Key="LayersToStructuredLayersConverter"/>
             <converters:DoubleToIntConverter x:Key="DoubleToIntConverter"/>
             <converters:DoubleToIntConverter x:Key="DoubleToIntConverter"/>
+            <converters:IsSpecifiedTypeConverter SpecifiedType="{x:Type tools:ZoomTool}" x:Key="IsZoomToolConverter"/>
+            <converters:IsSpecifiedTypeConverter SpecifiedType="{x:Type tools:MoveViewportTool}" x:Key="IsMoveViewportToolConverter"/>
             <ResourceDictionary.MergedDictionaries>
             <ResourceDictionary.MergedDictionaries>
                 <ResourceDictionary Source="pack://application:,,,/ColorPicker;component/Styles/DefaultColorPickerStyle.xaml" />
                 <ResourceDictionary Source="pack://application:,,,/ColorPicker;component/Styles/DefaultColorPickerStyle.xaml" />
             </ResourceDictionary.MergedDictionaries>
             </ResourceDictionary.MergedDictionaries>
@@ -181,6 +184,37 @@
                     Style="{StaticResource ToolSettingsGlyphButton}" Content="&#xE7A7;"/>
                     Style="{StaticResource ToolSettingsGlyphButton}" Content="&#xE7A7;"/>
             <Button Command="{Binding UndoSubViewModel.RedoCommand}" ToolTip="Redo"
             <Button Command="{Binding UndoSubViewModel.RedoCommand}" ToolTip="Redo"
                     Style="{StaticResource ToolSettingsGlyphButton}" Content="&#xE7A6;"/>
                     Style="{StaticResource ToolSettingsGlyphButton}" Content="&#xE7A6;"/>
+            <ToggleButton Width="30" BorderThickness="0"
+                          ToolTip="Pen Mode"
+                          IsChecked="{Binding StylusSubViewModel.IsPenModeEnabled}">
+                <ToggleButton.Style>
+                    <Style TargetType="ToggleButton">
+                        <Setter Property="Template">
+                            <Setter.Value>
+                                <ControlTemplate TargetType="ToggleButton">
+                                    <Border BorderBrush="{TemplateBinding BorderBrush}" 
+                                            Background="{TemplateBinding Background}">
+                                        <ContentPresenter HorizontalAlignment="Center"
+                                              VerticalAlignment="Center"/>
+                                    </Border>
+                                </ControlTemplate>
+                            </Setter.Value>
+                        </Setter>
+                        <Style.Triggers>
+                            <Trigger Property="IsChecked" Value="False">
+                                <Setter Property="Background" Value="Transparent"/>
+                            </Trigger>
+                            <Trigger Property="IsMouseOver" Value="True">
+                                <Setter Property="Background" Value="#606060"/>
+                            </Trigger>
+                            <Trigger Property="IsChecked" Value="True">
+                                <Setter Property="Background" Value="#707070"/>
+                            </Trigger>
+                        </Style.Triggers>
+                    </Style>
+                </ToggleButton.Style>
+                <Image Height="20" Source="../Images/penMode.png"/>
+            </ToggleButton>
             <Grid Margin="5,5,10,5" Background="{StaticResource BrighterAccentColor}" Width="5"/>
             <Grid Margin="5,5,10,5" Background="{StaticResource BrighterAccentColor}" Width="5"/>
             <Label Style="{StaticResource BaseLabel}" FontSize="12"
             <Label Style="{StaticResource BaseLabel}" FontSize="12"
                    VerticalAlignment="Center" Content="{Binding BitmapManager.SelectedTool.DisplayName}"
                    VerticalAlignment="Center" Content="{Binding BitmapManager.SelectedTool.DisplayName}"
@@ -225,16 +259,18 @@
                             <ui:DocumentsTemplateSelector.DocumentsViewTemplate>
                             <ui:DocumentsTemplateSelector.DocumentsViewTemplate>
                                 <DataTemplate DataType="{x:Type dataHolders:Document}">
                                 <DataTemplate DataType="{x:Type dataHolders:Document}">
                                     <usercontrols:DrawingViewPort
                                     <usercontrols:DrawingViewPort
-                                        ZoomPercentage="{Binding ZoomPercentage}"
-                                        RecenterZoombox="{Binding RecenterZoombox}"
+                                        CenterViewportTrigger="{Binding CenterViewportTrigger}"
+                                        ZoomViewportTrigger="{Binding ZoomViewportTrigger}"
                                         GridLinesVisible="{Binding XamlAccesibleViewModel.ViewportSubViewModel.GridLinesEnabled}"
                                         GridLinesVisible="{Binding XamlAccesibleViewModel.ViewportSubViewModel.GridLinesEnabled}"
                                         Cursor="{Binding XamlAccesibleViewModel.ToolsSubViewModel.ToolCursor}"
                                         Cursor="{Binding XamlAccesibleViewModel.ToolsSubViewModel.ToolCursor}"
                                         MiddleMouseClickedCommand="{Binding XamlAccesibleViewModel.ToolsSubViewModel.SelectToolCommand}"
                                         MiddleMouseClickedCommand="{Binding XamlAccesibleViewModel.ToolsSubViewModel.SelectToolCommand}"
-                                        ViewportPosition="{Binding ViewportPosition}"
                                         MouseMoveCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseMoveCommand}"
                                         MouseMoveCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseMoveCommand}"
                                         MouseDownCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseDownCommand}"
                                         MouseDownCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseDownCommand}"
                                         MouseXOnCanvas="{Binding MouseXOnCanvas, Mode=TwoWay}"
                                         MouseXOnCanvas="{Binding MouseXOnCanvas, Mode=TwoWay}"
                                         MouseYOnCanvas="{Binding MouseYOnCanvas, Mode=TwoWay}"
                                         MouseYOnCanvas="{Binding MouseYOnCanvas, Mode=TwoWay}"
+                                        UseTouchGestures="{Binding XamlAccesibleViewModel.StylusSubViewModel.UseTouchGestures}"
+                                        IsUsingZoomTool="{Binding XamlAccesibleViewModel.BitmapManager.SelectedTool, Converter={StaticResource IsZoomToolConverter}}"
+                                        IsUsingMoveViewportTool="{Binding XamlAccesibleViewModel.BitmapManager.SelectedTool, Converter={StaticResource IsMoveViewportToolConverter}}"
                                         Stylus.IsTapFeedbackEnabled="False" Stylus.IsTouchFeedbackEnabled="False">
                                         Stylus.IsTapFeedbackEnabled="False" Stylus.IsTouchFeedbackEnabled="False">
                                         <i:Interaction.Triggers>
                                         <i:Interaction.Triggers>
                                             <i:EventTrigger EventName="PreviewMouseDown">
                                             <i:EventTrigger EventName="PreviewMouseDown">
@@ -295,20 +331,20 @@
                                     <LayoutAnchorable ContentId="layers" Title="Layers" CanHide="False"
                                     <LayoutAnchorable ContentId="layers" Title="Layers" CanHide="False"
                                                          CanClose="False" CanAutoHide="False"
                                                          CanClose="False" CanAutoHide="False"
                                                          CanDockAsTabbedDocument="True" CanFloat="True">
                                                          CanDockAsTabbedDocument="True" CanFloat="True">
-                                        <usercontrols:LayersManager                                            
+                                        <layerUserControls:LayersManager                                            
                                             LayerCommandsViewModel="{Binding LayersSubViewModel}"
                                             LayerCommandsViewModel="{Binding LayersSubViewModel}"
                                             OpacityInputEnabled="{Binding BitmapManager.ActiveDocument, 
                                             OpacityInputEnabled="{Binding BitmapManager.ActiveDocument, 
                     Converter={StaticResource NotNullToBoolConverter}}">
                     Converter={StaticResource NotNullToBoolConverter}}">
-                                            <usercontrols:LayersManager.LayerTreeRoot>
+                                            <layerUserControls:LayersManager.LayerTreeRoot>
                                                 <MultiBinding Converter="{StaticResource LayersToStructuredLayersConverter}">
                                                 <MultiBinding Converter="{StaticResource LayersToStructuredLayersConverter}">
                                                     <Binding Path="BitmapManager.ActiveDocument.Layers" />
                                                     <Binding Path="BitmapManager.ActiveDocument.Layers" />
                                                     <Binding Path="BitmapManager.ActiveDocument.LayerStructure"/>
                                                     <Binding Path="BitmapManager.ActiveDocument.LayerStructure"/>
                                                 </MultiBinding>
                                                 </MultiBinding>
-                                            </usercontrols:LayersManager.LayerTreeRoot>
-                                        </usercontrols:LayersManager>
+                                            </layerUserControls:LayersManager.LayerTreeRoot>
+                                        </layerUserControls:LayersManager>
                                     </LayoutAnchorable>
                                     </LayoutAnchorable>
                                     <LayoutAnchorable x:Name="rawLayerAnchorable" ContentId="rawLayer" Title="Raw layers">
                                     <LayoutAnchorable x:Name="rawLayerAnchorable" ContentId="rawLayer" Title="Raw layers">
-                                        <usercontrols:RawLayersViewer Layers="{Binding BitmapManager.ActiveDocument.Layers}"
+                                        <layerUserControls:RawLayersViewer Layers="{Binding BitmapManager.ActiveDocument.Layers}"
                                                                       Structure="{Binding BitmapManager.ActiveDocument.LayerStructure}"/>
                                                                       Structure="{Binding BitmapManager.ActiveDocument.LayerStructure}"/>
                                     </LayoutAnchorable>
                                     </LayoutAnchorable>
                                 </LayoutAnchorablePane>
                                 </LayoutAnchorablePane>
@@ -329,7 +365,7 @@
         </Grid>
         </Grid>
 
 
         <Border Grid.Row="2" Grid.Column="0"
         <Border Grid.Row="2" Grid.Column="0"
-                    Background="{StaticResource AccentColor}" Grid.RowSpan="2" CornerRadius="5">
+                    Background="{StaticResource AccentColor}" Grid.RowSpan="2" CornerRadius="5,0,5,5">
         <StackPanel Orientation="Vertical" Cursor="Arrow" >
         <StackPanel Orientation="Vertical" Cursor="Arrow" >
 
 
             <ItemsControl ItemsSource="{Binding ToolsSubViewModel.ToolSet}">
             <ItemsControl ItemsSource="{Binding ToolsSubViewModel.ToolSet}">

+ 41 - 0
PixiEditor/Views/UserControls/AvalonDockWindows/ReferenceLayerWindow.xaml

@@ -0,0 +1,41 @@
+<UserControl x:Class="PixiEditor.Views.UserControls.AvalonDockWindows.ReferenceLayerWindow"
+             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:local="clr-namespace:PixiEditor.Views.UserControls.AvalonDockWindows" xmlns:views="clr-namespace:PixiEditor.Views" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+             mc:Ignorable="d" 
+             d:DesignHeight="200" d:DesignWidth="200"
+             Name="uc">
+    <UserControl.Resources>
+        <Style TargetType="TextBlock">
+            <Setter Property="Foreground" Value="White"/>
+        </Style>
+        <converters:NotNullToBoolConverter x:Key="NotNullToBoolConverter"/>
+    </UserControl.Resources>
+    
+    <StackPanel Margin="5">
+        <Grid>
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition/>
+                <ColumnDefinition Width="Auto"/>
+            </Grid.ColumnDefinitions>
+            <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Path:</TextBlock>
+            <TextBox Text="{Binding FilePath, ElementName=uc}" Grid.Column="1"
+                     Style="{StaticResource DarkTextBoxStyle}" FontSize="14"/>
+            <Button Grid.Column="2" Content="&#xE838;" VerticalAlignment="Center"
+                    Style="{StaticResource ToolSettingsGlyphButton}" Width="20"
+                    Command="{Binding OpenFilePickerCommand, ElementName=uc}"></Button>
+        </Grid>
+        <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
+            <TextBlock Text="Opacity: " Foreground="White" VerticalAlignment="Center"/>
+            <views:NumberInput Min="0" Max="100" Value="{Binding LayerOpacity, ElementName=uc, Mode=TwoWay}"
+                               Width="40" Height="20" VerticalAlignment="Center"/>
+            <TextBlock Text=" %" Foreground="White" VerticalAlignment="Center"/>
+        </StackPanel>
+        <Button Command="{Binding UpdateLayerCommand, ElementName=uc}"
+                Style="{StaticResource DarkRoundButton}" FontSize="14" 
+                Height="25" Margin="0,5">Update</Button>
+    </StackPanel>
+</UserControl>

+ 82 - 0
PixiEditor/Views/UserControls/AvalonDockWindows/ReferenceLayerWindow.xaml.cs

@@ -0,0 +1,82 @@
+using Microsoft.Win32;
+using PixiEditor.Helpers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.IO;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace PixiEditor.Views.UserControls.AvalonDockWindows
+{
+    /// <summary>
+    /// Interaction logic for ReferenceLayerWindow.xaml
+    /// </summary>
+    public partial class ReferenceLayerWindow : UserControl
+    {
+        public static readonly DependencyProperty DocumentProperty =
+            DependencyProperty.Register(nameof(Document), typeof(Document), typeof(ReferenceLayerWindow));
+
+        public Document Document
+        {
+            get => (Document)GetValue(DocumentProperty);
+            set => SetValue(DocumentProperty, value);
+        }
+
+        public static readonly DependencyProperty FilePathProperty =
+            DependencyProperty.Register(nameof(FilePath), typeof(string), typeof(ReferenceLayerWindow));
+
+        public string FilePath
+        {
+            get => (string)GetValue(FilePathProperty);
+            set => SetValue(FilePathProperty, value);
+        }
+
+        public static readonly DependencyProperty LayerOpacityProperty =
+            DependencyProperty.Register(nameof(LayerOpacity), typeof(float), typeof(ReferenceLayerWindow), new PropertyMetadata(100f));
+
+        public float LayerOpacity
+        {
+            get => (float)GetValue(LayerOpacityProperty);
+            set => SetValue(LayerOpacityProperty, value);
+        }
+
+        public static readonly DependencyProperty HasDocumentProperty =
+            DependencyProperty.Register(nameof(HasDocument), typeof(bool), typeof(ReferenceLayerWindow));
+
+        public bool HasDocument
+        {
+            get => (bool)GetValue(HasDocumentProperty);
+            set => SetValue(HasDocumentProperty, value);
+        }
+
+        public RelayCommand UpdateLayerCommand { get; set; }
+
+        public RelayCommand OpenFilePickerCommand { get; set; }
+
+        public ReferenceLayerWindow()
+        {
+            UpdateLayerCommand = new RelayCommand(UpdateLayer);
+            OpenFilePickerCommand = new RelayCommand(OpenFilePicker);
+            InitializeComponent();
+        }
+
+        private void UpdateLayer(object obj)
+        {
+            Document.ReferenceLayer.LayerBitmap = Importer.ImportImage(FilePath);
+            Document.ReferenceLayer.Opacity = LayerOpacity;
+        }
+
+        private void OpenFilePicker(object obj)
+        {
+            OpenFileDialog dialog = new OpenFileDialog()
+            {
+                Filter = "PNG Files|*.png|JPEG Files|*.jpg;*.jpeg",
+                CheckFileExists = true
+            };
+
+            if ((bool)dialog.ShowDialog())
+            {
+                FilePath = dialog.FileName;
+            }
+        }
+    }
+}

+ 5 - 5
PixiEditor/Views/UserControls/DiscordRPPreview.xaml

@@ -8,8 +8,8 @@
              d:DesignHeight="280" d:DesignWidth="300"
              d:DesignHeight="280" d:DesignWidth="300"
              x:Name="uc">
              x:Name="uc">
     <UserControl.Resources>
     <UserControl.Resources>
-        <converters:EmptyStringToVisibiltyConverter x:Key="EmptyStringToVisibilty"/>
-        <BoolToVisibilityConverter x:Key="BoolToVisibilty"/>
+        <converters:EmptyStringToVisibilityConverter x:Key="EmptyStringToVisibility"/>
+        <BoolToVisibilityConverter x:Key="BoolToVisibility"/>
         <converters:BoolToBrushConverter x:Key="BoolToBrush"/>
         <converters:BoolToBrushConverter x:Key="BoolToBrush"/>
         <converters:BrushTuple FirstBrush="#7289da" SecondBrush="#202225" x:Key="BackgroundBrushTuple"/>
         <converters:BrushTuple FirstBrush="#7289da" SecondBrush="#202225" x:Key="BackgroundBrushTuple"/>
         <converters:BrushTuple FirstBrush="White" SecondBrush="#7289da" x:Key="BotLabelTuple"/>
         <converters:BrushTuple FirstBrush="White" SecondBrush="#7289da" x:Key="BotLabelTuple"/>
@@ -49,15 +49,15 @@
                         </StackPanel>
                         </StackPanel>
                     </StackPanel>
                     </StackPanel>
                 </Grid>
                 </Grid>
-                <Grid Grid.Row="1" Background="#0D000000" Visibility="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToVisibilty}}">
+                <Grid Grid.Row="1" Background="#0D000000" Visibility="{Binding ElementName=uc, Path=IsPlaying, Converter={StaticResource BoolToVisibility}}">
                     <StackPanel Orientation="Vertical" Margin="15">
                     <StackPanel Orientation="Vertical" Margin="15">
                         <TextBlock FontWeight="Bold" FontSize="12" Foreground="White" Margin="0,0,0,10">PLAYING A "GAME"</TextBlock>
                         <TextBlock FontWeight="Bold" FontSize="12" Foreground="White" Margin="0,0,0,10">PLAYING A "GAME"</TextBlock>
                         <StackPanel Orientation="Horizontal">
                         <StackPanel Orientation="Horizontal">
                             <Image Source="../../Images/PixiEditorLogo.png" Height="70"/>
                             <Image Source="../../Images/PixiEditorLogo.png" Height="70"/>
                             <StackPanel Margin="15,0,0,0" VerticalAlignment="Center">
                             <StackPanel Margin="15,0,0,0" VerticalAlignment="Center">
                                 <TextBlock Foreground="White" FontSize="12" FontWeight="SemiBold">PixiEditor</TextBlock>
                                 <TextBlock Foreground="White" FontSize="12" FontWeight="SemiBold">PixiEditor</TextBlock>
-                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=Detail}" Visibility="{Binding ElementName=uc, Path=Detail, Converter={StaticResource EmptyStringToVisibilty}}"/>
-                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=State}" Visibility="{Binding ElementName=uc, Path=State, Converter={StaticResource EmptyStringToVisibilty}}"/>
+                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=Detail}" Visibility="{Binding ElementName=uc, Path=Detail, Converter={StaticResource EmptyStringToVisibility}}"/>
+                            <TextBlock Foreground="White" FontSize="12" Text="{Binding ElementName=uc, Path=State}" Visibility="{Binding ElementName=uc, Path=State, Converter={StaticResource EmptyStringToVisibility}}"/>
                             <TextBlock Foreground="White" FontSize="12">00:00 elapsed</TextBlock>
                             <TextBlock Foreground="White" FontSize="12">00:00 elapsed</TextBlock>
                             </StackPanel>
                             </StackPanel>
                         </StackPanel>
                         </StackPanel>

+ 26 - 18
PixiEditor/Views/UserControls/DrawingViewPort.xaml

@@ -8,22 +8,18 @@
              xmlns:vws="clr-namespace:PixiEditor.Views"
              xmlns:vws="clr-namespace:PixiEditor.Views"
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
              xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
              xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
-             xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:sys="clr-namespace:System;assembly=System.Runtime" x:Class="PixiEditor.Views.UserControls.DrawingViewPort"
-             mc:Ignorable="d" 
-             d:DesignHeight="450" d:DesignWidth="800" x:Name="uc">
+             xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:sys="clr-namespace:System;assembly=System.Runtime" xmlns:dataholders="clr-namespace:PixiEditor.Models.DataHolders" x:Class="PixiEditor.Views.UserControls.DrawingViewPort"
+    mc:Ignorable="d"
+    d:DesignHeight="450" d:DesignWidth="800" x:Name="uc">
     <UserControl.Resources>
     <UserControl.Resources>
         <converters:BoolToIntConverter x:Key="BoolToIntConverter" />
         <converters:BoolToIntConverter x:Key="BoolToIntConverter" />
         <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
         <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
         <converters:IntToViewportRectConverter x:Key="IntToViewportRectConverter" />
         <converters:IntToViewportRectConverter x:Key="IntToViewportRectConverter" />
-        <converters:ZoomToViewportConverter x:Key="ZoomToViewportConverter"/>
+        <converters:ZoomToViewportConverter x:Key="ZoomToViewportConverter" />
     </UserControl.Resources>
     </UserControl.Resources>
-    <vws:MainDrawingPanel ZoomPercentage="{Binding ZoomPercentage, ElementName=uc, Mode=TwoWay}"
-                          Center="{Binding RecenterZoombox, ElementName=uc, Mode=TwoWay}" 
-                          x:Name="DrawingPanel"
-                          CenterOnStart="True" Cursor="{Binding Cursor, ElementName=uc}" 
-                          MiddleMouseClickedCommand="{Binding MiddleMouseClickedCommand, ElementName=uc}" 
-                          MiddleMouseClickedCommandParameter="{x:Type tools:MoveViewportTool}"
-                          ViewportPosition="{Binding ViewportPosition, ElementName=uc, Mode=TwoWay}">
+
+    <local:Zoombox x:Name="zoombox" ClipToBounds="True" d:DataContext="{d:DesignInstance dataholders:Document}"
+                   UseTouchGestures="{Binding UseTouchGestures, ElementName=uc}">
         <i:Interaction.Triggers>
         <i:Interaction.Triggers>
             <i:EventTrigger EventName="MouseMove">
             <i:EventTrigger EventName="MouseMove">
                 <i:InvokeCommandAction Command="{Binding MouseMoveCommand, ElementName=uc}" />
                 <i:InvokeCommandAction Command="{Binding MouseMoveCommand, ElementName=uc}" />
@@ -31,20 +27,25 @@
             <i:EventTrigger EventName="MouseDown">
             <i:EventTrigger EventName="MouseDown">
                 <i:InvokeCommandAction Command="{Binding MouseDownCommand, ElementName=uc}"/>
                 <i:InvokeCommandAction Command="{Binding MouseDownCommand, ElementName=uc}"/>
             </i:EventTrigger>
             </i:EventTrigger>
+            <i:EventTrigger EventName="PreviewMouseDown">
+                <i:InvokeCommandAction Command="{Binding PreviewMouseDownCommand, ElementName=uc}"/>
+            </i:EventTrigger>
         </i:Interaction.Triggers>
         </i:Interaction.Triggers>
         <i:Interaction.Behaviors>
         <i:Interaction.Behaviors>
-            <behaviors:MouseBehavior RelativeTo="{Binding Item, ElementName=DrawingPanel}"
-                                                  MouseX="{Binding MouseXOnCanvas, ElementName=uc, Mode=TwoWay}"
-                                                  MouseY="{Binding MouseYOnCanvas, ElementName=uc, Mode=TwoWay}" />
+            <behaviors:MouseBehavior RelativeTo="{Binding ElementName=zoombox, Path=AdditionalContent}"
+                                                  MouseX="{Binding MouseXOnCanvas, Mode=TwoWay, ElementName=uc}"
+                                                  MouseY="{Binding MouseYOnCanvas, Mode=TwoWay, ElementName=uc}" />
         </i:Interaction.Behaviors>
         </i:Interaction.Behaviors>
-        <vws:MainDrawingPanel.Item>
+
+        <local:Zoombox.AdditionalContent>
             <Canvas Width="{Binding Width}"
             <Canvas Width="{Binding Width}"
+                                Loaded="OnCanvasLoaded"
                                 Height="{Binding Height}" VerticalAlignment="Center"
                                 Height="{Binding Height}" VerticalAlignment="Center"
                                 HorizontalAlignment="Center" RenderOptions.BitmapScalingMode="NearestNeighbor">
                                 HorizontalAlignment="Center" RenderOptions.BitmapScalingMode="NearestNeighbor">
                 <Canvas.Background>
                 <Canvas.Background>
                     <ImageBrush ImageSource="/Images/CheckerTile.png" TileMode="Tile" ViewportUnits="Absolute">
                     <ImageBrush ImageSource="/Images/CheckerTile.png" TileMode="Tile" ViewportUnits="Absolute">
                         <ImageBrush.Viewport>
                         <ImageBrush.Viewport>
-                            <Binding Path="Scale" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type xctk:Zoombox}}" Converter="{StaticResource ZoomToViewportConverter}">
+                            <Binding Path="Zoom" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type local:Zoombox}}" Converter="{StaticResource ZoomToViewportConverter}">
                                 <Binding.ConverterParameter>
                                 <Binding.ConverterParameter>
                                     <sys:Double>16</sys:Double>
                                     <sys:Double>16</sys:Double>
                                 </Binding.ConverterParameter>
                                 </Binding.ConverterParameter>
@@ -52,11 +53,18 @@
                         </ImageBrush.Viewport>
                         </ImageBrush.Viewport>
                     </ImageBrush>
                     </ImageBrush>
                 </Canvas.Background>
                 </Canvas.Background>
+                <Image Source="{Binding ReferenceLayer.LayerBitmap}"
+                       VerticalAlignment="Center" Stretch="Uniform"
+                       Visibility="{Binding ReferenceLayer.IsVisible, Converter={BoolToVisibilityConverter}}"
+                       HorizontalAlignment="Center" Width="{Binding Width}"
+                       Height="{Binding Height}"
+                       RenderOptions.BitmapScalingMode="NearestNeighbor"/>
                 <Image Source="{Binding PreviewLayer.LayerBitmap}" Panel.ZIndex="2"
                 <Image Source="{Binding PreviewLayer.LayerBitmap}" Panel.ZIndex="2"
                                    RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform"
                                    RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform"
                                    Width="{Binding PreviewLayer.Width}"
                                    Width="{Binding PreviewLayer.Width}"
                                    Height="{Binding PreviewLayer.Height}" 
                                    Height="{Binding PreviewLayer.Height}" 
                                    Margin="{Binding PreviewLayer.Offset}"/>
                                    Margin="{Binding PreviewLayer.Offset}"/>
+
                 <ItemsControl ItemsSource="{Binding Layers}">
                 <ItemsControl ItemsSource="{Binding Layers}">
                     <ItemsControl.ItemsPanel>
                     <ItemsControl.ItemsPanel>
                         <ItemsPanelTemplate>
                         <ItemsPanelTemplate>
@@ -130,6 +138,6 @@
                     </Rectangle>
                     </Rectangle>
                 </Grid>
                 </Grid>
             </Canvas>
             </Canvas>
-        </vws:MainDrawingPanel.Item>
-    </vws:MainDrawingPanel>
+        </local:Zoombox.AdditionalContent>
+    </local:Zoombox>
 </UserControl>
 </UserControl>

+ 125 - 60
PixiEditor/Views/UserControls/DrawingViewPort.xaml.cs

@@ -1,5 +1,6 @@
-using System;
-using System.Diagnostics;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Tools.Tools;
+using System;
 using System.Windows;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Input;
@@ -11,99 +12,163 @@ namespace PixiEditor.Views.UserControls
     /// </summary>
     /// </summary>
     public partial class DrawingViewPort : UserControl
     public partial class DrawingViewPort : UserControl
     {
     {
-        public DrawingViewPort()
+        public static readonly DependencyProperty MiddleMouseClickedCommandProperty =
+            DependencyProperty.Register(nameof(MiddleMouseClickedCommand), typeof(ICommand), typeof(DrawingViewPort), new PropertyMetadata(default(ICommand)));
+
+        public static readonly DependencyProperty MouseMoveCommandProperty =
+            DependencyProperty.Register(nameof(MouseMoveCommand), typeof(ICommand), typeof(DrawingViewPort), new PropertyMetadata(default(ICommand)));
+
+        public static readonly DependencyProperty MouseDownCommandProperty =
+            DependencyProperty.Register(nameof(MouseDownCommand), typeof(ICommand), typeof(DrawingViewPort), new PropertyMetadata(default(ICommand)));
+
+        public static readonly DependencyProperty MouseXOnCanvasProperty =
+            DependencyProperty.Register(nameof(MouseXOnCanvas), typeof(double), typeof(DrawingViewPort), new PropertyMetadata(0.0));
+
+        public static readonly DependencyProperty MouseYOnCanvasProperty =
+            DependencyProperty.Register(nameof(MouseYOnCanvas), typeof(double), typeof(DrawingViewPort), new PropertyMetadata(0.0));
+
+        public static readonly DependencyProperty GridLinesVisibleProperty =
+            DependencyProperty.Register(nameof(GridLinesVisible), typeof(bool), typeof(DrawingViewPort), new PropertyMetadata(false));
+
+        public static readonly DependencyProperty IsUsingZoomToolProperty =
+            DependencyProperty.Register(nameof(IsUsingZoomTool), typeof(bool), typeof(DrawingViewPort), new PropertyMetadata(false, ToolChanged));
+
+        public static readonly DependencyProperty IsUsingMoveViewportToolProperty =
+            DependencyProperty.Register(nameof(IsUsingMoveViewportTool), typeof(bool), typeof(DrawingViewPort), new PropertyMetadata(false, ToolChanged));
+
+        public static readonly DependencyProperty CenterViewportTriggerProperty =
+            DependencyProperty.Register(nameof(CenterViewportTrigger), typeof(ExecutionTrigger<Size>), typeof(DrawingViewPort),
+                new PropertyMetadata(default(ExecutionTrigger<Size>), CenterViewportTriggerChanged));
+
+        public static readonly DependencyProperty ZoomViewportTriggerProperty =
+            DependencyProperty.Register(nameof(ZoomViewportTrigger), typeof(ExecutionTrigger<double>), typeof(DrawingViewPort),
+                new PropertyMetadata(default(ExecutionTrigger<double>), ZoomViewportTriggerChanged));
+
+        public static readonly DependencyProperty UseTouchGesturesProperty =
+            DependencyProperty.Register(nameof(UseTouchGestures), typeof(bool), typeof(DrawingViewPort));
+
+        public ICommand MiddleMouseClickedCommand
         {
         {
-            InitializeComponent();
+            get => (ICommand)GetValue(MiddleMouseClickedCommandProperty);
+            set => SetValue(MiddleMouseClickedCommandProperty, value);
         }
         }
 
 
-        public float ZoomPercentage
+        public ICommand MouseMoveCommand
         {
         {
-            get { return (float)GetValue(ZoomPercentageProperty); }
-            set { SetValue(ZoomPercentageProperty, value); }
+            get => (ICommand)GetValue(MouseMoveCommandProperty);
+            set => SetValue(MouseMoveCommandProperty, 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(float), typeof(DrawingViewPort), new PropertyMetadata(100f));
-
-        public bool RecenterZoombox
+        public ICommand MouseDownCommand
         {
         {
-            get { return (bool)GetValue(RecenterZoomboxProperty); }
-            set { SetValue(RecenterZoomboxProperty, value); }
+            get => (ICommand)GetValue(MouseDownCommandProperty);
+            set => SetValue(MouseDownCommandProperty, value);
         }
         }
 
 
-        // Using a DependencyProperty as the backing store for RecenterZoombox.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty RecenterZoomboxProperty =
-            DependencyProperty.Register("RecenterZoombox", typeof(bool), typeof(DrawingViewPort), new PropertyMetadata(false));
+        public double MouseXOnCanvas
+        {
+            get => (double)GetValue(MouseXOnCanvasProperty);
+            set => SetValue(MouseXOnCanvasProperty, value);
+        }
 
 
-        public ICommand MiddleMouseClickedCommand
+        public double MouseYOnCanvas
         {
         {
-            get { return (ICommand)GetValue(MiddleMouseClickedCommandProperty); }
-            set { SetValue(MiddleMouseClickedCommandProperty, value); }
+            get => (double)GetValue(MouseYOnCanvasProperty);
+            set => SetValue(MouseYOnCanvasProperty, 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(DrawingViewPort), new PropertyMetadata(default(ICommand)));
+        public bool GridLinesVisible
+        {
+            get => (bool)GetValue(GridLinesVisibleProperty);
+            set => SetValue(GridLinesVisibleProperty, value);
+        }
 
 
-        public Point ViewportPosition
+        public bool IsUsingZoomTool
         {
         {
-            get { return (Point)GetValue(ViewportPositionProperty); }
-            set { SetValue(ViewportPositionProperty, value); }
+            get => (bool)GetValue(IsUsingZoomToolProperty);
+            set => SetValue(IsUsingZoomToolProperty, 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(DrawingViewPort), new PropertyMetadata(default(Point)));
+        public bool IsUsingMoveViewportTool
+        {
+            get => (bool)GetValue(IsUsingMoveViewportToolProperty);
+            set => SetValue(IsUsingMoveViewportToolProperty, value);
+        }
+        public bool UseTouchGestures
+        {
+            get => (bool)GetValue(UseTouchGesturesProperty);
+            set => SetValue(UseTouchGesturesProperty, value);
+        }
 
 
-        public ICommand MouseMoveCommand
+        public ExecutionTrigger<Size> CenterViewportTrigger
         {
         {
-            get { return (ICommand)GetValue(MouseMoveCommandProperty); }
-            set { SetValue(MouseMoveCommandProperty, value); }
+            get => (ExecutionTrigger<Size>)GetValue(CenterViewportTriggerProperty);
+            set => SetValue(CenterViewportTriggerProperty, value);
         }
         }
 
 
-        // 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(DrawingViewPort), new PropertyMetadata(default(ICommand)));
+        public ExecutionTrigger<double> ZoomViewportTrigger
+        {
+            get => (ExecutionTrigger<double>)GetValue(ZoomViewportTriggerProperty);
+            set => SetValue(ZoomViewportTriggerProperty, value);
+        }
 
 
-        public ICommand MouseDownCommand
+        public RelayCommand PreviewMouseDownCommand { get; private set; }
+        private static void ToolChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
         {
         {
-            get { return (ICommand)GetValue(MouseDownCommandProperty); }
-            set { SetValue(MouseDownCommandProperty, value); }
+            var panel = (DrawingViewPort)sender;
+            if (panel.IsUsingZoomTool)
+                panel.zoombox.ZoomMode = Zoombox.Mode.ZoomTool;
+            else if (panel.IsUsingMoveViewportTool)
+                panel.zoombox.ZoomMode = Zoombox.Mode.MoveTool;
+            else
+                panel.zoombox.ZoomMode = Zoombox.Mode.Normal;
         }
         }
 
 
-        // Using a DependencyProperty as the backing store for MouseDownCommand.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MouseDownCommandProperty =
-            DependencyProperty.Register("MouseDownCommand", typeof(ICommand), typeof(DrawingViewPort), new PropertyMetadata(default(ICommand)));
+        private static void CenterViewportTriggerChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
+        {
+            var viewport = (DrawingViewPort)sender;
+            if (args.OldValue != null)
+                ((ExecutionTrigger<Size>)args.OldValue).Triggered -= viewport.CenterZoomboxContent;
+            ((ExecutionTrigger<Size>)args.NewValue).Triggered += viewport.CenterZoomboxContent;
+        }
 
 
-        public double MouseXOnCanvas
+        private static void ZoomViewportTriggerChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
         {
         {
-            get { return (double)GetValue(MouseXOnCanvasProperty); }
-            set { SetValue(MouseXOnCanvasProperty, value); }
+            var viewport = (DrawingViewPort)sender;
+            if (args.OldValue != null)
+                ((ExecutionTrigger<double>)args.OldValue).Triggered -= viewport.ZoomZoomboxContent;
+            ((ExecutionTrigger<double>)args.NewValue).Triggered += viewport.ZoomZoomboxContent;
         }
         }
 
 
-        // Using a DependencyProperty as the backing store for MouseXOnCanvas.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MouseXOnCanvasProperty =
-            DependencyProperty.Register("MouseXOnCanvas", typeof(double), typeof(DrawingViewPort), new PropertyMetadata(0.0));
+        private bool loaded = false;
 
 
-        public double MouseYOnCanvas
+        public DrawingViewPort()
         {
         {
-            get { return (double)GetValue(MouseYOnCanvasProperty); }
-            set { SetValue(MouseYOnCanvasProperty, value); }
+            PreviewMouseDownCommand = new RelayCommand(ProcessMouseDown);
+            InitializeComponent();
         }
         }
 
 
-        // Using a DependencyProperty as the backing store for MouseXOnCanvas.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MouseYOnCanvasProperty =
-            DependencyProperty.Register("MouseYOnCanvas", typeof(double), typeof(DrawingViewPort), new PropertyMetadata(0.0));
+        private void CenterZoomboxContent(object sender, Size args)
+        {
+            zoombox.CenterContent(args);
+        }
+        private void ZoomZoomboxContent(object sender, double args)
+        {
+            zoombox.ZoomIntoCenter(args);
+        }
 
 
-        public bool GridLinesVisible
+        private void ProcessMouseDown(object parameter)
         {
         {
-            get { return (bool)GetValue(GridLinesVisibleProperty); }
-            set { SetValue(GridLinesVisibleProperty, value); }
+            if (Mouse.MiddleButton == MouseButtonState.Pressed && MiddleMouseClickedCommand.CanExecute(typeof(MoveViewportTool)))
+                MiddleMouseClickedCommand.Execute(typeof(MoveViewportTool));
         }
         }
 
 
-        // Using a DependencyProperty as the backing store for GridLinesVisible.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty GridLinesVisibleProperty =
-            DependencyProperty.Register("GridLinesVisible", typeof(bool), typeof(DrawingViewPort), new PropertyMetadata(false));
+        private void OnCanvasLoaded(object sender, EventArgs e)
+        {
+            if (loaded)
+                return;
+            zoombox.CenterContent();
+            loaded = true;
+        }
     }
     }
-}
+}

+ 1 - 1
PixiEditor/Views/UserControls/LayerGroupControl.xaml → PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml

@@ -1,4 +1,4 @@
-<UserControl x:Class="PixiEditor.Views.UserControls.LayerGroupControl"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.LayerGroupControl"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

+ 37 - 37
PixiEditor/Views/UserControls/LayerGroupControl.xaml.cs → PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml.cs

@@ -9,7 +9,7 @@ using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Input;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
 
 
-namespace PixiEditor.Views.UserControls
+namespace PixiEditor.Views.UserControls.Layers
 {
 {
     /// <summary>
     /// <summary>
     /// Interaction logic for LayerFolder.xaml.
     /// Interaction logic for LayerFolder.xaml.
@@ -22,9 +22,6 @@ namespace PixiEditor.Views.UserControls
             set { SetValue(GroupGuidProperty, value); }
             set { SetValue(GroupGuidProperty, value); }
         }
         }
 
 
-        public const string LayerGroupControlDataName = "PixiEditor.Views.UserControls.LayerGroupControl";
-        public const string LayerContainerDataName = "PixiEditor.Views.UserControls.LayerStructureItemContainer";
-
         public static readonly DependencyProperty GroupGuidProperty =
         public static readonly DependencyProperty GroupGuidProperty =
             DependencyProperty.Register("GroupGuid", typeof(Guid), typeof(LayerGroupControl), new PropertyMetadata(Guid.NewGuid()));
             DependencyProperty.Register("GroupGuid", typeof(Guid), typeof(LayerGroupControl), new PropertyMetadata(Guid.NewGuid()));
 
 
@@ -53,9 +50,12 @@ namespace PixiEditor.Views.UserControls
         }
         }
 
 
         public static readonly DependencyProperty GroupOpacityProperty =
         public static readonly DependencyProperty GroupOpacityProperty =
-            DependencyProperty.Register("GroupOpacity", typeof(float), typeof(LayerGroupControl), new PropertyMetadata(1f));
-
-
+            DependencyProperty.Register("GroupOpacity", typeof(float), typeof(LayerGroupControl), new PropertyMetadata(1f));
+
+
+        public static string LayerGroupControlDataName = typeof(LayerGroupControl).FullName;
+        public static string LayerContainerDataName = typeof(LayerStructureItemContainer).FullName;
+
         private static void LayersViewModelCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
         private static void LayersViewModelCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
         {
             LayerGroupControl control = (LayerGroupControl)d;
             LayerGroupControl control = (LayerGroupControl)d;
@@ -129,9 +129,9 @@ namespace PixiEditor.Views.UserControls
             item.Background = LayerItem.HighlightColor;
             item.Background = LayerItem.HighlightColor;
         }
         }
 
 
-        private void Grid_CenterEnter(object sender, DragEventArgs e)
-        {
-            centerGrid.Background = LayerItem.HighlightColor;
+        private void Grid_CenterEnter(object sender, DragEventArgs e)
+        {
+            centerGrid.Background = LayerItem.HighlightColor;
         }
         }
 
 
         private void Grid_DragLeave(object sender, DragEventArgs e)
         private void Grid_DragLeave(object sender, DragEventArgs e)
@@ -205,10 +205,10 @@ namespace PixiEditor.Views.UserControls
             document.MoveGroupInStructure(group, tempLayer.LayerGuid, above);
             document.MoveGroupInStructure(group, tempLayer.LayerGuid, above);
             document.LayerStructure.AssignParent(tempLayer.LayerGuid, null);
             document.LayerStructure.AssignParent(tempLayer.LayerGuid, null);
             document.RemoveLayer(tempLayer, false);
             document.RemoveLayer(tempLayer, false);
-        }
-
-        private void HandleDropInside(IDataObject dataObj, Grid grid)
-        {
+        }
+
+        private void HandleDropInside(IDataObject dataObj, Grid grid)
+        {
             Guid referenceLayer = GroupData.EndLayerGuid;
             Guid referenceLayer = GroupData.EndLayerGuid;
             LayerItem.RemoveDragEffect(grid);
             LayerItem.RemoveDragEffect(grid);
 
 
@@ -220,20 +220,20 @@ namespace PixiEditor.Views.UserControls
             if (dataObj.GetDataPresent(LayerGroupControlDataName))
             if (dataObj.GetDataPresent(LayerGroupControlDataName))
             {
             {
                 HandleGroupControlDrop(dataObj, referenceLayer, true, true);
                 HandleGroupControlDrop(dataObj, referenceLayer, true, true);
-            }
+            }
         }
         }
 
 
         private void Grid_Drop_Top(object sender, DragEventArgs e)
         private void Grid_Drop_Top(object sender, DragEventArgs e)
         {
         {
             HandleDrop(e.Data, (Grid)sender, true);
             HandleDrop(e.Data, (Grid)sender, true);
-        }
-
-        private void Grid_Drop_Center(object sender, DragEventArgs e)
-        {
-            HandleDropInside(e.Data, (Grid)sender);
-            LayerItem.RemoveDragEffect(centerGrid);
-        }
-
+        }
+
+        private void Grid_Drop_Center(object sender, DragEventArgs e)
+        {
+            HandleDropInside(e.Data, (Grid)sender);
+            LayerItem.RemoveDragEffect(centerGrid);
+        }
+
         private void Grid_Drop_Bottom(object sender, DragEventArgs e)
         private void Grid_Drop_Bottom(object sender, DragEventArgs e)
         {
         {
             HandleDrop(e.Data, (Grid)sender, false);
             HandleDrop(e.Data, (Grid)sender, false);
@@ -243,9 +243,9 @@ namespace PixiEditor.Views.UserControls
         {
         {
             var doc = LayersViewModel.Owner.BitmapManager.ActiveDocument;
             var doc = LayersViewModel.Owner.BitmapManager.ActiveDocument;
             var layer = doc.Layers.First(x => x.LayerGuid == GroupData.EndLayerGuid);
             var layer = doc.Layers.First(x => x.LayerGuid == GroupData.EndLayerGuid);
-            if (doc.ActiveLayerGuid != layer.LayerGuid)
-            {
-                doc.SetMainActiveLayer(doc.Layers.IndexOf(layer));
+            if (doc.ActiveLayerGuid != layer.LayerGuid)
+            {
+                doc.SetMainActiveLayer(doc.Layers.IndexOf(layer));
             }
             }
         }
         }
 
 
@@ -297,16 +297,16 @@ namespace PixiEditor.Views.UserControls
 
 
                 IsVisibleUndoTriggerable = value;
                 IsVisibleUndoTriggerable = value;
             }
             }
-        }
-
-        private void GroupControl_DragEnter(object sender, DragEventArgs e)
-        {
-            middleDropGrid.Visibility = Visibility.Visible;
-        }
-
-        private void GroupControl_DragLeave(object sender, DragEventArgs e)
-        {
-            middleDropGrid.Visibility = Visibility.Collapsed;
-        }
+        }
+
+        private void GroupControl_DragEnter(object sender, DragEventArgs e)
+        {
+            middleDropGrid.Visibility = Visibility.Visible;
+        }
+
+        private void GroupControl_DragLeave(object sender, DragEventArgs e)
+        {
+            middleDropGrid.Visibility = Visibility.Collapsed;
+        }
     }
     }
 }
 }

+ 1 - 1
PixiEditor/Views/UserControls/LayerItem.xaml → PixiEditor/Views/UserControls/Layers/LayerItem.xaml

@@ -1,4 +1,4 @@
-<UserControl x:Class="PixiEditor.Views.LayerItem"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.LayerItem"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

+ 15 - 15
PixiEditor/Views/UserControls/LayerItem.xaml.cs → PixiEditor/Views/UserControls/Layers/LayerItem.xaml.cs

@@ -8,7 +8,7 @@ using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
 
 
-namespace PixiEditor.Views
+namespace PixiEditor.Views.UserControls.Layers
 {
 {
     /// <summary>
     /// <summary>
     /// Interaction logic for LayerItem.xaml.
     /// Interaction logic for LayerItem.xaml.
@@ -156,23 +156,23 @@ namespace PixiEditor.Views
             Grid item = sender as Grid;
             Grid item = sender as Grid;
             RemoveDragEffect(item);
             RemoveDragEffect(item);
 
 
-            if (e.Data.GetDataPresent("PixiEditor.Views.UserControls.LayerStructureItemContainer"))
+            if (e.Data.GetDataPresent(LayerGroupControl.LayerContainerDataName))
             {
             {
-                var data = (LayerStructureItemContainer)e.Data.GetData("PixiEditor.Views.UserControls.LayerStructureItemContainer");
+                var data = (LayerStructureItemContainer)e.Data.GetData(LayerGroupControl.LayerContainerDataName);
                 Guid layer = data.Layer.LayerGuid;
                 Guid layer = data.Layer.LayerGuid;
                 var doc = data.LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
                 var doc = data.LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
 
 
                 doc.MoveLayerInStructure(layer, LayerGuid, above);
                 doc.MoveLayerInStructure(layer, LayerGuid, above);
-                if (dropInParentFolder)
-                {
-                    Guid? groupGuid = doc.LayerStructure.GetGroupByLayer(layer)?.Parent?.GroupGuid;
-                    doc.LayerStructure.AssignParent(layer, groupGuid);
+                if (dropInParentFolder)
+                {
+                    Guid? groupGuid = doc.LayerStructure.GetGroupByLayer(layer)?.Parent?.GroupGuid;
+                    doc.LayerStructure.AssignParent(layer, groupGuid);
                 }
                 }
             }
             }
 
 
-            if (e.Data.GetDataPresent("PixiEditor.Views.UserControls.LayerGroupControl"))
+            if (e.Data.GetDataPresent(LayerGroupControl.LayerGroupControlDataName))
             {
             {
-                var data = (LayerGroupControl)e.Data.GetData("PixiEditor.Views.UserControls.LayerGroupControl");
+                var data = (LayerGroupControl)e.Data.GetData(LayerGroupControl.LayerGroupControlDataName);
                 Guid folder = data.GroupGuid;
                 Guid folder = data.GroupGuid;
 
 
                 var document = data.LayersViewModel.Owner.BitmapManager.ActiveDocument;
                 var document = data.LayersViewModel.Owner.BitmapManager.ActiveDocument;
@@ -196,11 +196,11 @@ namespace PixiEditor.Views
         private void Grid_Drop_Bottom(object sender, DragEventArgs e)
         private void Grid_Drop_Bottom(object sender, DragEventArgs e)
         {
         {
             HandleGridDrop(sender, e, false);
             HandleGridDrop(sender, e, false);
-        }
-
-        private void Grid_Drop_Below(object sender, DragEventArgs e)
-        {
-            HandleGridDrop(sender, e, false, true);
-        }
+        }
+
+        private void Grid_Drop_Below(object sender, DragEventArgs e)
+        {
+            HandleGridDrop(sender, e, false, true);
+        }
     }
     }
 }
 }

+ 2 - 2
PixiEditor/Views/UserControls/LayerStructureItemContainer.xaml → PixiEditor/Views/UserControls/Layers/LayerStructureItemContainer.xaml

@@ -1,9 +1,9 @@
-<UserControl x:Class="PixiEditor.Views.UserControls.LayerStructureItemContainer"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.LayerStructureItemContainer"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
-             xmlns:vws="clr-namespace:PixiEditor.Views" xmlns:layers="clr-namespace:PixiEditor.Models.Layers" d:DataContext="{d:DesignInstance Type=layers:Layer}"
+             xmlns:vws="clr-namespace:PixiEditor.Views.UserControls.Layers" xmlns:layers="clr-namespace:PixiEditor.Models.Layers" d:DataContext="{d:DesignInstance Type=layers:Layer}"
              mc:Ignorable="d"
              mc:Ignorable="d"
              d:DesignHeight="60" d:DesignWidth="250" Name="layerStructureContainer">
              d:DesignHeight="60" d:DesignWidth="250" Name="layerStructureContainer">
     <vws:LayerItem Tag="{Binding ElementName=layerStructureContainer}"
     <vws:LayerItem Tag="{Binding ElementName=layerStructureContainer}"

+ 1 - 1
PixiEditor/Views/UserControls/LayerStructureItemContainer.xaml.cs → PixiEditor/Views/UserControls/Layers/LayerStructureItemContainer.xaml.cs

@@ -3,7 +3,7 @@ using System.Windows.Controls;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using PixiEditor.ViewModels.SubViewModels.Main;
 
 
-namespace PixiEditor.Views.UserControls
+namespace PixiEditor.Views.UserControls.Layers
 {
 {
     /// <summary>
     /// <summary>
     /// Interaction logic for LayerStructureItemContainer.xaml.
     /// Interaction logic for LayerStructureItemContainer.xaml.

+ 6 - 5
PixiEditor/Views/UserControls/LayersManager.xaml → PixiEditor/Views/UserControls/Layers/LayersManager.xaml

@@ -1,13 +1,12 @@
-<UserControl x:Class="PixiEditor.Views.UserControls.LayersManager"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.LayersManager"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:ui="clr-namespace:PixiEditor.Helpers.UI"
              xmlns:ui="clr-namespace:PixiEditor.Helpers.UI"
-             xmlns:local="clr-namespace:PixiEditor.Views.UserControls"
              xmlns:vws="clr-namespace:PixiEditor.Views" 
              xmlns:vws="clr-namespace:PixiEditor.Views" 
              xmlns:layers="clr-namespace:PixiEditor.Models.Layers"
              xmlns:layers="clr-namespace:PixiEditor.Models.Layers"
-             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" 
+             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters" xmlns:layerUserControls="clr-namespace:PixiEditor.Views.UserControls.Layers"
              mc:Ignorable="d"
              mc:Ignorable="d"
              d:DesignHeight="450" d:DesignWidth="250" x:Name="layersManager">
              d:DesignHeight="450" d:DesignWidth="250" x:Name="layersManager">
     <UserControl.Resources>
     <UserControl.Resources>
@@ -18,6 +17,7 @@
             <RowDefinition Height="37.5"/>
             <RowDefinition Height="37.5"/>
             <RowDefinition Height="15"/>
             <RowDefinition Height="15"/>
             <RowDefinition Height="1*"/>
             <RowDefinition Height="1*"/>
+            <RowDefinition Height="40"/>
         </Grid.RowDefinitions>
         </Grid.RowDefinitions>
         <DockPanel Background="{StaticResource MainColor}" Grid.Row="0" HorizontalAlignment="Stretch">
         <DockPanel Background="{StaticResource MainColor}" Grid.Row="0" HorizontalAlignment="Stretch">
             <StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
             <StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
@@ -70,7 +70,7 @@
                 </TreeView.ItemsPanel>
                 </TreeView.ItemsPanel>
                 <TreeView.Resources>
                 <TreeView.Resources>
                     <HierarchicalDataTemplate DataType="{x:Type layers:LayerGroup}" ItemsSource="{Binding Items}">
                     <HierarchicalDataTemplate DataType="{x:Type layers:LayerGroup}" ItemsSource="{Binding Items}">
-                        <local:LayerGroupControl GroupName="{Binding Name}" MouseDown="SelectActiveItem"
+                        <layerUserControls:LayerGroupControl GroupName="{Binding Name}" MouseDown="SelectActiveItem"
                                              IsVisibleUndoTriggerable="{Binding StructureData.IsVisible}" 
                                              IsVisibleUndoTriggerable="{Binding StructureData.IsVisible}" 
                                              GroupOpacity="{Binding StructureData.Opacity}"
                                              GroupOpacity="{Binding StructureData.Opacity}"
                                              LayersViewModel="{Binding LayerCommandsViewModel, ElementName=layersManager}" 
                                              LayersViewModel="{Binding LayerCommandsViewModel, ElementName=layersManager}" 
@@ -79,7 +79,7 @@
                                              MouseMove="LayerGroup_MouseMove"/>
                                              MouseMove="LayerGroup_MouseMove"/>
                     </HierarchicalDataTemplate>
                     </HierarchicalDataTemplate>
                     <DataTemplate DataType="{x:Type layers:Layer}">
                     <DataTemplate DataType="{x:Type layers:Layer}">
-                        <local:LayerStructureItemContainer    
+                        <layerUserControls:LayerStructureItemContainer    
                             MouseDown="SelectActiveItem"
                             MouseDown="SelectActiveItem"
                             MouseMove="LayerStructureItemContainer_MouseMove" 
                             MouseMove="LayerStructureItemContainer_MouseMove" 
                             ContainerIndex="{Binding Converter={StaticResource IndexOfConverter}}"
                             ContainerIndex="{Binding Converter={StaticResource IndexOfConverter}}"
@@ -89,5 +89,6 @@
             </TreeView>
             </TreeView>
             <Border Name="dropBorder" DragEnter="Grid_DragEnter" DragLeave="Grid_DragLeave" AllowDrop="True" Drop="Grid_Drop" Background="Transparent" BorderThickness="0, 5, 0, 0"></Border>
             <Border Name="dropBorder" DragEnter="Grid_DragEnter" DragLeave="Grid_DragLeave" AllowDrop="True" Drop="Grid_Drop" Background="Transparent" BorderThickness="0, 5, 0, 0"></Border>
         </DockPanel>
         </DockPanel>
+        <layerUserControls:ReferenceLayer Visibility="{Binding Path=OpacityInputEnabled, ElementName=layersManager, Converter={BoolToVisibilityConverter}}" Background="{StaticResource MainColor}" Layer="{Binding LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument.ReferenceLayer, ElementName=layersManager, Mode=TwoWay}" Grid.Row="3" VerticalAlignment="Bottom"/>
     </Grid>
     </Grid>
 </UserControl>
 </UserControl>

+ 251 - 250
PixiEditor/Views/UserControls/LayersManager.xaml.cs → PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs

@@ -1,5 +1,6 @@
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using PixiEditor.ViewModels.SubViewModels.Main;
@@ -7,25 +8,25 @@ using System;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Windows;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls;
-using System.Windows.Media;
+using System.Windows.Media;
 
 
-namespace PixiEditor.Views.UserControls
+namespace PixiEditor.Views.UserControls.Layers
 {
 {
     /// <summary>
     /// <summary>
     /// Interaction logic for LayersManager.xaml.
     /// Interaction logic for LayersManager.xaml.
     /// </summary>
     /// </summary>
     public partial class LayersManager : UserControl
     public partial class LayersManager : UserControl
-    {
-        public object SelectedItem
-        {
-            get { return (object)GetValue(SelectedItemProperty); }
-            set { SetValue(SelectedItemProperty, value); }
-        }
-
-        public static readonly DependencyProperty SelectedItemProperty =
-            DependencyProperty.Register("SelectedItem", typeof(object), typeof(LayersManager), new PropertyMetadata(0));
-
-
+    {
+        public object SelectedItem
+        {
+            get { return (object)GetValue(SelectedItemProperty); }
+            set { SetValue(SelectedItemProperty, value); }
+        }
+
+        public static readonly DependencyProperty SelectedItemProperty =
+            DependencyProperty.Register("SelectedItem", typeof(object), typeof(LayersManager), new PropertyMetadata(0));
+
+
         public ObservableCollection<object> LayerTreeRoot
         public ObservableCollection<object> LayerTreeRoot
         {
         {
             get { return (ObservableCollection<object>)GetValue(LayerTreeRootProperty); }
             get { return (ObservableCollection<object>)GetValue(LayerTreeRootProperty); }
@@ -45,240 +46,240 @@ namespace PixiEditor.Views.UserControls
         }
         }
 
 
         public static readonly DependencyProperty LayerCommandsViewModelProperty =
         public static readonly DependencyProperty LayerCommandsViewModelProperty =
-            DependencyProperty.Register("LayerCommandsViewModel", typeof(LayersViewModel), typeof(LayersManager), new PropertyMetadata(default(LayersViewModel), ViewModelChanged));
-
-        public bool OpacityInputEnabled
-        {
-            get { return (bool)GetValue(OpacityInputEnabledProperty); }
-            set { SetValue(OpacityInputEnabledProperty, value); }
-        }
-
-        public static readonly DependencyProperty OpacityInputEnabledProperty =
-            DependencyProperty.Register("OpacityInputEnabled", typeof(bool), typeof(LayersManager), new PropertyMetadata(false));
-
-        public LayersManager()
-        {
-            InitializeComponent();
-        }
-
-        private static void ViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            if (e.NewValue is LayersViewModel vm)
-            {
-                LayersManager manager = (LayersManager)d;
-                vm.Owner.BitmapManager.AddPropertyChangedCallback(nameof(vm.Owner.BitmapManager.ActiveDocument), () =>
-                {
-                    var doc = vm.Owner.BitmapManager.ActiveDocument;
-                    if (doc != null)
-                    {
-                        if (doc.ActiveLayer != null)
-                        {
-                            manager.SetActiveLayerAsSelectedItem(doc);
-                        }
-                        doc.AddPropertyChangedCallback(nameof(doc.ActiveLayer), () =>
-                        {
-                            manager.SetActiveLayerAsSelectedItem(doc);
-                        });
-                    }
-                });
-            }
-        }
-
-        private void SetActiveLayerAsSelectedItem(Document doc)
-        {
-            SelectedItem = doc.ActiveLayer;
-            SetInputOpacity(SelectedItem);
-        }
-
-        private void SetInputOpacity(object item)
-        {
-            if (item is Layer layer)
-            {
-                numberInput.Value = layer.Opacity * 100f;
-            }
-            else if (item is LayerStructureItemContainer container)
-            {
-                numberInput.Value = container.Layer.Opacity * 100f;
-            }
-            else if (item is LayerGroup group)
-            {
-                numberInput.Value = group.StructureData.Opacity * 100f;
-            }
-            else if (item is LayerGroupControl groupControl)
-            {
-                numberInput.Value = groupControl.GroupData.Opacity * 100f;
-            }
-        }
-
-        private void LayerStructureItemContainer_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
-        {
-            if (sender is LayerStructureItemContainer container && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
-            {
-                Dispatcher.InvokeAsync(() => DragDrop.DoDragDrop(container, container, DragDropEffects.Move));
-            }
-        }
-
-        private void HandleGroupOpacityChange(GuidStructureItem group, float value)
-        {
-            if (LayerCommandsViewModel.Owner?.BitmapManager?.ActiveDocument != null)
-            {
-                var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-
-                var processArgs = new object[] { group.GroupGuid, value };
-                var reverseProcessArgs = new object[] { group.GroupGuid, group.Opacity };
-
-                ChangeGroupOpacityProcess(processArgs);
-
-                LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument.LayerStructure.ExpandParentGroups(group);
-
-                doc.UndoManager.AddUndoChange(
-                new Change(
-                    ChangeGroupOpacityProcess,
-                    reverseProcessArgs,
-                    ChangeGroupOpacityProcess,
-                    processArgs,
-                    $"Change {group.Name} opacity"), false);
-            }
-        }
-
-        private void ChangeGroupOpacityProcess(object[] processArgs)
-        {
-            if (processArgs.Length > 0 && processArgs[0] is Guid groupGuid && processArgs[1] is float opacity)
-            {
-                var structure = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument.LayerStructure;
-                var group = structure.GetGroupByGuid(groupGuid);
-                group.Opacity = opacity;
-                var layers = structure.GetGroupLayers(group);
-                layers.ForEach(x => x.Opacity = x.Opacity); // This might seems stupid, but it raises property changed, without setting any value. This is used to trigger converters that use group opacity
-                numberInput.Value = opacity * 100;
-            }
-        }
-
-        private void LayerGroup_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
-        {
-            if (sender is LayerGroupControl container && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
-            {
-                Dispatcher.InvokeAsync(() => DragDrop.DoDragDrop(container, container, DragDropEffects.Move));
-            }
-        }
-
-        private void NumberInput_LostFocus(object sender, RoutedEventArgs e)
-        {
-            float val = numberInput.Value / 100f;
-
-            object item = SelectedItem;
-
-            if (item is Layer || item is LayerStructureItemContainer)
-            {
-
-                Layer layer = null;
-
-                if (item is Layer lr)
-                {
-                    layer = lr;
-                }
-                else if (item is LayerStructureItemContainer container)
-                {
-                    layer = container.Layer;
-                }
-
-                HandleLayerOpacityChange(val, layer);
-            }
-            else if (item is LayerGroup group)
-            {
-                HandleGroupOpacityChange(group.StructureData, val);
-            }
-            else if (item is LayerGroupControl groupControl)
-            {
-                HandleGroupOpacityChange(groupControl.GroupData, val);
-            }
-        }
-
-        private void HandleLayerOpacityChange(float val, Layer layer)
-        {
-            float oldOpacity = layer.Opacity;
-
-            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-
-            doc.RaisePropertyChange(nameof(doc.LayerStructure));
-
-            layer.OpacityUndoTriggerable = val;
-
-            doc.LayerStructure.ExpandParentGroups(layer.LayerGuid);
-
-            doc.RaisePropertyChange(nameof(doc.LayerStructure));
-
-            UndoManager undoManager = doc.UndoManager;
-
-
-            undoManager.AddUndoChange(
-                new Change(
-                    UpdateNumberInputLayerOpacityProcess,
-                    new object[] { oldOpacity },
-                    UpdateNumberInputLayerOpacityProcess,
-                    new object[] { val }));
-            undoManager.SquashUndoChanges(2);
-        }
-
-        private void UpdateNumberInputLayerOpacityProcess(object[] args)
-        {
-            if (args.Length > 0 && args[0] is float opacity)
-            {
-                numberInput.Value = opacity * 100;
-            }
-        }
-
-        private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
-        {
-            SetInputOpacity(SelectedItem);
-        }
-
-        private void Grid_Drop(object sender, DragEventArgs e)
-        {
-            dropBorder.BorderBrush = Brushes.Transparent;
-
-            if (e.Data.GetDataPresent(LayerGroupControl.LayerContainerDataName))
-            {
-                HandleLayerDrop(e.Data);
-            }
-
-            if (e.Data.GetDataPresent(LayerGroupControl.LayerGroupControlDataName))
-            {
-                HandleGroupControlDrop(e.Data);
-            }
-        }
-
-        private void HandleLayerDrop(IDataObject data)
-        {
-            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-            if (doc.Layers.Count == 0) return;
-
-            var layerContainer = (LayerStructureItemContainer)data.GetData(LayerGroupControl.LayerContainerDataName);
-            var refLayer = doc.Layers[0].LayerGuid;
-            doc.MoveLayerInStructure(layerContainer.Layer.LayerGuid, refLayer);
-            doc.LayerStructure.AssignParent(layerContainer.Layer.LayerGuid, null);
-        }
-
-        private void HandleGroupControlDrop(IDataObject data)
-        {
-            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
-            var groupContainer = (LayerGroupControl)data.GetData(LayerGroupControl.LayerGroupControlDataName);
-            doc.LayerStructure.MoveGroup(groupContainer.GroupGuid, 0);
-        }
-
-        private void Grid_DragEnter(object sender, DragEventArgs e)
-        {
-            ((Border)sender).BorderBrush = LayerItem.HighlightColor;
-        }
-
-        private void Grid_DragLeave(object sender, DragEventArgs e)
-        {
-            ((Border)sender).BorderBrush = Brushes.Transparent;
-        }
-
-        private void SelectActiveItem(object sender, System.Windows.Input.MouseButtonEventArgs e)
-        {
-            SelectedItem = sender;
-        }
+            DependencyProperty.Register("LayerCommandsViewModel", typeof(LayersViewModel), typeof(LayersManager), new PropertyMetadata(default(LayersViewModel), ViewModelChanged));
+
+        public bool OpacityInputEnabled
+        {
+            get { return (bool)GetValue(OpacityInputEnabledProperty); }
+            set { SetValue(OpacityInputEnabledProperty, value); }
+        }
+
+        public static readonly DependencyProperty OpacityInputEnabledProperty =
+            DependencyProperty.Register("OpacityInputEnabled", typeof(bool), typeof(LayersManager), new PropertyMetadata(false));
+
+        public LayersManager()
+        {
+            InitializeComponent();
+        }
+
+        private static void ViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            if (e.NewValue is LayersViewModel vm)
+            {
+                LayersManager manager = (LayersManager)d;
+                vm.Owner.BitmapManager.AddPropertyChangedCallback(nameof(vm.Owner.BitmapManager.ActiveDocument), () =>
+                {
+                    var doc = vm.Owner.BitmapManager.ActiveDocument;
+                    if (doc != null)
+                    {
+                        if (doc.ActiveLayer != null)
+                        {
+                            manager.SetActiveLayerAsSelectedItem(doc);
+                        }
+                        doc.AddPropertyChangedCallback(nameof(doc.ActiveLayer), () =>
+                        {
+                            manager.SetActiveLayerAsSelectedItem(doc);
+                        });
+                    }
+                });
+            }
+        }
+
+        private void SetActiveLayerAsSelectedItem(Document doc)
+        {
+            SelectedItem = doc.ActiveLayer;
+            SetInputOpacity(SelectedItem);
+        }
+
+        private void SetInputOpacity(object item)
+        {
+            if (item is Layer layer)
+            {
+                numberInput.Value = layer.Opacity * 100f;
+            }
+            else if (item is LayerStructureItemContainer container)
+            {
+                numberInput.Value = container.Layer.Opacity * 100f;
+            }
+            else if (item is LayerGroup group)
+            {
+                numberInput.Value = group.StructureData.Opacity * 100f;
+            }
+            else if (item is LayerGroupControl groupControl)
+            {
+                numberInput.Value = groupControl.GroupData.Opacity * 100f;
+            }
+        }
+
+        private void LayerStructureItemContainer_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
+        {
+            if (sender is LayerStructureItemContainer container && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
+            {
+                Dispatcher.InvokeAsync(() => DragDrop.DoDragDrop(container, container, DragDropEffects.Move));
+            }
+        }
+
+        private void HandleGroupOpacityChange(GuidStructureItem group, float value)
+        {
+            if (LayerCommandsViewModel.Owner?.BitmapManager?.ActiveDocument != null)
+            {
+                var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
+
+                var processArgs = new object[] { group.GroupGuid, value };
+                var reverseProcessArgs = new object[] { group.GroupGuid, group.Opacity };
+
+                ChangeGroupOpacityProcess(processArgs);
+
+                LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument.LayerStructure.ExpandParentGroups(group);
+
+                doc.UndoManager.AddUndoChange(
+                new Change(
+                    ChangeGroupOpacityProcess,
+                    reverseProcessArgs,
+                    ChangeGroupOpacityProcess,
+                    processArgs,
+                    $"Change {group.Name} opacity"), false);
+            }
+        }
+
+        private void ChangeGroupOpacityProcess(object[] processArgs)
+        {
+            if (processArgs.Length > 0 && processArgs[0] is Guid groupGuid && processArgs[1] is float opacity)
+            {
+                var structure = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument.LayerStructure;
+                var group = structure.GetGroupByGuid(groupGuid);
+                group.Opacity = opacity;
+                var layers = structure.GetGroupLayers(group);
+                layers.ForEach(x => x.Opacity = x.Opacity); // This might seems stupid, but it raises property changed, without setting any value. This is used to trigger converters that use group opacity
+                numberInput.Value = opacity * 100;
+            }
+        }
+
+        private void LayerGroup_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
+        {
+            if (sender is LayerGroupControl container && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
+            {
+                Dispatcher.InvokeAsync(() => DragDrop.DoDragDrop(container, container, DragDropEffects.Move));
+            }
+        }
+
+        private void NumberInput_LostFocus(object sender, RoutedEventArgs e)
+        {
+            float val = numberInput.Value / 100f;
+
+            object item = SelectedItem;
+
+            if (item is Layer || item is LayerStructureItemContainer)
+            {
+
+                Layer layer = null;
+
+                if (item is Layer lr)
+                {
+                    layer = lr;
+                }
+                else if (item is LayerStructureItemContainer container)
+                {
+                    layer = container.Layer;
+                }
+
+                HandleLayerOpacityChange(val, layer);
+            }
+            else if (item is LayerGroup group)
+            {
+                HandleGroupOpacityChange(group.StructureData, val);
+            }
+            else if (item is LayerGroupControl groupControl)
+            {
+                HandleGroupOpacityChange(groupControl.GroupData, val);
+            }
+        }
+
+        private void HandleLayerOpacityChange(float val, Layer layer)
+        {
+            float oldOpacity = layer.Opacity;
+
+            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
+
+            doc.RaisePropertyChange(nameof(doc.LayerStructure));
+
+            layer.OpacityUndoTriggerable = val;
+
+            doc.LayerStructure.ExpandParentGroups(layer.LayerGuid);
+
+            doc.RaisePropertyChange(nameof(doc.LayerStructure));
+
+            UndoManager undoManager = doc.UndoManager;
+
+
+            undoManager.AddUndoChange(
+                new Change(
+                    UpdateNumberInputLayerOpacityProcess,
+                    new object[] { oldOpacity },
+                    UpdateNumberInputLayerOpacityProcess,
+                    new object[] { val }));
+            undoManager.SquashUndoChanges(2);
+        }
+
+        private void UpdateNumberInputLayerOpacityProcess(object[] args)
+        {
+            if (args.Length > 0 && args[0] is float opacity)
+            {
+                numberInput.Value = opacity * 100;
+            }
+        }
+
+        private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
+        {
+            SetInputOpacity(SelectedItem);
+        }
+
+        private void Grid_Drop(object sender, DragEventArgs e)
+        {
+            dropBorder.BorderBrush = Brushes.Transparent;
+
+            if (e.Data.GetDataPresent(LayerGroupControl.LayerContainerDataName))
+            {
+                HandleLayerDrop(e.Data);
+            }
+
+            if (e.Data.GetDataPresent(LayerGroupControl.LayerGroupControlDataName))
+            {
+                HandleGroupControlDrop(e.Data);
+            }
+        }
+
+        private void HandleLayerDrop(IDataObject data)
+        {
+            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
+            if (doc.Layers.Count == 0) return;
+
+            var layerContainer = (LayerStructureItemContainer)data.GetData(LayerGroupControl.LayerContainerDataName);
+            var refLayer = doc.Layers[0].LayerGuid;
+            doc.MoveLayerInStructure(layerContainer.Layer.LayerGuid, refLayer);
+            doc.LayerStructure.AssignParent(layerContainer.Layer.LayerGuid, null);
+        }
+
+        private void HandleGroupControlDrop(IDataObject data)
+        {
+            var doc = LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
+            var groupContainer = (LayerGroupControl)data.GetData(LayerGroupControl.LayerGroupControlDataName);
+            doc.LayerStructure.MoveGroup(groupContainer.GroupGuid, 0);
+        }
+
+        private void Grid_DragEnter(object sender, DragEventArgs e)
+        {
+            ((Border)sender).BorderBrush = LayerItem.HighlightColor;
+        }
+
+        private void Grid_DragLeave(object sender, DragEventArgs e)
+        {
+            ((Border)sender).BorderBrush = Brushes.Transparent;
+        }
+
+        private void SelectActiveItem(object sender, System.Windows.Input.MouseButtonEventArgs e)
+        {
+            SelectedItem = sender;
+        }
     }
     }
 }
 }

+ 1 - 1
PixiEditor/Views/UserControls/RawLayersViewer.xaml → PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml

@@ -1,4 +1,4 @@
-<UserControl x:Class="PixiEditor.Views.UserControls.RawLayersViewer"
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.RawLayersViewer"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

+ 1 - 1
PixiEditor/Views/UserControls/RawLayersViewer.xaml.cs → PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml.cs

@@ -5,7 +5,7 @@ using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 
 
-namespace PixiEditor.Views.UserControls
+namespace PixiEditor.Views.UserControls.Layers
 {
 {
     /// <summary>
     /// <summary>
     /// Interaction logic for RawLayersViewer.xaml.
     /// Interaction logic for RawLayersViewer.xaml.

+ 93 - 0
PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml

@@ -0,0 +1,93 @@
+<UserControl x:Class="PixiEditor.Views.UserControls.Layers.ReferenceLayer"
+             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:local="clr-namespace:PixiEditor.Views.UserControls.Layers" xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:local1="clr-namespace:PixiEditor.Views.UserControls" xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+             mc:Ignorable="d" 
+             d:DesignHeight="60" d:DesignWidth="350" VerticalAlignment="Center" Name="uc">
+    <UserControl.Resources>
+        <converters:NotNullToVisibilityConverter x:Key="NotNullToVisibilityConverter"/>
+        <converters:NotNullToBoolConverter x:Key="NotNullToBoolConverter"/>
+    </UserControl.Resources>
+    <Border BorderBrush="{StaticResource DarkerAccentColor}" BorderThickness="0 2 0 0" MinWidth="60" Focusable="True">
+        <i:Interaction.Behaviors>
+            <behaviors:ClearFocusOnClickBehavior/>
+        </i:Interaction.Behaviors>
+        <Grid>
+            <Grid Background="Transparent"/>
+        <Grid Grid.Row="1" Grid.RowSpan="3" VerticalAlignment="Center">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="30"/>
+                    <ColumnDefinition Width="*"/>
+                </Grid.ColumnDefinitions>
+                <Grid Visibility="{Binding Layer, ElementName=uc, Converter={converters:NullToVisibilityConverter}}"  Grid.ColumnSpan="2" Grid.RowSpan="2" Panel.ZIndex="5">
+                    <Grid MouseDown="Grid_MouseDown" Cursor="Hand" Visibility="{Binding ElementName=visibilityCheckbox, Path=IsChecked, Converter={InverseBoolToVisibilityConverter}}"  Background="Transparent"/>
+                </Grid>
+                <Grid Grid.Column="0" Height="16" Name="layerVisibilityCheckboxGrid">
+                    <CheckBox Visibility="{Binding Layer, ElementName=uc, Converter={StaticResource NotNullToVisibilityConverter}}" Style="{StaticResource ImageCheckBox}" VerticalAlignment="Center"
+                      IsThreeState="False" HorizontalAlignment="Center" 
+                      IsChecked="{Binding Path=Layer.IsVisible, Mode=TwoWay, ElementName=uc}"/>
+                </Grid>
+                <StackPanel Name="middleStackPanel" Height="40" Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Center">
+                    <Border HorizontalAlignment="Left" Visibility="{Binding Layer, ElementName=uc, Converter={StaticResource NotNullToVisibilityConverter}}" Width="30" Height="30" BorderThickness="1" BorderBrush="Black" Background="{StaticResource MainColor}"
+                           Margin="5, 0, 10, 0">
+                        <Image Source="{Binding Layer.LayerBitmap, ElementName=uc}" Stretch="Uniform" Width="25" Height="25" 
+                       RenderOptions.BitmapScalingMode="NearestNeighbor"/>
+                    </Border>
+                    <Image Margin="0 0 5 0" Width="20" Source="/Images/Layer-add.png"  Visibility="{Binding Layer, ElementName=uc, Converter={converters:NullToVisibilityConverter}}"/>
+
+                    <local1:PrependTextBlock IsEnabled="{Binding ElementName=uc, Path=IsEnabled}" Margin="0 0 5 0" Prepend="Add " Foreground="White" HidePrepend="{Binding Layer, ElementName=uc, Converter={StaticResource NotNullToBoolConverter}}"
+                                             FontSize="15" VerticalAlignment="Center" Text="Reference Layer" />
+                    <Button Click="TrashButton_Click" Cursor="Hand" Grid.Column="1" Visibility="{Binding Layer, ElementName=uc, Converter={BoolToVisibilityConverter}}" Style="{StaticResource ImageButtonStyle}" Width="20" Height="20" HorizontalAlignment="Right">
+                        <Button.Background>
+                            <ImageBrush ImageSource="/Images/Trash.png"/>
+                        </Button.Background>
+                    </Button>
+                </StackPanel>
+                <CheckBox Panel.ZIndex="10" Name="visibilityCheckbox" Grid.Column="1" Margin="0,0,5,0" Height="16" HorizontalAlignment="Right">
+                    <CheckBox.Triggers>
+                        <EventTrigger RoutedEvent="CheckBox.Checked">
+                            <BeginStoryboard>
+                                <Storyboard>
+                                    <DoubleAnimation Storyboard.TargetName="middleStackPanel" Storyboard.TargetProperty="Height" From="40" To="0" Duration="0:0:0.15"/>
+                                    <DoubleAnimation Storyboard.TargetName="layerVisibilityCheckboxGrid" Storyboard.TargetProperty="Height" From="16" To="0" Duration="0:0:0.15"/>
+                                </Storyboard>
+                            </BeginStoryboard>
+                        </EventTrigger>
+                        <EventTrigger RoutedEvent="CheckBox.Unchecked">
+                        <BeginStoryboard>
+                            <Storyboard>
+                                <DoubleAnimation Storyboard.TargetName="middleStackPanel" Storyboard.TargetProperty="Height" From="0" To="40" Duration="0:0:0.15"/>
+                                    <DoubleAnimation Storyboard.TargetName="layerVisibilityCheckboxGrid" Storyboard.TargetProperty="Height" From="0" To="16" Duration="0:0:0.15"/>
+                                </Storyboard>
+                        </BeginStoryboard>
+                        </EventTrigger>
+
+                    </CheckBox.Triggers>
+                    <CheckBox.Template>
+                        <ControlTemplate TargetType="{x:Type CheckBox}">
+                            <StackPanel Orientation="Horizontal">
+                                <Image Width="14" Cursor="Hand" x:Name="checkboxImage" Source="/Images/ChevronDown.png">
+                                    <Image.RenderTransform>
+                                        <RotateTransform Angle="0"/>
+                                    </Image.RenderTransform>
+                                </Image>
+                                <ContentPresenter/>
+                            </StackPanel>
+                            <ControlTemplate.Triggers>
+                                <Trigger Property="IsChecked" Value="True">
+                                    <Setter TargetName="checkboxImage" Property="RenderTransform">
+                                        <Setter.Value>
+                                            <RotateTransform Angle="180" CenterX="7" CenterY="4"/>
+                                        </Setter.Value>
+                                    </Setter>
+                                </Trigger>
+                            </ControlTemplate.Triggers>
+                        </ControlTemplate>
+                    </CheckBox.Template>
+                </CheckBox>
+            </Grid>
+    </Grid>
+    </Border>
+</UserControl>

+ 70 - 0
PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml.cs

@@ -0,0 +1,70 @@
+using Microsoft.Win32;
+using PixiEditor.Models.IO;
+using PixiEditor.Models.Layers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+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.UserControls.Layers
+{
+    /// <summary>
+    /// Interaction logic for ReferenceLayer.xaml
+    /// </summary>
+    public partial class ReferenceLayer : UserControl
+    {
+        public Layer Layer
+        {
+            get { return (Layer)GetValue(ReferenceLayerProperty); }
+            set { SetValue(ReferenceLayerProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for ReferenceLayer.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ReferenceLayerProperty =
+            DependencyProperty.Register("Layer", typeof(Layer), typeof(ReferenceLayer), new PropertyMetadata(default(Layer)));
+
+
+        public ReferenceLayer()
+        {
+            InitializeComponent();
+        }
+
+        private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+            string path = OpenFilePicker();
+            if (path != null)
+            {
+                var bitmap = Importer.ImportImage(path);
+                Layer = new Layer("_Reference Layer", bitmap);
+            }
+        }
+
+        private string OpenFilePicker()
+        {
+
+            OpenFileDialog dialog = new OpenFileDialog
+            {
+                Title = "Reference layer path",
+                CheckPathExists = true,
+                Filter = "Image Files|*.png;*.jpeg;*.jpg|PNG Files|*.png|JPG Files|*.jpeg;*.jpg"
+            };
+
+            return (bool)dialog.ShowDialog() ? dialog.FileName : null;
+        }
+
+        private void TrashButton_Click(object sender, RoutedEventArgs e)
+        {
+            Layer = null;
+        }
+    }
+}

+ 0 - 22
PixiEditor/Views/UserControls/MainDrawingPanel.xaml

@@ -1,22 +0,0 @@
-<UserControl x:Class="PixiEditor.Views.MainDrawingPanel"
-             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: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_PreviewMouseDown" 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="False"
-                  Loaded="Zoombox_Loaded" IsAnimated="False" MouseDown="Zoombox_MouseDown"
-                  CurrentViewChanged="Zoombox_CurrentViewChanged" DragModifiers="Blocked" ZoomModifiers="None">
-        <i:Interaction.Triggers>
-            <i:EventTrigger EventName="MouseMove">
-                <i:InvokeCommandAction Command="{Binding MouseMoveCommand, ElementName=mainDrawingPanel, Mode=OneWay}" />
-            </i:EventTrigger>
-        </i:Interaction.Triggers>
-        <ContentPresenter Content="{Binding Item, ElementName=mainDrawingPanel}" />
-    </xctk:Zoombox>
-</UserControl>

+ 0 - 258
PixiEditor/Views/UserControls/MainDrawingPanel.xaml.cs

@@ -1,258 +0,0 @@
-using PixiEditor.Models.Tools.Tools;
-using PixiEditor.ViewModels;
-using System;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using Xceed.Wpf.Toolkit.Core.Input;
-using Xceed.Wpf.Toolkit.Zoombox;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     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));
-
-        // Using a DependencyProperty as the backing store for MouseX.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MouseXProperty =
-            DependencyProperty.Register("MouseX", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(null));
-
-        // Using a DependencyProperty as the backing store for MouseX.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MouseYProperty =
-            DependencyProperty.Register("MouseY", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(null));
-
-        // 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));
-
-        // 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));
-
-        // 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(MainDrawingPanel), new PropertyMetadata(default(FrameworkElement)));
-
-        // 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));
-
-
-        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);
-            set => SetValue(CenterProperty, value);
-        }
-
-        public double MouseX
-        {
-            get => (double)GetValue(MouseXProperty);
-            set => SetValue(MouseXProperty, value);
-        }
-
-        public double MouseY
-        {
-            get => (double)GetValue(MouseYProperty);
-            set => SetValue(MouseYProperty, value);
-        }
-
-        public ICommand MouseMoveCommand
-        {
-            get => (ICommand)GetValue(MouseMoveCommandProperty);
-            set => SetValue(MouseMoveCommandProperty, value);
-        }
-
-        public bool CenterOnStart
-        {
-            get => (bool)GetValue(CenterOnStartProperty);
-            set => SetValue(CenterOnStartProperty, value);
-        }
-
-        public object Item
-        {
-            get => GetValue(ItemProperty);
-            set => SetValue(ItemProperty, value);
-        }
-
-        public bool IsUsingZoomTool
-        {
-            get => (bool)GetValue(IsUsingZoomToolProperty);
-            set => SetValue(IsUsingZoomToolProperty, value);
-        }
-
-        public ICommand MiddleMouseClickedCommand
-        {
-            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)
-            {
-                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;
-            if (PresentationSource.FromVisual(panel.Zoombox) == null)
-            {
-                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,
-                panel.ClickPosition.Y + vector.Y);
-            panel.Zoombox.Position = newPos;
-        }
-
-        private static void OnCenterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            MainDrawingPanel panel = (MainDrawingPanel)d;
-            panel.Zoombox.FitToBounds();
-        }
-
-        private void Zoombox_CurrentViewChanged(object sender, ZoomboxViewChangedEventArgs e)
-        {
-            Zoombox.MinScale = 32 / ((FrameworkElement)Item).Width;
-        }
-
-        private void Zoombox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
-        {
-            if (ZoomPercentage == 100)
-            {
-                SetClickValues();
-            }
-        }
-
-        private void SetClickValues()
-        {
-            if (!IsUsingZoomTool)
-            {
-                return;
-            }
-
-            ClickScale = Zoombox.Scale;
-            SetZoomOrigin();
-        }
-
-        private void SetZoomOrigin()
-        {
-            var item = (FrameworkElement)Item;
-            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 void Zoombox_Loaded(object sender, RoutedEventArgs e)
-        {
-            if (CenterOnStart)
-            {
-                ((Zoombox)sender).FitToBounds();
-            }
-
-            ClickScale = Zoombox.Scale;
-        }
-
-        private void Zoombox_MouseWheel(object sender, MouseWheelEventArgs e)
-        {
-            SetZoomOrigin();
-        }
-
-        private void MainDrawingPanel_PreviewMouseDown(object sender, MouseButtonEventArgs e)
-        {
-            IsUsingZoomTool = ViewModelMain.Current.BitmapManager.SelectedTool is ZoomTool;
-            Mouse.Capture((IInputElement)sender, CaptureMode.SubTree);
-            ClickPosition = ((FrameworkElement)Item).TranslatePoint(new Point(0, 0), Zoombox);
-            SetClickValues();
-        }
-
-        private void MainDrawingPanel_PreviewMouseUp(object sender, MouseButtonEventArgs e)
-        {
-            ((IInputElement)sender).ReleaseMouseCapture();
-        }
-
-        private void Zoombox_MouseDown(object sender, MouseButtonEventArgs e)
-        {
-            if (e.MiddleButton == MouseButtonState.Pressed &&
-                MiddleMouseClickedCommand.CanExecute(MiddleMouseClickedCommandParameter))
-            {
-                MiddleMouseClickedCommand.Execute(MiddleMouseClickedCommandParameter);
-            }
-        }
-    }
-}

+ 1 - 1
PixiEditor/Views/UserControls/NumberInput.xaml

@@ -8,7 +8,7 @@
              mc:Ignorable="d"
              mc:Ignorable="d"
              d:DesignHeight="20" d:DesignWidth="40" x:Name="numberInput">
              d:DesignHeight="20" d:DesignWidth="40" x:Name="numberInput">
     <TextBox TextAlignment="Center" Style="{StaticResource DarkTextBoxStyle}" Focusable="True"
     <TextBox TextAlignment="Center" Style="{StaticResource DarkTextBoxStyle}" Focusable="True"
-               
+             InputScope="Number"
              PreviewTextInput="TextBox_PreviewTextInput" Text="{Binding ElementName=numberInput, Path=Value}">
              PreviewTextInput="TextBox_PreviewTextInput" Text="{Binding ElementName=numberInput, Path=Value}">
         <i:Interaction.Behaviors>
         <i:Interaction.Behaviors>
             <behaviours:TextBoxFocusBehavior/>
             <behaviours:TextBoxFocusBehavior/>

+ 14 - 4
PixiEditor/Views/UserControls/PrependTextBlock.xaml

@@ -8,9 +8,19 @@
              d:DesignHeight="450" d:DesignWidth="800"
              d:DesignHeight="450" d:DesignWidth="800"
              x:Name="uc">
              x:Name="uc">
     <StackPanel Orientation="Horizontal">
     <StackPanel Orientation="Horizontal">
-        <TextBlock Text="{Binding Prepend, ElementName=uc}"/>
-        
-        <TextBlock>
+        <StackPanel.Resources>
+            <Style TargetType="{x:Type TextBox}">
+                <Setter Property="Foreground" Value="White" />
+                <Style.Triggers>
+                    <Trigger Property="IsEnabled" Value="False">
+                        <Setter Property="Foreground" Value="#88888A" />
+                    </Trigger>
+                </Style.Triggers>
+            </Style>
+        </StackPanel.Resources>
+        <TextBlock Visibility="{Binding HidePrepend, ElementName=uc, Converter={InverseBoolToVisibilityConverter}}" Text="{Binding Prepend, ElementName=uc}" IsEnabled="{Binding ElementName=uc, Path=IsEnabled}"/>
+
+        <TextBlock IsEnabled="{Binding ElementName=uc, Path=IsEnabled}">
             <TextBlock.Text>
             <TextBlock.Text>
                 <PriorityBinding>
                 <PriorityBinding>
                     <Binding Path="Text" ElementName="uc"/>
                     <Binding Path="Text" ElementName="uc"/>
@@ -19,6 +29,6 @@
             </TextBlock.Text>
             </TextBlock.Text>
         </TextBlock>
         </TextBlock>
 
 
-        <TextBlock Text="{Binding Append, ElementName=uc}"/>
+        <TextBlock IsEnabled="{Binding ElementName=uc, Path=IsEnabled}" Visibility="{Binding HidePrepend, ElementName=uc, Converter={InverseBoolToVisibilityConverter}}" Text="{Binding Append, ElementName=uc}"/>
     </StackPanel>
     </StackPanel>
 </UserControl>
 </UserControl>

+ 12 - 0
PixiEditor/Views/UserControls/PrependTextBlock.xaml.cs

@@ -65,6 +65,18 @@ namespace PixiEditor.Views.UserControls
             set => SetValue(AppendColorProperty, value);
             set => SetValue(AppendColorProperty, value);
         }
         }
 
 
+        public bool HidePrepend
+        {
+            get { return (bool)GetValue(HidePrependProperty); }
+            set { SetValue(HidePrependProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for HidePrepend.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty HidePrependProperty =
+            DependencyProperty.Register("HidePrepend", typeof(bool), typeof(PrependTextBlock), new PropertyMetadata(false));
+
+
+
         public PrependTextBlock()
         public PrependTextBlock()
         {
         {
             InitializeComponent();
             InitializeComponent();

+ 2 - 2
PixiEditor/Views/UserControls/PreviewWindow.xaml

@@ -12,7 +12,7 @@
 
 
     <UserControl.Resources>
     <UserControl.Resources>
         <BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
         <BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
-        <converters:NotNullToVisibiltyConverter x:Key="NullToVisibiltyConverter"/>
+        <converters:NotNullToVisibilityConverter x:Key="NullToVisibilityConverter"/>
     </UserControl.Resources>
     </UserControl.Resources>
     <Grid>
     <Grid>
         <Grid.RowDefinitions>
         <Grid.RowDefinitions>
@@ -23,7 +23,7 @@
 
 
         <Viewbox Margin="30" VerticalAlignment="Center">
         <Viewbox Margin="30" VerticalAlignment="Center">
             <Grid x:Name="imageGrid" RenderOptions.BitmapScalingMode="NearestNeighbor"
             <Grid x:Name="imageGrid" RenderOptions.BitmapScalingMode="NearestNeighbor"
-              Visibility="{Binding Document, Converter={StaticResource NullToVisibiltyConverter}, ElementName=uc}"
+              Visibility="{Binding Document, Converter={StaticResource NullToVisibilityConverter}, ElementName=uc}"
               Height="{Binding Document.Height, ElementName=uc}" Width="{Binding Document.Width, ElementName=uc}"
               Height="{Binding Document.Height, ElementName=uc}" Width="{Binding Document.Width, ElementName=uc}"
               Background="{Binding ActiveItem.Value, ElementName=backgroundButton}" d:Width="8" d:Height="8">
               Background="{Binding ActiveItem.Value, ElementName=backgroundButton}" d:Width="8" d:Height="8">
                 <ItemsControl ItemsSource="{Binding Document.Layers, ElementName=uc}">
                 <ItemsControl ItemsSource="{Binding Document.Layers, ElementName=uc}">

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

@@ -13,7 +13,7 @@
         <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
         <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </UserControl.Resources>
     </UserControl.Resources>
     <TextBox IsEnabled="{Binding IsEnabled, ElementName=uc}" HorizontalContentAlignment="Center"
     <TextBox IsEnabled="{Binding IsEnabled, ElementName=uc}" HorizontalContentAlignment="Center"
-             Style="{StaticResource DarkTextBoxStyle}" MaxLength="4">
+             Style="{StaticResource DarkTextBoxStyle}" MaxLength="4" InputScope="Number">
         <TextBox.Text>
         <TextBox.Text>
             <Binding ElementName="uc"
             <Binding ElementName="uc"
                      Path="Size" Mode="TwoWay"
                      Path="Size" Mode="TwoWay"

+ 20 - 0
PixiEditor/Views/UserControls/Zoombox.xaml

@@ -0,0 +1,20 @@
+<ContentControl x:Class="PixiEditor.Views.UserControls.Zoombox"
+                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:local="clr-namespace:PixiEditor.Views.UserControls"
+                mc:Ignorable="d" 
+                x:Name="uc"
+                d:DesignHeight="450" d:DesignWidth="800">
+    <Canvas MouseDown="OnMouseDown" MouseUp="OnMouseUp" MouseMove="OnMouseMove" MouseWheel="OnScroll"
+            IsManipulationEnabled="{Binding UseTouchGestures, ElementName=uc}" ManipulationDelta="OnManipulationDelta"
+            x:Name="mainCanvas" Background="Transparent">
+        <Grid x:Name="mainGrid" SizeChanged="RecalculateMinZoomLevel">
+            <Grid.LayoutTransform>
+                <ScaleTransform x:Name="scaleTransform"/>
+            </Grid.LayoutTransform>
+            <ContentPresenter Content="{Binding AdditionalContent, ElementName=uc}"/>
+        </Grid>
+    </Canvas>
+</ContentControl>

+ 319 - 0
PixiEditor/Views/UserControls/Zoombox.xaml.cs

@@ -0,0 +1,319 @@
+using System;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Markup;
+
+namespace PixiEditor.Views.UserControls
+{
+    [ContentProperty(nameof(AdditionalContent))]
+    public partial class Zoombox : ContentControl, INotifyPropertyChanged
+    {
+        public enum Mode
+        {
+            Normal, MoveTool, ZoomTool
+        }
+
+        private interface IDragOperation
+        {
+            void Start(MouseButtonEventArgs e);
+
+            void Update(MouseEventArgs e);
+
+            void Terminate();
+        }
+
+        private class MoveDragOperation : IDragOperation
+        {
+            private Zoombox parent;
+            private Point prevMousePos;
+
+            public MoveDragOperation(Zoombox zoomBox)
+            {
+                parent = zoomBox;
+            }
+            public void Start(MouseButtonEventArgs e)
+            {
+                prevMousePos = e.GetPosition(parent.mainCanvas);
+                parent.mainCanvas.CaptureMouse();
+            }
+
+            public void Update(MouseEventArgs e)
+            {
+                var curMousePos = e.GetPosition(parent.mainCanvas);
+                parent.SpaceOriginPos += curMousePos - prevMousePos;
+                prevMousePos = curMousePos;
+            }
+
+            public void Terminate()
+            {
+                parent.mainCanvas.ReleaseMouseCapture();
+            }
+        }
+
+        private class ZoomDragOperation : IDragOperation
+        {
+            private Zoombox parent;
+
+            private double initZoomPower;
+            private Point initSpaceOriginPos;
+
+            private Point zoomOrigin;
+            private Point screenZoomOrigin;
+
+            public ZoomDragOperation(Zoombox zoomBox)
+            {
+                parent = zoomBox;
+            }
+            public void Start(MouseButtonEventArgs e)
+            {
+                screenZoomOrigin = e.GetPosition(parent.mainCanvas);
+                zoomOrigin = parent.ToZoomboxSpace(screenZoomOrigin);
+                initZoomPower = parent.ZoomPowerClamped;
+                initSpaceOriginPos = parent.SpaceOriginPos;
+                parent.mainCanvas.CaptureMouse();
+            }
+
+            public void Update(MouseEventArgs e)
+            {
+                var curScreenPos = e.GetPosition(parent.mainCanvas);
+                double deltaX = screenZoomOrigin.X - curScreenPos.X;
+                double deltaPower = deltaX / 10.0;
+                parent.ZoomPowerClamped = initZoomPower - deltaPower;
+
+                parent.SpaceOriginPos = initSpaceOriginPos;
+                var shiftedOriginPos = parent.ToScreenSpace(zoomOrigin);
+                var deltaOriginPos = shiftedOriginPos - screenZoomOrigin;
+                parent.SpaceOriginPos = initSpaceOriginPos - deltaOriginPos;
+            }
+
+            public void Terminate()
+            {
+                parent.mainCanvas.ReleaseMouseCapture();
+            }
+        }
+
+        public static readonly DependencyProperty AdditionalContentProperty =
+            DependencyProperty.Register(nameof(AdditionalContent), typeof(object), typeof(Zoombox),
+              new PropertyMetadata(null));
+
+        public static readonly DependencyProperty ZoomModeProperty =
+            DependencyProperty.Register(nameof(ZoomMode), typeof(Mode), typeof(Zoombox),
+              new PropertyMetadata(Mode.Normal, ZoomModeChanged));
+
+        public static readonly DependencyProperty UseTouchGesturesProperty =
+            DependencyProperty.Register(nameof(UseTouchGestures), typeof(bool), typeof(Zoombox));
+
+        private const double zoomFactor = 1.1;
+        private const double maxZoom = 50;
+        private double minZoom = -28;
+        public object AdditionalContent
+        {
+            get => GetValue(AdditionalContentProperty);
+            set => SetValue(AdditionalContentProperty, value);
+        }
+        public Mode ZoomMode
+        {
+            get => (Mode)GetValue(ZoomModeProperty);
+            set => SetValue(ZoomModeProperty, value);
+        }
+
+        public bool UseTouchGestures
+        {
+            get => (bool)GetValue(UseTouchGesturesProperty);
+            set => SetValue(UseTouchGesturesProperty, value);
+        }
+
+        public double Zoom => Math.Pow(zoomFactor, zoomPower);
+
+        private Point spaceOriginPos;
+        private Point SpaceOriginPos
+        {
+            get => spaceOriginPos;
+            set
+            {
+                spaceOriginPos = value;
+                Canvas.SetLeft(mainGrid, spaceOriginPos.X);
+                Canvas.SetTop(mainGrid, spaceOriginPos.Y);
+            }
+        }
+
+        private double zoomPower;
+        private double ZoomPowerClamped
+        {
+            get => zoomPower;
+            set
+            {
+                value = Math.Clamp(value, minZoom, maxZoom);
+                if (value == zoomPower)
+                    return;
+                zoomPower = value;
+                var mult = Zoom;
+                scaleTransform.ScaleX = mult;
+                scaleTransform.ScaleY = mult;
+                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Zoom)));
+            }
+        }
+        private double ZoomPowerTopCapped
+        {
+            get => zoomPower;
+            set
+            {
+                if (value > maxZoom)
+                    value = maxZoom;
+                if (value == zoomPower)
+                    return;
+                zoomPower = value;
+                var mult = Zoom;
+                scaleTransform.ScaleX = mult;
+                scaleTransform.ScaleY = mult;
+                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Zoom)));
+            }
+        }
+
+        private IDragOperation activeDragOperation = null;
+        private MouseButtonEventArgs activeMouseDownEventArgs = null;
+        private Point activeMouseDownPos;
+
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        private static void ZoomModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            Zoombox sender = (Zoombox)d;
+            sender.activeDragOperation?.Terminate();
+            sender.activeDragOperation = null;
+            sender.activeMouseDownEventArgs = null;
+        }
+
+        public Zoombox()
+        {
+            InitializeComponent();
+        }
+
+        public void CenterContent() => CenterContent(new Size(mainGrid.ActualWidth, mainGrid.ActualHeight));
+
+        public void CenterContent(Size newSize)
+        {
+            const double marginFactor = 1.1;
+            double scaleFactor = Math.Max(
+                newSize.Width * marginFactor / mainCanvas.ActualWidth,
+                newSize.Height * marginFactor / mainCanvas.ActualHeight);
+            ZoomPowerTopCapped = -Math.Log(scaleFactor, zoomFactor);
+            SpaceOriginPos = new Point(
+                mainCanvas.ActualWidth / 2 - newSize.Width * Zoom / 2,
+                mainCanvas.ActualHeight / 2 - newSize.Height * Zoom / 2);
+        }
+
+        public void ZoomIntoCenter(double delta)
+        {
+            ZoomInto(new Point(mainCanvas.ActualWidth / 2, mainCanvas.ActualHeight / 2), delta);
+        }
+
+        public void ZoomInto(Point mousePos, double delta)
+        {
+            var oldZoomboxMousePos = ToZoomboxSpace(mousePos);
+
+            ZoomPowerClamped += delta;
+
+            if (Math.Abs(ZoomPowerClamped) < 1) ZoomPowerClamped = 0;
+
+            var shiftedMousePos = ToScreenSpace(oldZoomboxMousePos);
+            var deltaMousePos = mousePos - shiftedMousePos;
+            SpaceOriginPos = SpaceOriginPos + deltaMousePos;
+        }
+
+        private void RecalculateMinZoomLevel(object sender, SizeChangedEventArgs args)
+        {
+            double fraction = Math.Max(
+                mainCanvas.ActualWidth / mainGrid.ActualWidth,
+                mainCanvas.ActualHeight / mainGrid.ActualHeight);
+            minZoom = Math.Min(0, Math.Log(fraction / 8, zoomFactor));
+        }
+
+        private Point ToScreenSpace(Point p)
+        {
+            double zoom = Zoom;
+            p.X *= zoom;
+            p.Y *= zoom;
+            p += (Vector)SpaceOriginPos;
+            return p;
+        }
+
+        private Point ToZoomboxSpace(Point mousePos)
+        {
+            double zoom = Zoom;
+            mousePos -= (Vector)SpaceOriginPos;
+            mousePos.X /= zoom;
+            mousePos.Y /= zoom;
+            return mousePos;
+        }
+
+        private void OnMouseDown(object sender, MouseButtonEventArgs e)
+        {
+            if (e.ChangedButton == MouseButton.Right)
+                return;
+            activeMouseDownEventArgs = e;
+            activeMouseDownPos = e.GetPosition(mainCanvas);
+            Keyboard.Focus(this);
+        }
+
+        private void InitiateDrag(MouseButtonEventArgs e)
+        {
+            if (ZoomMode == Mode.Normal)
+                return;
+
+            activeDragOperation?.Terminate();
+
+            if (ZoomMode == Mode.MoveTool)
+                activeDragOperation = new MoveDragOperation(this);
+            else if (ZoomMode == Mode.ZoomTool)
+                activeDragOperation = new ZoomDragOperation(this);
+
+            activeDragOperation.Start(e);
+        }
+
+        private void OnMouseUp(object sender, MouseButtonEventArgs e)
+        {
+            if (e.ChangedButton == MouseButton.Right)
+                return;
+            if (activeDragOperation != null)
+            {
+                activeDragOperation.Terminate();
+                activeDragOperation = null;
+            }
+            else
+            {
+                if (ZoomMode == Mode.ZoomTool && e.ChangedButton == MouseButton.Left)
+                    ZoomInto(e.GetPosition(mainCanvas), Keyboard.IsKeyDown(Key.LeftAlt) ? -1 : 1);
+            }
+            activeMouseDownEventArgs = null;
+        }
+
+        private void OnMouseMove(object sender, MouseEventArgs e)
+        {
+            if (activeDragOperation == null && activeMouseDownEventArgs != null)
+            {
+                var cur = e.GetPosition(mainCanvas);
+
+                if (Math.Abs(cur.X - activeMouseDownPos.X) > 3)
+                    InitiateDrag(activeMouseDownEventArgs);
+            }
+            activeDragOperation?.Update(e);
+        }
+
+        private void OnScroll(object sender, MouseWheelEventArgs e)
+        {
+            ZoomInto(e.GetPosition(mainCanvas), e.Delta / 100);
+        }
+
+        private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
+        {
+            if (e.Handled = UseTouchGestures)
+            {
+                ZoomInto(e.ManipulationOrigin, e.DeltaManipulation.Expansion.X / 5.0);
+                SpaceOriginPos += e.DeltaManipulation.Translation;
+            }
+        }
+    }
+}

+ 0 - 23
PixiEditorTests/ModelsTests/ToolsTests/ZoomToolTests.cs

@@ -1,23 +0,0 @@
-using PixiEditor.Models.Tools;
-using PixiEditor.Models.Tools.Tools;
-using PixiEditor.ViewModels;
-using PixiEditorTests.HelpersTests;
-using Xunit;
-
-namespace PixiEditorTests.ModelsTests.ToolsTests
-{
-    [Collection("Application collection")]
-    public class ZoomToolTests
-    {
-        [StaFact]
-        public void TestThatZoomSetsActiveDocumentZoomPercentage()
-        {
-            ViewModelMain vm = ViewModelHelper.MockedViewModelMain();
-            vm.BitmapManager.ActiveDocument = new PixiEditor.Models.DataHolders.Document(10, 10);
-            ZoomTool zoomTool = ToolBuilder.BuildTool<ZoomTool>(vm.Services);
-            double zoom = 110;
-            zoomTool.Zoom(zoom);
-            Assert.Equal(zoom, vm.BitmapManager.ActiveDocument.ZoomPercentage);
-        }
-    }
-}