@@ -6,6 +6,7 @@ use std::{
6
6
7
7
#[ cfg( not( target_arch = "wasm32" ) ) ]
8
8
use anyhow:: { bail, Context } ;
9
+ use data:: filter:: { Comparator , FieldSpecifier , FilterConfig , FilterOn } ;
9
10
use egui:: { Align , KeyboardShortcut , Modifiers } ;
10
11
use egui_extras:: { Column , TableBuilder } ;
11
12
use log:: info;
@@ -28,6 +29,7 @@ pub struct LogViewerApp {
28
29
shortcut_next : KeyboardShortcut ,
29
30
shortcut_first : KeyboardShortcut ,
30
31
shortcut_last : KeyboardShortcut ,
32
+ shortcut_unfilter : KeyboardShortcut ,
31
33
32
34
#[ serde( skip) ]
33
35
should_scroll : bool ,
@@ -48,6 +50,7 @@ impl Default for LogViewerApp {
48
50
shortcut_next : KeyboardShortcut :: new ( Modifiers :: NONE , egui:: Key :: ArrowDown ) ,
49
51
shortcut_first : KeyboardShortcut :: new ( Modifiers :: NONE , egui:: Key :: Home ) ,
50
52
shortcut_last : KeyboardShortcut :: new ( Modifiers :: NONE , egui:: Key :: End ) ,
53
+ shortcut_unfilter : KeyboardShortcut :: new ( Modifiers :: NONE , egui:: Key :: Escape ) ,
51
54
should_scroll : Default :: default ( ) ,
52
55
show_last_filename : true ,
53
56
}
@@ -137,16 +140,18 @@ impl LogViewerApp {
137
140
// TODO 3: Figure out if calculating these values only once is worth it.
138
141
// TODO 4: Remove hard coded "msg"
139
142
let heights: Vec < f32 > = data
140
- . rows ( )
141
- . iter ( )
143
+ . rows_iter ( )
142
144
. map ( |x| {
143
145
( 1f32 ) . max ( x. field_value ( "msg" ) . display ( ) . lines ( ) . count ( ) as f32 )
144
146
* text_height
145
147
} )
146
148
. collect ( ) ;
147
149
body. heterogeneous_rows ( heights. into_iter ( ) , |mut row| {
148
150
let row_index = row. index ( ) ;
149
- let log_row = & data. rows ( ) [ row_index] ;
151
+ let log_row = & data
152
+ . rows_iter ( )
153
+ . nth ( row_index)
154
+ . expect ( "len was passed above should only be valid indices" ) ;
150
155
151
156
let emphasis_info = if let Some ( selected_row) = data. selected_row {
152
157
row. set_selected ( selected_row == row_index) ;
@@ -157,7 +162,10 @@ impl LogViewerApp {
157
162
& self . data_display_options . main_list_fields ( ) [ emphasis_field_idx] ;
158
163
Some ( (
159
164
emphasis_field_idx,
160
- data. rows ( ) [ selected_row] . field_value ( field_name) ,
165
+ data. rows_iter ( )
166
+ . nth ( selected_row)
167
+ . expect ( "selected row should always be a valid index" )
168
+ . field_value ( field_name) ,
161
169
) )
162
170
} else {
163
171
None
@@ -444,52 +452,162 @@ impl LogViewerApp {
444
452
if ui. input_mut ( |i| i. consume_shortcut ( & self . shortcut_last ) ) {
445
453
self . move_selected_last ( ) ;
446
454
}
455
+
456
+ if ui. input_mut ( |i| i. consume_shortcut ( & self . shortcut_unfilter ) ) {
457
+ if let Some ( data) = self . data . as_mut ( ) {
458
+ data. unfilter ( ) ;
459
+ }
460
+ }
447
461
}
448
462
449
463
fn navigation_and_filtering_ui ( & mut self , ui : & mut egui:: Ui ) {
450
464
ui. horizontal ( |ui| {
451
- ui. label ( "Nav:" ) ;
452
- if ui
453
- . button ( "⏪" )
454
- . on_hover_text ( format ! (
455
- "First ({})" ,
456
- ui. ctx( ) . format_shortcut( & self . shortcut_first)
457
- ) )
458
- . clicked ( )
459
- {
460
- self . move_selected_first ( ) ;
465
+ self . navigation_ui ( ui) ;
466
+ ui. separator ( ) ;
467
+ self . filtering_ui ( ui) ;
468
+ } ) ;
469
+ }
470
+
471
+ fn filtering_ui ( & mut self , ui : & mut egui:: Ui ) {
472
+ if let Some ( data) = self . data . as_mut ( ) {
473
+ ui. label ( "Filter:" ) ;
474
+ let mut is_filter_enabled = data. filter . is_some ( ) ;
475
+ ui. checkbox ( & mut is_filter_enabled, "" ) ;
476
+ match ( is_filter_enabled, data. filter . is_some ( ) ) {
477
+ ( false , false ) | ( true , true ) => { } // Already match
478
+ ( true , false ) => data. filter = Some ( Default :: default ( ) ) ,
479
+ ( false , true ) => {
480
+ data. unfilter ( ) ;
481
+ data. filter = None
482
+ }
461
483
}
462
- if ui
463
- . button ( "⬆" )
464
- . on_hover_text ( format ! (
465
- "Previous ({})" ,
466
- ui. ctx( ) . format_shortcut( & self . shortcut_prev)
467
- ) )
468
- . clicked ( )
469
- {
470
- self . move_selected_prev ( ) ;
484
+ let mut should_apply_filter = false ;
485
+ if is_filter_enabled {
486
+ if ui. button ( "Apply" ) . clicked ( ) {
487
+ should_apply_filter = true ;
488
+ }
489
+ if data. is_filtered ( )
490
+ && ui
491
+ . button ( "Unfilter" )
492
+ . on_hover_text ( format ! (
493
+ "Clears Filter ({})" ,
494
+ ui. ctx( ) . format_shortcut( & self . shortcut_unfilter)
495
+ ) )
496
+ . clicked ( )
497
+ {
498
+ data. unfilter ( ) ;
499
+ }
471
500
}
472
- if ui
473
- . button ( "⬇" )
474
- . on_hover_text ( format ! (
475
- "Next ({})" ,
476
- ui. ctx( ) . format_shortcut( & self . shortcut_next)
477
- ) )
478
- . clicked ( )
479
- {
480
- self . move_selected_next ( ) ;
501
+
502
+ if let Some ( filter) = data. filter . as_mut ( ) {
503
+ let FilterConfig {
504
+ search_key,
505
+ filter_on,
506
+ comparator,
507
+ } = filter;
508
+ ui. label ( "Search Key: " ) ;
509
+ if ui. text_edit_singleline ( search_key) . lost_focus ( )
510
+ && ui. input ( |i| i. key_pressed ( egui:: Key :: Enter ) )
511
+ {
512
+ should_apply_filter = true ;
513
+ }
514
+
515
+ ui. spacing ( ) ;
516
+ egui:: ComboBox :: from_label ( "" )
517
+ . selected_text ( format ! ( "{}" , comparator) )
518
+ . show_ui ( ui, |ui| {
519
+ ui. selectable_value ( comparator, Comparator :: LessThan , "Less than" ) ;
520
+ ui. selectable_value (
521
+ comparator,
522
+ Comparator :: LessThanEqual ,
523
+ "Less than equal" ,
524
+ ) ;
525
+ ui. selectable_value ( comparator, Comparator :: Equal , "Equal" ) ;
526
+ ui. selectable_value ( comparator, Comparator :: GreaterThan , "Greater than" ) ;
527
+ ui. selectable_value (
528
+ comparator,
529
+ Comparator :: GreaterThanEqual ,
530
+ "Greater than equal" ,
531
+ ) ;
532
+ ui. selectable_value ( comparator, Comparator :: NotEqual , "Not equal" ) ;
533
+ ui. selectable_value ( comparator, Comparator :: Contains , "Contains" ) ;
534
+ ui. selectable_value ( comparator, Comparator :: NotContains , "Not contains" ) ;
535
+ } ) ;
536
+
537
+ ui. spacing ( ) ;
538
+ let mut is_any = filter_on. is_any ( ) ;
539
+ ui. toggle_value ( & mut is_any, "Any" ) ;
540
+ if is_any && !filter_on. is_any ( ) {
541
+ // Toggled on
542
+ * filter_on = FilterOn :: Any ;
543
+ }
544
+
545
+ let mut is_field = filter_on. is_field ( ) ;
546
+ ui. toggle_value ( & mut is_field, "Field" ) ;
547
+ if is_field && !filter_on. is_field ( ) {
548
+ // Toggled on
549
+ * filter_on = FilterOn :: Field ( Default :: default ( ) ) ;
550
+ }
551
+
552
+ if let FilterOn :: Field ( FieldSpecifier { name } ) = filter_on {
553
+ ui. spacing ( ) ;
554
+ if ui
555
+ . add ( egui:: TextEdit :: singleline ( name) . hint_text ( "Name" ) )
556
+ . lost_focus ( )
557
+ && ui. input ( |i| i. key_pressed ( egui:: Key :: Enter ) )
558
+ {
559
+ should_apply_filter = true ;
560
+ }
561
+ }
481
562
}
482
- if ui
483
- . button ( "⏩" )
484
- . on_hover_text ( format ! (
485
- "Last ({})" ,
486
- ui. ctx( ) . format_shortcut( & self . shortcut_last)
487
- ) )
488
- . clicked ( )
489
- {
490
- self . move_selected_last ( ) ;
563
+ if should_apply_filter {
564
+ data. apply_filter ( self . data_display_options . common_fields ( ) ) ;
491
565
}
492
- } ) ;
566
+ }
567
+ }
568
+
569
+ fn navigation_ui ( & mut self , ui : & mut egui:: Ui ) {
570
+ ui. label ( "Nav:" ) ;
571
+ if ui
572
+ . button ( "⏪" )
573
+ . on_hover_text ( format ! (
574
+ "First ({})" ,
575
+ ui. ctx( ) . format_shortcut( & self . shortcut_first)
576
+ ) )
577
+ . clicked ( )
578
+ {
579
+ self . move_selected_first ( ) ;
580
+ }
581
+ if ui
582
+ . button ( "⬆" )
583
+ . on_hover_text ( format ! (
584
+ "Previous ({})" ,
585
+ ui. ctx( ) . format_shortcut( & self . shortcut_prev)
586
+ ) )
587
+ . clicked ( )
588
+ {
589
+ self . move_selected_prev ( ) ;
590
+ }
591
+ if ui
592
+ . button ( "⬇" )
593
+ . on_hover_text ( format ! (
594
+ "Next ({})" ,
595
+ ui. ctx( ) . format_shortcut( & self . shortcut_next)
596
+ ) )
597
+ . clicked ( )
598
+ {
599
+ self . move_selected_next ( ) ;
600
+ }
601
+ if ui
602
+ . button ( "⏩" )
603
+ . on_hover_text ( format ! (
604
+ "Last ({})" ,
605
+ ui. ctx( ) . format_shortcut( & self . shortcut_last)
606
+ ) )
607
+ . clicked ( )
608
+ {
609
+ self . move_selected_last ( ) ;
610
+ }
493
611
}
494
612
fn data_load_ui ( & mut self , ui : & mut egui:: Ui ) {
495
613
ui. horizontal ( |ui| {
0 commit comments