Skip to content

Commit

Permalink
Add a Minimap control to Nodify (#124)
Browse files Browse the repository at this point in the history
* Added a Minimap control to Nodify

* Add  MaxViewportOffset and Zoom event

* Add ResizeToViewport

* Add playground settings

* Add IsReadOnly to minimap to allow disabling controls

* Add documentation
  • Loading branch information
miroiu authored Jul 23, 2024
1 parent ce85abe commit 3dad36b
Show file tree
Hide file tree
Showing 31 changed files with 943 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

> - Breaking Changes:
> - Features:
> - Added a Minimap control and EditorGestures.Minimap to Nodify
> - Added ContentContainerStyle, HeaderContainerStyle and FooterContainerStyle dependency properties to Node
> - Added BringIntoView that takes a Rect parameter to NodifyEditor
> - Added the NodifyEditor's DataContext as the parameter of the ItemsSelectStartedCommand, ItemsSelectCompletedCommand, ItemsDragStartedCommand and ItemsDragCompletedCommand commands
Expand Down
31 changes: 31 additions & 0 deletions Examples/Nodify.Playground/Editor/NodifyEditorView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,37 @@
<Grid Background="{StaticResource LargeGridLinesDrawingBrush}"
Visibility="{Binding ShowGridLines, Source={x:Static local:PlaygroundSettings.Instance}, Converter={shared:BooleanToVisibilityConverter}}"
Panel.ZIndex="-2" />

<nodify:Minimap ItemsSource="{Binding ItemsSource, ElementName=Editor}"
ViewportSize="{Binding ViewportSize, ElementName=Editor}"
ViewportLocation="{Binding ViewportLocation, ElementName=Editor}"
Visibility="{Binding ShowMinimap, Source={x:Static local:PlaygroundSettings.Instance}, Converter={shared:BooleanToVisibilityConverter}}"
IsReadOnly="{Binding DisableMinimapControls, Source={x:Static local:PlaygroundSettings.Instance}}"
ResizeToViewport="{Binding ResizeToViewport, Source={x:Static local:PlaygroundSettings.Instance}}"
MaxViewportOffset="{Binding MinimapMaxViewportOffset.Size, Source={x:Static local:PlaygroundSettings.Instance}}"
Zoom="Minimap_Zoom"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Width="300"
Height="200"
Margin="5 40">
<nodify:Minimap.ItemTemplate>
<DataTemplate DataType="{x:Type local:NodeViewModel}">
<Grid />
</DataTemplate>
</nodify:Minimap.ItemTemplate>
<nodify:Minimap.ItemContainerStyle>
<Style TargetType="{x:Type nodify:MinimapItem}"
BasedOn="{StaticResource {x:Type nodify:MinimapItem}}">
<Setter Property="Location"
Value="{Binding Location}" />
<Setter Property="Width"
Value="150" />
<Setter Property="Height"
Value="130" />
</Style>
</nodify:Minimap.ItemContainerStyle>
</nodify:Minimap>
</Grid>

</UserControl>
5 changes: 5 additions & 0 deletions Examples/Nodify.Playground/Editor/NodifyEditorView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ public NodifyEditorView()
{
InitializeComponent();
}

private void Minimap_Zoom(object sender, ZoomEventArgs e)
{
EditorInstance.ZoomAtPosition(e.Zoom, e.Location);
}
}
}
7 changes: 4 additions & 3 deletions Examples/Nodify.Playground/EditorSettings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;

namespace Nodify.Playground
{
Expand Down Expand Up @@ -398,14 +399,14 @@ public ArrowHeadShape ArrowHeadShape
set => SetProperty(ref _arrowHeadShape, value);
}

private PointEditor _connectionSourceOffset = new PointEditor { X = 14, Y = 0 };
private PointEditor _connectionSourceOffset = new Size(14, 0);
public PointEditor ConnectionSourceOffset
{
get => _connectionSourceOffset;
set => SetProperty(ref _connectionSourceOffset, value);
}

private PointEditor _connectionTargetOffset = new PointEditor { X = 14, Y = 0 };
private PointEditor _connectionTargetOffset = new Size(14, 0);
public PointEditor ConnectionTargetOffset
{
get => _connectionTargetOffset;
Expand All @@ -426,7 +427,7 @@ public double DirectionalArrowsOffset
set => SetProperty(ref _directionalArrowsOffset, value);
}

private PointEditor _connectionArrowSize = new PointEditor { X = 8, Y = 8 };
private PointEditor _connectionArrowSize = new Size(8, 8);
public PointEditor ConnectionArrowSize
{
get => _connectionArrowSize;
Expand Down
49 changes: 49 additions & 0 deletions Examples/Nodify.Playground/PlaygroundSettings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;

namespace Nodify.Playground
{
Expand All @@ -19,6 +20,26 @@ private PlaygroundSettings()
() => Instance.EditorInputMode,
val => Instance.EditorInputMode = val,
"Editor input mode"),
new ProxySettingViewModel<bool>(
() => Instance.ShowMinimap,
val => Instance.ShowMinimap = val,
"Show minimap",
"Set Enable nodes dragging optimization to false for realtime updates"),
new ProxySettingViewModel<bool>(
() => Instance.DisableMinimapControls,
val => Instance.DisableMinimapControls = val,
"Disable minimap controls",
"Whether the minimap can move and zoom the viewport"),
new ProxySettingViewModel<bool>(
() => Instance.ResizeToViewport,
val => Instance.ResizeToViewport = val,
"Minimap resize to viewport",
"Whether the minimap should resized to also display the viewport"),
new ProxySettingViewModel<PointEditor>(
() => Instance.MinimapMaxViewportOffset,
val => Instance.MinimapMaxViewportOffset = val,
"Minimap max viewport offset",
"The max position from the items extent that the viewport can move to"),
new ProxySettingViewModel<bool>(
() => Instance.ShowGridLines,
val => Instance.ShowGridLines = val,
Expand Down Expand Up @@ -76,6 +97,34 @@ public EditorInputMode EditorInputMode
.Then(() => EditorGestures.Mappings.Apply(value));
}

private bool _showMinimap = true;
public bool ShowMinimap
{
get => _showMinimap;
set => SetProperty(ref _showMinimap, value);
}

private bool _disableMinimapControls = false;
public bool DisableMinimapControls
{
get => _disableMinimapControls;
set => SetProperty(ref _disableMinimapControls, value);
}

private bool _resizeToViewport = false;
public bool ResizeToViewport
{
get => _resizeToViewport;
set => SetProperty(ref _resizeToViewport, value);
}

private PointEditor _minimapViewportOffset = new Size(2000, 2000);
public PointEditor MinimapMaxViewportOffset
{
get => _minimapViewportOffset;
set => SetProperty(ref _minimapViewportOffset, value);
}

private bool _shouldConnectNodes = true;
public bool ShouldConnectNodes
{
Expand Down
7 changes: 6 additions & 1 deletion Examples/Nodify.Playground/PointEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public Size Size
});
}

public string XLabel { get; set; } = "x";
public string YLabel { get; set; } = "y";

public static implicit operator PointEditor(Point point)
{
return new PointEditor
Expand All @@ -68,7 +71,9 @@ public static implicit operator PointEditor(Size size)
return new PointEditor
{
X = size.Width,
Y = size.Height
Y = size.Height,
XLabel = "w",
YLabel = "h"
};
}
}
Expand Down
16 changes: 10 additions & 6 deletions Examples/Nodify.Playground/PointEditorView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,21 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<TextBlock Text="X:"
TextAlignment="Center"
VerticalAlignment="Center" />
<TextBlock TextAlignment="Center"
VerticalAlignment="Center">
<Run Text="{Binding XLabel}" />
<Run Text=":" />
</TextBlock>
<TextBox Text="{Binding X, Mode=TwoWay}"
Grid.Column="1" />

<TextBlock Text="Y:"
TextAlignment="Center"
<TextBlock TextAlignment="Center"
VerticalAlignment="Center"
Grid.Row="1"
Margin="0 5 0 0" />
Margin="0 5 0 0">
<Run Text="{Binding YLabel}" />
<Run Text=":" />
</TextBlock>
<TextBox Text="{Binding Y, Mode=TwoWay}"
Margin="0 5 0 0"
Grid.Row="1"
Expand Down
102 changes: 102 additions & 0 deletions Examples/Nodify.Shapes/Canvas/CanvasView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,108 @@
</nodify:NodifyEditor.ItemContainerStyle>
</nodify:NodifyEditor>

<!--MINIMAP-->
<shared:ResizablePanel Directions="BottomLeft"
VerticalAlignment="Top"
HorizontalAlignment="Right"
Width="300"
Height="200"
MinWidth="250"
MinHeight="150"
BorderBrush="{x:Null}"
Margin="20">
<shared:ResizablePanel.Resources>
<Style TargetType="{x:Type shared:Resizer}">
<Setter Property="Cursor"
Value="SizeNESW" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type shared:Resizer}">
<Grid Margin="-3 -6 -6 -3" Background="Transparent">
<Rectangle Height="12"
Width="3"
Fill="#d2d4d7"
HorizontalAlignment="Left"
VerticalAlignment="Bottom" />
<Rectangle Height="3"
Width="12"
Fill="#d2d4d7"
VerticalAlignment="Bottom"
HorizontalAlignment="Left" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</shared:ResizablePanel.Resources>
<Border CornerRadius="3"
BorderBrush="#1e293b"
BorderThickness="3">
<Border.Effect>
<DropShadowEffect ShadowDepth="1" />
</Border.Effect>

<nodify:Minimap ItemsSource="{Binding ItemsSource, ElementName=Editor}"
ViewportLocation="{Binding ViewportLocation, ElementName=Editor}"
ViewportSize="{Binding ViewportSize, ElementName=Editor}"
ResizeToViewport="True"
Zoom="Minimap_Zoom">
<nodify:Minimap.Resources>
<DataTemplate DataType="{x:Type local:EllipseViewModel}">
<Ellipse Stretch="Fill"
Fill="{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}"
Stroke="{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
StrokeThickness="2"
Opacity="0.8" />
</DataTemplate>

<DataTemplate DataType="{x:Type local:RectangleViewModel}">
<Rectangle Stretch="Fill"
Fill="{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}"
Stroke="{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
StrokeThickness="2"
Opacity="0.8" />
</DataTemplate>

<DataTemplate DataType="{x:Type local:TriangleViewModel}">
<Polygon Points="0,100 50,0 100,100"
Stretch="Fill"
Fill="{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}"
Stroke="{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}"
StrokeThickness="2"
Opacity="0.8" />
</DataTemplate>
</nodify:Minimap.Resources>
<nodify:Minimap.Background>
<SolidColorBrush Color="#111a2d"
Opacity="0.5" />
</nodify:Minimap.Background>
<nodify:Minimap.ItemContainerStyle>
<Style TargetType="{x:Type nodify:MinimapItem}">
<Setter Property="Location"
Value="{Binding Location}" />
<Setter Property="Width"
Value="{Binding Width}" />
<Setter Property="Height"
Value="{Binding Height}" />
</Style>
</nodify:Minimap.ItemContainerStyle>
<nodify:Minimap.ViewportStyle>
<Style TargetType="Rectangle">
<Setter Property="StrokeThickness"
Value="3" />
<Setter Property="Fill">
<Setter.Value>
<SolidColorBrush Color="#445e87"
Opacity="0.3" />
</Setter.Value>
</Setter>
</Style>
</nodify:Minimap.ViewportStyle>
</nodify:Minimap>
</Border>
</shared:ResizablePanel>

<!--TOOLBARS-->

<Border VerticalAlignment="Bottom"
Expand Down
5 changes: 5 additions & 0 deletions Examples/Nodify.Shapes/Canvas/CanvasView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,10 @@ private void Toolbar_MouseDown(object sender, MouseButtonEventArgs e)
}

#endregion

private void Minimap_Zoom(object sender, ZoomEventArgs e)
{
Editor.ZoomAtPosition(e.Zoom, e.Location);
}
}
}
2 changes: 2 additions & 0 deletions Examples/Nodify.Shapes/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public partial class MainWindow : Window
public MainWindow()
{
InitializeComponent();

NodifyEditor.EnableDraggingContainersOptimizations = false;
}
}
}
8 changes: 4 additions & 4 deletions Nodify/Connections/BaseConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ public Size ArrowSize
}

/// <summary>
/// Splits the connection. Triggered by <see cref="EditorGestures.Connection.Split"/> gesture.
/// Splits the connection. Triggered by <see cref="EditorGestures.ConnectionGestures.Split"/> gesture.
/// Parameter is the location where the splitting ocurred.
/// </summary>
public ICommand? SplitCommand
Expand All @@ -282,7 +282,7 @@ public ICommand? SplitCommand
}

/// <summary>
/// Removes this connection. Triggered by <see cref="EditorGestures.Connection.Disconnect"/> gesture.
/// Removes this connection. Triggered by <see cref="EditorGestures.ConnectionGestures.Disconnect"/> gesture.
/// Parameter is the location where the disconnect ocurred.
/// </summary>
public ICommand? DisconnectCommand
Expand Down Expand Up @@ -352,14 +352,14 @@ public FontStretch FontStretch
public static readonly RoutedEvent DisconnectEvent = EventManager.RegisterRoutedEvent(nameof(Disconnect), RoutingStrategy.Bubble, typeof(ConnectionEventHandler), typeof(BaseConnection));
public static readonly RoutedEvent SplitEvent = EventManager.RegisterRoutedEvent(nameof(Split), RoutingStrategy.Bubble, typeof(ConnectionEventHandler), typeof(BaseConnection));

/// <summary>Triggered by the <see cref="EditorGestures.Connection.Disconnect"/> gesture.</summary>
/// <summary>Triggered by the <see cref="EditorGestures.ConnectionGestures.Disconnect"/> gesture.</summary>
public event ConnectionEventHandler Disconnect
{
add => AddHandler(DisconnectEvent, value);
remove => RemoveHandler(DisconnectEvent, value);
}

/// <summary>Triggered by the <see cref="EditorGestures.Connection.Split"/> gesture.</summary>
/// <summary>Triggered by the <see cref="EditorGestures.ConnectionGestures.Split"/> gesture.</summary>
public event ConnectionEventHandler Split
{
add => AddHandler(SplitEvent, value);
Expand Down
6 changes: 3 additions & 3 deletions Nodify/Connections/Connector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ public class Connector : Control
public static readonly RoutedEvent PendingConnectionDragEvent = EventManager.RegisterRoutedEvent(nameof(PendingConnectionDrag), RoutingStrategy.Bubble, typeof(PendingConnectionEventHandler), typeof(Connector));
public static readonly RoutedEvent DisconnectEvent = EventManager.RegisterRoutedEvent(nameof(Disconnect), RoutingStrategy.Bubble, typeof(ConnectorEventHandler), typeof(Connector));

/// <summary>Triggered by the <see cref="EditorGestures.Connector.Connect"/> gesture.</summary>
/// <summary>Triggered by the <see cref="EditorGestures.ConnectorGestures.Connect"/> gesture.</summary>
public event PendingConnectionEventHandler PendingConnectionStarted
{
add => AddHandler(PendingConnectionStartedEvent, value);
remove => RemoveHandler(PendingConnectionStartedEvent, value);
}

/// <summary>Triggered by the <see cref="EditorGestures.Connector.Connect"/> gesture.</summary>
/// <summary>Triggered by the <see cref="EditorGestures.ConnectorGestures.Connect"/> gesture.</summary>
public event PendingConnectionEventHandler PendingConnectionCompleted
{
add => AddHandler(PendingConnectionCompletedEvent, value);
Expand All @@ -43,7 +43,7 @@ public event PendingConnectionEventHandler PendingConnectionDrag
remove => RemoveHandler(PendingConnectionDragEvent, value);
}

/// <summary>Triggered by the <see cref="EditorGestures.Connector.Disconnect"/> gesture.</summary>
/// <summary>Triggered by the <see cref="EditorGestures.ConnectorGestures.Disconnect"/> gesture.</summary>
public event ConnectorEventHandler Disconnect
{
add => AddHandler(DisconnectEvent, value);
Expand Down
Loading

0 comments on commit 3dad36b

Please sign in to comment.