ソースを参照

Merge pull request #9 from flabbet/dev

Layers update
Krzysztof Krysiński 5 年 前
コミット
4d83ba0652
100 ファイル変更1955 行追加626 行削除
  1. 0 1
      PixiEditor/App.xaml
  2. 0 0
      PixiEditor/Helpers/Behaviours/AllowableCharactersTextBoxBehavior.cs
  3. 0 0
      PixiEditor/Helpers/Behaviours/HintTextBehavior.cs
  4. 0 0
      PixiEditor/Helpers/Behaviours/MouseBehaviour.cs
  5. 0 0
      PixiEditor/Helpers/Behaviours/TextBoxNumericFinisherBehavior.cs
  6. 30 0
      PixiEditor/Helpers/Converters/BoolToColorConverter.cs
  7. 37 0
      PixiEditor/Helpers/Converters/OppositeVisibilityConverter.cs
  8. 0 0
      PixiEditor/Helpers/Converters/ToolSizeToIntConverter.cs
  9. 0 0
      PixiEditor/Helpers/NotifyableObject.cs
  10. 0 0
      PixiEditor/Helpers/RelayCommand.cs
  11. 44 0
      PixiEditor/Helpers/UI/ReversedOrderStackPanel.cs
  12. 0 0
      PixiEditor/Images/BucketImage.png
  13. 0 0
      PixiEditor/Images/CircleImage.png
  14. 0 0
      PixiEditor/Images/Cross.png
  15. 0 0
      PixiEditor/Images/EarserImage.png
  16. 0 0
      PixiEditor/Images/LightenImage.png
  17. 0 0
      PixiEditor/Images/LineImage.png
  18. 0 0
      PixiEditor/Images/PenImage.png
  19. 0 0
      PixiEditor/Images/PipetteImage.png
  20. 0 0
      PixiEditor/Images/RectangleImage.png
  21. BIN
      PixiEditor/Images/transparentbg.png
  22. 1 1
      PixiEditor/Models/Colors/ExColor.cs
  23. 226 0
      PixiEditor/Models/Controllers/BitmapOperationsUtility.cs
  24. 55 0
      PixiEditor/Models/Controllers/MouseMovementController.cs
  25. 64 0
      PixiEditor/Models/Controllers/PixelChangesController.cs
  26. 6 17
      PixiEditor/Models/Controllers/UndoManager.cs
  27. 3 3
      PixiEditor/Models/DataHolders/Change.cs
  28. 19 0
      PixiEditor/Models/DataHolders/LayerChanges.cs
  29. 5 1
      PixiEditor/Models/DataHolders/StackEx.cs
  30. 1 1
      PixiEditor/Models/DataHolders/Tuple.cs
  31. 1 1
      PixiEditor/Models/Dialogs/CustomDialog.cs
  32. 1 1
      PixiEditor/Models/Dialogs/ExportFileDialog.cs
  33. 1 1
      PixiEditor/Models/Dialogs/ImportFileDialog.cs
  34. 1 1
      PixiEditor/Models/Dialogs/NewFileDialog.cs
  35. 1 1
      PixiEditor/Models/Enums/FileType.cs
  36. 11 0
      PixiEditor/Models/Enums/LayerAction.cs
  37. 87 0
      PixiEditor/Models/IO/Exporter.cs
  38. 1 1
      PixiEditor/Models/IO/Importer.cs
  39. 1 1
      PixiEditor/Models/IO/PixiFilesManager.cs
  40. 1 1
      PixiEditor/Models/Images/BitmapConverter.cs
  41. 1 1
      PixiEditor/Models/Images/ImageGenerator.cs
  42. 1 1
      PixiEditor/Models/Layers/BasicLayer.cs
  43. 115 0
      PixiEditor/Models/Layers/Layer.cs
  44. 3 2
      PixiEditor/Models/Layers/LayerGenerator.cs
  45. 1 1
      PixiEditor/Models/Layers/LightLayer.cs
  46. 54 0
      PixiEditor/Models/Position/Coordinates.cs
  47. 11 1
      PixiEditor/Models/Position/CoordinatesCalculator.cs
  48. 2 2
      PixiEditor/Models/Position/DoubleCords.cs
  49. 13 2
      PixiEditor/Models/Position/MousePositionConverter.cs
  50. 29 0
      PixiEditor/Models/Tools/BitmapPixelChanges.cs
  51. 132 0
      PixiEditor/Models/Tools/ShapeTool.cs
  52. 5 5
      PixiEditor/Models/Tools/Tool.cs
  53. 2 2
      PixiEditor/Models/Tools/ToolType.cs
  54. 8 8
      PixiEditor/Models/Tools/Tools/BrightnessTool.cs
  55. 137 0
      PixiEditor/Models/Tools/Tools/CircleTool.cs
  56. 5 5
      PixiEditor/Models/Tools/Tools/EarserTool.cs
  57. 13 15
      PixiEditor/Models/Tools/Tools/FloodFill.cs
  58. 24 0
      PixiEditor/Models/Tools/Tools/LineTool.cs
  59. 6 6
      PixiEditor/Models/Tools/Tools/PenTool.cs
  60. 43 0
      PixiEditor/Models/Tools/Tools/RectangleTool.cs
  61. 6 4
      PixiEditor/PixiEditor.csproj
  62. 2 2
      PixiEditor/PixiEditor.sln
  63. 0 0
      PixiEditor/Properties/AssemblyInfo.cs
  64. 0 0
      PixiEditor/Properties/Resources.Designer.cs
  65. 0 0
      PixiEditor/Properties/Resources.resx
  66. 0 0
      PixiEditor/Properties/Settings.Designer.cs
  67. 0 0
      PixiEditor/Properties/Settings.settings
  68. 0 0
      PixiEditor/Styles/MenuButtonStyle.xaml
  69. 106 0
      PixiEditor/Styles/ThemeStyle.xaml
  70. 0 0
      PixiEditor/ViewModels/FeedbackDialogViewModel.cs
  71. 0 0
      PixiEditor/ViewModels/ImportFilePopupViewModel.cs
  72. 0 0
      PixiEditor/ViewModels/MenuButtonViewModel.cs
  73. 1 1
      PixiEditor/ViewModels/NewFileMenuViewModel.cs
  74. 0 0
      PixiEditor/ViewModels/SaveFilePopupViewModel.cs
  75. 0 0
      PixiEditor/ViewModels/ViewModelBase.cs
  76. 392 0
      PixiEditor/ViewModels/ViewModelMain.cs
  77. 25 0
      PixiEditor/Views/EditableTextBlock.xaml
  78. 79 0
      PixiEditor/Views/EditableTextBlock.xaml.cs
  79. 0 0
      PixiEditor/Views/ImportFilePopup.xaml
  80. 0 0
      PixiEditor/Views/ImportFilePopup.xaml.cs
  81. 1 1
      PixiEditor/Views/MainDrawingPanel.xaml
  82. 1 1
      PixiEditor/Views/MainDrawingPanel.xaml.cs
  83. 85 47
      PixiEditor/Views/MainWindow.xaml
  84. 1 1
      PixiEditor/Views/MainWindow.xaml.cs
  85. 0 0
      PixiEditor/Views/MenuButton.xaml
  86. 0 0
      PixiEditor/Views/MenuButton.xaml.cs
  87. 54 0
      PixiEditor/Views/NewFilePopup.xaml
  88. 0 0
      PixiEditor/Views/NewFilePopup.xaml.cs
  89. 0 0
      PixiEditor/Views/SaveFilePopup.xaml
  90. 0 0
      PixiEditor/Views/SaveFilePopup.xaml.cs
  91. BIN
      PixiEditorDotNetCore3/Images/transparentbg.png
  92. 0 107
      PixiEditorDotNetCore3/Models/IO/Exporter.cs
  93. 0 73
      PixiEditorDotNetCore3/Models/Layers/Layer.cs
  94. 0 39
      PixiEditorDotNetCore3/Models/Position/Coordinates.cs
  95. 0 20
      PixiEditorDotNetCore3/Models/Tools/BitmapPixelChanges.cs
  96. 0 43
      PixiEditorDotNetCore3/Models/Tools/ShapeTool.cs
  97. 0 30
      PixiEditorDotNetCore3/Models/Tools/Tools/CircleTool.cs
  98. 0 24
      PixiEditorDotNetCore3/Models/Tools/Tools/LineTool.cs
  99. 0 24
      PixiEditorDotNetCore3/Models/Tools/Tools/RectangleTool.cs
  100. 0 126
      PixiEditorDotNetCore3/Models/Tools/ToolsManager.cs

+ 0 - 1
PixiEditorDotNetCore3/App.xaml → PixiEditor/App.xaml

@@ -1,7 +1,6 @@
 <Application x:Class="PixiEditor.App"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:local="clr-namespace:PixiEditor"
              StartupUri="Views/MainWindow.xaml">
     <Application.Resources>
         <ResourceDictionary>

+ 0 - 0
PixiEditorDotNetCore3/Helpers/Behaviours/AllowableCharactersTextBoxBehavior.cs → PixiEditor/Helpers/Behaviours/AllowableCharactersTextBoxBehavior.cs


+ 0 - 0
PixiEditorDotNetCore3/Helpers/Behaviours/HintTextBehavior.cs → PixiEditor/Helpers/Behaviours/HintTextBehavior.cs


+ 0 - 0
PixiEditorDotNetCore3/Helpers/Behaviours/MouseBehaviour.cs → PixiEditor/Helpers/Behaviours/MouseBehaviour.cs


+ 0 - 0
PixiEditorDotNetCore3/Helpers/Behaviours/TextBoxNumericFinisherBehavior.cs → PixiEditor/Helpers/Behaviours/TextBoxNumericFinisherBehavior.cs


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

@@ -0,0 +1,30 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class BoolToColorConverter : IValueConverter
+    {
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if(value.ToString() == "Transparent")
+            {
+                return false;
+            }
+            return true;
+        }
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if(value is bool)
+            {
+                if((bool)value == false)
+                {
+                    return "Transparent";
+                }
+            }
+            return "#638DCA";
+        }
+    }
+}

+ 37 - 0
PixiEditor/Helpers/Converters/OppositeVisibilityConverter.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using System.Windows;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class OppositeVisibilityConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if(value.ToString().ToLower() == "visible")
+            {
+                return Visibility.Hidden;
+            }
+            return Visibility.Visible;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if(value is Visibility)
+            {
+                if((Visibility)value == Visibility.Visible)
+                {
+                    return "Hidden";
+                }
+                else
+                {
+                    return "Visible";
+                }
+            }
+            return null;
+        }
+    }
+}

+ 0 - 0
PixiEditorDotNetCore3/Helpers/ToolSizeToIntConverter.cs → PixiEditor/Helpers/Converters/ToolSizeToIntConverter.cs


+ 0 - 0
PixiEditorDotNetCore3/Helpers/NotifyableObject.cs → PixiEditor/Helpers/NotifyableObject.cs


+ 0 - 0
PixiEditorDotNetCore3/Helpers/RelayCommand.cs → PixiEditor/Helpers/RelayCommand.cs


+ 44 - 0
PixiEditor/Helpers/UI/ReversedOrderStackPanel.cs

@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace PixiEditor.Helpers.UI
+{
+    public class ReversedOrderStackPanel : StackPanel
+    {
+        protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
+        {
+            bool fHorizontal = (Orientation == Orientation.Horizontal);
+            var rcChild = new Rect(arrangeSize);
+            double previousChildSize = 0.0;
+
+            var children = InternalChildren.Cast<UIElement>().Reverse();
+            foreach (UIElement child in children)
+            {
+                if (child == null)
+                    continue;
+
+                if (fHorizontal)
+                {
+                    rcChild.X += previousChildSize;
+                    previousChildSize = child.DesiredSize.Width;
+                    rcChild.Width = previousChildSize;
+                    rcChild.Height = Math.Max(arrangeSize.Height, child.DesiredSize.Height);
+                }
+                else
+                {
+                    rcChild.Y += previousChildSize;
+                    previousChildSize = child.DesiredSize.Height;
+                    rcChild.Height = previousChildSize;
+                    rcChild.Width = Math.Max(arrangeSize.Width, child.DesiredSize.Width);
+                }
+
+                child.Arrange(rcChild);
+            }
+
+            return arrangeSize;
+        }
+    }
+}

+ 0 - 0
PixiEditorDotNetCore3/Images/BucketImage.png → PixiEditor/Images/BucketImage.png


+ 0 - 0
PixiEditorDotNetCore3/Images/CircleImage.png → PixiEditor/Images/CircleImage.png


+ 0 - 0
PixiEditorDotNetCore3/Images/Cross.png → PixiEditor/Images/Cross.png


+ 0 - 0
PixiEditorDotNetCore3/Images/EarserImage.png → PixiEditor/Images/EarserImage.png


+ 0 - 0
PixiEditorDotNetCore3/Images/LightenImage.png → PixiEditor/Images/LightenImage.png


+ 0 - 0
PixiEditorDotNetCore3/Images/LineImage.png → PixiEditor/Images/LineImage.png


+ 0 - 0
PixiEditorDotNetCore3/Images/PenImage.png → PixiEditor/Images/PenImage.png


+ 0 - 0
PixiEditorDotNetCore3/Images/PipetteImage.png → PixiEditor/Images/PipetteImage.png


+ 0 - 0
PixiEditorDotNetCore3/Images/RectangleImage.png → PixiEditor/Images/RectangleImage.png


BIN
PixiEditor/Images/transparentbg.png


+ 1 - 1
PixiEditorDotNetCore3/Models/Colors/ExColor.cs → PixiEditor/Models/Colors/ExColor.cs

@@ -1,6 +1,6 @@
 using System.Windows.Media;
 
-namespace PixiEditorDotNetCore3.Models.Colors
+namespace PixiEditor.Models.Colors
 {
     public static class ExColor
     {

+ 226 - 0
PixiEditor/Models/Controllers/BitmapOperationsUtility.cs

@@ -0,0 +1,226 @@
+using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditor.Models.Controllers
+{
+    public class BitmapOperationsUtility : NotifyableObject
+    {
+        public MouseMovementController MouseController { get; set; }
+        public Tool SelectedTool { get; set; }
+
+        private ObservableCollection<Layer> _layers = new ObservableCollection<Layer>();
+
+        public ObservableCollection<Layer> Layers
+        {
+            get => _layers;
+            set { if (_layers != value) { _layers = value; } }
+        }
+        private int _activeLayerIndex;
+        public int ActiveLayerIndex
+        {
+            get => _activeLayerIndex;
+            set
+            {
+                _activeLayerIndex = value;
+                RaisePropertyChanged("ActiveLayerIndex");
+                RaisePropertyChanged("ActiveLayer");
+            }
+        }
+
+        private Layer _previewLayer;
+
+        public Layer PreviewLayer
+        {
+            get { return _previewLayer; }
+            set 
+            {
+                _previewLayer = value;
+                RaisePropertyChanged("PreviewLayer");
+            }
+        }
+
+
+        public Layer ActiveLayer => Layers.Count > 0 ? Layers[ActiveLayerIndex] : null;
+
+        public Color PrimaryColor { get; set; }
+        
+        public int ToolSize { get; set; }
+
+        public event EventHandler<BitmapChangedEventArgs> BitmapChanged;
+        public event EventHandler<LayersChangedEventArgs> LayersChanged;
+
+        private Coordinates _lastMousePos;
+        private BitmapPixelChanges _lastChangedPixels;
+
+        public BitmapOperationsUtility()
+        {
+            MouseController = new MouseMovementController();
+            MouseController.MousePositionChanged += Controller_MousePositionChanged;
+            MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
+        }
+
+        private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
+        {
+            if(SelectedTool.RequiresPreviewLayer)
+            {
+                BitmapPixelChanges oldValues = GetOldPixelsValues(_lastChangedPixels.ChangedPixels.Keys.ToArray());
+                Layers[ActiveLayerIndex].ApplyPixels(_lastChangedPixels);
+                BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(_lastChangedPixels, oldValues, ActiveLayerIndex));
+                _previewLayer.Clear();
+            }
+        }
+
+        private void Controller_MousePositionChanged(object sender, MouseMovementEventArgs e)
+        {
+            if(SelectedTool != null && SelectedTool.ToolType != ToolType.None && Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                var mouseMove = MouseController.LastMouseMoveCoordinates.ToList();
+                mouseMove.Reverse();
+
+                if (!SelectedTool.RequiresPreviewLayer)
+                {
+                    BitmapPixelChanges changedPixels = SelectedTool.Use(Layers[ActiveLayerIndex], mouseMove.ToArray(), PrimaryColor, ToolSize);
+                    BitmapPixelChanges oldPixelsValues = GetOldPixelsValues(changedPixels.ChangedPixels.Keys.ToArray());
+                    ActiveLayer.ApplyPixels(changedPixels);
+                    BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(changedPixels, oldPixelsValues, ActiveLayerIndex));
+                }
+                else
+                {
+                    UseToolOnPreviewLayer(mouseMove);
+                }
+
+                _lastMousePos = e.NewPosition;
+            }
+        }
+
+        private BitmapPixelChanges GetOldPixelsValues(Coordinates[] coordinates)
+        {
+            Dictionary<Coordinates, Color> values = new Dictionary<Coordinates, Color>();
+            Layers[ActiveLayerIndex].LayerBitmap.Lock();
+            for (int i = 0; i < coordinates.Length; i++)
+            {
+                values.Add(coordinates[i], Layers[ActiveLayerIndex].LayerBitmap.GetPixel(coordinates[i].X, coordinates[i].Y));
+            }
+            Layers[ActiveLayerIndex].LayerBitmap.Unlock();
+            return new BitmapPixelChanges(values);
+        }
+
+        private void UseToolOnPreviewLayer(List<Coordinates> mouseMove)
+        {
+            BitmapPixelChanges changedPixels;
+            if (mouseMove[0] != _lastMousePos)
+            {
+                GeneratePreviewLayer();
+                PreviewLayer.Clear();
+                changedPixels = SelectedTool.Use(Layers[ActiveLayerIndex], mouseMove.ToArray(), PrimaryColor, ToolSize);
+                PreviewLayer.ApplyPixels(changedPixels);
+                _lastChangedPixels = changedPixels;
+            }
+        }
+
+        private void GeneratePreviewLayer()
+        {
+            if (PreviewLayer == null)
+            {
+                PreviewLayer = new Layer("_previewLayer", Layers[0].Width, Layers[0].Height);
+            }
+        }
+
+        public void RemoveLayer(int layerIndex)
+        {
+            if (Layers.Count <= 1) return;
+
+            bool wasActive = Layers[layerIndex].IsActive;
+            Layers.RemoveAt(layerIndex);
+            if (wasActive)
+            {
+                SetActiveLayer(0);
+            }
+            else if(ActiveLayerIndex > Layers.Count - 1)
+            {
+                SetActiveLayer(Layers.Count - 1);
+            }
+        }
+
+        public void AddNewLayer(string name, int width, int height, bool setAsActive = true)
+        {
+            Layers.Add(new Layer(name, width, height));
+            if (setAsActive)
+            {
+                SetActiveLayer(Layers.Count - 1);
+            }
+            LayersChanged?.Invoke(this, new LayersChangedEventArgs(0, LayerAction.Add));
+        }
+
+        public void SetActiveLayer(int index)
+        {
+            if (ActiveLayerIndex <= Layers.Count - 1)
+            {
+                Layers[ActiveLayerIndex].IsActive = false;
+            }
+                ActiveLayerIndex = index;
+            Layers[ActiveLayerIndex].IsActive = true;
+            LayersChanged?.Invoke(this, new LayersChangedEventArgs(index, LayerAction.SetActive));
+        }
+
+        public WriteableBitmap GetCombinedLayersBitmap()
+        {
+            WriteableBitmap finalBitmap = Layers[0].LayerBitmap.Clone();
+            finalBitmap.Lock();
+            for (int i = 1; i < Layers.Count; i++)
+            {
+                for (int y = 0; y < finalBitmap.Height; y++)
+                {
+                    for (int x = 0; x < finalBitmap.Width; x++)
+                    {
+                        Color color = Layers[i].LayerBitmap.GetPixel(x, y);
+                        if (color.A != 0 || color.R != 0 || color.B != 0 || color.G != 0)
+                        {
+                            finalBitmap.SetPixel(x, y, color);
+                        }
+                    }
+                }
+            }
+            finalBitmap.Unlock();
+            return finalBitmap;
+        }
+    }
+}
+
+public class BitmapChangedEventArgs : EventArgs
+{
+    public BitmapPixelChanges PixelsChanged { get; set; }
+    public BitmapPixelChanges OldPixelsValues { get; set; }
+    public int ChangedLayerIndex { get; set; }
+
+    public BitmapChangedEventArgs(BitmapPixelChanges pixelsChanged, BitmapPixelChanges oldPixelsValues, int changedLayerIndex)
+    {
+        PixelsChanged = pixelsChanged;
+        OldPixelsValues = oldPixelsValues;
+        ChangedLayerIndex = changedLayerIndex;
+    }
+}
+
+public class LayersChangedEventArgs : EventArgs
+{
+    public int LayerAffected { get; set; }
+    public LayerAction LayerChangeType { get; set; }
+
+    public LayersChangedEventArgs(int layerAffected, LayerAction layerChangeType)
+    {
+        LayerAffected = layerAffected;
+        LayerChangeType = layerChangeType;
+    }
+}

+ 55 - 0
PixiEditor/Models/Controllers/MouseMovementController.cs

@@ -0,0 +1,55 @@
+using PixiEditor.Models.Position;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditor.Models.Controllers
+{
+    public class MouseMovementController
+    {
+        public List<Coordinates> LastMouseMoveCoordinates { get; } = new List<Coordinates>();
+        public event EventHandler<MouseMovementEventArgs> MousePositionChanged;
+        public event EventHandler StoppedRecordingChanges;
+        public bool IsRecordingChanges { get; private set; } = false;
+
+        public void StartRecordingMouseMovementChanges()
+        {
+            if (IsRecordingChanges == false)
+            {
+                LastMouseMoveCoordinates.Clear();
+                IsRecordingChanges = true;
+            }
+        }
+        public void RecordMouseMovementChange(Coordinates mouseCoordinates)
+        {
+            if (IsRecordingChanges == true)
+            {
+                if (LastMouseMoveCoordinates.Count == 0 || mouseCoordinates != LastMouseMoveCoordinates[LastMouseMoveCoordinates.Count - 1])
+                {
+                    LastMouseMoveCoordinates.Add(mouseCoordinates);
+                    MousePositionChanged?.Invoke(this, new MouseMovementEventArgs(mouseCoordinates));
+                }
+            }
+        }
+
+        public void StopRecordingMouseMovementChanges()
+        {
+            if (IsRecordingChanges)
+            {
+                IsRecordingChanges = false;
+                StoppedRecordingChanges?.Invoke(this, EventArgs.Empty);
+            }
+        }
+    }
+}
+
+public class MouseMovementEventArgs : EventArgs
+{
+    public Coordinates NewPosition { get; set; }
+
+    public MouseMovementEventArgs(Coordinates mousePosition)
+    {
+        NewPosition = mousePosition;
+    }
+}

+ 64 - 0
PixiEditor/Models/Controllers/PixelChangesController.cs

@@ -0,0 +1,64 @@
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Media;
+
+namespace PixiEditor.Models.Controllers
+{
+    public class PixelChangesController
+    {
+        LayerChanges LastChanges { get; set; }
+        LayerChanges LastOldValues { get; set; }
+
+        public void AddChanges(LayerChanges changes, LayerChanges oldValues)
+        {
+            if(LastChanges == null)
+            {
+                LastChanges = changes;
+                LastOldValues = oldValues;
+                return;
+            }
+
+            foreach (var change in changes.PixelChanges.ChangedPixels)
+            {
+                if (LastChanges.PixelChanges.ChangedPixels.ContainsKey(change.Key))
+                {
+                    continue;
+                }
+                else
+                {
+                    LastChanges.PixelChanges.ChangedPixels.Add(change.Key, change.Value);
+                }
+            }
+
+            foreach (var change in oldValues.PixelChanges.ChangedPixels)
+            {
+                if (LastOldValues.PixelChanges.ChangedPixels.ContainsKey(change.Key))
+                {
+                    continue;
+                }
+                else
+                {
+                    LastOldValues.PixelChanges.ChangedPixels.Add(change.Key, change.Value);
+                }
+            }
+        }
+
+        public Tuple<LayerChanges, LayerChanges> PopChanges()
+        {
+            Dictionary<Coordinates, Color> pixelChanges = LastChanges.PixelChanges.ChangedPixels.ToDictionary(entry => entry.Key, entry => entry.Value);
+            Dictionary<Coordinates, Color> oldValues = LastOldValues.PixelChanges.ChangedPixels.ToDictionary(entry => entry.Key, entry => entry.Value);
+            
+            var tmp = new LayerChanges(new BitmapPixelChanges(pixelChanges), LastChanges.LayerIndex);
+            var oldValuesTmp = new LayerChanges(new BitmapPixelChanges(oldValues), LastOldValues.LayerIndex);
+            
+            Tuple<LayerChanges, LayerChanges> outputChanges = new Tuple<LayerChanges, LayerChanges>(tmp, oldValuesTmp);
+            LastChanges = null;
+            LastOldValues = null;
+            return outputChanges;
+        }
+    }
+}

+ 6 - 17
PixiEditorDotNetCore3/Models/Controllers/UndoManager.cs → PixiEditor/Models/Controllers/UndoManager.cs

@@ -1,19 +1,10 @@
-using PixiEditor.Helpers;
-using PixiEditorDotNetCore3.Models.DataHolders;
-using System;
+using PixiEditor.Models.DataHolders;
 using System.Collections.Generic;
-using System.ComponentModel;
 using System.Diagnostics;
-using System.Drawing.Printing;
-using System.IO;
 using System.Linq;
 using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Media;
 
-namespace PixiEditorDotNetCore3.Models.Controllers
+namespace PixiEditor.Models.Controllers
 {
     public static class UndoManager
     {
@@ -55,7 +46,7 @@ namespace PixiEditorDotNetCore3.Models.Controllers
         /// <param name="oldValue">Old change value.</param>
         /// <param name="newValue">New change value.</param>
         /// <param name="undoDescription">Description of change.</param>
-        public static void RecordChanges(string property, object oldValue, string undoDescription = "")
+        public static void RecordChanges(string property, object oldValue, object newValue, string undoDescription = "")
         {
             if (_stopRecording == false)
             {
@@ -63,7 +54,7 @@ namespace PixiEditorDotNetCore3.Models.Controllers
                 {
                     _recordedChanges.RemoveAt(_recordedChanges.Count - 1);
                 }
-                _recordedChanges.Add(new Change(property, oldValue, undoDescription));
+                _recordedChanges.Add(new Change(property, oldValue, newValue, undoDescription));
 
             }
         }
@@ -92,7 +83,6 @@ namespace PixiEditorDotNetCore3.Models.Controllers
             }
             _lastChangeWasUndo = false;
             UndoStack.Push(change);
-            Debug.WriteLine("UndoStackCount: " + UndoStack.Count + " RedoStackCount: " + RedoStack.Count);
         }
         /// <summary>
         /// Adds property change to UndoStack
@@ -101,15 +91,14 @@ namespace PixiEditorDotNetCore3.Models.Controllers
         /// <param name="oldValue">Old value of property.</param>
         /// <param name="newValue">New value of property.</param>
         /// <param name="undoDescription">Description of change.</param>
-        public static void AddUndoChange(string property, object oldValue, string undoDescription = "")
+        public static void AddUndoChange(string property, object oldValue, object newValue, string undoDescription = "")
         {
             if (_lastChangeWasUndo == false && RedoStack.Count > 0) //Cleares RedoStack if las move wasn't redo or undo and if redo stack is greater than 0
             {
                 RedoStack.Clear();
             }
             _lastChangeWasUndo = false;
-            UndoStack.Push(new Change(property, oldValue, undoDescription));
-            Debug.WriteLine("UndoStackCount: " + UndoStack.Count + " RedoStackCount: " + RedoStack.Count);
+            UndoStack.Push(new Change(property, oldValue, newValue, undoDescription));
         }
 
         /// <summary>

+ 3 - 3
PixiEditorDotNetCore3/Models/DataHolders/Change.cs → PixiEditor/Models/DataHolders/Change.cs

@@ -5,7 +5,7 @@ using System.Runtime.Serialization;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace PixiEditorDotNetCore3.Models.DataHolders
+namespace PixiEditor.Models.DataHolders
 {
     [Serializable]
     public class Change
@@ -18,12 +18,12 @@ namespace PixiEditorDotNetCore3.Models.DataHolders
 
         public string Property { get; set; }
 
-        public Change(string property, object oldValue, string description = "")
+        public Change(string property, object oldValue, object newValue, string description = "")
         {
             Property = property;
             OldValue = oldValue;
             Description = description;
-            NewValue = OldValue;
+            NewValue = newValue;
         }
 
         public Change()

+ 19 - 0
PixiEditor/Models/DataHolders/LayerChanges.cs

@@ -0,0 +1,19 @@
+using PixiEditor.Models.Tools;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace PixiEditor.Models.DataHolders
+{
+    public class LayerChanges
+    {
+        public BitmapPixelChanges PixelChanges { get; set; }
+        public int LayerIndex { get; set; }
+
+        public LayerChanges(BitmapPixelChanges pixelChanges, int layerIndex)
+        {
+            PixelChanges = pixelChanges;
+            LayerIndex = layerIndex;
+        }
+    }
+}

+ 5 - 1
PixiEditorDotNetCore3/Models/DataHolders/StackEx.cs → PixiEditor/Models/DataHolders/StackEx.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace PixiEditorDotNetCore3.Models.DataHolders
+namespace PixiEditor.Models.DataHolders
 {
     public class StackEx<T>
     {
@@ -25,6 +25,10 @@ namespace PixiEditorDotNetCore3.Models.DataHolders
             items.Clear();
         }
 
+        /// <summary>
+        /// Returns top object without deleting it.
+        /// </summary>
+        /// <returns>Returns n - 1 item from stack.</returns>
         public T Peek()
         {
             return items[items.Count - 1];

+ 1 - 1
PixiEditorDotNetCore3/Models/DataHolders/Tuple.cs → PixiEditor/Models/DataHolders/Tuple.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.Text;
 
-namespace PixiEditorDotNetCore3.Models.DataHolders
+namespace PixiEditor.Models.DataHolders
 {
     public class Tuple<T1, T2, T3> : IEquatable<object>
     {

+ 1 - 1
PixiEditorDotNetCore3/Models/Dialogs/CustomDialog.cs → PixiEditor/Models/Dialogs/CustomDialog.cs

@@ -7,7 +7,7 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 
-namespace PixiEditorDotNetCore3.Models.Dialogs
+namespace PixiEditor.Models.Dialogs
 {
     public abstract class CustomDialog : NotifyableObject
     {

+ 1 - 1
PixiEditorDotNetCore3/Models/Dialogs/ExportFileDialog.cs → PixiEditor/Models/Dialogs/ExportFileDialog.cs

@@ -7,7 +7,7 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 
-namespace PixiEditorDotNetCore3.Models.Dialogs
+namespace PixiEditor.Models.Dialogs
 {
     public class ExportFileDialog : CustomDialog
     {

+ 1 - 1
PixiEditorDotNetCore3/Models/Dialogs/ImportFileDialog.cs → PixiEditor/Models/Dialogs/ImportFileDialog.cs

@@ -6,7 +6,7 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 
-namespace PixiEditorDotNetCore3.Models.Dialogs
+namespace PixiEditor.Models.Dialogs
 {
     class ImportFileDialog : CustomDialog
     {

+ 1 - 1
PixiEditorDotNetCore3/Models/Dialogs/NewFileDialog.cs → PixiEditor/Models/Dialogs/NewFileDialog.cs

@@ -7,7 +7,7 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 
-namespace PixiEditorDotNetCore3.Models.Dialogs
+namespace PixiEditor.Models.Dialogs
 {
     public class NewFileDialog : CustomDialog
     {

+ 1 - 1
PixiEditorDotNetCore3/Models/Enums/FileType.cs → PixiEditor/Models/Enums/FileType.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace PixiEditorDotNetCore3.Models.Enums
+namespace PixiEditor.Models.Enums
 {
     public enum FileType
     {

+ 11 - 0
PixiEditor/Models/Enums/LayerAction.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace PixiEditor.Models.Enums
+{
+    public enum LayerAction
+    {
+        Add, Remove, Move, SetActive
+    }
+}

+ 87 - 0
PixiEditor/Models/IO/Exporter.cs

@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Microsoft.Win32;
+using PixiEditor.Models.Dialogs;
+using PixiEditor.Models.Enums;
+
+namespace PixiEditor.Models.IO
+{
+    public class Exporter
+    {
+        public static string SavePath = null;
+        public static Size FileDimensions;
+
+        /// <summary>
+        /// Creates ExportFileDialog to get width, height and path of file.
+        /// </summary>
+        /// <param name="type">Type of file to be saved in.</param>
+        /// <param name="bitmap">Bitmap to be saved as file.</param>
+        public static void Export(FileType type, WriteableBitmap bitmap, Size fileDimensions)
+        {
+            ExportFileDialog info = new ExportFileDialog(fileDimensions);
+            //If OK on dialog has been clicked
+            if (info.ShowDialog() == true)
+            {
+                //If sizes are incorrect
+                if (info.FileWidth < bitmap.Width || info.FileHeight < bitmap.Height)
+                {
+                    MessageBox.Show("Incorrect height or width value", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+                    return;
+                }
+
+                SavePath = info.FilePath;
+                FileDimensions = new Size(info.FileWidth, info.FileHeight);
+                SaveAsPng(info.FilePath, info.FileHeight, info.FileWidth, bitmap);
+            }
+        }
+
+        /// <summary>
+        /// Saves file with info that has been recieved from ExportFileDialog before, doesn't work without before Export() usage.
+        /// </summary>
+        /// <param name="type">Type of file</param>
+        /// <param name="bitmap">Image to be saved as file.</param>
+        public static void ExportWithoutDialog(FileType type, WriteableBitmap bitmap)
+        {
+            try
+            {
+                SaveAsPng(SavePath, (int)FileDimensions.Height, (int)FileDimensions.Width, bitmap);
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show(ex.Message);
+            }
+        }
+        /// <summary>
+        /// Saves image to PNG file
+        /// </summary>
+        /// <param name="savePath">Save file path</param>
+        /// <param name="exportWidth">File width</param>
+        /// <param name="exportHeight">File height</param>
+        private static void SaveAsPng(string savePath, int exportWidth, int exportHeight, WriteableBitmap bitmap)
+        {
+            try
+            {
+                bitmap = bitmap.Resize(exportWidth, exportHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
+                using(FileStream stream = new FileStream(savePath, FileMode.Create))
+                {
+                    PngBitmapEncoder encoder = new PngBitmapEncoder();
+                    encoder.Frames.Add(BitmapFrame.Create(bitmap));
+                    encoder.Save(stream);
+                }
+            }
+            catch (Exception err)
+            {
+                MessageBox.Show(err.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+            }
+        }
+    }
+}

+ 1 - 1
PixiEditorDotNetCore3/Models/IO/Importer.cs → PixiEditor/Models/IO/Importer.cs

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 using System.Windows.Media.Imaging;
 using System.Windows.Media;
 
-namespace PixiEditorDotNetCore3.Models.IO
+namespace PixiEditor.Models.IO
 {
     public class Importer : NotifyableObject
     {

+ 1 - 1
PixiEditorDotNetCore3/Models/IO/PixiFilesManager.cs → PixiEditor/Models/IO/PixiFilesManager.cs

@@ -5,7 +5,7 @@ using System.IO;
 using System.Text;
 using Newtonsoft.Json;
 
-namespace PixiEditorDotNetCore3.Models.IO
+namespace PixiEditor.Models.IO
 {
     public static class PixiFilesManager
     {

+ 1 - 1
PixiEditorDotNetCore3/Models/Images/BitmapConverter.cs → PixiEditor/Models/Images/BitmapConverter.cs

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 
-namespace PixiEditorDotNetCore3.Models.Images
+namespace PixiEditor.Models.Images
 {
     public static class BitmapConverter
     {

+ 1 - 1
PixiEditorDotNetCore3/Models/Images/ImageGenerator.cs → PixiEditor/Models/Images/ImageGenerator.cs

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
 using System.Windows.Controls;
 using System.Windows.Media;
 
-namespace PixiEditorDotNetCore3.Models.Images
+namespace PixiEditor.Models.Images
 {
     public static class ImageGenerator
     {

+ 1 - 1
PixiEditorDotNetCore3/Models/Layers/BasicLayer.cs → PixiEditor/Models/Layers/BasicLayer.cs

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
 using System.Windows.Controls;
 using PixiEditor.Helpers;
 
-namespace PixiEditorDotNetCore3.Models.Layers
+namespace PixiEditor.Models.Layers
 {
     [Serializable]
     public class BasicLayer : NotifyableObject

+ 115 - 0
PixiEditor/Models/Layers/Layer.cs

@@ -0,0 +1,115 @@
+using PixiEditor.Models.Tools;
+using System;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditor.Models.Layers
+{
+    public class Layer : BasicLayer
+    {
+        private WriteableBitmap _layerBitmap;
+        private string _name;
+
+        public string Name
+        {
+            get { return _name; }
+            set 
+            { 
+                _name = value;
+                RaisePropertyChanged("Name");
+            }
+        }
+
+
+        private bool _isActive = false;
+        public bool IsActive
+        {
+            get => _isActive;
+            set
+            {
+                _isActive = value;
+                RaisePropertyChanged("IsActive");
+            }
+        }
+        private bool _isVisible = true;
+        public bool IsVisible
+        {
+            get => _isVisible;
+            set
+            {
+                _isVisible = value;
+                RaisePropertyChanged("IsVisible");
+            }
+        }
+
+        public WriteableBitmap LayerBitmap
+        {
+            get => _layerBitmap;
+            set
+            {
+                _layerBitmap = value;
+                RaisePropertyChanged("LayerBitmap");
+            }
+        }
+
+        public Layer(string name, int width, int height)
+        {
+            Name = name;
+            Layer layer = LayerGenerator.Generate(width, height);
+            LayerBitmap = layer.LayerBitmap;
+            Width = width;
+            Height = height;
+        }
+
+
+        public Layer(WriteableBitmap layerBitmap)
+        {
+            LayerBitmap = layerBitmap;
+            Width = (int)layerBitmap.Width;
+            Height = (int)layerBitmap.Height;
+        }
+
+        public void ApplyPixels(BitmapPixelChanges pixels)
+        {
+            LayerBitmap.Lock();
+
+            foreach (var coords in pixels.ChangedPixels)
+            {
+                LayerBitmap.SetPixel(Math.Clamp(coords.Key.X, 0, Width - 1), Math.Clamp(coords.Key.Y, 0, Height - 1),
+                    coords.Value);
+            }
+
+            LayerBitmap.Unlock();
+        }
+
+        public void Clear()
+        {
+            LayerBitmap.Lock();
+            LayerBitmap.Clear();
+            LayerBitmap.Unlock();
+        }
+
+        public byte[] ConvertBitmapToBytes()
+        {            
+            LayerBitmap.Lock();
+            byte[] byteArray = LayerBitmap.ToByteArray();
+            LayerBitmap.Unlock();
+            return byteArray;
+        }
+
+        public byte[] ConvertBitmapToBytes(WriteableBitmap bitmap)
+        {
+            bitmap.Lock();
+            byte[] byteArray = bitmap.ToByteArray();
+            bitmap.Unlock();
+            return byteArray;
+        }
+
+        public void Resize(int newWidth, int newHeight)
+        {
+            LayerBitmap.Resize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
+            Height = newHeight;
+            Width = newWidth;
+        }
+
+    }
+}

+ 3 - 2
PixiEditorDotNetCore3/Models/Layers/LayerGenerator.cs → PixiEditor/Models/Layers/LayerGenerator.cs

@@ -1,4 +1,4 @@
-using PixiEditorDotNetCore3.Models.Tools;
+using PixiEditor.Models.Tools;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -10,7 +10,7 @@ using System.Windows.Controls;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 
-namespace PixiEditorDotNetCore3.Models.Layers
+namespace PixiEditor.Models.Layers
 {
     public static class LayerGenerator
     {
@@ -46,5 +46,6 @@ namespace PixiEditorDotNetCore3.Models.Layers
             bitmap.Clear(System.Windows.Media.Colors.Transparent);
             return bitmap;
         }
+
     }
 }

+ 1 - 1
PixiEditorDotNetCore3/Models/Layers/LightLayer.cs → PixiEditor/Models/Layers/LightLayer.cs

@@ -8,7 +8,7 @@ using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using PixiEditor.Helpers;
 
-namespace PixiEditorDotNetCore3.Models.Layers
+namespace PixiEditor.Models.Layers
 {
     [Serializable]
     public class LightLayer : BasicLayer

+ 54 - 0
PixiEditor/Models/Position/Coordinates.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Models.Position
+{
+    public struct Coordinates
+    {
+        public int X { get; set; }
+
+        public int Y { get; set; }
+
+        public Coordinates(int x, int y)
+        {
+            X = x;
+            Y = y;
+        }
+
+        public override string ToString() => $"x: {X}, y: {Y}";
+
+        public static bool operator ==(Coordinates c1, Coordinates c2)
+        {
+            if (c1 == null || c2 == null) return false;
+            return c2.X == c1.X && c2.Y == c1.Y;
+        }
+
+        public static bool operator !=(Coordinates c1, Coordinates c2)
+        {
+            return !(c1 == c2);
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (obj.GetType() != typeof(Coordinates)) return false;
+            return this == (Coordinates)obj;
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                const int HashingBase = (int)2166136261;
+                const int HashingMultiplier = 16777619;
+
+                int hash = HashingBase;
+                hash = (hash * HashingMultiplier) ^ (!ReferenceEquals(null, X) ? X.GetHashCode() : 0);
+                hash = (hash * HashingMultiplier) ^ (!ReferenceEquals(null, Y) ? Y.GetHashCode() : 0);            
+                return hash;
+            }
+        }
+    }
+}

+ 11 - 1
PixiEditorDotNetCore3/Models/Position/CoordinatesCalculator.cs → PixiEditor/Models/Position/CoordinatesCalculator.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.Text;
 
-namespace PixiEditorDotNetCore3.Models.Position
+namespace PixiEditor.Models.Position
 {
     public static class CoordinatesCalculator
     {
@@ -32,6 +32,16 @@ namespace PixiEditorDotNetCore3.Models.Position
             return new DoubleCords(new Coordinates(x1, y1), new Coordinates(x2 - 1, y2 - 1));
         }
 
+        public static Coordinates GetCenterPoint(Coordinates startingPoint, Coordinates endPoint)
+        {
+            double width = endPoint.X - startingPoint.X + 1;
+            double height = endPoint.Y - startingPoint.Y + 1;
+
+            int x = startingPoint.X + (int)Math.Floor(width / 2);
+            int y = startingPoint.Y + (int)Math.Floor(height / 2);
+            return new Coordinates(x, y);
+        }
+
         public static Coordinates[] RectangleToCoordinates(int x1, int y1, int x2, int y2)
         {
             x2++;

+ 2 - 2
PixiEditorDotNetCore3/Models/Position/DoubleCords.cs → PixiEditor/Models/Position/DoubleCords.cs

@@ -4,9 +4,9 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace PixiEditorDotNetCore3.Models.Position
+namespace PixiEditor.Models.Position
 {
-    public class DoubleCords
+    public struct DoubleCords
     {
         public Coordinates Coords1 { get; set; }
         public Coordinates Coords2 { get; set; }

+ 13 - 2
PixiEditorDotNetCore3/Models/Position/MousePositionConverter.cs → PixiEditor/Models/Position/MousePositionConverter.cs

@@ -1,7 +1,8 @@
-using PixiEditorDotNetCore3.Models.Layers;
+using PixiEditor.Models.Layers;
+using System.Runtime.InteropServices;
 using System.Windows;
 
-namespace PixiEditorDotNetCore3.Models.Position
+namespace PixiEditor.Models.Position
 {
     public static class MousePositionConverter
     {
@@ -13,5 +14,15 @@ namespace PixiEditorDotNetCore3.Models.Position
             int yCoord = (int)(mousePosition.Y / baseLayer.Height);
             return new Coordinates(xCoord, yCoord);
         }
+
+        [DllImport("user32.dll")]
+        private static extern bool GetCursorPos(out System.Drawing.Point point);
+        
+        public static System.Drawing.Point GetCursorPosition()
+        {
+            System.Drawing.Point point;
+            GetCursorPos(out point);
+            return point;
+        }
     }
 }

+ 29 - 0
PixiEditor/Models/Tools/BitmapPixelChanges.cs

@@ -0,0 +1,29 @@
+using PixiEditor.Models.Position;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditor.Models.Tools
+{
+    public struct BitmapPixelChanges
+    {
+        public Dictionary<Coordinates, Color> ChangedPixels { get; set; } 
+
+
+        public BitmapPixelChanges(Dictionary<Coordinates, Color> changedPixels)
+        {
+            ChangedPixels = changedPixels;
+        }
+
+        public static BitmapPixelChanges FromSingleColoredArray(Coordinates[] coordinates, Color color)
+        {
+            Dictionary<Coordinates, Color> dict = new Dictionary<Coordinates, Color>();
+            for (int i = 0; i < coordinates.Length; i++)
+            {
+                dict.Add(coordinates[i], color);
+            }
+            return new BitmapPixelChanges(dict);
+        }
+    }
+}

+ 132 - 0
PixiEditor/Models/Tools/ShapeTool.cs

@@ -0,0 +1,132 @@
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditor.Models.Tools
+{
+    public abstract class ShapeTool : Tool
+    {
+        public override abstract ToolType ToolType { get; }
+
+        public abstract override BitmapPixelChanges Use(Layer layer, Coordinates[] coordinates, Color color, int toolSize);
+
+        public ShapeTool()
+        {
+            RequiresPreviewLayer = true;
+        }
+
+        protected Coordinates[] BresenhamLine(int x1, int y1, int x2, int y2)
+        {
+            List<Coordinates> coordinates = new List<Coordinates>();
+            if(x1 == x2 && y1 == y2)
+            {
+                return new Coordinates[] { new Coordinates(x1, y1) };
+            }
+
+            int d, dx, dy, ai, bi, xi, yi;
+            int x = x1, y = y1;
+
+            if (x1 < x2)
+            {
+                xi = 1;
+                dx = x2 - x1;
+            }
+            else
+            {
+                xi = -1;
+                dx = x1 - x2;
+            }
+
+            if (y1 < y2)
+            {
+                yi = 1;
+                dy = y2 - y1;
+            }
+            else
+            {
+                yi = -1;
+                dy = y1 - y2;
+            }
+            coordinates.Add(new Coordinates(x, y));
+
+            if (dx > dy)
+            {
+                ai = (dy - dx) * 2;
+                bi = dy * 2;
+                d = bi - dx;
+                
+                while (x != x2)
+                {
+                    
+                    if (d >= 0)
+                    {
+                        x += xi;
+                        y += yi;
+                        d += ai;
+                    }
+                    else
+                    {
+                        d += bi;
+                        x += xi;
+                    }
+                    coordinates.Add(new Coordinates(x, y));
+                }
+            }
+            else
+            {
+                ai = (dx - dy) * 2;
+                bi = dx * 2;
+                d = bi - dy;
+                
+                while (y != y2)
+                {
+                    
+                    if (d >= 0)
+                    {
+                        x += xi;
+                        y += yi;
+                        d += ai;
+                    }
+                    else
+                    {
+                        d += bi;
+                        y += yi;
+                    }
+                    coordinates.Add(new Coordinates(x, y));
+                }
+            }
+
+            return coordinates.ToArray();
+
+        }
+
+        protected DoubleCords CalculateCoordinatesForShapeRotation(Coordinates startingCords, Coordinates secondCoordinates)
+        {
+            Coordinates currentCoordinates = secondCoordinates;
+
+            if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
+            {
+                return new DoubleCords(new Coordinates(currentCoordinates.X, currentCoordinates.Y), new Coordinates(startingCords.X, startingCords.Y));
+            }
+            else if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
+            {
+                return new DoubleCords(new Coordinates(startingCords.X, startingCords.Y), new Coordinates(currentCoordinates.X, currentCoordinates.Y));
+            }
+            else if (startingCords.Y > currentCoordinates.Y)
+            {
+                return new DoubleCords(new Coordinates(startingCords.X, currentCoordinates.Y), new Coordinates(currentCoordinates.X, startingCords.Y));
+            }
+            else if(startingCords.X > currentCoordinates.X && startingCords.Y <= currentCoordinates.Y)
+            {
+                return new DoubleCords(new Coordinates(currentCoordinates.X, startingCords.Y), new Coordinates(startingCords.X, currentCoordinates.Y));
+            }
+            else
+            {
+                return new DoubleCords(startingCords, secondCoordinates);
+            }
+        }
+    }
+}

+ 5 - 5
PixiEditorDotNetCore3/Models/Tools/Tool.cs → PixiEditor/Models/Tools/Tool.cs

@@ -1,16 +1,16 @@
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
 using System;
 using System.Collections.Generic;
 using System.Text;
 using System.Windows.Media;
 
-namespace PixiEditorDotNetCore3.Models.Tools
+namespace PixiEditor.Models.Tools
 {
     public abstract class Tool
     {
-        public abstract BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize);
+        public abstract BitmapPixelChanges Use(Layer layer, Coordinates[] pixels, Color color, int toolSize);
         public abstract ToolType ToolType { get; }
-        public bool ExecutesItself = false;
+        public bool RequiresPreviewLayer { get; set; }
     }
 }

+ 2 - 2
PixiEditorDotNetCore3/Models/Tools/ToolType.cs → PixiEditor/Models/Tools/ToolType.cs

@@ -4,10 +4,10 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace PixiEditorDotNetCore3.Models.Tools
+namespace PixiEditor.Models.Tools
 {
     public enum ToolType
     {
-        Pen = 0, Bucket, Line, Circle, Rectangle, ColorPicker, Earser, Brightness
+        None, Pen, Bucket, Line, Circle, Rectangle, Earser, Brightness, ColorPicker
     }
 }

+ 8 - 8
PixiEditorDotNetCore3/Models/Tools/Tools/BrightnessTool.cs → PixiEditor/Models/Tools/Tools/BrightnessTool.cs

@@ -1,11 +1,11 @@
-using PixiEditorDotNetCore3.Models.Colors;
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
+using PixiEditor.Models.Colors;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 
-namespace PixiEditorDotNetCore3.Models.Tools.Tools
+namespace PixiEditor.Models.Tools.Tools
 {
     public class BrightnessTool : Tool
     {
@@ -13,13 +13,13 @@ namespace PixiEditorDotNetCore3.Models.Tools.Tools
         public const float DarkenFactor = -0.06f;
         public const float LightenFactor = 0.1f;
 
-        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        public override BitmapPixelChanges Use(Layer layer, Coordinates[] coordinates, Color color, int toolSize)
         {
-            if(Mouse.LeftButton == MouseButtonState.Pressed)
+            if(Keyboard.IsKeyDown(Key.LeftCtrl))
             {
-                return ChangeBrightness(layer, startingCoords, toolSize, LightenFactor);
+                return ChangeBrightness(layer, coordinates[0], toolSize, DarkenFactor);
             }
-                return ChangeBrightness(layer, startingCoords, toolSize, DarkenFactor);
+                return ChangeBrightness(layer, coordinates[0], toolSize, LightenFactor);
         }       
 
         private BitmapPixelChanges ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize, float correctionFactor)

+ 137 - 0
PixiEditor/Models/Tools/Tools/CircleTool.cs

@@ -0,0 +1,137 @@
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class CircleTool : ShapeTool
+    {
+        public override ToolType ToolType => ToolType.Circle;
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates[] coordinates, Color color, int toolSize)
+        {
+            DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(coordinates[^1], coordinates[0]);
+            return BitmapPixelChanges.FromSingleColoredArray(CreateEllipse(fixedCoordinates.Coords1, fixedCoordinates.Coords2), color);
+        }
+
+        public Coordinates[] CreateEllipse(Coordinates startCoordinates, Coordinates endCoordinates)
+        {            
+            Coordinates centerCoordinates = CoordinatesCalculator.GetCenterPoint(startCoordinates, endCoordinates);
+            int radiusX = endCoordinates.X - centerCoordinates.X;
+            int radiusY = endCoordinates.Y - centerCoordinates.Y;
+            return MidpointEllipse(radiusX, radiusY, centerCoordinates.X, centerCoordinates.Y);
+        }
+
+        public Coordinates[] MidpointEllipse(double rx, double ry, double xc, double yc)
+        {
+            List<Coordinates> outputCoordinates = new List<Coordinates>();
+            double dx, dy, d1, d2, x, y;
+            x = 0;
+            y = ry;
+
+            d1 = (ry * ry) - (rx * rx * ry) + (0.25f * rx * rx);
+            dx = 2 * ry * ry * x;
+            dy = 2 * rx * rx * y;
+
+            while (dx < dy)
+            {
+                outputCoordinates.AddRange(GetRegionPoints(x, xc, y, yc));
+                if (d1 < 0)
+                {
+                    x++;
+                    dx += (2 * ry * ry);
+                    d1 = d1 + dx + (ry * ry);
+                }
+                else
+                {
+                    x++;
+                    y--;
+                    dx += (2 * ry * ry);
+                    dy -= (2 * rx * rx);
+                    d1 = d1 + dx - dy + (ry * ry);
+                }
+            }
+
+            //Yeah, it scares me too
+            d2 = (ry * ry * ((x + 0.5f) * (x + 0.5f))) + (rx * rx * ((y - 1) * (y - 1))) - (rx * rx * ry * ry);
+
+            while(y >= 0)
+            {
+                outputCoordinates.AddRange(GetRegionPoints(x, xc, y, yc));
+
+                if(d2 > 0)
+                {
+                    y--;
+                    dy -= (2 * rx * rx);
+                    d2 = d2 + (rx * rx) - dy;
+                }
+                else
+                {
+                    y--;
+                    x++;
+                    dx += (2 * ry * ry);
+                    dy -= (2 * rx * rx);
+                    d2 = d2 + dx - dy + (rx * rx);
+                }
+            }
+
+            return outputCoordinates.Distinct().ToArray();
+
+        }
+
+        private Coordinates[] GetRegionPoints(double x, double xc, double y, double yc)
+        {
+            Coordinates[] outputCoordinates = new Coordinates[4];
+            outputCoordinates[0] = (new Coordinates((int)x + (int)xc, (int)y + (int)yc));
+            outputCoordinates[1] = (new Coordinates((int)-x + (int)xc, (int)y + (int)yc));
+            outputCoordinates[2] = (new Coordinates((int)x + (int)xc, (int)-y + (int)yc));
+            outputCoordinates[3] = (new Coordinates((int)-x + (int)xc, (int)-y + (int)yc));
+            return outputCoordinates;
+        }
+
+        public Coordinates[] BresenhamCircle(Coordinates startCoordinates, Coordinates endCoordinates, int size)
+        {
+            List<Coordinates> outputCoordinates = new List<Coordinates>();
+            Coordinates centerCoordinates = CoordinatesCalculator.GetCenterPoint(startCoordinates, endCoordinates);
+            int radius = endCoordinates.X - centerCoordinates.X;
+            int x = 0;
+            int y = radius;
+            int decision = 3 - 2 * radius;
+            outputCoordinates.AddRange(GetPixelsForOctant(centerCoordinates.X, centerCoordinates.Y, x, y));
+
+            while (x <= y)
+            {
+                if (decision <= 0)
+                {
+                    decision = decision + (4 * x) + 6;
+                }
+                else
+                {
+                    decision = decision + (4 * x) - (4 * y) + 10;
+                    y--;
+                }
+                x++;
+                outputCoordinates.AddRange(GetPixelsForOctant(centerCoordinates.X, centerCoordinates.Y, x, y));
+            }
+            return outputCoordinates.Distinct().ToArray();
+        }
+
+        private Coordinates[] GetPixelsForOctant(int xc, int yc, int x, int y)
+        {
+            Coordinates[] outputCords = new Coordinates[8];
+            outputCords[0] = new Coordinates(x + xc, y + yc);
+            outputCords[1] = new Coordinates(x + xc, -y + yc);
+            outputCords[2] = new Coordinates(-x + xc, -y + yc);
+            outputCords[3] = new Coordinates(-x + xc, y + yc);
+            outputCords[4] = new Coordinates(y + xc, x + yc);
+            outputCords[5] = new Coordinates(y + xc, -x + yc);
+            outputCords[6] = new Coordinates(-y + xc, -x + yc);
+            outputCords[7] = new Coordinates(-y + xc, x + yc);
+            return outputCords;
+        }
+
+    }
+}

+ 5 - 5
PixiEditorDotNetCore3/Models/Tools/Tools/EarserTool.cs → PixiEditor/Models/Tools/Tools/EarserTool.cs

@@ -1,20 +1,20 @@
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
 using System;
 using System.Collections.Generic;
 using System.Text;
 using System.Windows.Media;
 
-namespace PixiEditorDotNetCore3.Models.Tools.Tools
+namespace PixiEditor.Models.Tools.Tools
 {
     public class EarserTool : Tool
     {
         public override ToolType ToolType => ToolType.Earser;
 
-        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        public override BitmapPixelChanges Use(Layer layer, Coordinates[] coordinates, Color color, int toolSize)
         {
             PenTool pen = new PenTool();
-            return pen.Draw(startingCoords, System.Windows.Media.Colors.Transparent, toolSize);
+            return pen.Draw(coordinates[0], System.Windows.Media.Colors.Transparent, toolSize);
         }
     }
 }

+ 13 - 15
PixiEditorDotNetCore3/Models/Tools/Tools/FloodFill.cs → PixiEditor/Models/Tools/Tools/FloodFill.cs

@@ -1,53 +1,51 @@
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
 using System;
 using System.Collections.Generic;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 
-namespace PixiEditorDotNetCore3.Models.Tools.Tools
+namespace PixiEditor.Models.Tools.Tools
 {
     public class FloodFill : Tool
     {
         public override ToolType ToolType => ToolType.Bucket;
 
-        public FloodFill()
+        public override BitmapPixelChanges Use(Layer layer, Coordinates[] coordinates, Color color, int toolSize)
         {
-            ExecutesItself = true;
-        }
-
-        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
-        {
-            return ForestFire(layer, startingCoords, color);
+            return ForestFire(layer, coordinates[0], color);
         }
 
         public BitmapPixelChanges ForestFire(Layer layer, Coordinates startingCoords, Color newColor)
         {
             List<Coordinates> changedCoords = new List<Coordinates>();
 
-            Color colorToReplace = layer.LayerBitmap.GetPixel(startingCoords.X, startingCoords.Y);
+            WriteableBitmap bitmap = layer.LayerBitmap.Clone();
+            Color colorToReplace = bitmap.GetPixel(startingCoords.X, startingCoords.Y);
 
             var stack = new Stack<Tuple<int, int>>();
             stack.Push(Tuple.Create(startingCoords.X, startingCoords.Y));
 
+            bitmap.Lock();
             while (stack.Count > 0)
             {
                 var point = stack.Pop();
                 if (point.Item1 < 0 || point.Item1 > layer.Height - 1) continue;
                 if (point.Item2 < 0 || point.Item2 > layer.Width - 1) continue;
-                if (layer.LayerBitmap.GetPixel(point.Item1, point.Item2) == newColor) continue;
+                if (bitmap.GetPixel(point.Item1, point.Item2) == newColor) continue;
 
-                if (layer.LayerBitmap.GetPixel(point.Item1, point.Item2) == colorToReplace)
+                if (bitmap.GetPixel(point.Item1, point.Item2) == colorToReplace)
                 {
                     changedCoords.Add(new Coordinates(point.Item1, point.Item2));
-                    layer.LayerBitmap.SetPixel(point.Item1, point.Item2, newColor);
+                    bitmap.SetPixel(point.Item1, point.Item2, newColor);
                     stack.Push(Tuple.Create(point.Item1, point.Item2 - 1));
                     stack.Push(Tuple.Create(point.Item1 + 1, point.Item2));
                     stack.Push(Tuple.Create(point.Item1, point.Item2 + 1));
                     stack.Push(Tuple.Create(point.Item1 - 1, point.Item2));
                 }
             }
-            return new BitmapPixelChanges(changedCoords.ToArray(), newColor);
+            bitmap.Unlock();
+            return BitmapPixelChanges.FromSingleColoredArray(changedCoords.ToArray(), newColor);
         }
     }
 }

+ 24 - 0
PixiEditor/Models/Tools/Tools/LineTool.cs

@@ -0,0 +1,24 @@
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class LineTool : ShapeTool
+    {
+        public override ToolType ToolType => ToolType.Line;
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates[] coordinates, Color color, int toolSize)
+        {
+            return BitmapPixelChanges.FromSingleColoredArray(CreateLine(coordinates), color);
+        }
+
+        public Coordinates[] CreateLine(Coordinates[] coordinates)
+        {
+            Coordinates startingCoordinates = coordinates[^1];
+            Coordinates latestCoordinates = coordinates[0];
+            return BresenhamLine(startingCoordinates.X, startingCoordinates.Y, latestCoordinates.X, latestCoordinates.Y);
+        }
+    }
+}

+ 6 - 6
PixiEditorDotNetCore3/Models/Tools/Tools/PenTool.cs → PixiEditor/Models/Tools/Tools/PenTool.cs

@@ -1,20 +1,20 @@
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
 using System;
 using System.Collections.Generic;
 using System.Text;
 using System.Windows.Media;
 
-namespace PixiEditorDotNetCore3.Models.Tools.Tools
+namespace PixiEditor.Models.Tools.Tools
 {
     public class PenTool : Tool
     {
         public override ToolType ToolType => ToolType.Pen;
 
 
-        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        public override BitmapPixelChanges Use(Layer layer, Coordinates[] coordinates, Color color, int toolSize)
         {
-            return Draw(startingCoords, color, toolSize);
+            return Draw(coordinates[0], color, toolSize);
         }
 
         public BitmapPixelChanges Draw(Coordinates startingCoords, Color color, int toolSize)
@@ -25,7 +25,7 @@ namespace PixiEditorDotNetCore3.Models.Tools.Tools
             y1 = centeredCoords.Coords1.Y;
             x2 = centeredCoords.Coords2.X;
             y2 = centeredCoords.Coords2.Y;
-            return new BitmapPixelChanges(CoordinatesCalculator.RectangleToCoordinates(x1, y1, x2, y2), color);
+            return BitmapPixelChanges.FromSingleColoredArray(CoordinatesCalculator.RectangleToCoordinates(x1, y1, x2, y2), color);
         }
     }
 }

+ 43 - 0
PixiEditor/Models/Tools/Tools/RectangleTool.cs

@@ -0,0 +1,43 @@
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Media;
+
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class RectangleTool : ShapeTool
+    {
+        public override ToolType ToolType => ToolType.Rectangle;
+        public bool Filled { get; set; } = false;
+
+        public override BitmapPixelChanges Use(Layer layer, Coordinates[] coordinates, Color color, int toolSize)
+        {
+            return BitmapPixelChanges.FromSingleColoredArray(CreateRectangle(coordinates, toolSize), color);
+        }
+
+        public Coordinates[] CreateRectangle(Coordinates[] coordinates, int toolSize)
+        {
+            DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(coordinates[^1], coordinates[0]);
+            return CalculateRectanglePoints(fixedCoordinates, Filled);
+        }
+
+        private Coordinates[] CalculateRectanglePoints(DoubleCords coordinates, bool filled)
+        {
+            List<Coordinates> finalCoordinates = new List<Coordinates>();
+
+            for (int i = coordinates.Coords1.X; i < coordinates.Coords2.X + 1; i++)
+            {
+                finalCoordinates.Add(new Coordinates(i, coordinates.Coords1.Y));
+                finalCoordinates.Add(new Coordinates(i, coordinates.Coords2.Y));
+            }
+            for (int i = coordinates.Coords1.Y + 1; i <= coordinates.Coords2.Y - 1; i++)
+            {
+                finalCoordinates.Add(new Coordinates(coordinates.Coords1.X, i));
+                finalCoordinates.Add(new Coordinates(coordinates.Coords2.X, i));
+            }
+            finalCoordinates = finalCoordinates.Distinct().ToList();
+            return finalCoordinates.ToArray();
+        }
+    }
+}

+ 6 - 4
PixiEditorDotNetCore3/PixiEditorDotNetCore3.csproj → PixiEditor/PixiEditor.csproj

@@ -7,18 +7,20 @@
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
     <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
+    <AssemblyName>PixiEditor</AssemblyName>
+    <RootNamespace>PixiEditor</RootNamespace>
   </PropertyGroup>
   <ItemGroup>
     <PackageReference Include="Expression.Blend.Sdk">
       <Version>1.0.2</Version>
     </PackageReference>
     <PackageReference Include="Extended.Wpf.Toolkit">
-      <Version>3.5.0</Version>
+      <Version>3.8.1</Version>
     </PackageReference>
-    <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
-    <PackageReference Include="System.Drawing.Common" Version="4.6.0" />
+    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+    <PackageReference Include="System.Drawing.Common" Version="4.7.0" />
     <PackageReference Include="WriteableBitmapEx">
-      <Version>1.6.2</Version>
+      <Version>1.6.5</Version>
     </PackageReference>
   </ItemGroup>
   <ItemGroup>

+ 2 - 2
PixiEditorDotNetCore3/PixiEditorDotNetCore3.sln → PixiEditor/PixiEditor.sln

@@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 16
 VisualStudioVersion = 16.0.28729.10
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditorDotNetCore3", "PixiEditorDotNetCore3.csproj", "{B17A8190-059F-4FDD-A1B5-4F63D866778F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor", "PixiEditor.csproj", "{B17A8190-059F-4FDD-A1B5-4F63D866778F}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditorTests", "..\PixiEditorTests\PixiEditorTests.csproj", "{D61922EA-3BF3-4AFA-8930-3A8B30A9A195}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditorTests", "..\PixiEditorTests\PixiEditorTests.csproj", "{D61922EA-3BF3-4AFA-8930-3A8B30A9A195}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution

+ 0 - 0
PixiEditorDotNetCore3/Properties/AssemblyInfo.cs → PixiEditor/Properties/AssemblyInfo.cs


+ 0 - 0
PixiEditorDotNetCore3/Properties/Resources.Designer.cs → PixiEditor/Properties/Resources.Designer.cs


+ 0 - 0
PixiEditorDotNetCore3/Properties/Resources.resx → PixiEditor/Properties/Resources.resx


+ 0 - 0
PixiEditorDotNetCore3/Properties/Settings.Designer.cs → PixiEditor/Properties/Settings.Designer.cs


+ 0 - 0
PixiEditorDotNetCore3/Properties/Settings.settings → PixiEditor/Properties/Settings.settings


+ 0 - 0
PixiEditorDotNetCore3/Styles/MenuButtonStyle.xaml → PixiEditor/Styles/MenuButtonStyle.xaml


+ 106 - 0
PixiEditor/Styles/ThemeStyle.xaml

@@ -0,0 +1,106 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+
+    <Style TargetType="{x:Type Control}">
+        <Setter Property="Foreground" Value="Snow"/>
+    </Style>
+
+    <Style TargetType="Button" x:Key="BaseDarkButton">
+        <Setter Property="Background" Value="#404040"/>
+        <Setter Property="Foreground" Value="White"/>
+        <Setter Property="FontSize" Value="22"/>
+        <Setter Property="SnapsToDevicePixels" Value="True"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Border Background="{TemplateBinding Background}">
+                        <ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                    </Border>
+                    <ControlTemplate.Triggers>
+                        <Trigger Property="IsEnabled" Value="False">
+                            <Setter Property="Background" Value="Transparent"/>
+                            <Setter Property="Foreground" Value="Gray"/>
+                            <Setter Property="Cursor" Value="Arrow"/>
+                        </Trigger>
+                        <Trigger Property="IsMouseOver" Value="True">
+                            <Setter Property="Background" Value="#FF515151"/>
+                            <Setter Property="Foreground" Value="White"/>
+                            <Setter Property="Cursor" Value="Hand"/>
+                        </Trigger>
+                        <Trigger Property="IsPressed" Value="True">
+                            <Setter Property="Background" Value="#505050"/>
+                            <Setter Property="Foreground" Value="White"/>
+                        </Trigger>
+                    </ControlTemplate.Triggers>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style TargetType="Button"  x:Key="DarkRoundButton" BasedOn="{StaticResource BaseDarkButton}">
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="Button">
+                    <Border CornerRadius="4" Background="{TemplateBinding Background}">
+                        <ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                    </Border>
+                    <ControlTemplate.Triggers>
+                        <Trigger Property="IsEnabled" Value="False">
+                            <Setter Property="Background" Value="Transparent"/>
+                            <Setter Property="Foreground" Value="Gray"/>
+                            <Setter Property="Cursor" Value="Arrow"/>
+                        </Trigger>
+                        <Trigger Property="IsMouseOver" Value="True">
+                            <Setter Property="Background" Value="#FF515151"/>
+                            <Setter Property="Foreground" Value="White"/>
+                            <Setter Property="Cursor" Value="Hand"/>
+                        </Trigger>
+                        <Trigger Property="IsPressed" Value="True">
+                            <Setter Property="Background" Value="#505050"/>
+                            <Setter Property="Foreground" Value="White"/>
+                        </Trigger>
+                    </ControlTemplate.Triggers>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+
+    <Style TargetType="TextBox" x:Key="DarkTextBoxStyle">
+        <Setter Property="Background" Value="#202020"/>
+        <Setter Property="BorderThickness" Value="1"/>
+        <Setter Property="BorderBrush" Value="#404040"/>
+        <Setter Property="Foreground" Value="Snow"/>
+    </Style>
+
+    <Style TargetType="Button" x:Key="ToolButtonStyle">
+        <Setter Property="Height" Value="42"/>
+        <Setter Property="Width" Value="42"/>
+        <Setter Property="VerticalAlignment" Value="Top"/>
+        <Setter Property="HorizontalAlignment" Value="Center"/>
+        <Setter Property="Margin" Value="0,10,0,0"/>
+    </Style>
+
+    <Style x:Key="{x:Type ContextMenu}" TargetType="{x:Type ContextMenu}">
+        <Setter Property="OverridesDefaultStyle" Value="True"/>
+        <Setter Property="SnapsToDevicePixels" Value="True"/>
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type ContextMenu}">
+                    <Border Background="#303030" BorderBrush="White" BorderThickness="1" Opacity="0.96">
+                        <StackPanel ClipToBounds="True" Orientation="Vertical" IsItemsHost="True"/>
+                    </Border>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+    <ControlTemplate x:Key="{x:Static MenuItem.TopLevelItemTemplateKey}" TargetType="{x:Type MenuItem}">
+        <Border Name="Border" >
+            <Grid>
+                <ContentPresenter Margin="6,3,6,3" ContentSource="Header" RecognizesAccessKey="True" />
+            </Grid>
+        </Border>
+    </ControlTemplate>
+
+
+</ResourceDictionary>

+ 0 - 0
PixiEditorDotNetCore3/ViewModels/FeedbackDialogViewModel.cs → PixiEditor/ViewModels/FeedbackDialogViewModel.cs


+ 0 - 0
PixiEditorDotNetCore3/ViewModels/ImportFilePopupViewModel.cs → PixiEditor/ViewModels/ImportFilePopupViewModel.cs


+ 0 - 0
PixiEditorDotNetCore3/ViewModels/MenuButtonViewModel.cs → PixiEditor/ViewModels/MenuButtonViewModel.cs


+ 1 - 1
PixiEditorDotNetCore3/ViewModels/NewFileMenuViewModel.cs → PixiEditor/ViewModels/NewFileMenuViewModel.cs

@@ -1,5 +1,5 @@
 using PixiEditor.Helpers;
-using PixiEditorDotNetCore3.Models;
+using PixiEditor.Models;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;

+ 0 - 0
PixiEditorDotNetCore3/ViewModels/SaveFilePopupViewModel.cs → PixiEditor/ViewModels/SaveFilePopupViewModel.cs


+ 0 - 0
PixiEditorDotNetCore3/ViewModels/ViewModelBase.cs → PixiEditor/ViewModels/ViewModelBase.cs


+ 392 - 0
PixiEditor/ViewModels/ViewModelMain.cs

@@ -0,0 +1,392 @@
+using PixiEditor.Helpers;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.Tools;
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using PixiTools = PixiEditor.Models.Tools.Tools;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Dialogs;
+using PixiEditor.Models.IO;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.DataHolders;
+
+namespace PixiEditor.ViewModels
+{
+    class ViewModelMain : ViewModelBase
+    {
+
+        public RelayCommand SelectToolCommand { get; set; } //Command that handles tool switching 
+        public RelayCommand GenerateDrawAreaCommand { get; set; } //Command that generates draw area
+        public RelayCommand MouseMoveCommand { get; set; } //Command that is used to draw
+        public RelayCommand MouseDownCommand { get; set; }
+        public RelayCommand SaveFileCommand { get; set; } //Command that is used to save file
+        public RelayCommand UndoCommand { get; set; }
+        public RelayCommand RedoCommand { get; set; }
+        public RelayCommand MouseUpCommand { get; set; }
+        public RelayCommand RecenterZoomboxCommand { get; set; }
+        public RelayCommand OpenFileCommand { get; set; }
+        public RelayCommand SetActiveLayerCommand { get; set; }
+        public RelayCommand NewLayerCommand { get; set; }
+        public RelayCommand ReloadImageCommand { get; set; }
+        public RelayCommand DeleteLayerCommand { get; set; }
+        public RelayCommand MoveToBackCommand { get; set; }
+        public RelayCommand MoveToFrontCommand { get; set; }
+
+        private double _mouseXonCanvas;
+
+        public double MouseXOnCanvas //Mouse X coordinate relative to canvas
+        {
+            get => _mouseXonCanvas;
+            set { _mouseXonCanvas = value; RaisePropertyChanged("MouseXonCanvas"); }
+        }
+
+        private double _mouseYonCanvas;
+
+        public double MouseYOnCanvas //Mouse Y coordinate relative to canvas
+        {
+            get => _mouseYonCanvas;
+            set { _mouseYonCanvas = value; RaisePropertyChanged("MouseYonCanvas"); }
+        }
+
+
+        private Color _primaryColor = Colors.White;
+
+        public Color PrimaryColor //Primary color, hooked with left mouse button
+        {
+            get => _primaryColor;
+            set
+            {
+                if (_primaryColor != value)
+                {
+                    _primaryColor = value;
+                    BitmapUtility.PrimaryColor = value;
+                    RaisePropertyChanged("PrimaryColor");
+                }
+            }
+        }
+
+        private Color _secondaryColor = Colors.Black;
+
+        public Color SecondaryColor //Secondary color, hooked with right mouse button
+        {
+            get => _secondaryColor;
+            set { if (_secondaryColor != value) { _secondaryColor = value; RaisePropertyChanged("SecondaryColor"); } }
+        }
+
+        private ToolType _selectedTool;
+
+        public ToolType SelectedTool
+        {
+            get { return _selectedTool; }
+            set 
+            { 
+                if (_selectedTool != value) 
+                { 
+                    _selectedTool = value; 
+                    SetActiveTool(value); 
+                    RaisePropertyChanged("SelectedTool"); } }
+        }        
+
+
+        private int _toolSize;
+
+        public int ToolSize
+        {
+            get { return _toolSize; }
+            set 
+            { 
+                if (_toolSize != value) 
+                { 
+                    _toolSize = value;
+                    BitmapUtility.ToolSize = value;
+                    RaisePropertyChanged("ToolSize"); 
+                } 
+            }
+        }
+
+        public List<Tool> ToolSet { get; set; }
+
+        private LayerChanges _undoChanges;
+
+        public LayerChanges UndoChanges
+        {
+            get { return _undoChanges; }
+            set 
+            { 
+                _undoChanges = value;
+                BitmapUtility.Layers[value.LayerIndex].ApplyPixels(value.PixelChanges);
+            }
+        }
+
+
+        public BitmapOperationsUtility BitmapUtility { get; set; }
+        public PixelChangesController ChangesController { get; set; }
+
+        public ViewModelMain()
+        {
+            PixiFilesManager.InitializeTempDirectories();
+            BitmapUtility = new BitmapOperationsUtility();
+            BitmapUtility.BitmapChanged += BitmapUtility_BitmapChanged;
+            BitmapUtility.MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
+            ChangesController = new PixelChangesController();
+            SelectToolCommand = new RelayCommand(RecognizeTool);
+            GenerateDrawAreaCommand = new RelayCommand(GenerateDrawArea);
+            MouseMoveCommand = new RelayCommand(MouseMove);
+            MouseDownCommand = new RelayCommand(MouseDown);
+            SaveFileCommand = new RelayCommand(SaveFile, CanSave);
+            UndoCommand = new RelayCommand(Undo, CanUndo);
+            RedoCommand = new RelayCommand(Redo, CanRedo);
+            MouseUpCommand = new RelayCommand(MouseUp);
+            RecenterZoomboxCommand = new RelayCommand(RecenterZoombox);
+            OpenFileCommand = new RelayCommand(OpenFile);
+            SetActiveLayerCommand = new RelayCommand(SetActiveLayer);
+            NewLayerCommand = new RelayCommand(NewLayer, CanCreateNewLayer);
+            DeleteLayerCommand = new RelayCommand(DeleteLayer, CanDeleteLayer);
+            MoveToBackCommand = new RelayCommand(MoveLayerToBack, CanMoveToBack);
+            MoveToFrontCommand = new RelayCommand(MoveLayerToFront, CanMoveToFront);
+            ToolSet = new List<Tool> { new PixiTools.PenTool(), new PixiTools.FloodFill(), new PixiTools.LineTool(),
+            new PixiTools.CircleTool(), new PixiTools.RectangleTool(), new PixiTools.EarserTool(), new PixiTools.BrightnessTool() };       
+            UndoManager.SetMainRoot(this);
+            SetActiveTool(ToolType.Pen);
+            BitmapUtility.PrimaryColor = PrimaryColor;
+            ToolSize = 1;
+        }
+
+        private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
+        {
+            Tuple<LayerChanges, LayerChanges> changes = ChangesController.PopChanges();
+            UndoManager.AddUndoChange(new Change("UndoChanges", changes.Item2, changes.Item1)); //Item2 is old value
+        }
+
+        private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
+        {
+            ChangesController.AddChanges(new LayerChanges(e.PixelsChanged, e.ChangedLayerIndex), 
+                new LayerChanges(e.OldPixelsValues, e.ChangedLayerIndex));
+        }
+
+        public void MoveLayerToFront(object parameter)
+        {
+            int oldIndex = (int)parameter;
+            BitmapUtility.Layers.Move(oldIndex, oldIndex + 1);
+        }
+
+        public void MoveLayerToBack(object parameter)
+        {
+            int oldIndex = (int)parameter;
+            BitmapUtility.Layers.Move(oldIndex, oldIndex - 1);
+        }
+
+        public bool CanMoveToFront(object property)
+        {
+            return BitmapUtility.Layers.Count - 1 > (int)property;
+        }
+
+        public bool CanMoveToBack(object property)
+        {
+            return (int)property > 0;
+        }
+
+        public void SetActiveLayer(object parameter)
+        {
+            BitmapUtility.SetActiveLayer((int)parameter);
+        }
+
+        public void DeleteLayer(object parameter)
+        {
+            BitmapUtility.RemoveLayer((int)parameter);
+        }
+
+        public bool CanDeleteLayer(object property)
+        {
+            return BitmapUtility.Layers.Count > 1;
+        }
+
+        #region Undo/Redo
+        /// <summary>
+        /// Undo last action
+        /// </summary>
+        /// <param name="parameter"></param>
+        public void Undo(object parameter)
+        {
+            UndoManager.Undo();
+        }
+        /// <summary>
+        /// Returns true if undo can be done.
+        /// </summary>
+        /// <param name="property"></param>
+        /// <returns></returns>
+        private bool CanUndo(object property)
+        {
+            return UndoManager.CanUndo;
+        }
+        /// <summary>
+        /// Redo last action
+        /// </summary>
+        /// <param name="parameter"></param>
+        public void Redo(object parameter)
+        {
+            UndoManager.Redo();
+        }
+        /// <summary>
+        /// Returns true if redo can be done.
+        /// </summary>
+        /// <param name="property"></param>
+        /// <returns></returns>
+        private bool CanRedo(object property)
+        {
+            return UndoManager.CanRedo;
+        }
+        #endregion
+
+        /// <summary>
+        /// Recognizes selected tool from UI
+        /// </summary>
+        /// <param name="parameter"></param>
+        private void RecognizeTool(object parameter)
+        {
+            ToolType tool = (ToolType)Enum.Parse(typeof(ToolType), parameter.ToString());
+            SelectedTool = tool;
+        }
+
+        private void SetActiveTool(ToolType tool)
+        {
+            BitmapUtility.SelectedTool = ToolSet.Find(x=> x.ToolType == tool);
+        }
+        /// <summary>
+        /// When mouse is up stops recording changes.
+        /// </summary>
+        /// <param name="parameter"></param>
+        private void MouseUp(object parameter)
+        {
+            BitmapUtility.MouseController.StopRecordingMouseMovementChanges();
+        }
+
+        private void MouseDown(object parameter)
+        {
+            if(SelectedTool == ToolType.ColorPicker)
+            {
+                ExecuteColorPicker();
+            }
+            else if(Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                if (!BitmapUtility.MouseController.IsRecordingChanges)
+                {
+                    BitmapUtility.MouseController.StartRecordingMouseMovementChanges();
+                    BitmapUtility.MouseController.RecordMouseMovementChange(MousePositionConverter.CurrentCoordinates);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Method connected with command, it executes tool "activity"
+        /// </summary>
+        /// <param name="parameter"></param>
+        private void MouseMove(object parameter)
+        {
+            Coordinates cords = new Coordinates((int)MouseXOnCanvas, (int)MouseYOnCanvas);
+            MousePositionConverter.CurrentCoordinates = cords;
+
+                if (BitmapUtility.MouseController.IsRecordingChanges && Mouse.LeftButton == MouseButtonState.Pressed)
+                {
+                    BitmapUtility.MouseController.RecordMouseMovementChange(cords);
+                }
+        }
+
+
+
+        private void ExecuteColorPicker()
+        {
+            if (Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                using (var bitmap = new System.Drawing.Bitmap(1, 1))
+                {
+                    using (var graphics = System.Drawing.Graphics.FromImage(bitmap))
+                    {
+                        graphics.CopyFromScreen(MousePositionConverter.GetCursorPosition(), new System.Drawing.Point(0, 0), new System.Drawing.Size(1, 1));
+                    }
+                    var color = bitmap.GetPixel(0, 0);
+                    PrimaryColor = Color.FromArgb(color.A, color.R, color.G, color.B);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Generates new Layer and sets it as active one
+        /// </summary>
+        /// <param name="parameter"></param>
+        private void GenerateDrawArea(object parameter)
+        {
+            NewFileDialog newFile = new NewFileDialog();
+            if (newFile.ShowDialog())
+            {
+                BitmapUtility.Layers.Clear();
+                BitmapUtility.AddNewLayer("Base Layer", newFile.Width, newFile.Height, true);
+            }
+        }
+        #region SaveFile
+        /// <summary>
+        /// Generates export dialog or saves directly if save data is known.
+        /// </summary>
+        /// <param name="parameter"></param>
+        private void SaveFile(object parameter)
+        {
+            WriteableBitmap bitmap = BitmapUtility.GetCombinedLayersBitmap();
+            if (Exporter.SavePath == null)
+            {
+                Exporter.Export(FileType.PNG, bitmap, new Size(bitmap.PixelWidth, bitmap.PixelHeight));
+            }
+            else
+            {
+                Exporter.ExportWithoutDialog(FileType.PNG, bitmap);
+            }
+        }
+        /// <summary>
+        /// Returns true if file save is possible.
+        /// </summary>
+        /// <param name="property"></param>
+        /// <returns></returns>
+        private bool CanSave(object property)
+        {
+            return BitmapUtility.ActiveLayer != null;
+        }
+        #endregion
+
+        /// <summary>
+        /// Opens file from path.
+        /// </summary>
+        /// <param name="parameter"></param>
+        public void OpenFile(object parameter)
+        {
+            ImportFileDialog dialog = new ImportFileDialog();
+            if (dialog.ShowDialog())
+            {
+                BitmapUtility.Layers.Clear();
+                BitmapUtility.AddNewLayer("Base Layer", dialog.FileWidth, dialog.FileHeight, true);
+                BitmapUtility.ActiveLayer.LayerBitmap = Importer.ImportImage(dialog.FilePath, dialog.FileWidth, dialog.FileHeight);
+            }
+        }
+
+        /// <summary>
+        /// For now, shows not implemented info, lol.
+        /// </summary>
+        /// <param name="parameter"></param>
+        public void RecenterZoombox(object parameter)
+        {
+            MessageBox.Show("This feature is not implemented yet.", "Feature not implemented", MessageBoxButton.OK, MessageBoxImage.Information);
+        }
+
+        public void NewLayer(object parameter)
+        {
+            BitmapUtility.AddNewLayer($"New Layer {BitmapUtility.Layers.Count}", BitmapUtility.Layers[0].Width, BitmapUtility.Layers[0].Height);         
+        }
+
+        public bool CanCreateNewLayer(object parameter)
+        {
+                return BitmapUtility.Layers.Count > 0;
+        }
+    }
+}

+ 25 - 0
PixiEditor/Views/EditableTextBlock.xaml

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

+ 79 - 0
PixiEditor/Views/EditableTextBlock.xaml.cs

@@ -0,0 +1,79 @@
+using PixiEditor.Helpers;
+using System;
+using System.Collections.Generic;
+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
+{
+    /// <summary>
+    /// Interaction logic for EditableTextBlock.xaml
+    /// </summary>
+    public partial class EditableTextBlock : UserControl
+    {
+
+        public EditableTextBlock()
+        {
+            InitializeComponent();
+        }
+
+        public Visibility TextBlockVisibility
+        {
+            get { return (Visibility)GetValue(TextBlockVisibilityProperty); }
+            set { SetValue(TextBlockVisibilityProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for TextBlockVisibility.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty TextBlockVisibilityProperty =
+            DependencyProperty.Register("TextBlockVisibility", typeof(Visibility), typeof(EditableTextBlock), new PropertyMetadata(Visibility.Visible));
+
+
+
+
+        // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty TextProperty =
+            DependencyProperty.Register("Text", typeof(string), typeof(EditableTextBlock), new PropertyMetadata(default(string)));
+
+        public string Text
+        {
+            get { return (string)GetValue(TextProperty); }
+            set { SetValue(TextProperty, value); }
+        }
+
+
+        private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+            if(e.ClickCount == 2)
+            {
+                TextBlockVisibility = Visibility.Hidden;
+            }
+        }
+
+        private void TextBox_KeyDown(object sender, KeyEventArgs e)
+        {
+            if(e.Key == Key.Enter)
+            {
+                TextBlockVisibility = Visibility.Visible;
+            }
+        }
+
+        private void TextBox_LostFocus(object sender, RoutedEventArgs e)
+        {
+             TextBlockVisibility = Visibility.Visible;
+        }
+
+        private void textBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
+        {
+               TextBlockVisibility = Visibility.Visible;
+        }
+    }
+}

+ 0 - 0
PixiEditorDotNetCore3/Views/ImportFilePopup.xaml → PixiEditor/Views/ImportFilePopup.xaml


+ 0 - 0
PixiEditorDotNetCore3/Views/ImportFilePopup.xaml.cs → PixiEditor/Views/ImportFilePopup.xaml.cs


+ 1 - 1
PixiEditorDotNetCore3/Views/MainDrawingPanel.xaml → PixiEditor/Views/MainDrawingPanel.xaml

@@ -9,7 +9,7 @@
              xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
              mc:Ignorable="d" 
              d:DesignHeight="450" d:DesignWidth="800" x:Name="mainDrawingPanel">
-    <xctk:Zoombox IsAnimated="False" Name="Zoombox" KeepContentInBounds="True" Loaded="Zoombox_Loaded" ZoomModifiers="None" >
+    <xctk:Zoombox IsAnimated="False" Name="Zoombox" KeepContentInBounds="True" Loaded="Zoombox_Loaded" DragModifiers="Shift" ZoomModifiers="None" >
         <i:Interaction.Triggers>
             <i:EventTrigger EventName="MouseMove">
                 <i:InvokeCommandAction Command="{Binding MouseMoveCommand, ElementName=mainDrawingPanel, Mode=OneWay}"/>

+ 1 - 1
PixiEditorDotNetCore3/Views/MainDrawingPanel.xaml.cs → PixiEditor/Views/MainDrawingPanel.xaml.cs

@@ -1,4 +1,4 @@
-using PixiEditorDotNetCore3.Models;
+using PixiEditor.Models;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;

+ 85 - 47
PixiEditorDotNetCore3/Views/MainWindow.xaml → PixiEditor/Views/MainWindow.xaml

@@ -3,24 +3,27 @@
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:PixiEditor"
         xmlns:vm="clr-namespace:PixiEditor.ViewModels"
         xmlns:vws="clr-namespace:PixiEditor.Views"
         xmlns:helpers="clr-namespace:PixiEditor.Helpers"
+        xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
         xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
         xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
         xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
+        xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
+        xmlns:ui="clr-namespace:PixiEditor.Helpers.UI"      
         mc:Ignorable="d"
         Title="PixiEditor" Height="1000" Width="1600" Background="#FF252424" WindowStartupLocation="CenterScreen"  WindowState="Maximized" DataContext="{DynamicResource ViewModelMain}">
     <Window.Resources>
         <vm:ViewModelMain x:Key="ViewModelMain"/>
         <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
         <helpers:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter"/>
+        <converters:BoolToColorConverter x:Key="BoolToColorConverter"/>
     </Window.Resources>
     <Window.InputBindings>
         <KeyBinding 
             Modifiers="Ctrl" 
-            Key="N"
+            Key="N"                  
             Command="{Binding GenerateDrawAreaCommand}"/>
         <KeyBinding
             Modifiers="Ctrl"
@@ -34,44 +37,14 @@
             Modifiers="Ctrl"
             Key="Y"
             Command="{Binding RedoCommand}"/>
-        <KeyBinding            
-            Key="B"
-            Command="{Binding SelectToolCommand}"
-            CommandParameter="Pen"/>
-        <KeyBinding
-            Key="G"
-            Command="{Binding SelectToolCommand}"
-            CommandParameter="Bucket"/>
-        <KeyBinding
-            Key="L"
-            Command="{Binding SelectToolCommand}"
-            CommandParameter="Line"/>
-        <KeyBinding
-            Key="C"
-            Command="{Binding SelectToolCommand}"
-            CommandParameter="Circle"/>
-        <KeyBinding Key="R"
-                    Command="{Binding SelectToolCommand}"
-                    CommandParameter="Rectangle"/>
-        <KeyBinding Key="O"
-                    Command="{Binding SelectToolCommand}"
-                    CommandParameter="ColorPicker"/>
-        <KeyBinding Key="E"
-                    Command="{Binding SelectToolCommand}"
-                    CommandParameter="Earser"/>
-        <KeyBinding Key="U"
-                    Command="{Binding SelectToolCommand}"
-                    CommandParameter="Lighten"/>
-        <KeyBinding Key="O" Modifiers="Ctrl"
-                    Command="{Binding OpenFileCommand}"/>
 
 
     </Window.InputBindings>
     <Grid>
         <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="70*"/>
+            <ColumnDefinition Width="60*"/>
             <ColumnDefinition Width="1420*"/>
-            <ColumnDefinition Width="110*"/>
+            <ColumnDefinition Width="150*"/>
         </Grid.ColumnDefinitions>
         <Grid.RowDefinitions>
             <RowDefinition Height="30*"/>
@@ -80,7 +53,7 @@
         </Grid.RowDefinitions>
 
         <Border Grid.ColumnSpan="3" Background="#FF363434" Grid.Row="0" Height="30"/>
-        <WrapPanel Grid.ColumnSpan="2" Grid.RowSpan="2" Panel.ZIndex="100">
+        <WrapPanel Grid.ColumnSpan="2" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Panel.ZIndex="100">
             <vws:MenuButton Text="File" Margin="0,0,-140,0">
                 <vws:MenuButton.Item>
                     <StackPanel>
@@ -107,17 +80,17 @@
                 </vws:MenuButton.Item>
             </vws:MenuButton>
         </WrapPanel>
-        <Grid Grid.Column="1" Grid.Row="1" Background="DimGray" Margin="0,5,0,0">
+        <Grid Grid.Column="1" Grid.Row="1" Background="#303030" Margin="0,5,5,0">
             <Grid>
                 <vws:MainDrawingPanel CenterOnStart="True">
                     <vws:MainDrawingPanel.Item>
-                        <Canvas Width="{Binding ActiveLayer.Width}" Height="{Binding ActiveLayer.Height}" VerticalAlignment="Center" HorizontalAlignment="Center">
+                        <Canvas Width="{Binding BitmapUtility.ActiveLayer.Width}" Height="{Binding BitmapUtility.ActiveLayer.Height}" VerticalAlignment="Center" HorizontalAlignment="Center">
                             <i:Interaction.Triggers>
                                 <i:EventTrigger EventName="MouseMove">
-                                    <i:InvokeCommandAction Command="{Binding MouseMoveOrClickCommand}"/>
+                                    <i:InvokeCommandAction Command="{Binding MouseMoveCommand}"/>
                                 </i:EventTrigger>
                                 <i:EventTrigger EventName="MouseDown">
-                                    <i:InvokeCommandAction Command="{Binding MouseMoveOrClickCommand}"/>
+                                    <i:InvokeCommandAction Command="{Binding MouseDownCommand}"/>
                                 </i:EventTrigger>
                                 <i:EventTrigger EventName="MouseUp">
                                     <i:InvokeCommandAction Command="{Binding MouseUpCommand}"/>
@@ -126,15 +99,27 @@
                             <i:Interaction.Behaviors>
                                 <behaviors:MouseBehaviour MouseX="{Binding MouseXOnCanvas, Mode=OneWayToSource}" MouseY="{Binding MouseYOnCanvas, Mode=OneWayToSource}"/>
                             </i:Interaction.Behaviors>
-                            <Image Source="/Images/transparentbg.png" Height="{Binding ActiveLayer.Height}" Width="{Binding ActiveLayer.Width}" Opacity="0.2" Stretch="UniformToFill"/>
-                            <ContentControl Content="{Binding ActiveImage}"/>
+                            <Image Source="/Images/transparentbg.png" Height="{Binding BitmapUtility.ActiveLayer.Height}" Width="{Binding BitmapUtility.ActiveLayer.Width}" Opacity="0.9" Stretch="UniformToFill"/>
+                            <Image Source="{Binding BitmapUtility.PreviewLayer.LayerBitmap}" Panel.ZIndex="2" RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform" Width="{Binding BitmapUtility.PreviewLayer.Width}" Height="{Binding BitmapUtility.PreviewLayer.Height}"/>
+                            <ItemsControl ItemsSource="{Binding BitmapUtility.Layers}">
+                                <ItemsControl.ItemsPanel>
+                                    <ItemsPanelTemplate>
+                                        <Grid/>
+                                    </ItemsPanelTemplate>
+                                </ItemsControl.ItemsPanel>
+                                <ItemsControl.ItemTemplate>
+                                    <DataTemplate>
+                                        <Image Source="{Binding LayerBitmap}" Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}" RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform" Width="{Binding Width}" Height="{Binding Height}"/>
+                                    </DataTemplate>
+                                </ItemsControl.ItemTemplate>
+                            </ItemsControl>
                         </Canvas>
                     </vws:MainDrawingPanel.Item>
                 </vws:MainDrawingPanel>
             </Grid>
         </Grid>
 
-        <StackPanel Grid.Row="1" Grid.Column="0" Margin="0,5,5,0" Background="#363434" Grid.RowSpan="2">
+        <StackPanel Grid.Row="1" Grid.Column="0" Margin="0,5,5,0" Background="#404040">
             <TextBox Style="{StaticResource DarkTextBoxStyle}" Margin="0,10,0,0" Text="{Binding ToolSize, Mode=TwoWay,Converter={StaticResource ToolSizeToIntConverter}}" TextAlignment="Center" MaxLength="4">
                 <i:Interaction.Behaviors>
                     <behaviors:TextBoxNumericFinisherBehavior/>
@@ -182,20 +167,73 @@
             </Button>
         </StackPanel>
 
-        <Grid VerticalAlignment="Bottom" HorizontalAlignment="Center" Width="60" Grid.Column="0" Grid.Row="1" Margin="0,0,0,0" Height="65">
-            <Rectangle Height="40" Width="40" HorizontalAlignment="Left" VerticalAlignment="Top" Stroke="Black" StrokeThickness="1" Panel.ZIndex="1">
+        <DockPanel Grid.Column="2" Grid.Row="1" Background="#404040">
+            <Grid DockPanel.Dock="Top" HorizontalAlignment="Center" Width="100"  Margin="0,20,0,0" Height="100">
+            <Rectangle Height="70" Width="70" HorizontalAlignment="Left" VerticalAlignment="Top" Stroke="Black" StrokeThickness="1" Panel.ZIndex="1">
                 <Rectangle.Fill>
                     <SolidColorBrush Color="{Binding PrimaryColor, Mode=OneWay}"/>
                 </Rectangle.Fill>
             </Rectangle>
-            <xctk:ColorPicker Width="40" Panel.ZIndex="2" Height="40" VerticalAlignment="Top" HorizontalAlignment="Left" UsingAlphaChannel="True" AvailableColorsSortingMode="Alphabetical" ShowDropDownButton="False" Background="Transparent" BorderThickness="0" ShowRecentColors="True" SelectedColor="{Binding PrimaryColor, Mode=TwoWay}"></xctk:ColorPicker>
-            <Rectangle Height="40" Width="40" HorizontalAlignment="Right" VerticalAlignment="Bottom" Stroke="Black" StrokeThickness="1" Margin="0,0,4,5">
+            <xctk:ColorPicker Width="70" Panel.ZIndex="2" Height="70" VerticalAlignment="Top" HorizontalAlignment="Left" UsingAlphaChannel="True" AvailableColorsSortingMode="Alphabetical" ShowDropDownButton="False" Background="Transparent" BorderThickness="0" ShowRecentColors="True" SelectedColor="{Binding PrimaryColor, Mode=TwoWay}"></xctk:ColorPicker>
+            <Rectangle Height="70" Width="70" HorizontalAlignment="Right" VerticalAlignment="Bottom" Stroke="Black" StrokeThickness="1" Margin="0,0,4,5">
                 <Rectangle.Fill>
                     <SolidColorBrush Color="{Binding SecondaryColor, Mode=OneWay}"/>
                 </Rectangle.Fill>
             </Rectangle>
-            <xctk:ColorPicker Width="40" Height="40" HorizontalAlignment="Right" VerticalAlignment="Bottom" UsingAlphaChannel="True" AvailableColorsSortingMode="Alphabetical" ShowDropDownButton="False" Background="Transparent" BorderThickness="0" ShowRecentColors="True" Margin="0,0,4,5" SelectedColor="{Binding SecondaryColor, Mode=TwoWay}"/>
+            <xctk:ColorPicker Width="70" Height="70" HorizontalAlignment="Right" VerticalAlignment="Bottom" UsingAlphaChannel="True" AvailableColorsSortingMode="Alphabetical" ShowDropDownButton="False" Background="Transparent" BorderThickness="0" ShowRecentColors="True" Margin="0,0,4,5" SelectedColor="{Binding SecondaryColor, Mode=TwoWay}"/>
         </Grid>
 
+            <xcad:DockingManager Grid.Column="2" Grid.Row="1" DockPanel.Dock="Top">
+            <xcad:DockingManager.Style>
+                <Style TargetType="xcad:DockingManager">
+                    <Setter Property="Foreground" Value="Snow"/>
+                </Style>
+            </xcad:DockingManager.Style>
+            <xcad:LayoutRoot x:Name="LayoutRoot">
+                <xcad:LayoutPanel Orientation="Vertical">
+                    <xcad:LayoutAnchorablePane>
+                        <xcad:LayoutAnchorable ContentId="layers" Title="Layers" CanHide="False" CanClose="False" CanAutoHide="False" CanDockAsTabbedDocument="False" CanFloat="True">
+                            <StackPanel  Orientation="Vertical">
+                                <Button Command="{Binding NewLayerCommand}" Height="30" Content="New Layer" HorizontalAlignment="Stretch" Margin="5" Style="{StaticResource DarkRoundButton}"/>
+                                    <ItemsControl AllowDrop="True" ItemsSource="{Binding BitmapUtility.Layers}" x:Name="layersItemsControl" AlternationCount="9999">
+                                        <ItemsControl.ItemsPanel>
+                                            <ItemsPanelTemplate>
+                                                <ui:ReversedOrderStackPanel Orientation="Vertical"/>
+                                            </ItemsPanelTemplate>
+                                        </ItemsControl.ItemsPanel>
+                                        <ItemsControl.ItemTemplate>
+                                            <DataTemplate>
+                                                <Border BorderThickness="1" BorderBrush="Gray" MinWidth="60" Background="{Binding IsActive, Mode=TwoWay, Converter={StaticResource BoolToColorConverter}}">
+                                                    <DockPanel>
+                                                        <CheckBox VerticalAlignment="Center" Command="{Binding Path=DataContext.ReloadImageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" IsThreeState="False" IsChecked="{Binding Path=IsVisible}"/>
+                                                        <Button Background="Transparent" Style="{StaticResource BaseDarkButton}" FontSize="16" DockPanel.Dock="Left" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, 
+                            Path=DataContext.SetActiveLayerCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                            Path=(ItemsControl.AlternationIndex)}">
+                                                            <Button.ContextMenu>
+                                                                <ContextMenu>
+                                                                    <MenuItem Header="Delete" Command="{Binding DeleteLayerCommand, Source={StaticResource ViewModelMain}}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                            Path=(ItemsControl.AlternationIndex)}"/>
+                                                                    <MenuItem Header="Move to front" Command="{Binding MoveToFrontCommand, Source={StaticResource ViewModelMain}}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                            Path=(ItemsControl.AlternationIndex)}"/>
+                                                                    <MenuItem Header="Move to back" Command="{Binding MoveToBackCommand, Source={StaticResource ViewModelMain}}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                            Path=(ItemsControl.AlternationIndex)}"/>
+                                                                </ContextMenu>
+                                                            </Button.ContextMenu>
+                                                            <vws:EditableTextBlock x:Name="layerNameTB" Text="{Binding Name, Mode=TwoWay}">
+                                                            </vws:EditableTextBlock>
+                                                        </Button>
+                                                    </DockPanel>
+                                                </Border>
+                                            </DataTemplate>
+                                    </ItemsControl.ItemTemplate>
+                                </ItemsControl>
+                            </StackPanel>
+                        </xcad:LayoutAnchorable>
+                    </xcad:LayoutAnchorablePane>
+                </xcad:LayoutPanel>
+            </xcad:LayoutRoot>
+        </xcad:DockingManager>
+        </DockPanel>
+
     </Grid>
 </Window>

+ 1 - 1
PixiEditorDotNetCore3/Views/MainWindow.xaml.cs → PixiEditor/Views/MainWindow.xaml.cs

@@ -25,6 +25,6 @@ namespace PixiEditor
         public MainWindow()
         {
             InitializeComponent();
-        }
+        }
     }
 }

+ 0 - 0
PixiEditorDotNetCore3/Views/MenuButton.xaml → PixiEditor/Views/MenuButton.xaml


+ 0 - 0
PixiEditorDotNetCore3/Views/MenuButton.xaml.cs → PixiEditor/Views/MenuButton.xaml.cs


+ 54 - 0
PixiEditor/Views/NewFilePopup.xaml

@@ -0,0 +1,54 @@
+<Window x:Class="PixiEditor.Views.NewFilePopup"
+             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="http://schemas.microsoft.com/expression/2010/interactivity"
+             xmlns:vm="clr-namespace:PixiEditor.ViewModels"
+             xmlns:helpers="clr-namespace:PixiEditor.Helpers.Behaviours"
+             mc:Ignorable="d" 
+             d:DesignHeight="600" d:DesignWidth="450" DataContext="{DynamicResource NewFileMenuViewModel}" WindowStyle="None" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Height="600" Width="450" Name="newFilePopup">
+    <Window.Resources>
+        <vm:NewFileMenuViewModel x:Key="NewFileMenuViewModel"/>
+    </Window.Resources>
+    <Border BorderBrush="Black" BorderThickness="1">
+        <Grid Background="#404040">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="25*"/>
+                <RowDefinition Height="577*"/>
+            </Grid.RowDefinitions>
+            <Grid Grid.Row="0" Background="#303030">
+                <i:Interaction.Triggers>
+                    <i:EventTrigger EventName="MouseDown">
+                        <i:InvokeCommandAction Command="{Binding DragMoveCommand}"/>
+                    </i:EventTrigger>
+                </i:Interaction.Triggers>
+                <Button Width="20" Height="20" VerticalAlignment="Top" HorizontalAlignment="Right" BorderThickness="0" Command="{Binding CloseCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
+                    <Button.Background>
+                        <ImageBrush ImageSource="/Images/Cross.png" Stretch="Uniform"/>
+                    </Button.Background>
+                </Button>
+            </Grid>
+            <StackPanel HorizontalAlignment="Center" Margin="0,50,0,0" Background="#303030" VerticalAlignment="Top" Grid.Row="1" Width="350" Height="150">
+                <DockPanel Margin="50,35,0,0">
+                    <TextBlock Height="30" Foreground="Snow" Text="Height:" TextAlignment="Center" FontSize="16" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                    <TextBox Height="30" Width="150" Style="{StaticResource DarkTextBoxStyle}" FontSize="16" HorizontalAlignment="Left" TextAlignment="Center" Margin="5,0,0,0" Text="{Binding ElementName=newFilePopup, Path=FileHeight, Mode=TwoWay}" MaxLength="4">
+                        <i:Interaction.Behaviors>
+                            <helpers:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9./-]+$" MaxLength="4"/>
+                        </i:Interaction.Behaviors>
+                    </TextBox>
+                </DockPanel>
+                <DockPanel Margin="50,10,0,0">
+                    <TextBlock Height="30" Foreground="Snow" Text="Width:" TextAlignment="Center" FontSize="16" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                    <TextBox Height="30" Width="150" Style="{StaticResource DarkTextBoxStyle}" FontSize="16" HorizontalAlignment="Left" Margin="10,0,0,0" TextAlignment="Center" Text="{Binding ElementName=newFilePopup, Path=FileWidth, Mode=TwoWay}" MaxLength="4">
+                        <i:Interaction.Behaviors>
+                            <helpers:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9./-]+$" MaxLength="4"/>
+                        </i:Interaction.Behaviors>
+                    </TextBox>
+                </DockPanel>
+            </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" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
+        </Grid>
+    </Border>
+</Window>

+ 0 - 0
PixiEditorDotNetCore3/Views/NewFilePopup.xaml.cs → PixiEditor/Views/NewFilePopup.xaml.cs


+ 0 - 0
PixiEditorDotNetCore3/Views/SaveFilePopup.xaml → PixiEditor/Views/SaveFilePopup.xaml


+ 0 - 0
PixiEditorDotNetCore3/Views/SaveFilePopup.xaml.cs → PixiEditor/Views/SaveFilePopup.xaml.cs


BIN
PixiEditorDotNetCore3/Images/transparentbg.png


+ 0 - 107
PixiEditorDotNetCore3/Models/IO/Exporter.cs

@@ -1,107 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using Microsoft.Win32;
-using PixiEditorDotNetCore3.Models.Dialogs;
-using PixiEditorDotNetCore3.Models.Enums;
-
-namespace PixiEditorDotNetCore3.Models.IO
-{
-    public class Exporter
-    {
-        public static string _savePath = null;
-        public static Size _fileDimensions;
-
-        /// <summary>
-        /// Creates ExportFileDialog to get width, height and path of file.
-        /// </summary>
-        /// <param name="type">Type of file to be saved in.</param>
-        /// <param name="imageToSave">Image to be saved as file.</param>
-        public static void Export(FileType type, Image imageToSave, Size fileDimensions)
-        {
-            ExportFileDialog info = new ExportFileDialog(fileDimensions);
-            //If OK on dialog has been clicked
-            if (info.ShowDialog() == true)
-            {
-                //If sizes are incorrect
-                if (info.FileWidth < imageToSave.Width || info.FileHeight < imageToSave.Height)
-                {
-                    MessageBox.Show("Incorrect height or width value", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
-                    return;
-                }
-
-                _savePath = info.FilePath;
-                _fileDimensions = new Size(info.FileWidth, info.FileHeight);
-                SaveAsPng(info.FilePath, (int)imageToSave.Width, (int)imageToSave.Height, info.FileHeight, info.FileWidth, imageToSave);
-            }
-        }
-
-        /// <summary>
-        /// Saves file with info that has been recieved from ExportFileDialog before, doesn't work without before Export() usage.
-        /// </summary>
-        /// <param name="type">Type of file</param>
-        /// <param name="imageToSave">Image to be saved as file.</param>
-        public static void ExportWithoutDialog(FileType type, Image imageToSave)
-        {
-            try
-            {
-                SaveAsPng(_savePath, (int)imageToSave.Width, (int)imageToSave.Height, (int)_fileDimensions.Height, (int)_fileDimensions.Width, imageToSave);
-            }
-            catch (Exception ex)
-            {
-                MessageBox.Show(ex.Message);
-            }
-        }
-        /// <summary>
-        /// Saves image to PNG file
-        /// </summary>
-        /// <param name="savePath">Save file path</param>
-        /// <param name="originalWidth">Original width of image</param>
-        /// <param name="originalHeight">Original height of image</param>
-        /// <param name="exportWidth">File width</param>
-        /// <param name="exportHeight">File height</param>
-        /// <param name="imageToExport">Image to be saved</param>
-        private static void SaveAsPng(string savePath, int originalWidth, int originalHeight, int exportWidth, int exportHeight, Image imageToExport)
-        {
-            Rect bounds = VisualTreeHelper.GetDescendantBounds(imageToExport);
-            double dpi = 96d;
-
-
-            RenderTargetBitmap rtb = new RenderTargetBitmap(originalWidth * (exportWidth / originalWidth), originalHeight * (exportHeight / originalHeight), dpi, dpi, PixelFormats.Default);
-
-
-            DrawingVisual dv = new DrawingVisual();
-            using (DrawingContext dc = dv.RenderOpen())
-            {
-                VisualBrush vb = new VisualBrush(imageToExport);
-                dc.DrawRectangle(vb, null, new Rect(new Point(), new Size(originalWidth * (exportWidth / originalWidth), originalHeight * (exportHeight / originalHeight))));
-            }
-
-            rtb.Render(dv);
-            BitmapEncoder pngEncoder = new PngBitmapEncoder();
-            pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
-
-            try
-            {
-                MemoryStream ms = new MemoryStream();
-
-                pngEncoder.Save(ms);
-                ms.Close();
-
-                File.WriteAllBytes(savePath, ms.ToArray());
-            }
-            catch (Exception err)
-            {
-                MessageBox.Show(err.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
-            }
-        }
-    }
-}

+ 0 - 73
PixiEditorDotNetCore3/Models/Layers/Layer.cs

@@ -1,73 +0,0 @@
-using PixiEditorDotNetCore3.Models.Tools;
-using System;
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-
-namespace PixiEditorDotNetCore3.Models.Layers
-{
-    public class Layer : BasicLayer
-    {
-        private WriteableBitmap _layerBitmap;
-
-        public WriteableBitmap LayerBitmap
-        {
-            get { return _layerBitmap; }
-            set
-            {
-                _layerBitmap = value;
-                RaisePropertyChanged("LayerBitmap");
-            }
-        }
-
-        public Layer(int width, int height)
-        {
-            Layer layer = LayerGenerator.Generate(width, height);
-            LayerBitmap = layer.LayerBitmap;
-            Width = width;
-            Height = height;
-        }
-
-
-        public Layer(WriteableBitmap layerBitmap)
-        {
-            LayerBitmap = layerBitmap;
-            Width = (int)layerBitmap.Width;
-            Height = (int)layerBitmap.Height;
-        }
-
-        public void ApplyPixels(BitmapPixelChanges pixels, Color color)
-        {
-            LayerBitmap.Lock();
-
-            foreach (var coords in pixels.ChangedCoordinates)
-            {
-                LayerBitmap.SetPixel(Math.Clamp(coords.X, 0, Width - 1), Math.Clamp(coords.Y, 0, Height - 1),
-                    color);
-            }
-
-            LayerBitmap.Unlock();
-        }
-
-       
-        public byte[] ConvertBitmapToBytes()
-        {            
-            LayerBitmap.Lock();
-            byte[] byteArray = LayerBitmap.ToByteArray();
-            LayerBitmap.Unlock();
-            return byteArray;
-        }
-
-        public byte[] ConvertBitmapToBytes(WriteableBitmap bitmap)
-        {
-            bitmap.Lock();
-            byte[] byteArray = bitmap.ToByteArray();
-            bitmap.Unlock();
-            return byteArray;
-        }
-
-    }
-}

+ 0 - 39
PixiEditorDotNetCore3/Models/Position/Coordinates.cs

@@ -1,39 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace PixiEditorDotNetCore3.Models.Position
-{
-    public class Coordinates
-    {
-        private int _X;
-
-        public int X
-        {
-            get { return _X; }
-            set { _X = value; }
-        }
-
-        private int _Y;
-
-        public int Y
-        {
-            get { return _Y; }
-            set { _Y = value; }
-        }
-
-        public Coordinates()
-        {
-
-        }
-
-        public Coordinates(int x, int y)
-        {
-            X = x;
-            Y = y;
-        }
-
-    }
-}

+ 0 - 20
PixiEditorDotNetCore3/Models/Tools/BitmapPixelChanges.cs

@@ -1,20 +0,0 @@
-using PixiEditorDotNetCore3.Models.Position;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Windows.Media;
-
-namespace PixiEditorDotNetCore3.Models.Tools
-{
-    public struct BitmapPixelChanges
-    {
-        public Coordinates[] ChangedCoordinates { get; set; }
-        public Color PixelsColor { get; set; }
-
-        public BitmapPixelChanges(Coordinates[] changedCoordinates, Color color)
-        {
-            ChangedCoordinates = changedCoordinates;
-            PixelsColor = color;
-        }
-    }
-}

+ 0 - 43
PixiEditorDotNetCore3/Models/Tools/ShapeTool.cs

@@ -1,43 +0,0 @@
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Windows.Media;
-
-namespace PixiEditorDotNetCore3.Models.Tools
-{
-    public abstract class ShapeTool : Tool
-    {
-        public override abstract ToolType ToolType { get; }
-
-        public ShapeTool()
-        {
-            ExecutesItself = true;
-        }
-
-        public abstract override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize);
-
-        protected DoubleCords CalculateCoordinatesForShapeRotation(Coordinates startingCords)
-        {
-            Coordinates currentCoordinates = MousePositionConverter.CurrentCoordinates;
-
-            if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
-            {
-                return new DoubleCords(new Coordinates(currentCoordinates.X, currentCoordinates.Y), new Coordinates(startingCords.X, startingCords.Y));
-            }
-            else if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
-            {
-                return new DoubleCords(new Coordinates(startingCords.X, startingCords.Y), new Coordinates(currentCoordinates.X, currentCoordinates.Y));
-            }
-            else if (startingCords.Y > currentCoordinates.Y)
-            {
-                return new DoubleCords(new Coordinates(startingCords.X, currentCoordinates.Y), new Coordinates(currentCoordinates.X, startingCords.Y));
-            }
-            else
-            {
-                return new DoubleCords(new Coordinates(currentCoordinates.X, startingCords.Y), new Coordinates(startingCords.X, currentCoordinates.Y));
-            }
-        }
-    }
-}

+ 0 - 30
PixiEditorDotNetCore3/Models/Tools/Tools/CircleTool.cs

@@ -1,30 +0,0 @@
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-
-namespace PixiEditorDotNetCore3.Models.Tools.Tools
-{
-    public class CircleTool : ShapeTool
-    {
-        public override ToolType ToolType => ToolType.Circle;
-
-        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
-        {
-            CreateCircle(layer, startingCoords, color, toolSize);
-            return new BitmapPixelChanges();
-        }
-
-        public void CreateCircle(Layer layer, Coordinates coordinates, Color color, int size)
-        {
-            DoubleCords calculatedCords = CalculateCoordinatesForShapeRotation(coordinates);
-            layer.LayerBitmap.DrawEllipse(calculatedCords.Coords1.X, calculatedCords.Coords1.Y, calculatedCords.Coords2.X,
-                calculatedCords.Coords2.Y, color);
-        }
-
-        
-    }
-}

+ 0 - 24
PixiEditorDotNetCore3/Models/Tools/Tools/LineTool.cs

@@ -1,24 +0,0 @@
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-
-namespace PixiEditorDotNetCore3.Models.Tools.Tools
-{
-    public class LineTool : ShapeTool
-    {
-        public override ToolType ToolType => ToolType.Line;
-
-        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
-        {
-            CreateLine(layer, startingCoords, color, toolSize);
-            return new BitmapPixelChanges();
-        }
-
-        public void CreateLine(Layer layer, Coordinates coordinates, Color color, int size)
-        {
-            layer.LayerBitmap.DrawLineBresenham(coordinates.X, coordinates.Y, MousePositionConverter.CurrentCoordinates.X,
-                MousePositionConverter.CurrentCoordinates.Y, color);
-        }
-    }
-}

+ 0 - 24
PixiEditorDotNetCore3/Models/Tools/Tools/RectangleTool.cs

@@ -1,24 +0,0 @@
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-
-namespace PixiEditorDotNetCore3.Models.Tools.Tools
-{
-    public class RectangleTool : ShapeTool
-    {
-        public override ToolType ToolType => ToolType.Rectangle;
-
-        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
-        {
-            CreateRectangle(layer,startingCoords,color,toolSize);
-            return new BitmapPixelChanges();
-        }
-
-        public void CreateRectangle(Layer layer, Coordinates startingCoords, Color color, int toolSize)
-        {
-            DoubleCords coordinates = CalculateCoordinatesForShapeRotation(startingCoords);
-            layer.LayerBitmap.DrawRectangle(coordinates.Coords1.X, coordinates.Coords1.Y, coordinates.Coords2.X, coordinates.Coords2.Y, color);
-        }
-    }
-}

+ 0 - 126
PixiEditorDotNetCore3/Models/Tools/ToolsManager.cs

@@ -1,126 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Media;
-using System.Windows.Input;
-using System.Windows.Media.Imaging;
-using PixiEditor.ViewModels;
-using System.Timers;
-using System.Windows.Threading;
-using System.Threading;
-using PixiEditorDotNetCore3.Models.Layers;
-using PixiEditorDotNetCore3.Models.Position;
-
-namespace PixiEditorDotNetCore3.Models.Tools
-{
-    public class ToolsManager
-    {
-        public List<Tool> Tools { get; set; } = new List<Tool>();
-        public Tool SelectedTool { get; private set; }
-        private bool _toolRecievedData = false;
-
-        private System.Timers.Timer _loopTimer;
-        private Layer _layer;
-        private WriteableBitmap _clonedBitmap;
-        private Coordinates _startCoordinates;
-        private Color _color;
-        private int _toolSzie;
-
-        public ToolsManager(List<Tool> tools)
-        {
-            Tools = tools;
-            _loopTimer = new System.Timers.Timer
-            {
-                Interval = 15,
-                Enabled = false,
-                AutoReset = true
-            };
-            _loopTimer.Elapsed += LoopTimer_Elapsed;
-        }
-
-        private void LoopTimer_Elapsed(object sender, ElapsedEventArgs e)
-        {
-            Application.Current.Dispatcher.Invoke(() =>
-            {
-                _layer.LayerBitmap.Lock();
-
-                if (_clonedBitmap != null)
-                {
-                    RestoreLastBitmap();
-                }
-
-                BitmapPixelChanges changes = SelectedTool.Use(_layer, _startCoordinates, _color, _toolSzie);
-
-                if (!SelectedTool.ExecutesItself)
-                {
-                    _layer.ApplyPixels(changes, changes.PixelsColor);
-                }
-                _layer.LayerBitmap.Unlock();
-
-            });
-        }
-
-        private void RestoreLastBitmap()
-        {
-            _layer.LayerBitmap.Clear();
-            _layer.LayerBitmap.Blit(new Rect(new Size(_layer.Width, _layer.Height)), _clonedBitmap, new Rect(new Size(_layer.Width, _layer.Height)), WriteableBitmapExtensions.BlendMode.Additive);
-        }
-
-        public void SetTool(ToolType tool)
-        {
-            SelectedTool = Tools.Find(x => x.ToolType == tool);
-        }
-
-        public void StopExectuingTool()
-        {
-            _loopTimer.Enabled = false;
-            _toolRecievedData = false;
-            _clonedBitmap = null;
-        }
-
-        private void StartTimer()
-        {
-            _toolRecievedData = true;
-            _loopTimer.Enabled = true;
-        }
-
-        private void CloneBitmapIfToolIsShape()
-        {
-            if (SelectedTool.GetType().BaseType == typeof(ShapeTool))
-            {
-                _clonedBitmap = _layer.LayerBitmap.Clone();
-            }
-        }
-
-        /// <summary>
-        /// Executes tool action
-        /// </summary>
-        /// <param name="layer">Layer to operate on.</param>
-        /// <param name="startingCoords">Click coordinates.</param>
-        /// <param name="color">Color that tool will use.</param>
-        /// <param name="toolSize">Size/thickness of tool</param>
-        /// <param name="tool">Tool to execute</param>
-        /// <returns></returns>
-        public void ExecuteTool(Layer layer, Coordinates startingCoords, Color color, int toolSize)
-        {
-            if (toolSize < 1)
-                return;
-
-            if (_toolRecievedData == false || (_toolRecievedData == true && SelectedTool.GetType().BaseType != typeof(ShapeTool)))
-            {
-                _startCoordinates = startingCoords;
-                _layer = layer;
-                _color = color;
-                _toolSzie = toolSize;
-            }
-
-            if (_loopTimer.Enabled == false)
-            {
-                StartTimer();
-                CloneBitmapIfToolIsShape();
-            }
-        }
-
-    }
-}

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません