@@ -302,6 +302,10 @@ impl App {
302302 self . state . active_panel = self . state . active_panel . next ( ) ;
303303 self . focus_effect = Some ( effects:: border_chase ( ) ) ;
304304 }
305+ KeyCode :: BackTab => {
306+ self . state . active_panel = self . state . active_panel . prev ( ) ;
307+ self . focus_effect = Some ( effects:: border_chase ( ) ) ;
308+ }
305309 KeyCode :: Char ( 's' ) => {
306310 self . state . sort_col = self . state . sort_col . next ( ) ;
307311 self . state . invalidate_display_cache ( ) ;
@@ -358,6 +362,10 @@ impl App {
358362 self . state . filter . toggle_winner ( name) ;
359363 self . reset_log_selection ( ) ;
360364 }
365+ FilterItem :: Loser ( name) => {
366+ self . state . filter . toggle_loser ( name) ;
367+ self . reset_log_selection ( ) ;
368+ }
361369 FilterItem :: Participant ( name) => {
362370 self . state . filter . toggle_participant ( name) ;
363371 self . reset_log_selection ( ) ;
@@ -366,6 +374,14 @@ impl App {
366374 self . state . filter . toggle_street ( * round) ;
367375 self . reset_log_selection ( ) ;
368376 }
377+ FilterItem :: WinSize ( bucket) => {
378+ self . state . filter . toggle_win_size ( * bucket) ;
379+ self . reset_log_selection ( ) ;
380+ }
381+ FilterItem :: LossSize ( bucket) => {
382+ self . state . filter . toggle_loss_size ( * bucket) ;
383+ self . reset_log_selection ( ) ;
384+ }
369385 FilterItem :: PlayerCount ( count) => {
370386 self . state . filter . toggle_player_count ( * count) ;
371387 self . reset_log_selection ( ) ;
@@ -473,12 +489,13 @@ impl App {
473489 pub fn handle_sim_message ( & mut self , msg : SimMessage < GameResult > ) {
474490 match msg {
475491 SimMessage :: GameResult ( result) => {
476- let entry = GameLogEntry {
477- game_number : self . state . games_completed + 1 ,
478- agent_names : result. agent_names . clone ( ) ,
479- profits : result. profits . clone ( ) ,
480- ending_round : result. ending_round ,
481- } ;
492+ let entry = GameLogEntry :: new (
493+ self . state . games_completed + 1 ,
494+ result. agent_names . clone ( ) ,
495+ result. profits . clone ( ) ,
496+ result. ending_round ,
497+ result. big_blind ,
498+ ) ;
482499 self . state . update ( result) ;
483500 self . filtered_log . on_new_game ( & entry, & self . state . filter ) ;
484501 }
@@ -658,6 +675,18 @@ mod tests {
658675 assert_eq ! ( app. state. active_panel, Panel :: Table ) ;
659676 }
660677
678+ #[ test]
679+ fn test_shift_tab_switches_panel_reverse ( ) {
680+ let mut app = App :: new ( Some ( 10 ) ) ;
681+ assert_eq ! ( app. state. active_panel, Panel :: Table ) ;
682+ app. handle_key ( key ( KeyCode :: BackTab ) ) ;
683+ assert_eq ! ( app. state. active_panel, Panel :: Filter ) ;
684+ app. handle_key ( key ( KeyCode :: BackTab ) ) ;
685+ assert_eq ! ( app. state. active_panel, Panel :: GameLog ) ;
686+ app. handle_key ( key ( KeyCode :: BackTab ) ) ;
687+ assert_eq ! ( app. state. active_panel, Panel :: Table ) ;
688+ }
689+
661690 #[ test]
662691 fn test_s_cycles_sort_column ( ) {
663692 let mut app = App :: new ( Some ( 10 ) ) ;
@@ -686,6 +715,7 @@ mod tests {
686715 profits : vec ! [ 10.0 ] ,
687716 ending_round : RoundLabel :: Preflop ,
688717 seat_stats : vec ! [ SeatStats :: default ( ) ] ,
718+ big_blind : 10.0 ,
689719 } ;
690720 app. handle_sim_message ( SimMessage :: GameResult ( result) ) ;
691721 assert_eq ! ( app. state. games_completed, 1 ) ;
@@ -712,6 +742,7 @@ mod tests {
712742 profits : profits. to_vec ( ) ,
713743 ending_round : round,
714744 seat_stats,
745+ big_blind : 10.0 ,
715746 } ) ) ;
716747 }
717748
@@ -764,7 +795,7 @@ mod tests {
764795 }
765796
766797 #[ test]
767- fn test_filter_panel_enter_toggles_item ( ) {
798+ fn test_filter_panel_enter_toggles_winner ( ) {
768799 let mut app = App :: new ( Some ( 10 ) ) ;
769800 add_game (
770801 & mut app,
@@ -800,6 +831,79 @@ mod tests {
800831 assert ! ( app. state. filter. winners. contains( "Alice" ) ) ;
801832 }
802833
834+ #[ test]
835+ fn test_filter_panel_enter_toggles_loser ( ) {
836+ let mut app = App :: new ( Some ( 10 ) ) ;
837+ add_game (
838+ & mut app,
839+ & [ "Alice" , "Bob" ] ,
840+ & [ 10.0 , -10.0 ] ,
841+ RoundLabel :: River ,
842+ ) ;
843+
844+ app. state . active_panel = Panel :: Filter ;
845+ // Header "Winner" + 2 winners + Header "Loser" = index 3 is header
846+ // index 4 is Loser("Alice")
847+ app. state . filter . selected = 4 ;
848+ app. handle_key ( key ( KeyCode :: Enter ) ) ;
849+ assert ! ( app. state. filter. losers. contains( "Alice" ) ) ;
850+
851+ app. handle_key ( key ( KeyCode :: Enter ) ) ;
852+ assert ! ( !app. state. filter. losers. contains( "Alice" ) ) ;
853+ }
854+
855+ #[ test]
856+ fn test_filter_panel_enter_toggles_win_size ( ) {
857+ let mut app = App :: new ( Some ( 10 ) ) ;
858+ add_game (
859+ & mut app,
860+ & [ "Alice" , "Bob" ] ,
861+ & [ 10.0 , -10.0 ] ,
862+ RoundLabel :: River ,
863+ ) ;
864+
865+ app. state . active_panel = Panel :: Filter ;
866+ // Winner(2) + Loser(2) + Participant(2) + headers(3) = 9
867+ // + Header "Street" = 10, + 5 streets = 15
868+ // + Header "Win Size" = 16, first WinSize = 17
869+ // With 2 agents: indices are:
870+ // 0: Header Winner, 1-2: winners, 3: Header Loser, 4-5: losers,
871+ // 6: Header Participant, 7-8: participants,
872+ // 9: Header Street, 10-14: streets,
873+ // 15: Header Win Size, 16: WinSize(Small)
874+ app. state . filter . selected = 16 ;
875+ app. handle_key ( key ( KeyCode :: Enter ) ) ;
876+ assert ! (
877+ app. state
878+ . filter
879+ . win_sizes
880+ . contains( & crate :: tui:: state:: ProfitBucket :: Small )
881+ ) ;
882+ }
883+
884+ #[ test]
885+ fn test_filter_panel_enter_toggles_loss_size ( ) {
886+ let mut app = App :: new ( Some ( 10 ) ) ;
887+ add_game (
888+ & mut app,
889+ & [ "Alice" , "Bob" ] ,
890+ & [ 10.0 , -10.0 ] ,
891+ RoundLabel :: River ,
892+ ) ;
893+
894+ app. state . active_panel = Panel :: Filter ;
895+ // After Win Size section: 16-19 are WinSize buckets
896+ // 20: Header Loss Size, 21: LossSize(Small)
897+ app. state . filter . selected = 21 ;
898+ app. handle_key ( key ( KeyCode :: Enter ) ) ;
899+ assert ! (
900+ app. state
901+ . filter
902+ . loss_sizes
903+ . contains( & crate :: tui:: state:: ProfitBucket :: Small )
904+ ) ;
905+ }
906+
803907 #[ test]
804908 fn test_filter_panel_header_is_noop ( ) {
805909 let mut app = App :: new ( Some ( 10 ) ) ;
0 commit comments