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 all commits
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
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
2 changes: 1 addition & 1 deletion Examples/Nodify.Calculator/OperationViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public OperationViewModel()
});
}

private void OnInputValueChanged(object sender, PropertyChangedEventArgs e)
private void OnInputValueChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ConnectorViewModel.Value))
{
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
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
5 changes: 3 additions & 2 deletions Nodify/Connections/BaseConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
{
#region Dependency Properties

public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source), typeof(Point), typeof(BaseConnection), new FrameworkPropertyMetadata(BoxValue.Point, FrameworkPropertyMetadataOptions.AffectsRender));

Check warning on line 114 in Nodify/Connections/BaseConnection.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'BaseConnection.SourceProperty'

Check warning on line 114 in Nodify/Connections/BaseConnection.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'BaseConnection.SourceProperty'
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(nameof(Target), typeof(Point), typeof(BaseConnection), new FrameworkPropertyMetadata(BoxValue.Point, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty SourceOffsetProperty = DependencyProperty.Register(nameof(SourceOffset), typeof(Size), typeof(BaseConnection), new FrameworkPropertyMetadata(BoxValue.ConnectionOffset, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty TargetOffsetProperty = DependencyProperty.Register(nameof(TargetOffset), typeof(Size), typeof(BaseConnection), new FrameworkPropertyMetadata(BoxValue.ConnectionOffset, FrameworkPropertyMetadataOptions.AffectsRender));
Expand Down Expand Up @@ -647,7 +647,8 @@

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 @@

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