Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow changing editor gestures at runtime #104

Merged
merged 4 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@
> - 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
> - 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
69 changes: 69 additions & 0 deletions Examples/Nodify.Playground/EditorInputMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Windows.Input;

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

public enum EditorInputMappings
{
Default,
Blender3D
miroiu marked this conversation as resolved.
Show resolved Hide resolved
}

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

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, EditorInputMappings mappings)
{
var newMappings = mappings.ToInputMappings();
value.Apply(newMappings);
}

public static EditorGestures ToInputMappings(this EditorInputMappings mappings)
{
return mappings switch
{
EditorInputMappings.Blender3D => new Blender3DInputMappings(),
_ => new EditorGestures()
};
}
}

public class Blender3DInputMappings : EditorGestures
miroiu marked this conversation as resolved.
Show resolved Hide resolved
{
public Blender3DInputMappings()
{
Editor.Pan.Value = new AnyGesture(new MouseGesture(MouseAction.LeftClick), new MouseGesture(MouseAction.MiddleClick));
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(new SelectionGestures(MouseAction.RightClick));
}
}
}
25 changes: 23 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<EditorInputMappings>(
() => Instance.EditorInputMappings,
val => Instance.EditorInputMappings = 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,20 @@ private PlaygroundSettings()

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

private EditorInputMappings _editorInputMappings;
public EditorInputMappings EditorInputMappings
{
get => _editorInputMappings;
set => SetProperty(ref _editorInputMappings, value);
}

private EditorInputMode _editorInputMode;
public EditorInputMode EditorInputMode
{
get => _editorInputMode;
set => SetProperty(ref _editorInputMode, value);
}

private bool _shouldConnectNodes = true;
public bool ShouldConnectNodes
{
Expand Down
15 changes: 15 additions & 0 deletions Examples/Nodify.Playground/PlaygroundViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,27 @@ public PlaygroundViewModel()

BindingOperations.EnableCollectionSynchronization(GraphViewModel.Nodes, GraphViewModel.Nodes);
BindingOperations.EnableCollectionSynchronization(GraphViewModel.Connections, GraphViewModel.Connections);

Settings.PropertyChanged += OnSettingsChanged;
}

private void OnSettingsChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(PlaygroundSettings.EditorInputMappings))
{
EditorGestures.Mappings.Apply(Settings.EditorInputMappings);
}
else if (e.PropertyName == nameof(PlaygroundSettings.EditorInputMode))
{
EditorGestures.Mappings.Apply(Settings.EditorInputMode);
}
}

public ICommand GenerateRandomNodesCommand { get; }
public ICommand PerformanceTestCommand { get; }
public ICommand ToggleConnectionsCommand { get; }
public ICommand ResetCommand { get; }

public PlaygroundSettings Settings => PlaygroundSettings.Instance;

private void ResetGraph()
Expand Down
5 changes: 3 additions & 2 deletions Nodify/Connections/BaseConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,8 @@ protected override void OnMouseDown(MouseButtonEventArgs e)

this.CaptureMouseSafe();

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 @@ -668,7 +669,7 @@ protected override void OnMouseDown(MouseButtonEventArgs 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 @@ -327,11 +327,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 @@ -351,12 +352,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.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);
Expand All @@ -375,7 +377,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
Loading