@@ -33,6 +33,8 @@ pub struct AppState {
33
33
/// widget
34
34
selection : ListState ,
35
35
filter : Filter ,
36
+ multi_select : bool ,
37
+ selected_commands : Vec < Command > ,
36
38
drawable : bool ,
37
39
}
38
40
@@ -61,6 +63,8 @@ impl AppState {
61
63
visit_stack : vec ! [ root_id] ,
62
64
selection : ListState :: default ( ) . with_selected ( Some ( 0 ) ) ,
63
65
filter : Filter :: new ( ) ,
66
+ multi_select : false ,
67
+ selected_commands : Vec :: new ( ) ,
64
68
drawable : false ,
65
69
} ;
66
70
state. update_items ( ) ;
@@ -199,12 +203,29 @@ impl AppState {
199
203
|ListEntry {
200
204
node, has_children, ..
201
205
} | {
206
+ let is_selected = self . selected_commands . contains ( & node. command ) ;
207
+ let ( indicator, style) = if is_selected {
208
+ ( self . theme . multi_select_icon ( ) , Style :: default ( ) . bold ( ) )
209
+ } else {
210
+ ( "" , Style :: new ( ) )
211
+ } ;
202
212
if * has_children {
203
- Line :: from ( format ! ( "{} {}" , self . theme. dir_icon( ) , node. name) )
204
- . style ( self . theme . dir_color ( ) )
213
+ Line :: from ( format ! (
214
+ "{} {} {}" ,
215
+ self . theme. dir_icon( ) ,
216
+ node. name,
217
+ indicator
218
+ ) )
219
+ . style ( self . theme . dir_color ( ) )
205
220
} else {
206
- Line :: from ( format ! ( "{} {}" , self . theme. cmd_icon( ) , node. name) )
207
- . style ( self . theme . cmd_color ( ) )
221
+ Line :: from ( format ! (
222
+ "{} {} {}" ,
223
+ self . theme. cmd_icon( ) ,
224
+ node. name,
225
+ indicator
226
+ ) )
227
+ . style ( self . theme . cmd_color ( ) )
228
+ . patch_style ( style)
208
229
}
209
230
} ,
210
231
) ) ;
@@ -216,11 +237,15 @@ impl AppState {
216
237
} else {
217
238
Style :: new ( )
218
239
} )
219
- . block (
220
- Block :: default ( )
221
- . borders ( Borders :: ALL )
222
- . title ( format ! ( "Linux Toolbox - {}" , env!( "BUILD_DATE" ) ) ) ,
223
- )
240
+ . block ( Block :: default ( ) . borders ( Borders :: ALL ) . title ( format ! (
241
+ "Linux Toolbox - {} {}" ,
242
+ env!( "BUILD_DATE" ) ,
243
+ if self . multi_select {
244
+ "[Multi-Select]"
245
+ } else {
246
+ ""
247
+ }
248
+ ) ) )
224
249
. scroll_padding ( 1 ) ;
225
250
frame. render_stateful_widget ( list, chunks[ 1 ] , & mut self . selection ) ;
226
251
@@ -254,15 +279,15 @@ impl AppState {
254
279
match key. code {
255
280
KeyCode :: Tab => {
256
281
if self . current_tab . selected ( ) . unwrap ( ) == self . tabs . len ( ) - 1 {
257
- self . current_tab . select_first ( ) ; // Select first tab when it is at last
282
+ self . current_tab . select_first ( ) ;
258
283
} else {
259
284
self . current_tab . select_next ( ) ;
260
285
}
261
286
self . refresh_tab ( ) ;
262
287
}
263
288
KeyCode :: BackTab => {
264
289
if self . current_tab . selected ( ) . unwrap ( ) == 0 {
265
- self . current_tab . select ( Some ( self . tabs . len ( ) - 1 ) ) ; // Select last tab when it is at first
290
+ self . current_tab . select ( Some ( self . tabs . len ( ) - 1 ) ) ;
266
291
} else {
267
292
self . current_tab . select_previous ( ) ;
268
293
}
@@ -329,20 +354,48 @@ impl AppState {
329
354
KeyCode :: Char ( '/' ) => self . enter_search ( ) ,
330
355
KeyCode :: Char ( 't' ) => self . theme . next ( ) ,
331
356
KeyCode :: Char ( 'T' ) => self . theme . prev ( ) ,
357
+ KeyCode :: Char ( 'v' ) | KeyCode :: Char ( 'V' ) => self . toggle_multi_select ( ) ,
358
+ KeyCode :: Char ( ' ' ) if self . multi_select => self . toggle_selection ( ) ,
332
359
_ => { }
333
360
} ,
334
361
335
362
_ => ( ) ,
336
363
} ;
337
364
true
338
365
}
339
-
366
+ fn toggle_multi_select ( & mut self ) {
367
+ if self . is_current_tab_multi_selectable ( ) {
368
+ self . multi_select = !self . multi_select ;
369
+ if !self . multi_select {
370
+ self . selected_commands . clear ( ) ;
371
+ }
372
+ }
373
+ }
374
+ fn toggle_selection ( & mut self ) {
375
+ if let Some ( command) = self . get_selected_command ( ) {
376
+ if self . selected_commands . contains ( & command) {
377
+ self . selected_commands . retain ( |c| c != & command) ;
378
+ } else {
379
+ self . selected_commands . push ( command) ;
380
+ }
381
+ }
382
+ }
383
+ pub fn is_current_tab_multi_selectable ( & self ) -> bool {
384
+ let index = self . current_tab . selected ( ) . unwrap_or ( 0 ) ;
385
+ self . tabs
386
+ . get ( index)
387
+ . map_or ( false , |tab| tab. multi_selectable )
388
+ }
340
389
fn update_items ( & mut self ) {
341
390
self . filter . update_items (
342
391
& self . tabs ,
343
392
self . current_tab . selected ( ) . unwrap ( ) ,
344
393
* self . visit_stack . last ( ) . unwrap ( ) ,
345
394
) ;
395
+ if !self . is_current_tab_multi_selectable ( ) {
396
+ self . multi_select = false ;
397
+ self . selected_commands . clear ( ) ;
398
+ }
346
399
}
347
400
348
401
/// Checks either the current tree node is the root node (can we go up the tree or no)
@@ -471,9 +524,15 @@ impl AppState {
471
524
}
472
525
473
526
fn handle_enter ( & mut self ) {
474
- if let Some ( cmd) = self . get_selected_command ( ) {
475
- let command = RunningCommand :: new ( cmd) ;
527
+ if self . selected_item_is_cmd ( ) {
528
+ if self . selected_commands . is_empty ( ) {
529
+ if let Some ( cmd) = self . get_selected_command ( ) {
530
+ self . selected_commands . push ( cmd) ;
531
+ }
532
+ }
533
+ let command = RunningCommand :: new ( self . selected_commands . clone ( ) ) ;
476
534
self . spawn_float ( command, 80 , 80 ) ;
535
+ self . selected_commands . clear ( ) ;
477
536
} else {
478
537
self . go_to_selected_dir ( ) ;
479
538
}
0 commit comments