Skip to content

Commit

Permalink
Add selecting methods to NodifyEditor (#156)
Browse files Browse the repository at this point in the history
* Add selecting methods to NodifyEditor
  • Loading branch information
miroiu authored Nov 27, 2024
1 parent 9f55f4c commit edd3ff8
Show file tree
Hide file tree
Showing 8 changed files with 597 additions and 514 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
> - Made the setter of NodifyEditor.IsPanning private
> - Renamed StartCutting to BeginCutting in NodifyEditor
> - Renamed PushItems to UpdatePushedArea and StartPushingItems to BeginPushingItems in NodifyEditor
> - Renamed UnselectAllConnection to UnselectAllConnections in NodifyEditor
> - Features:
> - Added BeginPanning, UpdatePanning, EndPanning, CancelPanning and AllowPanningCancellation to NodifyEditor
> - Added UpdateCuttingLine to NodifyEditor
> - Added BeginSelecting, UpdateSelection, EndSelecting, CancelSelecting and AllowSelectionCancellation to NodifyEditor
> - Bugfixes:
#### **Version 6.6.0**
Expand Down
2 changes: 1 addition & 1 deletion Nodify/EditorStates/EditorCuttingState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace Nodify
{
public class EditorCuttingState : EditorState
{
public bool Canceled { get; set; } = CuttingLine.AllowCuttingCancellation;
private bool Canceled { get; set; } = CuttingLine.AllowCuttingCancellation;

public EditorCuttingState(NodifyEditor editor) : base(editor)
{
Expand Down
20 changes: 9 additions & 11 deletions Nodify/EditorStates/EditorDefaultState.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Windows;
using System.Windows.Input;
using static Nodify.SelectionHelper;

namespace Nodify
{
Expand Down Expand Up @@ -32,23 +31,22 @@ public EditorDefaultState(NodifyEditor editor) : base(editor)
public override void HandleMouseDown(MouseButtonEventArgs e)
{
EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;
if (gestures.Cutting.Matches(e.Source, e))
if (Editor.CanSelectMultipleItems && gestures.Selection.Select.Matches(e.Source, e))
{
PushState(new EditorCuttingState(Editor));
SelectionType selectionType = SelectionHelper.GetSelectionType(e);
PushState(new EditorSelectingState(Editor, selectionType));
}
else if (gestures.PushItems.Matches(e.Source, e))
else if (!Editor.DisablePanning && gestures.Pan.Matches(e.Source, e))
{
PushState(new EditorPushingItemsState(Editor));
PushState(new EditorPanningState(Editor));
}
else if (gestures.Selection.Select.Matches(e.Source, e))
else if (gestures.Cutting.Matches(e.Source, e))
{
SelectionType selectionType = GetSelectionType(e);
var selecting = new EditorSelectingState(Editor, selectionType);
PushState(selecting);
PushState(new EditorCuttingState(Editor));
}
else if (!Editor.DisablePanning && gestures.Pan.Matches(e.Source, e))
else if (gestures.PushItems.Matches(e.Source, e))
{
PushState(new EditorPanningState(Editor));
PushState(new EditorPushingItemsState(Editor));
}
}

Expand Down
2 changes: 1 addition & 1 deletion Nodify/EditorStates/EditorPushingItemsState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class EditorPushingItemsState : EditorState
private Point _prevPosition;
private const int _minDragDistance = 10;

public bool Canceled { get; set; } = NodifyEditor.AllowPushItemsCancellation;
private bool Canceled { get; set; } = NodifyEditor.AllowPushItemsCancellation;

public EditorPushingItemsState(NodifyEditor editor) : base(editor)
{
Expand Down
41 changes: 20 additions & 21 deletions Nodify/EditorStates/EditorSelectingState.cs
Original file line number Diff line number Diff line change
@@ -1,51 +1,46 @@
using System.Windows.Input;
using static Nodify.SelectionHelper;

namespace Nodify
{
/// <summary>The selecting state of the editor.</summary>
public class EditorSelectingState : EditorState
{
private readonly SelectionType _type;
private bool _canceled;

/// <summary>The selection helper.</summary>
protected SelectionHelper Selection { get; }
private bool Canceled { get; set; } = NodifyEditor.AllowSelectionCancellation;

/// <summary>Constructs an instance of the <see cref="EditorSelectingState"/> state.</summary>
/// <param name="editor">The owner of the state.</param>
/// <param name="type">The selection strategy.</param>
public EditorSelectingState(NodifyEditor editor, SelectionType type) : base(editor)
{
Selection = new SelectionHelper(editor);
_type = type;
}

/// <inheritdoc />
public override void Enter(EditorState? from)
{
Editor.UnselectAllConnection();
Canceled = false;

_canceled = false;
Selection.Start(Editor.MouseLocation, _type);
Editor.BeginSelecting(Editor.MouseLocation, _type);
}

/// <inheritdoc />
public override void Exit()
{
if (_canceled)
// TODO: This is not canceled on LostMouseCapture (add OnLostMouseCapture/OnCancel callback?)
if (Canceled)
{
Selection.Abort();
Editor.CancelSelecting();
}
else
{
Selection.End();
Editor.EndSelecting();
}
}

/// <inheritdoc />
public override void HandleMouseMove(MouseEventArgs e)
=> Selection.Update(Editor.MouseLocation);
public override void HandleMouseMove(MouseEventArgs e)
=> Editor.UpdateSelection(Editor.MouseLocation);

/// <inheritdoc />
public override void HandleMouseDown(MouseButtonEventArgs e)
Expand All @@ -60,12 +55,15 @@ public override void HandleMouseDown(MouseButtonEventArgs e)
public override void HandleMouseUp(MouseButtonEventArgs e)
{
EditorGestures.SelectionGestures gestures = EditorGestures.Mappings.Editor.Selection;

bool canCancel = gestures.Cancel.Matches(e.Source, e);
bool canComplete = gestures.Select.Matches(e.Source, e);
if (canCancel || canComplete)
if(gestures.Select.Matches(e.Source, e))
{
_canceled = !canComplete && canCancel;
PopState();
}
else if(NodifyEditor.AllowSelectionCancellation && gestures.Cancel.Matches(e.Source, e))
{
Canceled = true;
e.Handled = true; // prevents opening context menu

PopState();
}
}
Expand All @@ -76,9 +74,10 @@ public override void HandleAutoPanning(MouseEventArgs e)

public override void HandleKeyUp(KeyEventArgs e)
{
if (EditorGestures.Mappings.Editor.Selection.Cancel.Matches(e.Source, e))
EditorGestures.SelectionGestures gestures = EditorGestures.Mappings.Editor.Selection;
if (NodifyEditor.AllowSelectionCancellation && gestures.Cancel.Matches(e.Source, e))
{
_canceled = true;
Canceled = true;
PopState();
}
}
Expand Down
111 changes: 44 additions & 67 deletions Nodify/Helpers/SelectionHelper.cs
Original file line number Diff line number Diff line change
@@ -1,96 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Nodify
{
/// <summary>
/// Helps with selecting <see cref="ItemContainer"/>s and updating the <see cref="NodifyEditor.SelectedArea"/> and <see cref="NodifyEditor.IsSelecting"/> properties.
/// Helps with selecting <see cref="ItemContainer"/>s.
/// </summary>
public sealed class SelectionHelper
internal sealed class SelectionHelper
{
private readonly NodifyEditor _host;
private Point _startLocation;
private Point _endLocation;
private SelectionType _selectionType;
private bool _isRealtime;
private IReadOnlyList<ItemContainer> _initialSelection = new List<ItemContainer>();

/// <summary>Constructs a new instance of a <see cref="SelectionHelper"/>.</summary>
/// <param name="host">The editor to select items from.</param>
public SelectionHelper(NodifyEditor host)
=> _host = host;

/// <summary>Available selection logic.</summary>
public enum SelectionType
{
/// <summary>Replaces the old selection.</summary>
Replace,
/// <summary>Removes items from existing selection.</summary>
Remove,
/// <summary>Adds items to the current selection.</summary>
Append,
/// <summary>Inverts the selection.</summary>
Invert
}
private IReadOnlyCollection<ItemContainer> _items = Array.Empty<ItemContainer>();
private IReadOnlyList<ItemContainer> _initialSelection = Array.Empty<ItemContainer>();
private Rect _selectedArea;

/// <summary>Attempts to start a new selection.</summary>
/// <param name="containers">The containers that can be part of the selection.</param>
/// <param name="location">The location inside the graph.</param>
/// <param name="selectionType">The type of selection.</param>
/// <remarks>Will not do anything if selection is in progress.</remarks>
public void Start(Point location, SelectionType selectionType)
public Rect Start(IEnumerable<ItemContainer> containers, Point location, SelectionType selectionType, bool realtime)
{
if (!_host.IsSelecting)
{
_selectionType = selectionType;
_initialSelection = _host.SelectedContainers;
_items = containers.Where(x => x.IsSelectable).ToList();
_initialSelection = containers.Where(x => x.IsSelected).ToList();

_isRealtime = _host.EnableRealtimeSelection;
_startLocation = location;
_selectionType = selectionType;

_host.SelectedArea = new Rect();
_host.IsSelecting = true;
}
_isRealtime = realtime;
_startLocation = location;
_endLocation = location;

_selectedArea = new Rect();
return _selectedArea;
}

/// <summary>Update the end location for the selection.</summary>
/// <param name="endLocation">An absolute location.</param>
public void Update(Point endLocation)
public Rect Update(Point endLocation)
{
double left = endLocation.X < _startLocation.X ? endLocation.X : _startLocation.X;
double top = endLocation.Y < _startLocation.Y ? endLocation.Y : _startLocation.Y;
double width = Math.Abs(endLocation.X - _startLocation.X);
double height = Math.Abs(endLocation.Y - _startLocation.Y);
_endLocation = endLocation;

double left = _endLocation.X < _startLocation.X ? _endLocation.X : _startLocation.X;
double top = _endLocation.Y < _startLocation.Y ? _endLocation.Y : _startLocation.Y;
double width = Math.Abs(_endLocation.X - _startLocation.X);
double height = Math.Abs(_endLocation.Y - _startLocation.Y);

_host.SelectedArea = new Rect(left, top, width, height);
_selectedArea = new Rect(left, top, width, height);

if (_isRealtime)
{
PreviewSelection(_host.SelectedArea);
PreviewSelection(_selectedArea);
}

return _selectedArea;
}

/// <summary>Commits the current selection to the editor.</summary>
public void End()
/// <summary>Increase the selected area by the specified amount.</summary>
public Rect Update(Vector amount)
{
if (_host.IsSelecting)
{
PreviewSelection(_host.SelectedArea);
_endLocation += amount;

_host.ApplyPreviewingSelection();
_host.IsSelecting = false;
}
return Update(_endLocation);
}

/// <summary>Aborts the current selection.</summary>
public void Abort()
/// <summary>Commits the current selection to the editor.</summary>
public Rect End()
{
if (_host.IsSelecting)
{
_host.ClearPreviewingSelection();
_host.IsSelecting = false;
}
PreviewSelection(_selectedArea);
_items = Array.Empty<ItemContainer>();
_initialSelection = Array.Empty<ItemContainer>();

return _selectedArea;
}

private void PreviewSelection(Rect area)
Expand Down Expand Up @@ -128,10 +113,8 @@ private void PreviewSelection(Rect area)

private void PreviewUnselectAll()
{
ItemCollection items = _host.Items;
for (var i = 0; i < items.Count; i++)
foreach (var container in _items)
{
var container = (ItemContainer)_host.ItemContainerGenerator.ContainerFromIndex(i);
container.IsPreviewingSelection = false;
}
}
Expand All @@ -145,10 +128,8 @@ private void PreviewSelectArea(Rect area, bool append = false, bool fit = false)

if (area.X != 0 || area.Y != 0 || area.Width > 0 || area.Height > 0)
{
ItemCollection items = _host.Items;
for (var i = 0; i < items.Count; i++)
foreach (var container in _items)
{
var container = (ItemContainer)_host.ItemContainerGenerator.ContainerFromIndex(i);
if (container.IsSelectableInArea(area, fit))
{
container.IsPreviewingSelection = true;
Expand All @@ -159,10 +140,8 @@ private void PreviewSelectArea(Rect area, bool append = false, bool fit = false)

private void PreviewUnselectArea(Rect area, bool fit = false)
{
ItemCollection items = _host.Items;
for (var i = 0; i < items.Count; i++)
foreach (var container in _items)
{
var container = (ItemContainer)_host.ItemContainerGenerator.ContainerFromIndex(i);
if (container.IsSelectableInArea(area, fit))
{
container.IsPreviewingSelection = false;
Expand All @@ -180,10 +159,8 @@ private static void PreviewSelectContainers(IReadOnlyList<ItemContainer> contain

private void PreviewInvertSelection(Rect area, bool fit = false)
{
ItemCollection items = _host.Items;
for (var i = 0; i < items.Count; i++)
foreach (var container in _items)
{
var container = (ItemContainer)_host.ItemContainerGenerator.ContainerFromIndex(i);
if (container.IsSelectableInArea(area, fit))
{
container.IsPreviewingSelection = !container.IsPreviewingSelection;
Expand Down
Loading

0 comments on commit edd3ff8

Please sign in to comment.