Browse Source

Added undo to move tool and select tool

flabbet 5 years ago
parent
commit
60772d45bc

+ 2 - 0
PixiEditor/Models/Controllers/BitmapManager.cs

@@ -146,6 +146,7 @@ namespace PixiEditor.Models.Controllers
 
 
         private void MouseController_StartedRecordingChanges(object sender, EventArgs e)
         private void MouseController_StartedRecordingChanges(object sender, EventArgs e)
         {
         {
+            SelectedTool.OnMouseDown();
             if (PreviewLayer != null)
             if (PreviewLayer != null)
             {
             {
                 PreviewLayer.Clear();
                 PreviewLayer.Clear();
@@ -154,6 +155,7 @@ namespace PixiEditor.Models.Controllers
 
 
         private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
         private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
         {
         {
+            SelectedTool.OnMouseUp();
             if (IsOperationTool(SelectedTool) && (SelectedTool as BitmapOperationTool).RequiresPreviewLayer)
             if (IsOperationTool(SelectedTool) && (SelectedTool as BitmapOperationTool).RequiresPreviewLayer)
             {
             {
                 BitmapOperations.StopAction();
                 BitmapOperations.StopAction();

+ 6 - 0
PixiEditor/Models/Controllers/ClipboardController.cs

@@ -69,6 +69,12 @@ namespace PixiEditor.Models.Controllers
             }
             }
         }
         }
 
 
+        public bool IsImageInClipboard()
+        {
+            DataObject dao = (DataObject)Clipboard.GetDataObject();
+            return dao.GetDataPresent("PNG") || dao.GetDataPresent(DataFormats.Dib) || dao.GetDataPresent(DataFormats.Bitmap);
+        }
+
         private void AddImageToLayers(WriteableBitmap image)
         private void AddImageToLayers(WriteableBitmap image)
         {
         {
             Document doc = ViewModelMain.Current.BitmapManager.ActiveDocument;
             Document doc = ViewModelMain.Current.BitmapManager.ActiveDocument;

+ 9 - 6
PixiEditor/Models/Images/BitmapUtils.cs

@@ -35,16 +35,19 @@ namespace PixiEditor.Models.Images
         {
         {
             WriteableBitmap finalBitmap = bitmaps[0].Clone();
             WriteableBitmap finalBitmap = bitmaps[0].Clone();
             finalBitmap.Lock();
             finalBitmap.Lock();
-            for (int i = 1; i < bitmaps.Length; i++)
+            using (finalBitmap.GetBitmapContext())
             {
             {
-                for (int y = 0; y < finalBitmap.Height; y++)
+                for (int i = 1; i < bitmaps.Length; i++)
                 {
                 {
-                    for (int x = 0; x < finalBitmap.Width; x++)
+                    for (int y = 0; y < finalBitmap.Height; y++)
                     {
                     {
-                        System.Windows.Media.Color color = bitmaps[i].GetPixel(x, y);
-                        if (color.A != 0 || color.R != 0 || color.B != 0 || color.G != 0)
+                        for (int x = 0; x < finalBitmap.Width; x++)
                         {
                         {
-                            finalBitmap.SetPixel(x, y, color);
+                            System.Windows.Media.Color color = bitmaps[i].GetPixel(x, y);
+                            if (color.A != 0 || color.R != 0 || color.B != 0 || color.G != 0)
+                            {
+                                finalBitmap.SetPixel(x, y, color);
+                            }
                         }
                         }
                     }
                     }
                 }
                 }

+ 1 - 0
PixiEditor/Models/Tools/BitmapOperationTool.cs

@@ -8,6 +8,7 @@ namespace PixiEditor.Models.Tools
     {
     {
         public abstract BitmapPixelChanges Use(Layer layer, Coordinates[] mouseMove, Color color);
         public abstract BitmapPixelChanges Use(Layer layer, Coordinates[] mouseMove, Color color);
         public bool RequiresPreviewLayer { get; set; }
         public bool RequiresPreviewLayer { get; set; }
+        public bool UseDefaultUndoMethod { get; set; } = true;
 
 
     }
     }
 }
 }

+ 3 - 0
PixiEditor/Models/Tools/Tool.cs

@@ -8,6 +8,9 @@ namespace PixiEditor.Models.Tools
     {
     {
         public abstract ToolType ToolType { get; }
         public abstract ToolType ToolType { get; }
         public string ImagePath => $"/Images/{ToolType}Image.png";
         public string ImagePath => $"/Images/{ToolType}Image.png";
+        public virtual void OnMouseDown() { }
+        public virtual void OnMouseUp() { }
+        public virtual void AfterAddedUndo() { }
         public bool HideHighlight { get; set; } = false;
         public bool HideHighlight { get; set; } = false;
         public string Tooltip { get; set; }
         public string Tooltip { get; set; }
 
 

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

@@ -1,4 +1,6 @@
-using PixiEditor.Models.DataHolders;
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
@@ -19,6 +21,7 @@ namespace PixiEditor.Models.Tools.Tools
         private bool _clearedPixels = false;
         private bool _clearedPixels = false;
         private Coordinates[] _currentSelection;
         private Coordinates[] _currentSelection;
         private bool _updateViewModelSelection = true;
         private bool _updateViewModelSelection = true;
+        private BitmapPixelChanges _clearedPixelsChange;
 
 
         public MoveTool()
         public MoveTool()
         {
         {
@@ -26,12 +29,24 @@ namespace PixiEditor.Models.Tools.Tools
             Cursor = Cursors.Arrow;
             Cursor = Cursors.Arrow;
             HideHighlight = true;
             HideHighlight = true;
             RequiresPreviewLayer = true;
             RequiresPreviewLayer = true;
+            UseDefaultUndoMethod = true;
+        }
+
+        public override void AfterAddedUndo()
+        {
+            //Injecting to default undo system change custom changes made by this tool
+            BitmapPixelChanges beforeMovePixels = BitmapPixelChanges.FromArrays(_startSelection, _startPixelColors);
+            Change changes = UndoManager.UndoStack.Peek();
+            (changes.OldValue as LayerChanges).PixelChanges.ChangedPixels.
+                AddRangeNewOnly(beforeMovePixels.ChangedPixels);
+            (changes.NewValue as LayerChanges).PixelChanges.ChangedPixels.AddRangeNewOnly(_clearedPixelsChange.ChangedPixels);
+
         }
         }
 
 
         public override BitmapPixelChanges Use(Layer layer, Coordinates[] mouseMove, Color color)
         public override BitmapPixelChanges Use(Layer layer, Coordinates[] mouseMove, Color color)
         {
         {
             Coordinates start = mouseMove[^1];
             Coordinates start = mouseMove[^1];
-            if (_lastStartMousePos != start)
+            if (_lastStartMousePos != start) //I am aware that this could be moved to OnMouseDown, but it is executed before Use, so I didn't want to complicate for now
             {
             {
                 ResetSelectionValues(start);
                 ResetSelectionValues(start);
                 if (ViewModelMain.Current.ActiveSelection.SelectedPoints == null) //Move every pixel if none is selected
                 if (ViewModelMain.Current.ActiveSelection.SelectedPoints == null) //Move every pixel if none is selected
@@ -87,7 +102,8 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             if (_clearedPixels == false)
             if (_clearedPixels == false)
             {
             {
-                layer.ApplyPixels(BitmapPixelChanges.FromSingleColoredArray(selection, System.Windows.Media.Colors.Transparent));
+                _clearedPixelsChange = BitmapPixelChanges.FromSingleColoredArray(selection, System.Windows.Media.Colors.Transparent);
+                layer.ApplyPixels(_clearedPixelsChange);
 
 
                 _clearedPixels = true;
                 _clearedPixels = true;
             }
             }

+ 22 - 2
PixiEditor/Models/Tools/Tools/SelectTool.cs

@@ -1,4 +1,6 @@
-using PixiEditor.Models.Layers;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
 using System.Collections.Generic;
 using System.Collections.Generic;
@@ -10,8 +12,26 @@ namespace PixiEditor.Models.Tools.Tools
     {
     {
         public override ToolType ToolType => ToolType.Select;
         public override ToolType ToolType => ToolType.Select;
 
 
-        public override void Use(Coordinates[] pixels)
+        Selection _oldSelection = null;
+
+        public override void OnMouseDown()
+        {
+            _oldSelection = null;
+            if (ViewModelMain.Current.ActiveSelection != null && ViewModelMain.Current.ActiveSelection.SelectedPoints != null)
+            {
+                _oldSelection = ViewModelMain.Current.ActiveSelection;
+                _oldSelection.SelectedPoints = _oldSelection.SelectedPoints.ToArray();
+            }
+        }
+
+        public override void OnMouseUp()
         {
         {
+            UndoManager.AddUndoChange(new Change("ActiveSelection", _oldSelection,
+                ViewModelMain.Current.ActiveSelection, "Select pixels"));
+        }
+
+        public override void Use(Coordinates[] pixels)
+        {            
             Select(pixels);
             Select(pixels);
         }
         }
 
 

+ 33 - 5
PixiEditor/ViewModels/ViewModelMain.cs

@@ -43,6 +43,8 @@ namespace PixiEditor.ViewModels
         public RelayCommand DeselectCommand { get; set; }
         public RelayCommand DeselectCommand { get; set; }
         public RelayCommand SelectAllCommand { get; set; }
         public RelayCommand SelectAllCommand { get; set; }
         public RelayCommand CopyCommand { get; set; }
         public RelayCommand CopyCommand { get; set; }
+        public RelayCommand DuplicateCommand { get; set; }
+        public RelayCommand CutCommand { get; set; }
         public RelayCommand PasteCommand { get; set; }
         public RelayCommand PasteCommand { get; set; }
         public RelayCommand ClipCanvasCommand { get; set; }
         public RelayCommand ClipCanvasCommand { get; set; }
 
 
@@ -176,7 +178,9 @@ namespace PixiEditor.ViewModels
             DeselectCommand = new RelayCommand(Deselect, SelectionIsNotEmpty);
             DeselectCommand = new RelayCommand(Deselect, SelectionIsNotEmpty);
             SelectAllCommand = new RelayCommand(SelectAll, CanSelectAll);
             SelectAllCommand = new RelayCommand(SelectAll, CanSelectAll);
             CopyCommand = new RelayCommand(Copy, SelectionIsNotEmpty);
             CopyCommand = new RelayCommand(Copy, SelectionIsNotEmpty);
-            PasteCommand = new RelayCommand(Paste);
+            DuplicateCommand = new RelayCommand(Duplicate, SelectionIsNotEmpty);
+            CutCommand = new RelayCommand(Cut, SelectionIsNotEmpty);
+            PasteCommand = new RelayCommand(Paste, CanPaste);
             ClipCanvasCommand = new RelayCommand(ClipCanvas, DocumentIsNotNull);
             ClipCanvasCommand = new RelayCommand(ClipCanvas, DocumentIsNotNull);
             ToolSet = new ObservableCollection<Tool> {new MoveTool(), new PenTool(), new SelectTool(), new FloodFill(), new LineTool(),
             ToolSet = new ObservableCollection<Tool> {new MoveTool(), new PenTool(), new SelectTool(), new FloodFill(), new LineTool(),
             new CircleTool(), new RectangleTool(), new EarserTool(), new ColorPickerTool(), new BrightnessTool()};
             new CircleTool(), new RectangleTool(), new EarserTool(), new ColorPickerTool(), new BrightnessTool()};
@@ -201,7 +205,9 @@ namespace PixiEditor.ViewModels
                     new Shortcut(Key.D, DeselectCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.D, DeselectCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.A, SelectAllCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.A, SelectAllCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.C, CopyCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.C, CopyCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.V, PasteCommand, modifier: ModifierKeys.Control)
+                    new Shortcut(Key.V, PasteCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.J, DuplicateCommand, modifier: ModifierKeys.Control),
+                    new Shortcut(Key.X, CutCommand, modifier: ModifierKeys.Control),
                 }
                 }
             };
             };
             UndoManager.SetMainRoot(this);
             UndoManager.SetMainRoot(this);
@@ -217,11 +223,29 @@ namespace PixiEditor.ViewModels
                 BitmapManager.ActiveDocument.ClipCanvas();
                 BitmapManager.ActiveDocument.ClipCanvas();
         }
         }
 
 
+        public void Duplicate(object parameter)
+        {
+            Copy(null);
+            Paste(null);
+        }
+
+        public void Cut(object parameter)
+        {
+            Copy(null);
+            BitmapManager.ActiveLayer.
+                ApplyPixels(BitmapPixelChanges.FromSingleColoredArray(ActiveSelection.SelectedPoints, Colors.Transparent));
+        }
+
         public void Paste(object parameter)
         public void Paste(object parameter)
         {
         {
             ClipboardController.PasteFromClipboard();
             ClipboardController.PasteFromClipboard();
         }
         }
 
 
+        private bool CanPaste(object property)
+        {
+            return DocumentIsNotNull(null) && ClipboardController.IsImageInClipboard();
+        }
+
         private void Copy(object parameter)
         private void Copy(object parameter)
         {
         {
             ClipboardController.CopyToClipboard(BitmapManager.ActiveDocument.Layers.ToArray(), ActiveSelection.SelectedPoints);
             ClipboardController.CopyToClipboard(BitmapManager.ActiveDocument.Layers.ToArray(), ActiveSelection.SelectedPoints);
@@ -250,7 +274,7 @@ namespace PixiEditor.ViewModels
 
 
         private bool SelectionIsNotEmpty(object property)
         private bool SelectionIsNotEmpty(object property)
         {
         {
-            return ActiveSelection.SelectedPoints != null;
+            return ActiveSelection != null && ActiveSelection.SelectedPoints != null;
         }
         }
 
 
         public void SetTool(object parameter)
         public void SetTool(object parameter)
@@ -270,11 +294,15 @@ namespace PixiEditor.ViewModels
 
 
         private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
         private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
         {
         {
-            if (BitmapManager.IsOperationTool(BitmapManager.SelectedTool))
+            if (BitmapManager.IsOperationTool(BitmapManager.SelectedTool) 
+                && (BitmapManager.SelectedTool as BitmapOperationTool).UseDefaultUndoMethod)
             {
             {
                 Tuple<LayerChanges, LayerChanges> changes = ChangesController.PopChanges();
                 Tuple<LayerChanges, LayerChanges> changes = ChangesController.PopChanges();
                 if (changes.Item1.PixelChanges.ChangedPixels.Count > 0)
                 if (changes.Item1.PixelChanges.ChangedPixels.Count > 0)
+                {
                     UndoManager.AddUndoChange(new Change("UndoChanges", changes.Item2, changes.Item1)); //Item2 is old value
                     UndoManager.AddUndoChange(new Change("UndoChanges", changes.Item2, changes.Item1)); //Item2 is old value
+                    BitmapManager.SelectedTool.AfterAddedUndo();
+                }
             }
             }
         }
         }
 
 
@@ -496,7 +524,7 @@ namespace PixiEditor.ViewModels
 
 
         private void NewDocument(int width, int height)
         private void NewDocument(int width, int height)
         {
         {
-            BitmapManager.ActiveDocument = new Document(width, height);
+            BitmapManager.ActiveDocument = new Models.DataHolders.Document(width, height);
             BitmapManager.AddNewLayer("Base Layer", width, height, true);
             BitmapManager.AddNewLayer("Base Layer", width, height, true);
             BitmapManager.PreviewLayer = null;
             BitmapManager.PreviewLayer = null;
             UndoManager.UndoStack.Clear();
             UndoManager.UndoStack.Clear();

+ 2 - 0
PixiEditor/Views/MainWindow.xaml

@@ -70,8 +70,10 @@
                     <MenuItem Header="_Undo" InputGestureText="Ctrl+Z" Command="{Binding UndoCommand}"/>
                     <MenuItem Header="_Undo" InputGestureText="Ctrl+Z" Command="{Binding UndoCommand}"/>
                     <MenuItem Header="_Redo" InputGestureText="Ctrl+Y" Command="{Binding RedoCommand}"/>
                     <MenuItem Header="_Redo" InputGestureText="Ctrl+Y" Command="{Binding RedoCommand}"/>
                     <Separator/>
                     <Separator/>
+                    <MenuItem Header="_Cut" Command="{Binding CutCommand}" InputGestureText="Ctrl+X"/>
                     <MenuItem Header="_Copy" Command="{Binding CopyCommand}" InputGestureText="Ctrl+C"/>
                     <MenuItem Header="_Copy" Command="{Binding CopyCommand}" InputGestureText="Ctrl+C"/>
                     <MenuItem Header="_Paste" Command="{Binding PasteCommand}" InputGestureText="Ctrl+V"/>
                     <MenuItem Header="_Paste" Command="{Binding PasteCommand}" InputGestureText="Ctrl+V"/>
+                    <MenuItem Header="_Duplicate" Command="{Binding DuplicateCommand}" InputGestureText="Ctrl+D"/>
                 </MenuItem>
                 </MenuItem>
                 <MenuItem Header="_Select">
                 <MenuItem Header="_Select">
                     <MenuItem Header="_Select All" Command="{Binding SelectAllCommand}" InputGestureText="Ctrl+A"/>
                     <MenuItem Header="_Select All" Command="{Binding SelectAllCommand}" InputGestureText="Ctrl+A"/>