Skip to content

Commit

Permalink
Allow changing editor gestures at runtime (miroiu#104)
Browse files Browse the repository at this point in the history
Separated selection gestures for ItemContainer and GroupingNode from the NodifyEditor
Added new gesture types: AnyGesture, AllGestures, and NodifyGesture
Fixed a bug where the item container would incorrectly transition to the dragging state on mouse over
  • Loading branch information
miroiu authored and BAndysc committed May 13, 2024
1 parent d498ad7 commit 1449a1d
Show file tree
Hide file tree
Showing 21 changed files with 436 additions and 115 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
> - Breaking Changes:
> - Added a parameter for the orientation to DrawArrowGeometry, DrawDefaultArrowhead, DrawRectangleArrowhead and DrawEllipseArrowhead in BaseConnection
> - Added source and target parameters to GetTextPosition in BaseConnection
> - EditorGestures is now a singleton instead of a static class (can be inherited to create custom mappings)
> - Selection gestures for ItemContainer and GroupingNode are now separated from the NodifyEditor selection gestures
> - Renamed EditorGestures.Editor.Zoom to ZoomModifierKey
> - Features:
> - Added SourceOrientation and TargetOrientation to BaseConnection to support vertical connectors (vertical/mixed connection orientation)
> - Added DirectionalArrowsCount to BaseConnection to allow drawing multipe arrows on a connection flowing in the connection direction
> - Added DrawDirectionalArrowsGeometry and DrawDirectionalArrowheadGeometry to BaseConnection to allow customizing the directional arrows
> - Improved EditorGestures to allow changing input gestures at runtime
> - Added new gesture types: AnyGesture, AllGestures, and InputGestureRef
> - Bugfixes:
> - Fixed BaseConnection.Text not always displaying in the center of the connection
> - Fixed a bug where the item container would incorrectly transition to the dragging state on mouse over
#### **Version 5.2.0**

Expand Down
70 changes: 70 additions & 0 deletions Examples/Nodify.Playground/EditorInputMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Windows.Input;

namespace Nodify.Playground
{
public enum EditorInputMode
{
Default,
PanOnly,
SelectOnly
}

public enum EditorGesturesMappings
{
Default,
Custom
}

public static class EditorInputModeExtensions
{
public static void Apply(this EditorGestures mappings, EditorInputMode inputMode)
{
mappings.Apply(PlaygroundSettings.Instance.EditorGesturesMappings.ToGesturesMappings());

switch (inputMode)
{
case EditorInputMode.PanOnly:
mappings.Editor.Selection.Apply(EditorGestures.SelectionGestures.None);
mappings.ItemContainer.Selection.Apply(EditorGestures.SelectionGestures.None);
mappings.ItemContainer.Drag.Value = MultiGesture.None;
mappings.Connector.Connect.Value = MultiGesture.None;
break;
case EditorInputMode.SelectOnly:
mappings.Editor.Pan.Value = MultiGesture.None;
mappings.ItemContainer.Drag.Value = MultiGesture.None;
mappings.Connector.Connect.Value = MultiGesture.None;
break;
case EditorInputMode.Default:
break;
}
}

public static void Apply(this EditorGestures value, EditorGesturesMappings mappings)
{
var newMappings = mappings.ToGesturesMappings();
value.Apply(newMappings);
}

public static EditorGestures ToGesturesMappings(this EditorGesturesMappings mappings)
{
return mappings switch
{
EditorGesturesMappings.Custom => new CustomGesturesMappings(),
_ => new EditorGestures()
};
}
}

public class CustomGesturesMappings : EditorGestures
{
public CustomGesturesMappings()
{
Editor.Pan.Value = new AnyGesture(new MouseGesture(MouseAction.LeftClick), new MouseGesture(MouseAction.MiddleClick));
Editor.ZoomModifierKey = ModifierKeys.Control;
Editor.Selection.Apply(new SelectionGestures(MouseAction.RightClick));
// comment to drag with right click - we copy the default gestures of the item container which uses left click for selection
ItemContainer.Drag.Value = new AnyGesture(ItemContainer.Selection.Replace.Value, ItemContainer.Selection.Remove.Value, ItemContainer.Selection.Append.Value, ItemContainer.Selection.Invert.Value);
ItemContainer.Selection.Apply(Editor.Selection);
}
}
}
6 changes: 3 additions & 3 deletions Examples/Nodify.Playground/EditorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private EditorSettings()
() => Instance.ConnectionStyle,
val => Instance.ConnectionStyle = val,
"Connection style: "),
new ProxySettingViewModel<string>(
new ProxySettingViewModel<string?>(
() => Instance.ConnectionText,
val => Instance.ConnectionText = val,
"Connection text: "),
Expand Down Expand Up @@ -344,8 +344,8 @@ public ConnectionStyle ConnectionStyle
set => SetProperty(ref _connectionStyle, value);
}

private string _connectionText;
public string ConnectionText
private string? _connectionText;
public string? ConnectionText
{
get => _connectionText;
set => SetProperty(ref _connectionText, value);
Expand Down
4 changes: 3 additions & 1 deletion Examples/Nodify.Playground/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
global using Avalonia.Controls.ApplicationLifetimes;
global using Avalonia.Media;
global using Avalonia.Threading;
global using Avalonia.Layout;
global using Avalonia.Layout;
global using ModifierKeys = Avalonia.Input.KeyModifiers;
global using Nodify.Compatibility;
27 changes: 25 additions & 2 deletions Examples/Nodify.Playground/PlaygroundSettings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Nodify.Playground
Expand All @@ -12,6 +11,14 @@ private PlaygroundSettings()
{
Settings = new ObservableCollection<ISettingViewModel>()
{
new ProxySettingViewModel<EditorGesturesMappings>(
() => Instance.EditorGesturesMappings,
val => Instance.EditorGesturesMappings = val,
"Editor input mappings"),
new ProxySettingViewModel<EditorInputMode>(
() => Instance.EditorInputMode,
val => Instance.EditorInputMode = val,
"Editor input mode"),
new ProxySettingViewModel<bool>(
() => Instance.ShowGridLines,
val => Instance.ShowGridLines = val,
Expand Down Expand Up @@ -53,6 +60,22 @@ private PlaygroundSettings()

public static PlaygroundSettings Instance { get; } = new PlaygroundSettings();

private EditorGesturesMappings _editorGesturesMappings;
public EditorGesturesMappings EditorGesturesMappings
{
get => _editorGesturesMappings;
set => SetProperty(ref _editorGesturesMappings, value)
.Then(() => EditorGestures.Mappings.Apply(value));
}

private EditorInputMode _editorInputMode;
public EditorInputMode EditorInputMode
{
get => _editorInputMode;
set => SetProperty(ref _editorInputMode, value)
.Then(() => EditorGestures.Mappings.Apply(value));
}

private bool _shouldConnectNodes = true;
public bool ShouldConnectNodes
{
Expand Down
9 changes: 7 additions & 2 deletions Nodify/Compatibility/Input/InputKeyGesture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@

namespace Nodify.Compatibility;

internal class InputKeyGesture : InputGesture
public class InputKeyGesture : InputGesture
{
private KeyGesture keyGesture;

public InputKeyGesture(Key key, KeyModifiers modifiers = KeyModifiers.None)
{
keyGesture = new KeyGesture(key, modifiers);
}


public InputKeyGesture(KeyGesture gesture)
{
keyGesture = gesture;
}

public override bool Matches(object targetElement, EventArgs inputEventArgs)
{
return keyGesture.Matches(inputEventArgs as KeyEventArgs);
Expand Down
2 changes: 1 addition & 1 deletion Nodify/Compatibility/Input/MouseGesture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Nodify.Compatibility;

internal class MouseGesture : InputGesture
public class MouseGesture : InputGesture
{
//------------------------------------------------------
//
Expand Down
5 changes: 3 additions & 2 deletions Nodify/Connections/BaseConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,8 @@ protected override void OnPointerPressed(PointerPressedEventArgs e)
e.Pointer.Capture(this);
this.PropagateMouseCapturedWithin(true);

if (EditorGestures.Connection.Split.Matches(e.Source, e) && (SplitCommand?.CanExecute(this) ?? false))
EditorGestures.ConnectionGestures gestures = EditorGestures.Mappings.Connection;
if (gestures.Split.Matches(e.Source, e) && (SplitCommand?.CanExecute(this) ?? false))
{
Point splitLocation = e.GetPosition(this);
object? connection = DataContext;
Expand All @@ -673,7 +674,7 @@ protected override void OnPointerPressed(PointerPressedEventArgs e)

e.Handled = true;
}
else if (EditorGestures.Connection.Disconnect.Matches(e.Source, e) && (DisconnectCommand?.CanExecute(this) ?? false))
else if (gestures.Disconnect.Matches(e.Source, e) && (DisconnectCommand?.CanExecute(this) ?? false))
{
Point splitLocation = e.GetPosition(this);
object? connection = DataContext;
Expand Down
12 changes: 7 additions & 5 deletions Nodify/Connections/Connector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -334,11 +334,12 @@ protected override void OnMouseDown(MouseButtonEventArgs e)

e.Handled = true;

if (EditorGestures.Connector.Disconnect.Matches(e.Source, e))
EditorGestures.ConnectorGestures gestures = EditorGestures.Mappings.Connector;
if (gestures.Disconnect.Matches(e.Source, e))
{
OnDisconnect();
}
else if (EditorGestures.Connector.Connect.Matches(e.Source, e))
else if (gestures.Connect.Matches(e.Source, e))
{
if (EnableStickyConnections && IsPendingConnection)
{
Expand All @@ -359,12 +360,13 @@ protected override void OnMouseUp(MouseButtonEventArgs e)
// Don't select the ItemContainer when starting a pending connecton for sticky connections
e.Handled = EnableStickyConnections && IsPendingConnection;

if (!EnableStickyConnections && EditorGestures.Connector.Connect.Matches(e.Source, e))
EditorGestures.ConnectorGestures gestures = EditorGestures.Mappings.Connector;
if (!EnableStickyConnections && gestures.Connect.Matches(e.Source, e))
{
OnConnectorDragCompleted(e: e);
e.Handled = true;
}
else if (AllowPendingConnectionCancellation && IsPendingConnection && EditorGestures.Connector.CancelAction.Matches(e.Source, e))
else if (AllowPendingConnectionCancellation && IsPendingConnection && gestures.CancelAction.Matches(e.Source, e))
{
// Cancel pending connection
OnConnectorDragCompleted(cancel: true, e: e);
Expand Down Expand Up @@ -397,7 +399,7 @@ protected override void OnMouseUp(MouseButtonEventArgs e)
/// <inheritdoc />
protected override void OnKeyUp(KeyEventArgs e)
{
if (AllowPendingConnectionCancellation && EditorGestures.Connector.CancelAction.Matches(e.Source, e))
if (AllowPendingConnectionCancellation && EditorGestures.Mappings.Connector.CancelAction.Matches(e.Source, e))
{
// Cancel pending connection
OnConnectorDragCompleted(cancel: true);
Expand Down
8 changes: 4 additions & 4 deletions Nodify/EditorCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ public enum Alignment
/// </summary>
public static RoutedUICommand ZoomIn { get; } = new RoutedUICommand("Zoom in", nameof(ZoomIn), typeof(EditorCommands), new InputGestureCollection
{
EditorGestures.ZoomIn
EditorGestures.Mappings.Editor.ZoomIn
});

/// <summary>
/// Zoom out relative to the editor's viewport center.
/// </summary>
public static RoutedUICommand ZoomOut { get; } = new RoutedUICommand("Zoom out", nameof(ZoomOut), typeof(EditorCommands), new InputGestureCollection
{
EditorGestures.ZoomOut
EditorGestures.Mappings.Editor.ZoomOut
});

/// <summary>
Expand All @@ -50,15 +50,15 @@ public enum Alignment
/// </summary>
public static RoutedUICommand BringIntoView { get; } = new RoutedUICommand("Bring location into view", nameof(BringIntoView), typeof(EditorCommands), new InputGestureCollection
{
EditorGestures.ResetViewportLocation
EditorGestures.Mappings.Editor.ResetViewportLocation
});

/// <summary>
/// Scales the editor's viewport to fit all the <see cref="ItemContainer"/>s if that's possible.
/// </summary>
public static RoutedUICommand FitToScreen { get; } = new RoutedUICommand("Fit to screen", nameof(FitToScreen), typeof(EditorCommands), new InputGestureCollection
{
EditorGestures.FitToScreen
EditorGestures.Mappings.Editor.FitToScreen
});

/// <summary>
Expand Down
Loading

0 comments on commit 1449a1d

Please sign in to comment.