Skip to content

Commit

Permalink
Merge pull request #159 from PixiEditor/undo-selection
Browse files Browse the repository at this point in the history
Undo selection
  • Loading branch information
flabbet committed Mar 8, 2021
2 parents fd7b427 + 2d3e2f8 commit bef0171
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 25 deletions.
15 changes: 15 additions & 0 deletions PixiEditor/Helpers/Extensions/ToolbarHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using PixiEditor.Models.Tools.ToolSettings.Settings;
using PixiEditor.Models.Tools.ToolSettings.Toolbars;

namespace PixiEditor.Helpers.Extensions
{
public static class ToolbarHelpers
{
public static EnumSetting<TEnum> GetEnumSetting<TEnum>(this Toolbar toolbar, string name)
where TEnum : struct, Enum
{
return toolbar.GetSetting<EnumSetting<TEnum>>(name);
}
}
}
43 changes: 43 additions & 0 deletions PixiEditor/Helpers/SelectionHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections.Generic;
using PixiEditor.Models.DataHolders;
using PixiEditor.Models.Enums;
using PixiEditor.Models.Position;
using PixiEditor.Models.Undo;

namespace PixiEditor.Helpers
{
public static class SelectionHelpers
{
public static void AddSelectionUndoStep(Document document, IEnumerable<Coordinates> oldPoints, SelectionType mode)
{
#pragma warning disable SA1117 // Parameters should be on same line or separate lines. Justification: Making it readable
if (mode == SelectionType.New && document.ActiveSelection.SelectedPoints.Count != 0)
{
// Add empty selection as the old one get's fully deleted first
document.UndoManager.AddUndoChange(
new Change(
SetSelectionProcess, new object[] { document, new List<Coordinates>(oldPoints) },
SetSelectionProcess, new object[] { document, new List<Coordinates>() }));
document.UndoManager.AddUndoChange(
new Change(
SetSelectionProcess, new object[] { document, new List<Coordinates>() },
SetSelectionProcess, new object[] { document, new List<Coordinates>(document.ActiveSelection.SelectedPoints) }));
}
else
{
document.UndoManager.AddUndoChange(
new Change(
SetSelectionProcess, new object[] { document, new List<Coordinates>(oldPoints) },
SetSelectionProcess, new object[] { document, new List<Coordinates>(document.ActiveSelection.SelectedPoints) }));
#pragma warning restore SA1117 // Parameters should be on same line or separate lines
}
}

private static void SetSelectionProcess(object[] arguments)
{
Document document = (Document)arguments[0];

document.ActiveSelection.SetSelection((IEnumerable<Coordinates>)arguments[1], SelectionType.New);
}
}
}
2 changes: 2 additions & 0 deletions PixiEditor/Models/DataHolders/Selection.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Media;
using PixiEditor.Helpers;
Expand All @@ -9,6 +10,7 @@

namespace PixiEditor.Models.DataHolders
{
[DebuggerDisplay("{SelectedPoints.Count} selected Pixels")]
public class Selection : NotifyableObject
{
private readonly Color selectionBlue;
Expand Down
96 changes: 96 additions & 0 deletions PixiEditor/Models/Tools/ToolSettings/Settings/EnumSetting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;

namespace PixiEditor.Models.Tools.ToolSettings.Settings
{
public class EnumSetting<TEnum> : Setting<TEnum, ComboBox>
where TEnum : struct, Enum
{
private int selectedIndex = 0;

/// <summary>
/// Gets or sets the selected Index of the <see cref="ComboBox"/>.
/// </summary>
public int SelectedIndex
{
get => selectedIndex;
set
{
if (SetProperty(ref selectedIndex, value))
{
RaisePropertyChanged(nameof(Value));
}
}
}

/// <summary>
/// Gets or sets the selected value of the <see cref="ComboBox"/>.
/// </summary>
public new TEnum Value
{
get => (TEnum)(SettingControl.SelectedItem as ComboBoxItem).Tag;
set
{
SettingControl.SelectedItem = SettingControl.Items.Cast<ComboBoxItem>().First(x => x.Tag == (object)value);
RaisePropertyChanged(nameof(Value));
}
}

public EnumSetting(string name, string label)
: base(name)
{
SettingControl = GenerateDropdown();

Label = label;
}

public EnumSetting(string name, string label, TEnum defaultValue)
: this(name, label)
{
Value = defaultValue;
}

private static ComboBox GenerateDropdown()
{
ComboBox combobox = new ComboBox
{
VerticalAlignment = VerticalAlignment.Center
};

GenerateItems(combobox);

Binding binding = new Binding(nameof(SelectedIndex))
{
Mode = BindingMode.TwoWay
};

combobox.SetBinding(Selector.SelectedIndexProperty, binding);

return combobox;
}

private static void GenerateItems(ComboBox comboBox)
{
string[] names = Enum.GetNames<TEnum>();
TEnum[] values = Enum.GetValues<TEnum>();

for (int i = 0; i < names.Length; i++)
{
ComboBoxItem item = new ComboBoxItem
{
Content = names[i],
Tag = values[i]
};

comboBox.Items.Add(item);
}
}
}
}
21 changes: 17 additions & 4 deletions PixiEditor/Models/Tools/ToolSettings/Settings/Setting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@
using System.Windows.Controls;
using PixiEditor.Helpers;

#pragma warning disable SA1402 // File may only contain a single type, Justification: "Same class with generic value"

namespace PixiEditor.Models.Tools.ToolSettings.Settings
{
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"StyleCop.CSharp.MaintainabilityRules",
"SA1402:File may only contain a single type",
Justification = "Same class with generic value")]
public abstract class Setting<T, TControl> : Setting<T>
where TControl : Control
{
protected Setting(string name)
: base(name)
{
}

public new TControl SettingControl
{
get => (TControl)base.SettingControl;
set => base.SettingControl = value;
}
}

public abstract class Setting<T> : Setting
{
protected Setting(string name)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using PixiEditor.Models.Tools.ToolSettings.Settings;
using PixiEditor.Models.Enums;
using PixiEditor.Models.Tools.ToolSettings.Settings;

namespace PixiEditor.Models.Tools.ToolSettings.Toolbars
{
public class SelectToolToolbar : Toolbar
{
public SelectToolToolbar()
{
Settings.Add(new DropdownSetting("SelectMode", new[] { "New", "Add", "Subtract" }, "Selection type"));
Settings.Add(new EnumSetting<SelectionType>("SelectMode", "Selection type"));
}
}
}
28 changes: 10 additions & 18 deletions PixiEditor/Models/Tools/Tools/SelectTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
using PixiEditor.Helpers;
using PixiEditor.Helpers.Extensions;
using PixiEditor.Models.Controllers;
using PixiEditor.Models.DataHolders;
using PixiEditor.Models.Enums;
Expand All @@ -17,7 +19,9 @@ namespace PixiEditor.Models.Tools.Tools
{
public class SelectTool : ReadonlyTool
{
private Selection oldSelection;
private IEnumerable<Coordinates> oldSelectedPoints;

private static Selection ActiveSelection { get => ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection; }

public SelectTool()
{
Expand All @@ -30,32 +34,20 @@ public SelectTool()

public override void OnRecordingLeftMouseDown(MouseEventArgs e)
{
Enum.TryParse((Toolbar.GetSetting<DropdownSetting>("SelectMode")?.Value as ComboBoxItem)?.Content as string, out SelectionType selectionType);
SelectionType = selectionType;
SelectionType = Toolbar.GetEnumSetting<SelectionType>("SelectMode").Value;

oldSelection = null;
Selection selection = ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection;
if (selection != null && selection.SelectedPoints != null)
{
oldSelection = selection;
}
oldSelectedPoints = new ReadOnlyCollection<Coordinates>(ActiveSelection.SelectedPoints);
}

public override void OnStoppedRecordingMouseUp(MouseEventArgs e)
{
if (ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints.Count() <= 1)
if (ActiveSelection.SelectedPoints.Count <= 1)
{
// If we have not selected multiple points, clear the selection
ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.Clear();
ActiveSelection.Clear();
}

ViewModelMain.Current.BitmapManager.ActiveDocument.UndoManager.AddUndoChange(
new Change(
"SelectedPoints",
oldSelection.SelectedPoints,
new ObservableCollection<Coordinates>(ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints),
"Select pixels",
ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection));
SelectionHelpers.AddSelectionUndoStep(ViewModelMain.Current.BitmapManager.ActiveDocument, oldSelectedPoints, SelectionType);
}

public override void Use(Coordinates[] pixels)
Expand Down
14 changes: 14 additions & 0 deletions PixiEditor/NotifyableObject.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace PixiEditor.Helpers
{
Expand All @@ -16,5 +18,17 @@ protected void RaisePropertyChanged(string property)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}

protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
{
return false;
}

backingStore = value;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using PixiEditor.Helpers;
using PixiEditor.Models.DataHolders;
using PixiEditor.Models.Enums;
Expand All @@ -23,12 +24,20 @@ public SelectionViewModel(ViewModelMain owner)
public void SelectAll(object parameter)
{
SelectTool select = new SelectTool();

var oldSelection = new List<Coordinates>(Owner.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints);

Owner.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(select.GetAllSelection(), SelectionType.New);
SelectionHelpers.AddSelectionUndoStep(Owner.BitmapManager.ActiveDocument, oldSelection, SelectionType.New);
}

public void Deselect(object parameter)
{
var oldSelection = new List<Coordinates>(Owner.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints);

Owner.BitmapManager.ActiveDocument.ActiveSelection?.Clear();

SelectionHelpers.AddSelectionUndoStep(Owner.BitmapManager.ActiveDocument, oldSelection, SelectionType.New);
}

public bool SelectionIsNotEmpty(object property)
Expand Down
1 change: 0 additions & 1 deletion PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ public void Redo(object parameter)
/// <param name="parameter">CommandParameter.</param>
public void Undo(object parameter)
{
Owner.SelectionSubViewModel.Deselect(null);
Owner.BitmapManager.ActiveDocument.UndoManager.Undo();
}

Expand Down
20 changes: 20 additions & 0 deletions PixiEditorTests/ModelsTests/DataHoldersTests/SelectionTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using PixiEditor.Helpers;
using PixiEditor.Models.DataHolders;
using PixiEditor.Models.Enums;
using PixiEditor.Models.Position;
Expand Down Expand Up @@ -55,5 +57,23 @@ public void TestClearWorks()
Assert.Empty(selection.SelectedPoints);
Assert.Equal(0, selection.SelectionLayer.Width + selection.SelectionLayer.Height);
}

[Fact]
public void TestThatUndoWorks()
{
Document document = new Document(10, 10);

IEnumerable<Coordinates> oldSelection = new List<Coordinates>(document.ActiveSelection.SelectedPoints);

document.ActiveSelection.SetSelection(new[] { new Coordinates(0, 0), new Coordinates(5, 7) }, SelectionType.Add);

Assert.NotEqual(oldSelection, document.ActiveSelection.SelectedPoints);

SelectionHelpers.AddSelectionUndoStep(document, oldSelection, SelectionType.Add);

document.UndoManager.Undo();

Assert.Equal(oldSelection, document.ActiveSelection.SelectedPoints);
}
}
}

0 comments on commit bef0171

Please sign in to comment.