@@ -103,25 +103,76 @@ private async void CheckPDBAvail_Click(object sender, RoutedEventArgs e) {
103103 downloadStatus . Text = failedUrls . Count > 0 ? string . Join ( "," , failedUrls ) : "All PDBs for this build are available!" ;
104104 }
105105
106- private void FindNext_Click ( object sender , RoutedEventArgs e ) {
107- if ( string . IsNullOrWhiteSpace ( searchText . Text ) ) return ;
108- var found = SearchTree ( treeviewSyms . Items , searchText . Text . Trim ( ) . ToLower ( CultureInfo . CurrentCulture ) ) ;
109- downloadStatus . Text = found ? "Found match!" : "No matches found." ;
106+ private void FindNext_Click ( object sender , RoutedEventArgs e ) => FindInTree ( forward : true ) ;
107+ private void FindPrev_Click ( object sender , RoutedEventArgs e ) => FindInTree ( forward : false ) ;
108+
109+ private void Find_Executed ( object sender , ExecutedRoutedEventArgs e ) {
110+ findBarBorder . Visibility = Visibility . Visible ;
111+ Dispatcher . BeginInvoke ( System . Windows . Threading . DispatcherPriority . Input , new Action ( ( ) => {
112+ searchText . Focus ( ) ;
113+ Keyboard . Focus ( searchText ) ;
114+ searchText . SelectAll ( ) ;
115+ } ) ) ;
110116 }
111117
112- private bool SearchTree ( ItemCollection items , string searchTerm ) {
113- foreach ( TreeViewItem item in items ) {
114- if ( item . Tag is SQLBuildInfo bld && bld . ToString ( ) . ToLower ( CultureInfo . CurrentCulture ) . Contains ( searchTerm ) ) {
115- item . IsSelected = true ;
116- item . BringIntoView ( ) ;
117- // expand parent chain
118+ private void FindClose_Click ( object sender , RoutedEventArgs e ) {
119+ findBarBorder . Visibility = Visibility . Collapsed ;
120+ matchInfo . Text = "" ;
121+ }
122+
123+ private void SearchBox_KeyDown ( object sender , KeyEventArgs e ) {
124+ if ( e . Key == Key . Enter || e . Key == Key . F3 ) {
125+ FindInTree ( forward : Keyboard . Modifiers != ModifierKeys . Shift ) ;
126+ e . Handled = true ;
127+ } else if ( e . Key == Key . Escape ) {
128+ FindClose_Click ( sender , e ) ;
129+ e . Handled = true ;
130+ }
131+ }
132+
133+ private List < TreeViewItem > _flatItems ;
134+ private int _lastFoundIndex = - 1 ;
135+
136+ private void FindInTree ( bool forward ) {
137+ if ( string . IsNullOrWhiteSpace ( searchText . Text ) ) return ;
138+ var term = searchText . Text . Trim ( ) . ToLower ( CultureInfo . CurrentCulture ) ;
139+
140+ // Build flat list of all leaf items
141+ _flatItems = new List < TreeViewItem > ( ) ;
142+ CollectLeafItems ( treeviewSyms . Items , _flatItems ) ;
143+
144+ if ( _flatItems . Count == 0 ) { matchInfo . Text = "No items" ; return ; }
145+
146+ int start = forward ? _lastFoundIndex + 1 : _lastFoundIndex - 1 ;
147+ int count = _flatItems . Count ;
148+
149+ for ( int i = 0 ; i < count ; i ++ ) {
150+ int idx = forward
151+ ? ( start + i + count ) % count
152+ : ( start - i + count ) % count ;
153+ var item = _flatItems [ idx ] ;
154+ if ( item . Tag is SQLBuildInfo bld && bld . ToString ( ) . ToLower ( CultureInfo . CurrentCulture ) . Contains ( term ) ) {
155+ // Expand parent chain
118156 var parent = item . Parent as TreeViewItem ;
119157 while ( parent != null ) { parent . IsExpanded = true ; parent = parent . Parent as TreeViewItem ; }
120- return true ;
158+ item . IsSelected = true ;
159+ item . BringIntoView ( ) ;
160+ _lastFoundIndex = idx ;
161+ matchInfo . Text = "Found" ;
162+ downloadStatus . Text = "" ;
163+ return ;
121164 }
122- if ( item . Items . Count > 0 && SearchTree ( item . Items , searchTerm ) ) return true ;
123165 }
124- return false ;
166+ matchInfo . Text = "No matches" ;
167+ }
168+
169+ private void CollectLeafItems ( ItemCollection items , List < TreeViewItem > result ) {
170+ foreach ( TreeViewItem item in items ) {
171+ if ( item . Items . Count > 0 )
172+ CollectLeafItems ( item . Items , result ) ;
173+ else
174+ result . Add ( item ) ;
175+ }
125176 }
126177 }
127178}
0 commit comments