Add project files.
This commit is contained in:
39
Nodify/Editor/States/Cutting.cs
Normal file
39
Nodify/Editor/States/Cutting.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Nodify.Interactivity
|
||||
{
|
||||
public static partial class EditorState
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the cutting state in the <see cref="NodifyEditor"/>, allowing users to cut connections between elements using a drag gesture.
|
||||
/// </summary>
|
||||
public class Cutting : DragState<NodifyEditor>
|
||||
{
|
||||
protected override bool HasContextMenu => Element.HasContextMenu;
|
||||
protected override bool CanBegin => !Element.IsSelecting && !Element.IsPanning && !Element.IsPushingItems;
|
||||
protected override bool CanCancel => NodifyEditor.AllowCuttingCancellation;
|
||||
protected override bool IsToggle => EnableToggledCuttingMode;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Cutting"/> class.
|
||||
/// </summary>
|
||||
/// <param name="editor">The <see cref="NodifyEditor"/> associated with this state.</param>
|
||||
public Cutting(NodifyEditor editor)
|
||||
: base(editor, EditorGestures.Mappings.Editor.Cutting, EditorGestures.Mappings.Editor.CancelAction)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnBegin(InputEventArgs e)
|
||||
=> Element.BeginCutting();
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
=> Element.UpdateCuttingLine(Element.MouseLocation);
|
||||
|
||||
protected override void OnEnd(InputEventArgs e)
|
||||
=> Element.EndCutting();
|
||||
|
||||
protected override void OnCancel(InputEventArgs e)
|
||||
=> Element.CancelCutting();
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Nodify/Editor/States/EditorState.cs
Normal file
71
Nodify/Editor/States/EditorState.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
namespace Nodify.Interactivity
|
||||
{
|
||||
public static partial class EditorState
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether panning is allowed while selecting items in the editor.
|
||||
/// </summary>
|
||||
public static bool AllowPanningWhileSelecting { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether panning is allowed while cutting connections in the editor.
|
||||
/// </summary>
|
||||
public static bool AllowPanningWhileCutting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether panning is allowed while pushing items in the editor.
|
||||
/// </summary>
|
||||
public static bool AllowPanningWhilePushingItems { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether zooming is allowed while selecting items in the editor.
|
||||
/// </summary>
|
||||
public static bool AllowZoomingWhileSelecting { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether zooming is allowed while cutting connections in the editor.
|
||||
/// </summary>
|
||||
public static bool AllowZoomingWhileCutting { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether zooming is allowed while pushing items in the editor.
|
||||
/// </summary>
|
||||
public static bool AllowZoomingWhilePushingItems { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether zooming is allowed while panning the editor viewport.
|
||||
/// </summary>
|
||||
public static bool AllowZoomingWhilePanning { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether toggled selecting mode is enabled, allowing the user to start and end the interaction in two steps with the same input gesture.
|
||||
/// </summary>
|
||||
public static bool EnableToggledSelectingMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether toggled panning mode is enabled, allowing the user to start and end the interaction in two steps with the same input gesture.
|
||||
/// </summary>
|
||||
public static bool EnableToggledPanningMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether toggled cutting mode is enabled, allowing the user to start and end the interaction in two steps with the same input gesture.
|
||||
/// </summary>
|
||||
public static bool EnableToggledCuttingMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether toggled pushing items mode is enabled, allowing the user to start and end the interaction in two steps with the same input gesture.
|
||||
/// </summary>
|
||||
public static bool EnableToggledPushingItemsMode { get; set; }
|
||||
|
||||
internal static void RegisterDefaultHandlers()
|
||||
{
|
||||
InputProcessor.Shared<NodifyEditor>.RegisterHandlerFactory(elem => new Panning(elem));
|
||||
InputProcessor.Shared<NodifyEditor>.RegisterHandlerFactory(elem => new PanningWithMouseWheel(elem));
|
||||
InputProcessor.Shared<NodifyEditor>.RegisterHandlerFactory(elem => new Selecting(elem));
|
||||
InputProcessor.Shared<NodifyEditor>.RegisterHandlerFactory(elem => new Zooming(elem));
|
||||
InputProcessor.Shared<NodifyEditor>.RegisterHandlerFactory(elem => new PushingItems(elem));
|
||||
InputProcessor.Shared<NodifyEditor>.RegisterHandlerFactory(elem => new Cutting(elem));
|
||||
InputProcessor.Shared<NodifyEditor>.RegisterHandlerFactory(elem => new KeyboardNavigation(elem));
|
||||
}
|
||||
}
|
||||
}
|
||||
147
Nodify/Editor/States/KeyboardNavigation.cs
Normal file
147
Nodify/Editor/States/KeyboardNavigation.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Nodify.Interactivity
|
||||
{
|
||||
public static partial class EditorState
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the keyboard navigation state of the <see cref="NodifyEditor"/>, allowing users to navigate and interact with nodes and connections using the keyboard.
|
||||
/// </summary>
|
||||
public class KeyboardNavigation : InputElementState<NodifyEditor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="KeyboardNavigation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="element">The <see cref="NodifyEditor"/> associated with this state.</param>
|
||||
public KeyboardNavigation(NodifyEditor element) : base(element)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
if (!Element.IsKeyboardFocusWithin || !(e.OriginalSource is DependencyObject originalSource))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double navigationStepSize = GetNavigationStepSize();
|
||||
var gestures = EditorGestures.Mappings.Editor.Keyboard;
|
||||
|
||||
if (e.Key == Key.Tab && Keyboard.Modifiers.HasFlag(ModifierKeys.Control))
|
||||
{
|
||||
var parentContainer = originalSource.GetParent(Element.IsNavigationTrigger) as UIElement;
|
||||
e.Handled = parentContainer?.Focus() is true;
|
||||
}
|
||||
else if (Element.IsNavigationTrigger(originalSource))
|
||||
{
|
||||
if (gestures.Pan.TryGetNavigationDirection(e, out var panDirection))
|
||||
{
|
||||
var panning = new Vector(-panDirection.X * navigationStepSize, panDirection.Y * navigationStepSize);
|
||||
Element.UpdatePanning(panning);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (CanDragSelection() && gestures.DragSelection.TryGetNavigationDirection(e, out var dragDirection))
|
||||
{
|
||||
var dragging = new Vector(dragDirection.X * navigationStepSize, -dragDirection.Y * navigationStepSize);
|
||||
Element.BeginDragging();
|
||||
Element.UpdateDragging(dragging);
|
||||
Element.EndDragging();
|
||||
|
||||
if (NodifyEditor.PanViewportOnKeyboardDrag)
|
||||
{
|
||||
var panning = new Vector(-dragDirection.X * navigationStepSize, dragDirection.Y * navigationStepSize);
|
||||
Element.UpdatePanning(panning);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (gestures.NavigateSelection.TryGetFocusDirection(e, out var direction))
|
||||
{
|
||||
Element.MoveFocus(direction);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnKeyUp(KeyEventArgs e)
|
||||
{
|
||||
var gestures = EditorGestures.Mappings.Editor.Keyboard;
|
||||
|
||||
if (gestures.ToggleSelected.Matches(e.Source, e))
|
||||
{
|
||||
if (Keyboard.FocusedElement is ItemContainer itemContainer)
|
||||
{
|
||||
itemContainer.Select(SelectionType.Invert);
|
||||
if (NodifyEditor.AutoPanOnNodeFocus)
|
||||
{
|
||||
Element.BringIntoView(itemContainer.Bounds, NodifyEditor.BringIntoViewEdgeOffset);
|
||||
}
|
||||
}
|
||||
else if (Keyboard.FocusedElement is ConnectionContainer connectionContainer)
|
||||
{
|
||||
connectionContainer.Select(SelectionType.Invert);
|
||||
if (NodifyEditor.AutoPanOnNodeFocus)
|
||||
{
|
||||
Element.BringIntoView(connectionContainer.Bounds, NodifyEditor.BringIntoViewEdgeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (gestures.DeselectAll.Matches(e.Source, e))
|
||||
{
|
||||
if (Element.SelectedContainersCount > 0 && Element.ActiveNavigationLayer?.Id == KeyboardNavigationLayerId.Nodes)
|
||||
{
|
||||
Element.UnselectAll();
|
||||
e.Handled = true;
|
||||
}
|
||||
// TODO: How to get the selected connections count without a hard reference to the connections multi selector?
|
||||
// This currently assumes we have a binding to the SelectedConnectionsProperty dependency property
|
||||
else if (Element.SelectedConnections?.Count > 0 && Element.ActiveNavigationLayer?.Id == KeyboardNavigationLayerId.Connections)
|
||||
{
|
||||
Element.UnselectAllConnections();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
else if (gestures.NextNavigationLayer.Matches(e.Source, e))
|
||||
{
|
||||
Element.ActivateNextNavigationLayer();
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (gestures.PrevNavigationLayer.Matches(e.Source, e))
|
||||
{
|
||||
Element.ActivatePreviousNavigationLayer();
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (Keyboard.FocusedElement is ItemContainer { IsSelected: true } container
|
||||
&& EditorGestures.Mappings.GroupingNode.ToggleContentSelection.Matches(e.Source, e))
|
||||
{
|
||||
var groupingNode = container.GetChildOfType<GroupingNode>();
|
||||
if (groupingNode != null)
|
||||
{
|
||||
groupingNode.ToggleContentSelection();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanDragSelection()
|
||||
{
|
||||
return Element.ActiveNavigationLayer?.Id == KeyboardNavigationLayerId.Nodes && Element.SelectedContainersCount > 0;
|
||||
}
|
||||
|
||||
private double GetNavigationStepSize()
|
||||
{
|
||||
double cellSize = Element.GridCellSize;
|
||||
|
||||
if (cellSize >= NodifyEditor.MinimumNavigationStepSize)
|
||||
return cellSize;
|
||||
|
||||
int factor = (int)Math.Ceiling(NodifyEditor.MinimumNavigationStepSize / cellSize);
|
||||
return factor * cellSize / Element.ViewportZoom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Nodify/Editor/States/Panning.cs
Normal file
91
Nodify/Editor/States/Panning.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Nodify.Interactivity
|
||||
{
|
||||
public static partial class EditorState
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the panning state of the <see cref="NodifyEditor"/>, allowing the user to pan the viewport by clicking and dragging.
|
||||
/// </summary>
|
||||
public class Panning : DragState<NodifyEditor>
|
||||
{
|
||||
protected override bool HasContextMenu => Element.HasContextMenu;
|
||||
protected override bool CanBegin => IsPanningAllowed();
|
||||
protected override bool CanCancel => NodifyEditor.AllowPanningCancellation;
|
||||
protected override bool IsToggle => EnableToggledPanningMode;
|
||||
|
||||
|
||||
private Point _prevPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Panning"/> class.
|
||||
/// </summary>
|
||||
/// <param name="editor">The <see cref="NodifyEditor"/> associated with this state.</param>
|
||||
public Panning(NodifyEditor editor)
|
||||
: base(editor, EditorGestures.Mappings.Editor.Pan, EditorGestures.Mappings.Editor.CancelAction)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnBegin(InputEventArgs e)
|
||||
{
|
||||
_prevPosition = Mouse.GetPosition(Element);
|
||||
Element.BeginPanning();
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
var currentMousePosition = e.GetPosition(Element);
|
||||
Element.UpdatePanning((currentMousePosition - _prevPosition) / Element.ViewportZoom);
|
||||
_prevPosition = currentMousePosition;
|
||||
}
|
||||
|
||||
protected override void OnEnd(InputEventArgs e)
|
||||
=> Element.EndPanning();
|
||||
|
||||
protected override void OnCancel(InputEventArgs e)
|
||||
=> Element.CancelPanning();
|
||||
|
||||
private bool IsPanningAllowed()
|
||||
{
|
||||
return !Element.DisablePanning
|
||||
&& (AllowPanningWhileSelecting || !Element.IsSelecting)
|
||||
&& (AllowPanningWhileCutting || !Element.IsCutting)
|
||||
&& (AllowPanningWhilePushingItems || !Element.IsPushingItems);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the panning state of the <see cref="NodifyEditor"/> using the mouse wheel.
|
||||
/// Allows the user to pan horizontally or vertically by holding modifier keys while scrolling the mouse wheel.
|
||||
/// </summary>
|
||||
public class PanningWithMouseWheel : InputElementState<NodifyEditor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PanningWithMouseWheel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="editor">The <see cref="NodifyEditor"/> associated with this state.</param>
|
||||
public PanningWithMouseWheel(NodifyEditor editor) : base(editor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnMouseWheel(MouseWheelEventArgs e)
|
||||
{
|
||||
EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;
|
||||
if (gestures.PanWithMouseWheel && Keyboard.Modifiers == gestures.PanHorizontalModifierKey)
|
||||
{
|
||||
double offset = Math.Sign(e.Delta) * Mouse.MouseWheelDeltaForOneLine / 2 / Element.ViewportZoom;
|
||||
Element.UpdatePanning(new Vector(offset, 0d));
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (gestures.PanWithMouseWheel && Keyboard.Modifiers == gestures.PanVerticalModifierKey)
|
||||
{
|
||||
double offset = Math.Sign(e.Delta) * Mouse.MouseWheelDeltaForOneLine / 2 / Element.ViewportZoom;
|
||||
Element.UpdatePanning(new Vector(0d, offset));
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Nodify/Editor/States/PushingItems.cs
Normal file
61
Nodify/Editor/States/PushingItems.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Nodify.Interactivity
|
||||
{
|
||||
public static partial class EditorState
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the state of the <see cref="NodifyEditor"/> during a "push items" operation, allowing users to move items within the editor by dragging.
|
||||
/// </summary>
|
||||
public class PushingItems : DragState<NodifyEditor>
|
||||
{
|
||||
protected override bool HasContextMenu => Element.HasContextMenu;
|
||||
protected override bool CanBegin => !Element.IsSelecting && !Element.IsPanning && !Element.IsCutting;
|
||||
protected override bool CanCancel => NodifyEditor.AllowPushItemsCancellation;
|
||||
protected override bool IsToggle => EnableToggledPushingItemsMode;
|
||||
|
||||
private Point _prevPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PushingItems"/> class.
|
||||
/// </summary>
|
||||
/// <param name="editor">The <see cref="NodifyEditor"/> associated with this state.</param>
|
||||
public PushingItems(NodifyEditor editor)
|
||||
: base(editor, EditorGestures.Mappings.Editor.PushItems, EditorGestures.Mappings.Editor.CancelAction)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnBegin(InputEventArgs e)
|
||||
=> _prevPosition = Element.MouseLocation;
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
if (Element.IsPushingItems)
|
||||
{
|
||||
Element.UpdatePushedArea(Element.MouseLocation - _prevPosition);
|
||||
_prevPosition = Element.MouseLocation;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Math.Abs(Element.MouseLocation.X - _prevPosition.X) >= NodifyEditor.MouseActionSuppressionThreshold)
|
||||
{
|
||||
Element.BeginPushingItems(_prevPosition, Orientation.Horizontal);
|
||||
}
|
||||
else if (Math.Abs(Element.MouseLocation.Y - _prevPosition.Y) >= NodifyEditor.MouseActionSuppressionThreshold)
|
||||
{
|
||||
Element.BeginPushingItems(_prevPosition, Orientation.Vertical);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEnd(InputEventArgs e)
|
||||
=> Element.EndPushingItems();
|
||||
|
||||
protected override void OnCancel(InputEventArgs e)
|
||||
=> Element.CancelPushingItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Nodify/Editor/States/Selecting.cs
Normal file
43
Nodify/Editor/States/Selecting.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Nodify.Interactivity
|
||||
{
|
||||
public static partial class EditorState
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the selecting state of the <see cref="NodifyEditor"/>.
|
||||
/// This state is responsible for handling item selection within the editor.
|
||||
/// </summary>
|
||||
public class Selecting : DragState<NodifyEditor>
|
||||
{
|
||||
protected override bool HasContextMenu => Element.HasContextMenu;
|
||||
protected override bool CanBegin => Element.CanSelectMultipleItems && !Element.IsPanning && !Element.IsCutting && !Element.IsPushingItems;
|
||||
protected override bool CanCancel => NodifyEditor.AllowSelectionCancellation;
|
||||
protected override bool IsToggle => EnableToggledSelectingMode;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Selecting"/> class.
|
||||
/// </summary>
|
||||
/// <param name="editor">The <see cref="NodifyEditor"/> associated with this state.</param>
|
||||
public Selecting(NodifyEditor editor)
|
||||
: base(editor, EditorGestures.Mappings.Editor.Selection.Select, EditorGestures.Mappings.Editor.Selection.Cancel)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnBegin(InputEventArgs e)
|
||||
{
|
||||
var selectionType = EditorGestures.Mappings.Editor.Selection.GetSelectionType(e);
|
||||
Element.BeginSelecting(selectionType);
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
=> Element.UpdateSelection(Element.MouseLocation);
|
||||
|
||||
protected override void OnEnd(InputEventArgs e)
|
||||
=> Element.EndSelecting();
|
||||
|
||||
protected override void OnCancel(InputEventArgs e)
|
||||
=> Element.CancelSelecting();
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Nodify/Editor/States/Zooming.cs
Normal file
43
Nodify/Editor/States/Zooming.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Nodify.Interactivity
|
||||
{
|
||||
public static partial class EditorState
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the zooming state of the <see cref="NodifyEditor"/>.
|
||||
/// This state handles zooming operations using the mouse wheel with an optional modifier key.
|
||||
/// </summary>
|
||||
public class Zooming : InputElementState<NodifyEditor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Zooming"/> class.
|
||||
/// </summary>
|
||||
/// <param name="editor">The <see cref="NodifyEditor"/> associated with this state.</param>
|
||||
public Zooming(NodifyEditor editor) : base(editor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnMouseWheel(MouseWheelEventArgs e)
|
||||
{
|
||||
EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;
|
||||
if (gestures.ZoomModifierKey == Keyboard.Modifiers && IsZoomingAllowed())
|
||||
{
|
||||
double zoom = Math.Pow(2.0, e.Delta / 3.0 / Mouse.MouseWheelDeltaForOneLine);
|
||||
Element.ZoomAtPosition(zoom, Element.MouseLocation);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsZoomingAllowed()
|
||||
{
|
||||
return !Element.DisableZooming
|
||||
&& (AllowZoomingWhileSelecting || !Element.IsSelecting)
|
||||
&& (AllowZoomingWhileCutting || !Element.IsCutting)
|
||||
&& (AllowZoomingWhilePushingItems || !Element.IsPushingItems)
|
||||
&& (AllowZoomingWhilePanning || !Element.IsPanning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user