1
1
use std:: {
2
+ borrow:: BorrowMut ,
2
3
collections:: HashMap ,
3
4
sync:: { Arc , Mutex } ,
4
5
time:: Duration ,
@@ -46,6 +47,8 @@ pub struct Menu {
46
47
schema_index : usize ,
47
48
list_state : ListState ,
48
49
menu_focus : MenuFocus ,
50
+ search : Option < String > ,
51
+ search_focused : bool ,
49
52
}
50
53
51
54
impl Menu {
@@ -57,6 +60,8 @@ impl Menu {
57
60
schema_index : 0 ,
58
61
list_state : ListState :: default ( ) ,
59
62
menu_focus : MenuFocus :: default ( ) ,
63
+ search : None ,
64
+ search_focused : false ,
60
65
}
61
66
}
62
67
@@ -124,6 +129,12 @@ impl Menu {
124
129
MenuFocus :: Schema => self . schema_index = 0 ,
125
130
}
126
131
}
132
+
133
+ pub fn reset_search ( & mut self ) {
134
+ self . search = None ;
135
+ self . search_focused = false ;
136
+ self . list_state = ListState :: default ( ) . with_selected ( Some ( 0 ) ) ;
137
+ }
127
138
}
128
139
129
140
impl < ' a > SettableTableList < ' a > for Menu {
@@ -174,18 +185,53 @@ impl Component for Menu {
174
185
}
175
186
if let Some ( Event :: Key ( key) ) = event {
176
187
match key. code {
177
- KeyCode :: Right | KeyCode :: Char ( 'l' ) => self . change_focus ( MenuFocus :: Table ) ,
178
- KeyCode :: Left | KeyCode :: Char ( 'h' ) => self . change_focus ( MenuFocus :: Schema ) ,
179
- KeyCode :: Down | KeyCode :: Char ( 'j' ) => self . scroll_down ( ) ,
180
- KeyCode :: Up | KeyCode :: Char ( 'k' ) => self . scroll_up ( ) ,
181
- KeyCode :: Char ( 'g' ) => self . scroll_top ( ) ,
182
- KeyCode :: Char ( 'G' ) => self . scroll_bottom ( ) ,
188
+ KeyCode :: Right => self . change_focus ( MenuFocus :: Table ) ,
189
+ KeyCode :: Left => self . change_focus ( MenuFocus :: Schema ) ,
190
+ KeyCode :: Down => self . scroll_down ( ) ,
191
+ KeyCode :: Up => self . scroll_up ( ) ,
192
+ KeyCode :: Char ( c) => {
193
+ if self . search . is_some ( ) && self . search_focused {
194
+ if let Some ( search) = self . search . as_mut ( ) {
195
+ search. push ( c) ;
196
+ self . list_state = ListState :: default ( ) . with_selected ( Some ( 0 ) ) ;
197
+ }
198
+ } else {
199
+ match key. code {
200
+ KeyCode :: Char ( '/' ) => {
201
+ self . search_focused = true ;
202
+ if self . search . is_none ( ) {
203
+ self . search = Some ( "" . to_owned ( ) )
204
+ }
205
+ } ,
206
+ KeyCode :: Char ( 'l' ) => self . change_focus ( MenuFocus :: Table ) ,
207
+ KeyCode :: Char ( 'h' ) => self . change_focus ( MenuFocus :: Schema ) ,
208
+ KeyCode :: Char ( 'j' ) => self . scroll_down ( ) ,
209
+ KeyCode :: Char ( 'k' ) => self . scroll_up ( ) ,
210
+ KeyCode :: Char ( 'g' ) => self . scroll_top ( ) ,
211
+ KeyCode :: Char ( 'G' ) => self . scroll_bottom ( ) ,
212
+ _ => { } ,
213
+ }
214
+ }
215
+ } ,
183
216
KeyCode :: Enter => {
184
- if let Some ( selected) = self . list_state . selected ( ) {
217
+ if self . search . is_some ( ) && self . search_focused {
218
+ self . search_focused = false ;
219
+ } else if let Some ( selected) = self . list_state . selected ( ) {
185
220
let ( schema, tables) = self . table_map . get_index ( self . schema_index ) . unwrap ( ) ;
186
221
self . command_tx . as_ref ( ) . unwrap ( ) . send ( Action :: MenuSelect ( schema. clone ( ) , tables[ selected] . clone ( ) ) ) ?;
187
222
}
188
223
} ,
224
+ KeyCode :: Esc => self . reset_search ( ) ,
225
+ KeyCode :: Backspace => {
226
+ if let Some ( search) = self . search . as_mut ( ) {
227
+ if !search. is_empty ( ) {
228
+ search. pop ( ) ;
229
+ self . list_state = ListState :: default ( ) . with_selected ( Some ( 0 ) ) ;
230
+ } else {
231
+ self . reset_search ( ) ;
232
+ }
233
+ }
234
+ } ,
189
235
_ => { } ,
190
236
}
191
237
} ;
@@ -196,34 +242,70 @@ impl Component for Menu {
196
242
let focused = app_state. focus == Focus :: Menu ;
197
243
let parent_block = Block :: default ( ) ;
198
244
let stable_keys = self . table_map . keys ( ) . enumerate ( ) ;
199
- let constraints = stable_keys. clone ( ) . map ( |( i, k) | {
200
- match i {
201
- x if x == self . schema_index => Constraint :: Min ( 5 ) ,
202
- _ => Constraint :: Length ( 1 ) ,
203
- }
204
- } ) ;
245
+ let mut constraints: Vec < Constraint > = stable_keys
246
+ . clone ( )
247
+ . map ( |( i, k) | {
248
+ match i {
249
+ x if x == self . schema_index => Constraint :: Min ( 5 ) ,
250
+ _ => Constraint :: Length ( 1 ) ,
251
+ }
252
+ } )
253
+ . collect ( ) ;
254
+ if let Some ( search) = self . search . as_ref ( ) {
255
+ constraints. insert ( 0 , Constraint :: Length ( 1 ) ) ;
256
+ }
205
257
let layout =
206
258
Layout :: default ( ) . constraints ( constraints) . direction ( Direction :: Vertical ) . split ( parent_block. inner ( area) ) ;
259
+ if let Some ( search) = self . search . as_ref ( ) {
260
+ f. render_widget (
261
+ Text :: styled (
262
+ "/ " . to_owned ( ) + search. to_owned ( ) . as_str ( ) ,
263
+ if !focused {
264
+ Style :: new ( ) . dim ( )
265
+ } else if self . search_focused {
266
+ Style :: default ( ) . fg ( Color :: Yellow )
267
+ } else {
268
+ Style :: default ( )
269
+ } ,
270
+ ) ,
271
+ layout[ 0 ] ,
272
+ )
273
+ }
207
274
stable_keys. for_each ( |( i, k) | {
275
+ let layout_index = if self . search . is_some ( ) { i + 1 } else { i } ;
208
276
match i {
209
277
x if x == self . schema_index => {
210
- let block = Block :: default ( ) . title ( k. as_str ( ) . to_owned ( ) + "(schema)" ) . borders ( Borders :: ALL ) . border_style (
211
- if focused && self . menu_focus == MenuFocus :: Schema {
278
+ let block = Block :: default ( )
279
+ . title ( k. as_str ( ) . to_owned ( ) + "(schema)" )
280
+ . borders ( Borders :: ALL )
281
+ . border_style ( if focused && self . menu_focus == MenuFocus :: Schema {
212
282
Style :: default ( ) . fg ( Color :: Green )
213
283
} else if focused {
214
284
Style :: default ( )
215
285
} else {
216
286
Style :: new ( ) . dim ( )
217
- } ,
218
- ) ;
219
- let block_margin = layout[ i ] . inner ( Margin { vertical : 1 , horizontal : 0 } ) ;
287
+ } )
288
+ . padding ( Padding { left : 0 , right : 1 , top : 0 , bottom : 0 } ) ;
289
+ let block_margin = layout[ layout_index ] . inner ( Margin { vertical : 1 , horizontal : 0 } ) ;
220
290
let tables = self . table_map . get_key_value ( k) . unwrap ( ) . 1 . clone ( ) ;
221
- let table_length = tables. len ( ) ;
291
+ let filtered_tables: Vec < String > = tables
292
+ . into_iter ( )
293
+ . filter ( |t| {
294
+ if let Some ( search) = self . search . as_ref ( ) {
295
+ t. to_lowercase ( ) . contains ( search. to_lowercase ( ) . trim ( ) )
296
+ } else {
297
+ true
298
+ }
299
+ } )
300
+ . collect ( ) ;
301
+ let table_length = filtered_tables. len ( ) ;
222
302
let available_height = block. inner ( parent_block. inner ( area) ) . height as usize ;
223
- let list = List :: default ( ) . items ( tables) . block ( block) . highlight_style (
224
- Style :: default ( ) . bg ( if focused { Color :: Green } else { Color :: White } ) . fg ( Color :: DarkGray ) ,
303
+ let list = List :: default ( ) . items ( filtered_tables) . block ( block) . highlight_style (
304
+ Style :: default ( )
305
+ . bg ( if focused && !self . search_focused { Color :: Green } else { Color :: White } )
306
+ . fg ( Color :: DarkGray ) ,
225
307
) ;
226
- f. render_stateful_widget ( list, layout[ i ] , & mut self . list_state ) ;
308
+ f. render_stateful_widget ( list, layout[ layout_index ] , & mut self . list_state ) ;
227
309
let vertical_scrollbar = Scrollbar :: new ( ScrollbarOrientation :: VerticalRight ) . symbols ( scrollbar:: VERTICAL ) ;
228
310
let mut vertical_scrollbar_state =
229
311
ScrollbarState :: new ( table_length. saturating_sub ( available_height) ) . position ( self . list_state . offset ( ) ) ;
@@ -235,7 +317,7 @@ impl Component for Menu {
235
317
"└" . to_owned ( ) + k. to_owned ( ) . as_str ( ) + "(schema)" ,
236
318
if focused { Style :: default ( ) } else { Style :: new ( ) . dim ( ) } ,
237
319
) ,
238
- layout[ i ] ,
320
+ layout[ layout_index ] ,
239
321
) ;
240
322
} ,
241
323
0 => {
@@ -244,7 +326,7 @@ impl Component for Menu {
244
326
"┌" . to_owned ( ) + k. to_owned ( ) . as_str ( ) + "(schema)" ,
245
327
if focused { Style :: default ( ) } else { Style :: new ( ) . dim ( ) } ,
246
328
) ,
247
- layout[ i ] ,
329
+ layout[ layout_index ] ,
248
330
) ;
249
331
} ,
250
332
_ => {
@@ -253,7 +335,7 @@ impl Component for Menu {
253
335
"├" . to_owned ( ) + k. to_owned ( ) . as_str ( ) + "(schema)" ,
254
336
if focused { Style :: default ( ) } else { Style :: new ( ) . dim ( ) } ,
255
337
) ,
256
- layout[ i ] ,
338
+ layout[ layout_index ] ,
257
339
)
258
340
} ,
259
341
} ;
0 commit comments