88using System . Linq ;
99using System . Reflection ;
1010using System . Text ;
11+ using System . Text . RegularExpressions ;
1112using Terminal . Gui . App ;
1213using Terminal . Gui . Configuration ;
1314using Terminal . Gui . Drawing ;
@@ -27,6 +28,7 @@ internal sealed class ConsoleGui : IDisposable
2728 private bool _cancelled ;
2829 private Label ? _filterLabel ;
2930 private TextField ? _filterField ;
31+ private View ? _filterErrorView ;
3032 private Label ? _header ;
3133 private ListView ? _listView ;
3234 // _inputSource contains the full set of Input data and tracks any items the user
@@ -119,15 +121,15 @@ void OnWinSubViewLayout(object? sender, EventArgs e)
119121
120122 _header ! . Text = GridViewHelpers . GetPaddedString ( gridHeaders , _gridViewDetails ! . ListViewOffset ,
121123 _gridViewDetails . ListViewColumnWidths ) ;
122- ApplyFilter ( ) ;
123124 UpdateDisplayStrings ( _listViewSource ) ;
125+ ApplyFilter ( ) ;
124126 }
125127 }
126128
127129 private GridViewDataSource LoadData ( )
128130 {
129131 var items = new List < GridViewRow > ( ) ;
130- if ( _applicationData == null )
132+ if ( _applicationData == null )
131133 return new GridViewDataSource ( items ) ;
132134
133135 for ( int i = 0 ; i < _applicationData . DataTable . Data . Count ; i ++ )
@@ -173,25 +175,57 @@ private void ApplyFilter()
173175 // The ListView is always filled with a (filtered) copy of _inputSource.
174176 // We listen for `MarkChanged` events on this filtered list and apply those changes up to _inputSource.
175177
178+ GridViewRow ? selectedItem = null ;
179+
176180 if ( _listViewSource != null )
177181 {
178- _listViewSource . MarkChanged -= ListViewSource_MarkChanged ;
182+ // Get the item that is currently selected so we can restore selection after re-applying filter
183+ selectedItem = _listViewSource ? . GridViewRowList . ElementAtOrDefault ( _listView ? . SelectedItem ?? 0 ) ;
184+ _listViewSource ! . MarkChanged -= ListViewSource_MarkChanged ;
179185 _listViewSource = null ;
180186 }
181187
182188 if ( _inputSource is null )
183189 {
184190 _inputSource = LoadData ( ) ;
185191 }
192+
193+
186194 if ( _applicationData != null )
187- _listViewSource = new GridViewDataSource ( GridViewHelpers . FilterData ( _inputSource . GridViewRowList , _applicationData . Filter ?? string . Empty ) ) ;
195+ {
196+ try
197+ {
198+ _listViewSource = new GridViewDataSource ( GridViewHelpers . FilterData ( _inputSource . GridViewRowList ,
199+ _applicationData . Filter ?? string . Empty ) ) ;
200+ }
201+ catch ( RegexParseException ex )
202+ {
203+ _filterErrorView ! . Text = ex . Message ;
204+ }
205+ }
206+
188207 _listViewSource ? . MarkChanged + = ListViewSource_MarkChanged ;
189208 _listView ? . Source = _listViewSource ;
209+
210+ // Restore selection - find the previously selected item in the new filtered list
211+ if ( selectedItem is not null && _listViewSource != null )
212+ {
213+ int newIndex =
214+ _listViewSource . GridViewRowList . FindIndex ( i => i . OriginalIndex == selectedItem . OriginalIndex ) ;
215+ if ( newIndex >= 0 )
216+ {
217+ _listView ! . SelectedItem = newIndex ;
218+ }
219+ }
220+ if ( _listView ? . SelectedItem == - 1 )
221+ {
222+ _listView ! . SelectedItem = 0 ;
223+ }
190224 }
191225
192226 private void ListViewSource_MarkChanged ( object ? s , GridViewDataSource . RowMarkedEventArgs a )
193227 {
194- _inputSource . GridViewRowList [ a . Row . OriginalIndex ] . IsMarked = a . Row . IsMarked ;
228+ _inputSource ? . GridViewRowList [ a . Row . OriginalIndex ] . IsMarked = a . Row . IsMarked ;
195229 }
196230
197231 private static void Accept ( )
@@ -211,12 +245,6 @@ private Window CreateTopLevelWindow()
211245 var win = new Window
212246 {
213247 Title = _applicationData ! . Title ?? "Out-ConsoleGridView" ,
214- X = _applicationData . MinUI ? - 1 : 0 ,
215- Y = _applicationData . MinUI ? - 1 : 0 ,
216-
217- // By using Dim.Fill(), it will automatically resize without manual intervention
218- Width = Dim . Fill ( _applicationData . MinUI ? - 1 : 0 ) ,
219- Height = Dim . Fill ( _applicationData . MinUI ? - 1 : 1 )
220248 } ;
221249
222250 if ( _applicationData . MinUI )
@@ -293,14 +321,12 @@ private void AddStatusBar(Window win, bool visible)
293321 $ "{ Application . Driver } v{ FileVersionInfo . GetVersionInfo ( Assembly . GetAssembly ( typeof ( Application ) ) ! . Location ) . ProductVersion } ", null ) ) ;
294322 }
295323
296- var statusBar = new StatusBar ( shortcuts ) ;
297- statusBar . Visible = visible ;
298- win . Add ( statusBar ) ;
324+ win . Add ( new StatusBar ( shortcuts ) ) ;
299325 }
300326
301327 private void CalculateColumnWidths ( List < string > gridHeaders )
302328 {
303- _gridViewDetails . ListViewColumnWidths = new int [ gridHeaders . Count ] ;
329+ _gridViewDetails ! . ListViewColumnWidths = new int [ gridHeaders . Count ] ;
304330 var listViewColumnWidths = _gridViewDetails . ListViewColumnWidths ;
305331
306332 for ( int i = 0 ; i < gridHeaders . Count ; i ++ )
@@ -371,33 +397,35 @@ private void AddFilter(Window win)
371397 _filterField . KeyBindings . Remove ( Key . A . WithCtrl ) ;
372398 _filterField . KeyBindings . Remove ( Key . D . WithCtrl ) ;
373399
374- var filterErrorLabel = new Label
400+ _filterErrorView = new View
375401 {
376402 Text = string . Empty ,
377403 X = Pos . Right ( _filterLabel ) + 1 ,
378404 Y = Pos . Top ( _filterLabel ) + 1 ,
379- Width = Dim . Fill ( ) - _filterLabel . Text ! . Length
405+ Width = Dim . Fill ( ) - _filterLabel . Text ! . Length ,
406+ // This enables the height to go 0, and the view to disappear when there is no error
407+ Height = Dim . Auto ( DimAutoStyle . Text ) ,
408+ SchemeName = "Error"
380409 } ;
381410
382411 _filterField . TextChanged += ( sender , e ) =>
383412 {
384413 string ? filterText = _filterField . Text ? . ToString ( ) ;
385414 try
386415 {
387- filterErrorLabel . Text = " " ;
388- filterErrorLabel . SetNeedsDraw ( ) ;
389- _applicationData ! . Filter = filterText ;
416+ _filterErrorView . Text = string . Empty ;
417+ _filterErrorView . SetNeedsDraw ( ) ;
418+ _applicationData ! . Filter = filterText ! ;
390419 ApplyFilter ( ) ;
391420
392421 }
393422 catch ( Exception ex )
394423 {
395- filterErrorLabel . Text = ex . Message ;
396- filterErrorLabel . SchemeName = "Error" ;
424+ _filterErrorView . Text = ex . Message ;
397425 }
398426 } ;
399427
400- win . Add ( _filterLabel , _filterField , filterErrorLabel ) ;
428+ win . Add ( _filterLabel , _filterField , _filterErrorView ) ;
401429
402430 _filterField . Text = _applicationData . Filter ?? string . Empty ;
403431 _filterField . CursorPosition = _filterField . Text . Length ;
@@ -415,7 +443,7 @@ private void AddHeaders(Window win, List<string> gridHeaders)
415443 }
416444 else
417445 {
418- _header . Y = 2 ;
446+ _header . Y = Pos . Bottom ( _filterErrorView ! ) ;
419447 }
420448 win . Add ( _header ) ;
421449
@@ -451,8 +479,7 @@ private void AddListView(Window win)
451479 _listView . AllowsMarking = _applicationData . OutputMode != OutputModeOption . None ;
452480 _listView . AllowsMultipleSelection = _applicationData . OutputMode == OutputModeOption . Multiple ;
453481
454- // In Terminal.Gui v2, key bindings work differently
455- // The ListView already handles Space for toggling marks by default
482+ _listView . SelectedItem = 0 ;
456483
457484 win . Add ( _listView ) ;
458485 }
0 commit comments