@@ -111,6 +111,16 @@ private void OnExecute()
111111 . SetMinimumLevel ( Verbosity )
112112 . AddConsole ( ) ) ;
113113
114+ var graph = GetDependencyGraph ( loggerFactory ) ;
115+
116+ Application . Init ( ) ;
117+ Application . QuitKey = Key . Esc ;
118+ Application . Top . Add ( new AppWindow ( graph ) ) ;
119+ Application . Run ( ) ;
120+ }
121+
122+ private DependencyGraph GetDependencyGraph ( ILoggerFactory loggerFactory )
123+ {
114124 var analyzer = new DependencyAnalyzer ( loggerFactory ) ;
115125 DependencyGraph graph ;
116126 if ( ! string . IsNullOrEmpty ( Package ) )
@@ -125,140 +135,199 @@ private void OnExecute()
125135 {
126136 graph = analyzer . Analyze ( Project , Framework ) ;
127137 }
138+ return graph ;
139+ }
128140
129- Application . Init ( ) ;
141+ private class AppWindow : Window
142+ {
143+ private readonly DependencyGraph _graph ;
144+ private readonly ImmutableList < Node > _dependencies ;
145+ private ImmutableList < Node > _visibleDependencies ;
146+ private bool _assembliesVisible ;
147+ private int _lastSelectedDependencyIndex ;
148+
149+ private readonly ListView _dependenciesView ;
150+ private readonly ListView _runtimeDependsView ;
151+ private readonly ListView _packageDependsView ;
152+ private readonly ListView _reverseDependsView ;
153+
154+ class DependsListItemModel
155+ {
156+ public Node Node { get ; }
157+ public string DisplayText { get ; }
130158
131- var top = new CustomWindow ( ) ;
159+ public DependsListItemModel ( Node node , string label )
160+ {
161+ Node = node ?? throw new ArgumentNullException ( nameof ( node ) ) ;
162+ DisplayText = $ "{ node } { ( string . IsNullOrEmpty ( label ) ? string . Empty : " (Wanted: " + label + ")" ) } ";
163+ }
132164
133- var left = new FrameView ( "Dependencies" )
134- {
135- Width = Dim . Percent ( 50 ) ,
136- Height = Dim . Fill ( 1 )
137- } ;
138- var right = new View ( )
139- {
140- X = Pos . Right ( left ) ,
141- Width = Dim . Fill ( ) ,
142- Height = Dim . Fill ( 1 )
143- } ;
144- var helpText = new Label ( "Use arrow keys and Tab to move around. Ctrl+D to toggle assembly visibility. Esc to quit." )
145- {
146- Y = Pos . AnchorEnd ( 1 )
147- } ;
165+ public override string ToString ( )
166+ {
167+ return DisplayText ;
168+ }
169+ }
148170
149- var runtimeDepends = new FrameView ( "Runtime depends" )
150- {
151- Width = Dim . Fill ( ) ,
152- Height = Dim . Percent ( 33f )
153- } ;
154- var packageDepends = new FrameView ( "Package depends" )
155- {
156- Y = Pos . Bottom ( runtimeDepends ) ,
157- Width = Dim . Fill ( ) ,
158- Height = Dim . Percent ( 50f )
159- } ;
160- var reverseDepends = new FrameView ( "Reverse depends" )
171+ public AppWindow ( DependencyGraph graph ) : base ( "Depends" , 0 )
161172 {
162- Y = Pos . Bottom ( packageDepends ) ,
163- Width = Dim . Fill ( ) ,
164- Height = Dim . Fill ( )
165- } ;
173+ _graph = graph ?? throw new ArgumentNullException ( nameof ( graph ) ) ;
174+ _dependencies = _graph . Nodes . OrderBy ( x => x . Id ) . ToImmutableList ( ) ;
175+ _visibleDependencies = _dependencies ;
176+ _assembliesVisible = true ;
177+ _lastSelectedDependencyIndex = - 1 ;
166178
167- var orderedDependencyList = graph . Nodes . OrderBy ( x => x . Id ) . ToImmutableList ( ) ;
168- var dependenciesView = new ListView ( orderedDependencyList )
169- {
170- CanFocus = true ,
171- AllowsMarking = false
172- } ;
173- left . Add ( dependenciesView ) ;
174- var runtimeDependsView = new ListView ( Array . Empty < Node > ( ) )
175- {
176- CanFocus = true ,
177- AllowsMarking = false
178- } ;
179- runtimeDepends . Add ( runtimeDependsView ) ;
180- var packageDependsView = new ListView ( Array . Empty < Node > ( ) )
181- {
182- CanFocus = true ,
183- AllowsMarking = false
184- } ;
185- packageDepends . Add ( packageDependsView ) ;
186- var reverseDependsView = new ListView ( Array . Empty < Node > ( ) )
187- {
188- CanFocus = true ,
189- AllowsMarking = false
190- } ;
191- reverseDepends . Add ( reverseDependsView ) ;
179+ ColorScheme = new ColorScheme
180+ {
181+ Focus = Application . Driver . MakeAttribute ( Color . Black , Color . White ) ,
182+ Normal = Application . Driver . MakeAttribute ( Color . White , Color . Black ) ,
183+ HotFocus = Application . Driver . MakeAttribute ( Color . Black , Color . White ) ,
184+ HotNormal = Application . Driver . MakeAttribute ( Color . White , Color . Black )
185+ } ;
192186
193- right . Add ( runtimeDepends , packageDepends , reverseDepends ) ;
194- top . Add ( left , right , helpText ) ;
195- Application . Top . Add ( top ) ;
187+ var left = new FrameView ( "Dependencies" )
188+ {
189+ Width = Dim . Percent ( 50 ) ,
190+ Height = Dim . Fill ( 1 )
191+ } ;
192+ var right = new View ( )
193+ {
194+ X = Pos . Right ( left ) ,
195+ Width = Dim . Fill ( ) ,
196+ Height = Dim . Fill ( 1 )
197+ } ;
198+ var helpText = new Label ( "Use arrow keys and Tab to move around. Ctrl+D to toggle assembly visibility. Esc to quit." )
199+ {
200+ Y = Pos . AnchorEnd ( 1 )
201+ } ;
196202
197- top . Dependencies = orderedDependencyList ;
198- top . VisibleDependencies = orderedDependencyList ;
199- top . DependenciesView = dependenciesView ;
203+ var runtimeDepends = new FrameView ( "Runtime depends" )
204+ {
205+ Width = Dim . Fill ( ) ,
206+ Height = Dim . Percent ( 33f )
207+ } ;
208+ var packageDepends = new FrameView ( "Package depends" )
209+ {
210+ Y = Pos . Bottom ( runtimeDepends ) ,
211+ Width = Dim . Fill ( ) ,
212+ Height = Dim . Percent ( 33f )
213+ } ;
214+ var reverseDepends = new FrameView ( "Reverse depends" )
215+ {
216+ Y = Pos . Bottom ( packageDepends ) ,
217+ Width = Dim . Fill ( ) ,
218+ Height = Dim . Fill ( )
219+ } ;
220+ _dependenciesView = new ListView ( )
221+ {
222+ CanFocus = true ,
223+ AllowsMarking = false ,
224+ Width = Dim . Fill ( ) ,
225+ Height = Dim . Fill ( )
226+ } ;
227+ left . Add ( _dependenciesView ) ;
228+ _runtimeDependsView = new ListView ( )
229+ {
230+ CanFocus = true ,
231+ AllowsMarking = false ,
232+ Width = Dim . Fill ( ) ,
233+ Height = Dim . Fill ( )
234+ } ;
235+ runtimeDepends . Add ( _runtimeDependsView ) ;
236+ _packageDependsView = new ListView ( )
237+ {
238+ CanFocus = true ,
239+ AllowsMarking = false ,
240+ Width = Dim . Fill ( ) ,
241+ Height = Dim . Fill ( )
242+ } ;
243+ packageDepends . Add ( _packageDependsView ) ;
244+ _reverseDependsView = new ListView ( )
245+ {
246+ CanFocus = true ,
247+ AllowsMarking = false ,
248+ Width = Dim . Fill ( ) ,
249+ Height = Dim . Fill ( )
250+ } ;
251+ reverseDepends . Add ( _reverseDependsView ) ;
200252
201- dependenciesView . SelectedItem = 0 ;
202- UpdateLists ( ) ;
253+ right . Add ( runtimeDepends , packageDepends , reverseDepends ) ;
254+ Add ( left , right , helpText ) ;
203255
204- dependenciesView . SelectedChanged += UpdateLists ;
256+ _runtimeDependsView . OpenSelectedItem += RuntimeDependsView_OpenSelectedItem ;
257+ _packageDependsView . OpenSelectedItem += PackageDependsView_OpenSelectedItem ;
258+ _reverseDependsView . OpenSelectedItem += ReverseDependsView_OpenSelectedItem ;
205259
206- Application . Run ( ) ;
260+ _dependenciesView . SelectedItemChanged += DependenciesView_SelectedItemChanged ; ;
261+ _dependenciesView . SetSource ( _visibleDependencies ) ;
262+ }
207263
208- void UpdateLists ( )
264+ public override bool ProcessKey ( KeyEvent keyEvent )
209265 {
210- var selectedNode = top . VisibleDependencies [ dependenciesView . SelectedItem ] ;
266+ if ( keyEvent . Key == ( Key . D | Key . CtrlMask ) )
267+ {
268+ _assembliesVisible = ! _assembliesVisible ;
269+ _visibleDependencies = _assembliesVisible ?
270+ _dependencies :
271+ _dependencies . Where ( d => ! ( d is AssemblyReferenceNode ) ) . ToImmutableList ( ) ;
211272
212- runtimeDependsView . SetSource ( graph . Edges . Where ( x => x . Start . Equals ( selectedNode ) && x . End is AssemblyReferenceNode )
213- . Select ( x => x . End ) . ToImmutableList ( ) ) ;
214- packageDependsView . SetSource ( graph . Edges . Where ( x => x . Start . Equals ( selectedNode ) && x . End is PackageReferenceNode )
215- . Select ( x => $ "{ x . End } { ( string . IsNullOrEmpty ( x . Label ) ? string . Empty : " (Wanted: " + x . Label + ")" ) } ") . ToImmutableList ( ) ) ;
216- reverseDependsView . SetSource ( graph . Edges . Where ( x => x . End . Equals ( selectedNode ) )
217- . Select ( x => $ "{ x . Start } { ( string . IsNullOrEmpty ( x . Label ) ? string . Empty : " (Wanted: " + x . Label + ")" ) } ") . ToImmutableList ( ) ) ;
273+ _dependenciesView . SetSource ( _visibleDependencies ) ;
274+ _lastSelectedDependencyIndex = - 1 ;
275+ _dependenciesView . SetFocus ( ) ;
276+ return true ;
277+ }
278+
279+ return base . ProcessKey ( keyEvent ) ;
218280 }
219- }
220281
221- private class CustomWindow : Window
222- {
223- public CustomWindow ( ) : base ( "Depends" , 0 )
282+ private void DependenciesView_SelectedItemChanged ( ListViewItemEventArgs args )
224283 {
225- ColorScheme = new ColorScheme
284+ // The ListView.SelectedItemChanged event is fired on enter (focus), see https://github.com/migueldeicaza/gui.cs/issues/831
285+ // To keep the current selection in the right pane (runtime, package & reverse depends lists), call UpdateLists() only if the selected item has actually been changed.
286+ if ( _lastSelectedDependencyIndex != args . Item )
226287 {
227- Focus = Application . Driver . MakeAttribute ( Color . Black , Color . White ) ,
228- Normal = Application . Driver . MakeAttribute ( Color . White , Color . Black ) ,
229- HotFocus = Application . Driver . MakeAttribute ( Color . Black , Color . White ) ,
230- HotNormal = Application . Driver . MakeAttribute ( Color . Black , Color . White )
231- } ;
288+ _lastSelectedDependencyIndex = args . Item ;
289+ UpdateLists ( ) ;
290+ }
232291 }
233292
234- public ListView DependenciesView { get ; set ; }
235- public ImmutableList < Node > Dependencies { get ; set ; }
236- public ImmutableList < Node > VisibleDependencies { get ; set ; }
237-
238- private bool _assembliesVisible = true ;
239-
240- public override bool ProcessKey ( KeyEvent keyEvent )
293+ private void RuntimeDependsView_OpenSelectedItem ( ListViewItemEventArgs args )
241294 {
242- if ( keyEvent . Key == Key . Esc )
295+ if ( _assembliesVisible )
243296 {
244- Application . RequestStop ( ) ;
245- return true ;
297+ SetSelectedDependency ( ( Node ) args . Value ) ;
246298 }
247- if ( keyEvent . Key == Key . ControlD )
248- {
249- _assembliesVisible = ! _assembliesVisible ;
299+ // else: would be nice to provide a feedback so that the user understands that navigation is not possible.
300+ }
250301
251- VisibleDependencies = _assembliesVisible ?
252- Dependencies :
253- Dependencies . Where ( d => ! ( d is AssemblyReferenceNode ) ) . ToImmutableList ( ) ;
302+ private void PackageDependsView_OpenSelectedItem ( ListViewItemEventArgs args )
303+ {
304+ var node = ( ( DependsListItemModel ) args . Value ) . Node ;
305+ SetSelectedDependency ( node ) ;
306+ }
254307
255- DependenciesView . SetSource ( VisibleDependencies ) ;
308+ private void ReverseDependsView_OpenSelectedItem ( ListViewItemEventArgs args )
309+ {
310+ var node = ( ( DependsListItemModel ) args . Value ) . Node ;
311+ SetSelectedDependency ( node ) ;
312+ }
256313
257- DependenciesView . SelectedItem = 0 ;
258- return true ;
259- }
314+ private void SetSelectedDependency ( Node node )
315+ {
316+ var index = _visibleDependencies . FindIndex ( x => x . Equals ( node ) ) ;
317+ _dependenciesView . SelectedItem = index ;
318+ _dependenciesView . SetFocus ( ) ;
319+ }
260320
261- return base . ProcessKey ( keyEvent ) ;
321+ private void UpdateLists ( )
322+ {
323+ var selectedNode = _visibleDependencies [ _dependenciesView . SelectedItem ] ;
324+
325+ _runtimeDependsView . SetSource ( _graph . Edges . Where ( x => x . Start . Equals ( selectedNode ) && x . End is AssemblyReferenceNode )
326+ . Select ( x => x . End ) . ToImmutableList ( ) ) ;
327+ _packageDependsView . SetSource ( _graph . Edges . Where ( x => x . Start . Equals ( selectedNode ) && x . End is PackageReferenceNode )
328+ . Select ( x => new DependsListItemModel ( x . End , x . Label ) ) . ToImmutableList ( ) ) ;
329+ _reverseDependsView . SetSource ( _graph . Edges . Where ( x => x . End . Equals ( selectedNode ) )
330+ . Select ( x => new DependsListItemModel ( x . Start , x . Label ) ) . ToImmutableList ( ) ) ;
262331 }
263332 }
264333 }
0 commit comments