Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0317dc8
Added support for keyboard navigation layers
miroiu Jun 7, 2025
2f2e129
Set default active layer
miroiu Jun 7, 2025
cd5c0d9
Implemented more keyboard navigation gestures and layer management
miroiu Jun 10, 2025
b204f12
Connections navigation
miroiu Jun 10, 2025
6984428
Add custom connection focus navigator and remove focus style on conne…
miroiu Jun 11, 2025
3655f14
Add focus visual to connections
miroiu Jun 11, 2025
a6f7e07
Add connection focus visual pen resource key
miroiu Jun 11, 2025
fdfe204
Add default focus visual
miroiu Jun 12, 2025
4e39bc8
Draw connection focus visual in an adorner
miroiu Jun 12, 2025
75397db
Focus the container inside viewport when no container is focused
miroiu Jun 12, 2025
d043d35
Fix tab navigation for editor and focus editor when the focused eleme…
miroiu Jun 13, 2025
155c2dc
Pan viewport on keyboard drag
miroiu Jun 14, 2025
a535c57
Bring selected element into view
miroiu Jun 14, 2025
44851ea
Fix nested editors crashing
miroiu Jun 14, 2025
0c34839
Fix navigation speed for small grid cell size
miroiu Jun 14, 2025
f1c26f6
Add comments to gestures
miroiu Jun 14, 2025
5dd5f57
Handle keyboard navigation in nested editors
miroiu Jun 16, 2025
27ebe0d
Improve keyboard navigation in Shapes app
miroiu Jun 16, 2025
fff6558
Add keyboard navigation to decorators layer
miroiu Jun 16, 2025
111bd1f
Added keyboard navigation to the minimap control
miroiu Jun 17, 2025
2fa6e13
Add focus visual styles
miroiu Jun 17, 2025
4932918
Fix editor focus visual
miroiu Jun 17, 2025
3feb086
Apply custom focus visual style to default controls
miroiu Jun 17, 2025
77c6732
Added AutoPanOnNodeFocus
miroiu Jun 17, 2025
5cdf1e5
Merge branch 'master' into feature/keyboard-focus
miroiu Jun 17, 2025
64dfcb9
Update changelog and summary comments
miroiu Jun 17, 2025
c7f72be
Remove preview events from minimap
miroiu Jun 17, 2025
3fc5f9f
Fix focus visual in playground app
miroiu Jun 18, 2025
fa92387
Update api reference
miroiu Jun 18, 2025
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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,27 @@

> - Breaking Changes:
> - Added ProcessHandledEvents to IInputHandler and removed it from InputProcessor
> - Renamed EditorGestures.Editor.ResetViewportLocation to EditorGestures.Editor.ResetViewport
> - Features:
> - Introduced a new BringIntoView method overload in NodifyEditor that accepts an offset from the viewport edges
> - Added BringIntoViewEdgeOffset to NodifyEditor to control the viewport edge offset when bringing the focused element into view
> - Added ResetViewport to NodifyEditor to reset the viewport's location and zoom
> - Improved tab and directional navigation, ensuring that focused elements are automatically brought into view
> - Added keyboard navigation layers for nodes, connections and decorators; restricting keyboard navigation to the active layer
> - Added ActiveNavigationLayer, ActivateNextNavigationLayer, ActivatePreviousNavigationLayer, RegisterNavigationLayer, RemoveNavigationLayer and ActivateNavigationLayer to NodifyEditor for keyboard layers management
> - Added KeyboardNavigationLayer property to NodifyEditor that allows navigating through the ItemContainers
> - Added AutoRegisterConnectionsLayer, AutoRegisterDecoratorsLayer, AutoFocusFirstElement, AutoPanOnNodeFocus, PanViewportOnKeyboardDrag and MinimumNavigationStepSize to NodifyEditor
> - Added EditorGestures.Editor.Keyboard for keyboard navigation gestures
> - Added FindNextFocusTarget, OnElementFocused and OnKeyboardNavigationLayerActivated virtual methods to NodifyEditor
> - Added new gestures for keyboard navigation available in EditorGestures.Editor.Keyboard
> - Added ToggleContentSelection to GroupingNode and its corresponding gesture to toggle the selection of nodes inside the group
> - Added ZoomIn, ZoomOut and ResetViewport methods to the Minimap control
> - Added ZoomIn, ZoomOut, ResetViewport and Pan gestures to EditorGestures.Minimap
> - Added NavigationStepSize static property to Minimap
> - Added Unbind to all gestures inside EditorGestures
> - Added the KeyComboGesture that requires a trigger key to be held down before pressing a combo key
> - Added FocusVisualPen and FocusVisualPadding dependency properties to BaseConnection
> - Added default focus visuals for base editor controls that can be included by referencing the FocusVisual.xaml file
> - Bugfixes:

#### **Version 7.0.4**
Expand Down
30 changes: 30 additions & 0 deletions Examples/Nodify.Calculator/App.xaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
<Application x:Class="Nodify.Calculator.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:nodify="https://miroiu.github.io/nodify"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Nodify;component/Themes/Dark.xaml" />
<ResourceDictionary Source="pack://application:,,,/Nodify;component/Themes/FocusVisual.xaml" />
<ResourceDictionary Source="pack://application:,,,/Nodify.Shared;component/Themes/Icons.xaml" />
<ResourceDictionary Source="pack://application:,,,/Nodify.Shared;component/Themes/Dark.xaml" />

<ResourceDictionary>

<Style x:Key="{x:Static SystemParameters.FocusVisualStyleKey}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle StrokeThickness="1"
StrokeDashArray="2"
Margin="-2"
RadiusX="3"
RadiusY="3"
Stroke="DodgerBlue" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<!--WPF is wonderful-->
<Style x:Key="OriginalNodeInputStyle"
TargetType="{x:Type nodify:NodeInput}"
BasedOn="{StaticResource {x:Type nodify:NodeInput}}" />

<Style x:Key="OriginalNodeOutputStyle"
TargetType="{x:Type nodify:NodeOutput}"
BasedOn="{StaticResource {x:Type nodify:NodeOutput}}" />

</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Expand Down
35 changes: 28 additions & 7 deletions Examples/Nodify.Calculator/EditorView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
x:Name="Editor">
<nodify:NodifyEditor.Resources>
<Style TargetType="{x:Type nodify:NodeInput}"
BasedOn="{StaticResource {x:Type nodify:NodeInput}}">
BasedOn="{StaticResource OriginalNodeInputStyle}">
<Setter Property="Header"
Value="{Binding}" />
<Setter Property="IsConnected"
Expand All @@ -166,7 +166,7 @@
</Style>

<Style TargetType="{x:Type nodify:NodeOutput}"
BasedOn="{StaticResource {x:Type nodify:NodeOutput}}">
BasedOn="{StaticResource OriginalNodeOutputStyle}">
<Setter Property="Header"
Value="{Binding}" />
<Setter Property="IsConnected"
Expand Down Expand Up @@ -235,6 +235,11 @@
Command="{Binding GroupSelectionCommand}" />
</nodify:NodifyEditor.InputBindings>

<nodify:NodifyEditor.CommandBindings>
<CommandBinding Command="{x:Static ApplicationCommands.ContextMenu}"
Executed="OpenContextMenu_Executed" />
</nodify:NodifyEditor.CommandBindings>

<CompositeCollection>
<nodify:DecoratorContainer DataContext="{Binding OperationsMenu}"
Location="{Binding Location}">
Expand All @@ -251,7 +256,8 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<ItemsControl ItemsSource="{Binding Input}">
<ItemsControl ItemsSource="{Binding Input}"
Focusable="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<nodify:NodeInput />
Expand All @@ -278,9 +284,11 @@
<StackPanel>
<Button Style="{StaticResource IconButton}"
Content="{StaticResource PlusIcon}"
FocusVisualStyle="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}"
Command="{Binding AddInputCommand}" />
<Button Style="{StaticResource IconButton}"
Content="{StaticResource RemoveKeyIcon}"
FocusVisualStyle="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}"
Command="{Binding RemoveInputCommand}" />
</StackPanel>
</DataTemplate>
Expand Down Expand Up @@ -353,8 +361,13 @@
</DataTemplate>

<DataTemplate DataType="{x:Type local:OperationGroupViewModel}">
<nodify:GroupingNode Header="{Binding Title}"
ActualSize="{Binding GroupSize, Mode=TwoWay}" />
<nodify:GroupingNode ActualSize="{Binding GroupSize, Mode=TwoWay}">
<nodify:GroupingNode.Header>
<shared:EditableTextBlock Text="{Binding Title}"
FontWeight="SemiBold"
IsEditing="True" />
</nodify:GroupingNode.Header>
</nodify:GroupingNode>
</DataTemplate>

<DataTemplate DataType="{x:Type local:OperationViewModel}">
Expand All @@ -371,6 +384,11 @@
Command="{Binding GroupSelectionCommand}" />
</nodify:NodifyEditor.InputBindings>

<nodify:NodifyEditor.CommandBindings>
<CommandBinding Command="{x:Static ApplicationCommands.ContextMenu}"
Executed="OpenContextMenu_Executed" />
</nodify:NodifyEditor.CommandBindings>

<nodify:NodifyEditor.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Name="AnimateBorder"
Expand Down Expand Up @@ -401,7 +419,8 @@
Opacity="0.7" />
</Border.Background>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.AvailableOperations}">
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.AvailableOperations}"
Focusable="False">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin"
Expand All @@ -420,7 +439,9 @@
Input="{Binding Input}"
BorderBrush="{StaticResource AnimatedBrush}"
BorderThickness="2"
MouseMove="OnNodeDrag">
MouseMove="OnNodeDrag"
Focusable="True"
KeyboardNavigation.TabNavigation="None">
<nodify:Node.Output>
<CompositeCollection>
<sys:String>Output</sys:String>
Expand Down
15 changes: 15 additions & 0 deletions Examples/Nodify.Calculator/EditorView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,20 @@ private void OnNodeDrag(object sender, MouseEventArgs e)
DragDrop.DoDragDrop(this, data, DragDropEffects.Copy);
}
}

private void OpenContextMenu_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (e.Source is NodifyEditor editor && editor.DataContext is CalculatorViewModel calculator)
{
if (calculator.OperationsMenu.IsVisible)
{
calculator.OperationsMenu.Close();
}
else
{
calculator.OperationsMenu.OpenAt(editor.ViewportLocation + new Vector(editor.ViewportSize.Width / 3, editor.ViewportSize.Height / 3));
}
}
}
}
}
11 changes: 11 additions & 0 deletions Examples/Nodify.Calculator/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Nodify.Interactivity;
using System.Windows;
using System.Windows.Input;

namespace Nodify.Calculator
{
Expand All @@ -10,6 +11,16 @@ public MainWindow()
InitializeComponent();

EditorGestures.Mappings.Editor.Cutting.Unbind();

EventManager.RegisterClassHandler(
typeof(UIElement),
Keyboard.PreviewGotKeyboardFocusEvent,
(KeyboardFocusChangedEventHandler)OnPreviewGotKeyboardFocus);
}

private void OnPreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Title = e.NewFocus.ToString();
}
}
}
11 changes: 8 additions & 3 deletions Examples/Nodify.Calculator/OperationsMenuView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
MinWidth="250"
d:DesignHeight="400"
d:DesignWidth="250"
d:DataContext="{d:DesignInstance local:OperationsMenuViewModel}">
d:DataContext="{d:DesignInstance local:OperationsMenuViewModel}"
Visibility="{Binding IsVisible, Converter={shared:BooleanToVisibilityConverter}}">
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource {x:Type TextBlock}}">
Expand All @@ -22,15 +23,17 @@
CornerRadius="3"
Background="{DynamicResource Node.BackgroundBrush}"
BorderBrush="{StaticResource NodifyEditor.SelectionRectangleStrokeBrush}"
BorderThickness="2"
Visibility="{Binding IsVisible, Converter={shared:BooleanToVisibilityConverter}}">
BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<ItemsControl Grid.Row="1"
x:Name="OperationsList"
Focusable="True"
KeyboardNavigation.TabNavigation="Cycle"
ItemsSource="{Binding AvailableOperations}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:OperationInfoViewModel}">
Expand All @@ -46,6 +49,8 @@
HorizontalContentAlignment="Left">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle"
Value="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
Expand Down
41 changes: 40 additions & 1 deletion Examples/Nodify.Calculator/OperationsMenuView.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,51 @@
using System.Windows.Controls;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Nodify.Calculator
{
public partial class OperationsMenuView : UserControl
{
private readonly WeakReference<UIElement?> _focusToRestore = new WeakReference<UIElement?>(null!);

public OperationsMenuView()
{
InitializeComponent();

IsVisibleChanged += OperationsMenuView_IsVisibleChanged;
}

private void OperationsMenuView_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (!IsLoaded)
{
return;
}

if (e.NewValue is true)
{
_focusToRestore.SetTarget(Keyboard.FocusedElement as UIElement);
Dispatcher.BeginInvoke(new Action(() => OperationsList.Focus()), System.Windows.Threading.DispatcherPriority.Input);
}
else if (e.NewValue is false)
{
if (_focusToRestore.TryGetTarget(out var elementToFocus))
{
Dispatcher.BeginInvoke(new Action(() =>
{
elementToFocus!.Focus();
}), System.Windows.Threading.DispatcherPriority.Input);
}
}
}

protected override void OnKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
SetCurrentValue(VisibilityProperty, Visibility.Collapsed);
}
}
}
}
21 changes: 21 additions & 0 deletions Examples/Nodify.Playground/App.xaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
<Application x:Class="Nodify.Playground.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:nodify="https://miroiu.github.io/nodify"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Style x:Key="{x:Static SystemParameters.FocusVisualStyleKey}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle StrokeThickness="1"
StrokeDashArray="2"
Margin="-2"
RadiusX="3"
RadiusY="3"
Stroke="DodgerBlue" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<ResourceDictionary Source="pack://application:,,,/Nodify;component/Themes/Nodify.xaml" />
<ResourceDictionary Source="pack://application:,,,/Nodify;component/Themes/FocusVisual.xaml" />
<ResourceDictionary Source="pack://application:,,,/Nodify.Shared;component/Themes/Icons.xaml" />
<ResourceDictionary Source="pack://application:,,,/Nodify.Shared;component/Themes/Nodify.xaml" />
<ResourceDictionary Source="pack://application:,,,/Nodify.Playground;component/Themes/Nodify.xaml" />
<ResourceDictionary>
<Color x:Key="NodifyEditor.FocusVisualColor">DodgerBlue</Color>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Expand Down
12 changes: 10 additions & 2 deletions Examples/Nodify.Playground/Editor/NodifyEditorView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@
Value="{Binding SelectableConnections, Source={x:Static local:EditorSettings.Instance}}" />
<Setter Property="IsSelected"
Value="{Binding IsSelected}" />
<Setter Property="StrokeThickness"
Value="{Binding ConnectionStrokeThickness, Source={x:Static local:EditorSettings.Instance}}" />
<Setter Property="OutlineThickness"
Value="{Binding ConnectionOutlineThickness, Source={x:Static local:EditorSettings.Instance}}" />
<Setter Property="FocusVisualPadding"
Value="{Binding ConnectionFocusVisualPadding, Source={x:Static local:EditorSettings.Instance}}" />
</Style>

<DataTemplate x:Key="CircuitConnectionTemplate">
Expand Down Expand Up @@ -531,7 +537,8 @@
</nodify:Node.ContentTemplate>
<nodify:Node.HeaderTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl ItemsSource="{Binding}"
Focusable="False">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ConnectorViewModel}">
<nodify:NodeInput Orientation="Vertical" />
Expand All @@ -548,7 +555,8 @@
</nodify:Node.HeaderTemplate>
<nodify:Node.FooterTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl ItemsSource="{Binding}"
Focusable="False">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ConnectorViewModel}">
<nodify:NodeOutput Orientation="Vertical" />
Expand Down
Loading
Loading