Skip to content

Commit f25f7a7

Browse files
committed
Collapse and expand of sub-graphs
1 parent f41ba05 commit f25f7a7

20 files changed

+384
-178
lines changed

CSharpCodeAnalyst/Configuration/ApplicationSettings.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
public class ApplicationSettings
44
{
5-
public int WarningCodeElementLimitForCycle { get; set; } = 50;
5+
public int WarningCodeElementLimit { get; set; } = 50;
66
public string DefaultProjectExcludeFilter { get; set; } = string.Empty;
77
public bool DefaultShowQuickHelp { get; set; }
88
}

CSharpCodeAnalyst/Exploration/CodeGraphExplorer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public SearchResult FindSpecializations(CodeGraph codeGraph, CodeElement element
130130
ArgumentNullException.ThrowIfNull(codeGraph);
131131
ArgumentNullException.ThrowIfNull(element);
132132

133-
var dependencies = codeGraph.Nodes.Values.SelectMany(n => n.Dependencies)
133+
var dependencies = codeGraph.GetAllDependencies()
134134
.Where(d => (d.Type == DependencyType.Overrides ||
135135
d.Type == DependencyType.Implements) &&
136136
d.TargetId == element.Id).ToList();

CSharpCodeAnalyst/GraphArea/DependencyGraphViewer.cs

+106-25
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Microsoft.Msagl.Drawing;
1212
using Microsoft.Msagl.WpfGraphControl;
1313
using Color = Microsoft.Msagl.Drawing.Color;
14+
using Node = Microsoft.Msagl.Drawing.Node;
1415

1516
namespace CSharpCodeAnalyst.GraphArea;
1617

@@ -20,12 +21,13 @@ namespace CSharpCodeAnalyst.GraphArea;
2021
/// Dependencies of the same type (i.e a method Calls another multiple times) are handled
2122
/// in the parser. In this case the dependency holds all source references.
2223
/// </summary>
23-
internal class DependencyGraphViewer : IDependencyGraphViewer, IDependencyGraphBinding, INotifyPropertyChanged
24+
internal partial class DependencyGraphViewer : IDependencyGraphViewer, IDependencyGraphBinding, INotifyPropertyChanged
2425
{
2526
private readonly List<IContextCommand> _contextCommands = [];
2627
private readonly MsaglBuilder _msaglBuilder;
2728
private readonly IPublisher _publisher;
28-
private readonly LinkedList<CodeGraph> _undoStack = new();
29+
30+
private readonly LinkedList<UndoState> _undoStack = new();
2931

3032
private readonly int _undoStackSize = 10;
3133

@@ -45,6 +47,7 @@ internal class DependencyGraphViewer : IDependencyGraphViewer, IDependencyGraphB
4547
private IViewerEdge? _lastHighlightedEdge;
4648

4749
private GraphViewer? _msaglViewer;
50+
private PresentationState _presentationState = new();
4851

4952
private RenderOption _renderOption = new DefaultRenderOptions();
5053
private bool _showFlatGraph;
@@ -77,20 +80,13 @@ public void ShowFlatGraph(bool value)
7780
RefreshGraph();
7881
}
7982

80-
/// <summary>
81-
/// Adding an existing element or dependency is prevented.
82-
/// Note from the originalCodeElement we don't add parent or children.
83-
/// We just use this information to integrate the node into the existing canvas.
84-
/// </summary>
85-
public void AddToGraph(IEnumerable<CodeElement> originalCodeElements, IEnumerable<Dependency> newDependencies)
83+
private void AddToGraphInternal(IEnumerable<CodeElement> originalCodeElements, IEnumerable<Dependency> newDependencies)
8684
{
8785
if (_msaglViewer is null)
8886
{
8987
return;
9088
}
9189

92-
PushUndo();
93-
9490
IntegrateNewFromOriginal(originalCodeElements);
9591

9692
// Add dependencies we explicitly requested.
@@ -104,25 +100,56 @@ public void AddToGraph(IEnumerable<CodeElement> originalCodeElements, IEnumerabl
104100
RefreshGraph();
105101
}
106102

103+
/// <summary>
104+
/// Adding an existing element or dependency is prevented.
105+
/// Note from the originalCodeElement we don't add parent or children.
106+
/// We just use this information to integrate the node into the existing canvas.
107+
/// </summary>
108+
public void AddToGraph(IEnumerable<CodeElement> originalCodeElements, IEnumerable<Dependency> newDependencies)
109+
{
110+
if (_msaglViewer is null)
111+
{
112+
return;
113+
}
114+
115+
PushUndo();
116+
AddToGraphInternal(originalCodeElements, newDependencies);
117+
}
118+
107119
public void AddContextCommand(IContextCommand command)
108120
{
109121
_contextCommands.Add(command);
110122
}
111123

112-
public void Clear()
124+
private void Clear(bool withUndoStack)
113125
{
114126
if (_msaglViewer is null)
115127
{
116128
return;
117129
}
118130

119131
_clonedCodeGraph = new CodeGraph();
120-
_undoStack.Clear();
132+
133+
if (withUndoStack)
134+
{
135+
ClearUndo();
136+
}
137+
138+
// Nothing collapsed by default
139+
_presentationState = new PresentationState();
121140
RefreshGraph();
122141
}
142+
public void Clear()
143+
{
144+
Clear(true);
145+
}
123146

147+
private void ClearUndo()
148+
{
149+
_undoStack.Clear();
150+
}
124151

125-
public void Reset()
152+
public void Layout()
126153
{
127154
//_msaglViewer?.SetInitialTransform();
128155
RefreshGraph();
@@ -187,19 +214,36 @@ public void ShowGlobalContextMenu()
187214
globalContextMenu.IsOpen = true;
188215
}
189216

217+
218+
190219
public bool Undo()
191220
{
192221
if (_undoStack.Any() is false)
193222
{
194223
return false;
195224
}
196225

197-
_clonedCodeGraph = _undoStack.First();
226+
var state = _undoStack.First();
198227
_undoStack.RemoveFirst();
228+
229+
_clonedCodeGraph = state.CodeGraph;
230+
_presentationState = state.PresentationState;
231+
199232
RefreshGraph();
200233
return true;
201234
}
202235

236+
public void ImportCycleGroup(List<CodeElement> codeElements, List<Dependency> dependencies)
237+
{
238+
PushUndo();
239+
Clear(false);
240+
241+
// Everything is collapsed by default. This allows to import large graphs.
242+
var defaultState = codeElements.Where(c => c.Children.Any()).ToDictionary(c => c.Id, c => true);
243+
_presentationState = new PresentationState(defaultState);
244+
AddToGraphInternal(codeElements, dependencies);
245+
}
246+
203247

204248
public event PropertyChangedEventHandler? PropertyChanged;
205249

@@ -211,7 +255,8 @@ private void PushUndo()
211255
_undoStack.RemoveLast();
212256
}
213257

214-
_undoStack.AddFirst(_clonedCodeGraph.Clone(null, null));
258+
var state = new UndoState(_clonedCodeGraph.Clone(null, null), _presentationState.Clone());
259+
_undoStack.AddFirst(state);
215260
}
216261

217262
private void ClearEdgeColoring()
@@ -264,7 +309,8 @@ private void RefreshGraph()
264309
{
265310
if (_msaglViewer != null)
266311
{
267-
var graph = _msaglBuilder.CreateGraphFromCodeStructure(_clonedCodeGraph, _showFlatGraph);
312+
var graph = _msaglBuilder.CreateGraphFromCodeStructure(_clonedCodeGraph, _presentationState,
313+
_showFlatGraph);
268314

269315
_renderOption.Apply(graph);
270316
_msaglViewer.Graph = graph;
@@ -400,18 +446,38 @@ bool IsCtrlPressed()
400446
var node = clickedObject.Node;
401447
var contextMenu = new ContextMenu();
402448

403-
var addParentMenuItem = new MenuItem { Header = "Add parent" };
404-
addParentMenuItem.Click += (_, _) => AddParentRequest(node);
449+
MenuItem item = null;
450+
if (node.UserData is CodeElement codeElement)
451+
{
452+
if (_presentationState.IsCollapsed(codeElement.Id) &&
453+
codeElement.Children.Any())
454+
{
455+
item = new MenuItem { Header = "Expand" };
456+
item.Click += (_, _) => Expand(codeElement.Id);
457+
contextMenu.Items.Add(item);
458+
}
405459

406-
var findInTreeMenuItem = new MenuItem { Header = "Find in Tree" };
407-
findInTreeMenuItem.Click += (_, _) => FindInTree(node);
460+
if (!_presentationState.IsCollapsed(codeElement.Id) &&
461+
codeElement.Children.Any())
462+
{
463+
item = new MenuItem { Header = "Collapse" };
464+
item.Click += (_, _) => Collapse(codeElement.Id);
465+
contextMenu.Items.Add(item);
466+
}
467+
}
468+
469+
item = new MenuItem { Header = "Delete Node" };
470+
item.Click += (_, _) => DeleteNode(node);
471+
contextMenu.Items.Add(item);
472+
473+
item = new MenuItem { Header = "Find in Tree" };
474+
item.Click += (_, _) => FindInTree(node);
475+
contextMenu.Items.Add(item);
408476

409-
var deleteMenuItem = new MenuItem { Header = "Delete Node" };
410-
deleteMenuItem.Click += (_, _) => DeleteNode(node);
477+
item = new MenuItem { Header = "Add parent" };
478+
item.Click += (_, _) => AddParentRequest(node);
479+
contextMenu.Items.Add(item);
411480

412-
contextMenu.Items.Add(deleteMenuItem);
413-
contextMenu.Items.Add(findInTreeMenuItem);
414-
contextMenu.Items.Add(addParentMenuItem);
415481
contextMenu.Items.Add(new Separator());
416482
var lastItemIsSeparator = true;
417483

@@ -451,6 +517,21 @@ bool IsCtrlPressed()
451517
}
452518
}
453519

520+
private void Collapse(string id)
521+
{
522+
PushUndo();
523+
_presentationState.SetCollapsedState(id, true);
524+
RefreshGraph();
525+
}
526+
527+
private void Expand(string id)
528+
{
529+
PushUndo();
530+
_presentationState.SetCollapsedState(id, false);
531+
532+
RefreshGraph();
533+
}
534+
454535
private void DeleteAllMarkedElements()
455536
{
456537
if (_msaglViewer is null)

CSharpCodeAnalyst/GraphArea/GraphViewModel.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,9 @@ internal void Clear()
238238
_viewer.Clear();
239239
}
240240

241-
internal void ResetZoom()
241+
internal void Layout()
242242
{
243-
_viewer.Reset();
243+
_viewer.Layout();
244244
}
245245

246246
internal void FindIncomingCalls(CodeElement method)
@@ -325,4 +325,9 @@ public void ShowGlobalContextMenu()
325325
{
326326
_viewer.ShowGlobalContextMenu();
327327
}
328+
329+
public void ImportCycleGroup(List<CodeElement> codeElements, List<Dependency> dependencies)
330+
{
331+
_viewer.ImportCycleGroup(codeElements, dependencies);
332+
}
328333
}

CSharpCodeAnalyst/GraphArea/IDependencyGraphViewer.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal interface IDependencyGraphViewer
1919
/// <summary>
2020
/// Renders the graph and re-layouts it.
2121
/// </summary>
22-
void Reset();
22+
void Layout();
2323

2424
/// <summary>
2525
/// Note:
@@ -36,4 +36,5 @@ internal interface IDependencyGraphViewer
3636
void SetHighlightMode(HighlightMode valueMode);
3737
void ShowGlobalContextMenu();
3838
bool Undo();
39+
void ImportCycleGroup(List<CodeElement> codeElements, List<Dependency> dependencies);
3940
}

0 commit comments

Comments
 (0)