|
@@ -1,12 +1,11 @@
|
|
|
using PixiEditor.Helpers;
|
|
|
+using PixiEditor.Helpers.Extensions;
|
|
|
using PixiEditor.Models.Enums;
|
|
|
using PixiEditor.Models.Layers;
|
|
|
-using PixiEditor.Models.Position;
|
|
|
+using PixiEditor.Models.Undo;
|
|
|
using SkiaSharp;
|
|
|
-using System.Collections.Generic;
|
|
|
-using System.Collections.ObjectModel;
|
|
|
+using System;
|
|
|
using System.Diagnostics;
|
|
|
-using System.Linq;
|
|
|
using System.Windows;
|
|
|
|
|
|
namespace PixiEditor.Models.DataHolders
|
|
@@ -14,59 +13,48 @@ namespace PixiEditor.Models.DataHolders
|
|
|
[DebuggerDisplay("{SelectedPoints.Count} selected Pixels")]
|
|
|
public class Selection : NotifyableObject
|
|
|
{
|
|
|
- private readonly SKColor selectionBlue;
|
|
|
- private Layer selectionLayer;
|
|
|
+ public bool isEmpty { get; private set; } = true;
|
|
|
|
|
|
- public Selection(Coordinates[] selectedPoints)
|
|
|
+ private readonly SKColor selectionBlue = new SKColor(142, 202, 255, 255);
|
|
|
+ private Layer selectionLayer = new Layer("_selectionLayer");
|
|
|
+ private Document owner;
|
|
|
+
|
|
|
+ public Selection(Document owner)
|
|
|
{
|
|
|
- SelectedPoints = new ObservableCollection<Coordinates>(selectedPoints);
|
|
|
- SelectionLayer = new Layer("_selectionLayer");
|
|
|
- selectionBlue = new SKColor(142, 202, 255, 255);
|
|
|
+ this.owner = owner;
|
|
|
}
|
|
|
|
|
|
- public ObservableCollection<Coordinates> SelectedPoints { get; private set; }
|
|
|
-
|
|
|
public Layer SelectionLayer
|
|
|
{
|
|
|
get => selectionLayer;
|
|
|
- set
|
|
|
+ private set
|
|
|
{
|
|
|
selectionLayer = value;
|
|
|
RaisePropertyChanged(nameof(SelectionLayer));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public void SetSelection(IEnumerable<Coordinates> selection, SelectionType mode)
|
|
|
+ public void TranslateSelection(int dX, int dY)
|
|
|
{
|
|
|
- SKColor selectionColor = selectionBlue;
|
|
|
- switch (mode)
|
|
|
- {
|
|
|
- case SelectionType.New:
|
|
|
- SelectedPoints = new ObservableCollection<Coordinates>(selection);
|
|
|
- SelectionLayer.Reset();
|
|
|
- break;
|
|
|
- case SelectionType.Add:
|
|
|
- SelectedPoints = new ObservableCollection<Coordinates>(SelectedPoints.Concat(selection).Distinct());
|
|
|
- break;
|
|
|
- case SelectionType.Subtract:
|
|
|
- SelectedPoints = new ObservableCollection<Coordinates>(SelectedPoints.Except(selection));
|
|
|
- selectionColor = SKColors.Transparent;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- SelectionLayer.SetPixels(BitmapPixelChanges.FromSingleColoredArray(selection, selectionColor));
|
|
|
+ selectionLayer.Offset = new Thickness(selectionLayer.OffsetX + dX, selectionLayer.OffsetY + dY, 0, 0);
|
|
|
}
|
|
|
|
|
|
- public void TranslateSelection(int dX, int dY)
|
|
|
+ public void SetSelectionWithUndo(Int32Rect rect, SelectionShape shape, SelectionType mode)
|
|
|
{
|
|
|
- selectionLayer.Offset = new Thickness(selectionLayer.OffsetX + dX, selectionLayer.OffsetY + dY, 0, 0);
|
|
|
- for (int i = 0; i < SelectedPoints.Count; i++)
|
|
|
- {
|
|
|
- SelectedPoints[i] = new Coordinates(SelectedPoints[i].X + dX, SelectedPoints[i].Y + dY);
|
|
|
- }
|
|
|
+ Int32Rect updateRect;
|
|
|
+ if (mode == SelectionType.New)
|
|
|
+ updateRect = rect.Expand(new(selectionLayer.OffsetX, selectionLayer.OffsetY, selectionLayer.Width, selectionLayer.Height));
|
|
|
+ else
|
|
|
+ updateRect = rect;
|
|
|
+ LayerChunk chunk = new LayerChunk(selectionLayer, new(updateRect.X, updateRect.Y, updateRect.X + updateRect.Width, updateRect.Y + updateRect.Height));
|
|
|
+ StorageBasedChange change = new(owner, new LayerChunk[] { chunk });
|
|
|
+
|
|
|
+ DrawSelection(rect, shape, mode);
|
|
|
+
|
|
|
+ owner.UndoManager.AddUndoChange(change.ToChange(SelectionUndoProcess, new object[] { owner }, "Update selection"));
|
|
|
}
|
|
|
|
|
|
- public void SetSelection(Int32Rect rect, bool isCirclular, SelectionType mode)
|
|
|
+ private void DrawSelection(Int32Rect rect, SelectionShape shape, SelectionType mode)
|
|
|
{
|
|
|
using SKPaint paint = new()
|
|
|
{
|
|
@@ -84,23 +72,47 @@ namespace PixiEditor.Models.DataHolders
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ if (!rect.HasArea)
|
|
|
+ return;
|
|
|
+ isEmpty = false;
|
|
|
SelectionLayer.DynamicResizeAbsolute(new Int32Rect(rect.X, rect.Y, rect.Width, rect.Height));
|
|
|
- if (isCirclular)
|
|
|
+ if (shape == SelectionShape.Circle)
|
|
|
{
|
|
|
float cx = rect.X + rect.Width / 2f;
|
|
|
float cy = rect.Y + rect.Height / 2f;
|
|
|
SelectionLayer.LayerBitmap.SkiaSurface.Canvas.DrawOval(cx, cy, rect.Width / 2f, rect.Height / 2f, paint);
|
|
|
}
|
|
|
- else
|
|
|
+ else if (shape == SelectionShape.Rectangle)
|
|
|
{
|
|
|
SelectionLayer.LayerBitmap.SkiaSurface.Canvas.DrawRect(rect.X, rect.Y, rect.Width, rect.Height, paint);
|
|
|
}
|
|
|
+ else
|
|
|
+ {
|
|
|
+ throw new NotImplementedException($"Selection shape '{shape}' has not been implemented");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void SelectionUndoProcess(Layer[] layers, UndoLayer[] data, object[] args)
|
|
|
+ {
|
|
|
+ if (args.Length > 0 && args[0] is Document document)
|
|
|
+ {
|
|
|
+ for (int i = 0; i < layers.Length; i++)
|
|
|
+ {
|
|
|
+ Layer layer = layers[i];
|
|
|
+ UndoLayer layerData = data[i];
|
|
|
+ var foundLayer = document.ActiveSelection.SelectionLayer;
|
|
|
+ StorageBasedChange.ApplyChunkToLayer(foundLayer, layerData, layer.LayerBitmap);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- public void Clear()
|
|
|
+ public void ClearWithUndo()
|
|
|
{
|
|
|
+ SKRectI updateRect = new SKRectI(selectionLayer.OffsetX, selectionLayer.OffsetY, selectionLayer.OffsetX + selectionLayer.Width, selectionLayer.OffsetY + selectionLayer.Height);
|
|
|
+ StorageBasedChange change = new(owner, new LayerChunk[] { new(selectionLayer, updateRect) });
|
|
|
SelectionLayer.Reset();
|
|
|
- SelectedPoints.Clear();
|
|
|
+ isEmpty = true;
|
|
|
+ owner.UndoManager.AddUndoChange(change.ToChange(SelectionUndoProcess, new object[] { owner }, "Clear selection"));
|
|
|
}
|
|
|
}
|
|
|
}
|