Skip to content

Commit abbaf9e

Browse files
authored
Add the ability to select connections (#139)
* Add multi-select and support for custom connections * Added CanSelectMultipleItems to NodifyEditor
1 parent efa15e0 commit abbaf9e

22 files changed

Lines changed: 8770 additions & 7794 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
> - Breaking Changes:
66
> - Features:
7+
> - Added SelectedConnection, SelectedConnections, CanSelectMultipleConnections and CanSelectMultipleItems dependency properties to NodifyEditor
8+
> - Added IsSelected and IsSelectable attached dependency properties to BaseConnection
9+
> - Added PrioritizeBaseConnectionForSelection static field to BaseConnection
10+
> - Added EditorGestures.Connection.Selection
711
> - Bugfixes:
812
913
#### **Version 6.4.0**

Examples/Nodify.Playground/Editor/ConnectionViewModel.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ public ConnectorViewModel Output
2626
set => SetProperty(ref _output, value);
2727
}
2828

29-
public void Split(Point point)
30-
=> Graph.Schema.SplitConnection(this, point);
31-
32-
public void Remove()
33-
=> Graph.Connections.Remove(this);
29+
private bool _isSelected;
30+
public bool IsSelected
31+
{
32+
get => _isSelected;
33+
set => SetProperty(ref _isSelected, value);
34+
}
3435

3536
public ICommand SplitCommand { get; }
3637
public ICommand DisconnectCommand { get; }
@@ -40,5 +41,11 @@ public ConnectionViewModel()
4041
SplitCommand = new DelegateCommand<Point>(Split);
4142
DisconnectCommand = new DelegateCommand(Remove);
4243
}
44+
45+
public void Split(Point point)
46+
=> Graph.Schema.SplitConnection(this, point);
47+
48+
public void Remove()
49+
=> Graph.Connections.Remove(this);
4350
}
4451
}

Examples/Nodify.Playground/Editor/NodifyEditorView.xaml

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,23 @@
9898
<Setter Property="Opacity"
9999
Value="1" />
100100
</Trigger>
101-
<Trigger Property="IsMouseDirectlyOver"
102-
Value="False">
103-
<Setter Property="OutlineBrush"
104-
Value="Transparent" />
101+
<Trigger Property="IsSelectable"
102+
Value="True">
103+
<Setter Property="Cursor"
104+
Value="Hand" />
105105
</Trigger>
106+
<MultiTrigger>
107+
<MultiTrigger.Conditions>
108+
<Condition Property="IsMouseDirectlyOver"
109+
Value="False" />
110+
<Condition Property="IsSelected"
111+
Value="False" />
112+
</MultiTrigger.Conditions>
113+
<MultiTrigger.Setters>
114+
<Setter Property="OutlineBrush"
115+
Value="Transparent" />
116+
</MultiTrigger.Setters>
117+
</MultiTrigger>
106118
</Style.Triggers>
107119
<Setter Property="Opacity" Value="{Binding Source={StaticResource ConnectionAnimationPlaceholder}, Path=Opacity}" />
108120
<Setter Property="Stroke" Value="{DynamicResource Connection.StrokeBrush}"/>
@@ -113,7 +125,6 @@
113125
Opacity="0.15" />
114126
</Setter.Value>
115127
</Setter>
116-
<Setter Property="Cursor" Value="Hand"/>
117128
<Setter Property="ToolTip" Value="Double click to split"/>
118129
<Setter Property="Source" Value="{Binding Output.Anchor}" />
119130
<Setter Property="Target" Value="{Binding Input.Anchor}" />
@@ -135,6 +146,10 @@
135146
<Setter Property="IsAnimatingDirectionalArrows" Value="{Binding IsAnimatingConnections, Source={x:Static local:EditorSettings.Instance}}" />
136147
<Setter Property="DirectionalArrowsAnimationDuration" Value="{Binding DirectionalArrowsAnimationDuration, Source={x:Static local:EditorSettings.Instance}}" />
137148
<Setter Property="Text" Value="{Binding ConnectionText, Source={x:Static local:EditorSettings.Instance}}" />
149+
<Setter Property="IsSelectable"
150+
Value="{Binding SelectableConnections, Source={x:Static local:EditorSettings.Instance}}" />
151+
<Setter Property="IsSelected"
152+
Value="{Binding IsSelected}" />
138153
</Style>
139154

140155
<DataTemplate x:Key="CircuitConnectionTemplate">
@@ -213,9 +228,13 @@
213228
<Grid>
214229
<nodify:NodifyEditor x:Name="Editor"
215230
ItemsSource="{Binding Nodes}"
231+
SelectedItems="{Binding SelectedNodes}"
232+
CanSelectMultipleItems="{Binding CanSelectMultipleNodes, Source={x:Static local:EditorSettings.Instance}}"
216233
Connections="{Binding Connections}"
234+
SelectedConnection="{Binding SelectedConnection}"
235+
SelectedConnections="{Binding SelectedConnections}"
236+
CanSelectMultipleConnections="{Binding CanSelectMultipleConnections, Source={x:Static local:EditorSettings.Instance}}"
217237
PendingConnection="{Binding PendingConnection}"
218-
SelectedItems="{Binding SelectedNodes}"
219238
DisconnectConnectorCommand="{Binding DisconnectConnectorCommand}"
220239
ViewportLocation="{Binding Location.Value, Source={x:Static local:EditorSettings.Instance}}"
221240
ViewportSize="{Binding ViewportSize, Mode=OneWayToSource}"
@@ -547,6 +566,53 @@
547566
</Style>
548567
</nodify:Minimap.ItemContainerStyle>
549568
</nodify:Minimap>
569+
570+
<Border Width="250"
571+
MaxHeight="300"
572+
Margin="0 50"
573+
CornerRadius="3"
574+
HorizontalAlignment="Right"
575+
VerticalAlignment="Top"
576+
Visibility="{Binding SelectedConnection, Converter={shared:BooleanToVisibilityConverter}}"
577+
Background="{DynamicResource PanelBackgroundBrush}">
578+
<StackPanel Margin="10">
579+
<StackPanel.Resources>
580+
<Style TargetType="{x:Type TextBlock}"
581+
BasedOn="{StaticResource {x:Type TextBlock}}">
582+
<Setter Property="Margin"
583+
Value="0 0 0 5" />
584+
</Style>
585+
</StackPanel.Resources>
586+
587+
<StackPanel Margin="0 0 0 14">
588+
<TextBlock Text="Selected connection"
589+
Foreground="{DynamicResource Node.ForegroundBrush}"
590+
FontWeight="Bold" />
591+
</StackPanel>
592+
593+
<TextBlock TextWrapping="Wrap"
594+
Margin="0 0 0 14"
595+
Foreground="{DynamicResource Node.ForegroundBrush}">
596+
<Run>From</Run>
597+
<Run Text="{Binding SelectedConnection.Output.Node.Title}"
598+
Foreground="Red" />
599+
<Run> - </Run>
600+
<Run Text="{Binding SelectedConnection.Output.Title}"
601+
Foreground="Red" />
602+
<Run>to</Run>
603+
<Run Text="{Binding SelectedConnection.Input.Node.Title}"
604+
Foreground="Red" />
605+
<Run> - </Run>
606+
<Run Text="{Binding SelectedConnection.Input.Title}"
607+
Foreground="Red" />
608+
</TextBlock>
609+
610+
<Button Command="{Binding SelectedConnection.DisconnectCommand}"
611+
HorizontalAlignment="Left"
612+
Style="{StaticResource HollowButton}"
613+
Content="Delete" />
614+
</StackPanel>
615+
</Border>
550616
</Grid>
551617

552618
</UserControl>

Examples/Nodify.Playground/Editor/NodifyEditorViewModel.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ public class NodifyEditorViewModel : ObservableObject
99
public NodifyEditorViewModel()
1010
{
1111
Schema = new GraphSchema();
12-
12+
1313
PendingConnection = new PendingConnectionViewModel
1414
{
1515
Graph = this
1616
};
1717

18-
DeleteSelectionCommand = new DelegateCommand(DeleteSelection, () => SelectedNodes.Count > 0);
18+
DeleteSelectionCommand = new DelegateCommand(DeleteSelection, () => SelectedNodes.Count > 0 || SelectedConnections.Count > 0);
1919
CommentSelectionCommand = new RequeryCommand(() => Schema.AddCommentAroundNodes(SelectedNodes, "New comment"), () => SelectedNodes.Count > 0);
2020
DisconnectConnectorCommand = new DelegateCommand<ConnectorViewModel>(c => c.Disconnect());
2121
CreateConnectionCommand = new DelegateCommand<object>(target => Schema.TryAddConnection(PendingConnection.Source!, target), target => PendingConnection.Source != null && target != null);
@@ -63,6 +63,13 @@ public NodifyObservableCollection<NodeViewModel> SelectedNodes
6363
set => SetProperty(ref _selectedNodes, value);
6464
}
6565

66+
private NodifyObservableCollection<ConnectionViewModel> _selectedConnections = new NodifyObservableCollection<ConnectionViewModel>();
67+
public NodifyObservableCollection<ConnectionViewModel> SelectedConnections
68+
{
69+
get => _selectedConnections;
70+
set => SetProperty(ref _selectedConnections, value);
71+
}
72+
6673
private NodifyObservableCollection<ConnectionViewModel> _connections = new NodifyObservableCollection<ConnectionViewModel>();
6774
public NodifyObservableCollection<ConnectionViewModel> Connections
6875
{
@@ -78,6 +85,14 @@ public Size ViewportSize
7885
}
7986

8087
public PendingConnectionViewModel PendingConnection { get; }
88+
89+
private ConnectionViewModel? _selectedConnection;
90+
public ConnectionViewModel? SelectedConnection
91+
{
92+
get => _selectedConnection;
93+
set => SetProperty(ref _selectedConnection, value);
94+
}
95+
8196
public GraphSchema Schema { get; }
8297

8398
public ICommand DeleteSelectionCommand { get; }
@@ -87,6 +102,11 @@ public Size ViewportSize
87102

88103
private void DeleteSelection()
89104
{
105+
foreach (var connection in SelectedConnections.ToList())
106+
{
107+
connection.Remove();
108+
}
109+
90110
var selected = SelectedNodes.ToList();
91111

92112
for (int i = 0; i < selected.Count; i++)

Examples/Nodify.Playground/EditorSettings.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ private EditorSettings()
2626
val => Instance.EnableRealtimeSelection = val,
2727
"Realtime selection: ",
2828
"Selects items when finished if disabled."),
29+
new ProxySettingViewModel<bool>(
30+
() => Instance.CanSelectMultipleNodes,
31+
val => Instance.CanSelectMultipleNodes = val,
32+
"Can select multiple nodes: "),
2933
new ProxySettingViewModel<bool>(
3034
() => Instance.EnablePendingConnectionSnapping,
3135
val => Instance.EnablePendingConnectionSnapping = val,
@@ -86,6 +90,15 @@ private EditorSettings()
8690
val => Instance.AutoPanningEdgeDistance = val,
8791
"Auto panning edge distance: ",
8892
"Distance from edge to trigger auto panning"),
93+
new ProxySettingViewModel<bool>(
94+
() => Instance.SelectableConnections,
95+
val => Instance.SelectableConnections = val,
96+
"Selectable connections: ",
97+
"Whether connections can be selected."),
98+
new ProxySettingViewModel<bool>(
99+
() => Instance.CanSelectMultipleConnections,
100+
val => Instance.CanSelectMultipleConnections = val,
101+
"Can select multiple connections: "),
89102
new ProxySettingViewModel<ConnectionStyle>(
90103
() => Instance.ConnectionStyle,
91104
val => Instance.ConnectionStyle = val,
@@ -365,6 +378,27 @@ public PointEditor Location
365378
set => SetProperty(ref _location, value);
366379
}
367380

381+
private bool _canSelectMultipleConnections = true;
382+
public bool CanSelectMultipleConnections
383+
{
384+
get => _canSelectMultipleConnections;
385+
set => SetProperty(ref _canSelectMultipleConnections, value);
386+
}
387+
388+
private bool _canSelectMultipleNodes = true;
389+
public bool CanSelectMultipleNodes
390+
{
391+
get => _canSelectMultipleNodes;
392+
set => SetProperty(ref _canSelectMultipleNodes, value);
393+
}
394+
395+
private bool _selectableConnections = true;
396+
public bool SelectableConnections
397+
{
398+
get => _selectableConnections;
399+
set => SetProperty(ref _selectableConnections, value);
400+
}
401+
368402
private ConnectionStyle _connectionStyle;
369403
public ConnectionStyle ConnectionStyle
370404
{

Examples/Nodify.Playground/MainWindow.xaml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,25 +185,27 @@
185185
<Setter Property="Foreground"
186186
Value="{DynamicResource ForegroundBrush}" />
187187
<Setter Property="Margin"
188-
Value="0 0 10 0" />
188+
Value="0 0 15 0" />
189189
</Style>
190190
</Grid.Resources>
191191

192192
<StackPanel Orientation="Horizontal">
193193
<TextBlock ToolTip="The number of selected items.">
194194
<TextBlock.Inlines>
195-
<Run Text="Selected: " />
195+
<Run Text="Selected nodes: " />
196196
<Run Foreground="YellowGreen"
197197
Text="{Binding GraphViewModel.SelectedNodes.Count, Mode=OneWay}" />
198198
<Run Text="/" />
199199
<Run Text="{Binding GraphViewModel.Nodes.Count, Mode=OneWay}" />
200200
</TextBlock.Inlines>
201201
</TextBlock>
202-
<TextBlock ToolTip="The number of connections.">
202+
<TextBlock ToolTip="The number of selected connections.">
203203
<TextBlock.Inlines>
204-
<Run Text="Connections: " />
204+
<Run Text="Selected connections: " />
205205
<Run Foreground="YellowGreen"
206-
Text="{Binding GraphViewModel.Connections.Count, Mode=OneWay}" />
206+
Text="{Binding GraphViewModel.SelectedConnections.Count, Mode=OneWay}" />
207+
<Run Text="/" />
208+
<Run Text="{Binding GraphViewModel.Connections.Count, Mode=OneWay}" />
207209
</TextBlock.Inlines>
208210
</TextBlock>
209211
</StackPanel>

Examples/Nodify.Shapes/Canvas/CanvasView.xaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
SourcePosition="{Binding Source.Position}"
145145
TargetPosition="{Binding Target.Position}"
146146
Cursor="Hand"
147+
IsSelectable="True"
147148
Fill="Transparent"
148149
StrokeThickness="3">
149150
<nodify:StepConnection.Stroke>
@@ -162,6 +163,15 @@
162163
</Setter.Value>
163164
</Setter>
164165
</Trigger>
166+
<Trigger Property="IsSelected"
167+
Value="True">
168+
<Setter Property="OutlineBrush">
169+
<Setter.Value>
170+
<SolidColorBrush Color="DodgerBlue"
171+
Opacity="0.25" />
172+
</Setter.Value>
173+
</Setter>
174+
</Trigger>
165175
</Style.Triggers>
166176
</Style>
167177
</nodify:StepConnection.Style>

0 commit comments

Comments
 (0)