@@ -11,13 +11,16 @@ use crate::settings::{
1111} ;
1212use crate :: timing:: formatter:: { Delta , Regular , TimeFormatter } ;
1313use crate :: {
14- analysis, comparison, CachedImageId , GeneralLayoutSettings , Segment , TimeSpan , Timer ,
15- TimingMethod ,
14+ analysis, comparison, run :: SegmentGroupsIter , CachedImageId , GeneralLayoutSettings , Segment ,
15+ TimeSpan , Timer , TimingMethod ,
1616} ;
1717use serde_json:: { to_writer, Result } ;
18- use std:: borrow:: Cow ;
19- use std:: cmp:: { max, min} ;
20- use std:: io:: Write ;
18+ use std:: {
19+ borrow:: Cow ,
20+ cmp:: { max, min} ,
21+ io:: Write ,
22+ iter,
23+ } ;
2124
2225#[ cfg( test) ]
2326mod tests;
@@ -197,10 +200,17 @@ pub struct SplitState {
197200 /// Describes if this segment is the segment the active attempt is currently
198201 /// on.
199202 pub is_current_split : bool ,
203+ /// Describes whether the segment is part of a segment group.
204+ pub is_subsplit : bool ,
205+ /// Describes whether the row should be considered an even or an odd row.
206+ /// This is useful for visualizing the rows with alternating colors.
207+ pub is_even : bool ,
200208 /// The index of the segment based on all the segments of the run. This may
201209 /// differ from the index of this `SplitState` in the `State` object, as
202- /// there can be a scrolling window, showing only a subset of segments. Each
203- /// index is guaranteed to be unique.
210+ /// there can be a scrolling window, showing only a subset of segments.
211+ /// Indices are not guaranteed to be unique, as they may appear in both
212+ /// group headers and in segments within the groups. Only the pair of index
213+ /// and `is_subsplit` is unique.
204214 pub index : usize ,
205215}
206216
@@ -379,12 +389,27 @@ impl Component {
379389 let run = timer. run ( ) ;
380390 self . icon_ids . resize ( run. len ( ) , CachedImageId :: default ( ) ) ;
381391
392+ let current_split = timer. current_split_index ( ) ;
393+
394+ let mut index_of_segment_in_focus = None ;
395+ let mut flattened_count = 0 ;
396+ for ( flattened_index, segment) in flatten (
397+ run. segment_groups_iter ( ) ,
398+ current_split. unwrap_or_else ( || run. len ( ) ) ,
399+ )
400+ . enumerate ( )
401+ {
402+ if segment. in_focus {
403+ index_of_segment_in_focus = Some ( flattened_index) ;
404+ }
405+ flattened_count += 1 ;
406+ }
407+
382408 let mut visual_split_count = self . settings . visual_split_count ;
383409 if visual_split_count == 0 {
384- visual_split_count = run . len ( ) ;
410+ visual_split_count = flattened_count ;
385411 }
386412
387- let current_split = timer. current_split_index ( ) ;
388413 let method = timer. current_timing_method ( ) ;
389414
390415 let always_show_last_split = if self . settings . always_show_last_split {
@@ -393,27 +418,27 @@ impl Component {
393418 1
394419 } ;
395420 let skip_count = min (
396- current_split . map_or ( 0 , |c_s| {
421+ index_of_segment_in_focus . map_or ( 0 , |c_s| {
397422 c_s. saturating_sub (
398423 visual_split_count
399424 . saturating_sub ( 2 )
400425 . saturating_sub ( self . settings . split_preview_count )
401426 . saturating_add ( always_show_last_split) ,
402427 ) as isize
403428 } ) ,
404- run . len ( ) as isize - visual_split_count as isize ,
429+ flattened_count as isize - visual_split_count as isize ,
405430 ) ;
406431 self . scroll_offset = min (
407432 max ( self . scroll_offset , -skip_count) ,
408- run . len ( ) as isize - skip_count - visual_split_count as isize ,
433+ flattened_count as isize - skip_count - visual_split_count as isize ,
409434 ) ;
410435 let skip_count = max ( 0 , skip_count + self . scroll_offset ) as usize ;
411436 let take_count = visual_split_count + always_show_last_split as usize - 1 ;
412437 let always_show_last_split = self . settings . always_show_last_split ;
413438
414439 let show_final_separator = self . settings . separator_last_split
415440 && always_show_last_split
416- && skip_count + take_count + 1 < run . len ( ) ;
441+ && skip_count + take_count + 1 < flattened_count ;
417442
418443 let Settings {
419444 show_thin_separators,
@@ -425,46 +450,56 @@ impl Component {
425450
426451 let mut icon_changes = Vec :: new ( ) ;
427452
428- let mut splits: Vec < _ > = run
429- . segments ( )
430- . iter ( )
431- . enumerate ( )
432- . zip ( self . icon_ids . iter_mut ( ) )
433- . skip ( skip_count)
434- . filter ( |& ( ( i, _) , _) | {
435- i - skip_count < take_count || ( always_show_last_split && i + 1 == run. len ( ) )
436- } )
437- . map ( |( ( i, segment) , icon_id) | {
438- let columns = columns
453+ let mut splits = Vec :: with_capacity ( visual_split_count) ;
454+
455+ for ( flattened_index, segment) in flatten (
456+ run. segment_groups_iter ( ) ,
457+ current_split. unwrap_or_else ( || run. len ( ) ) ,
458+ )
459+ . enumerate ( )
460+ . skip ( skip_count)
461+ . filter ( |& ( i, _) | {
462+ i - skip_count < take_count || ( always_show_last_split && i + 1 == flattened_count)
463+ } ) {
464+ let columns = if segment. kind
465+ != FlattenedSegmentGroupItemKind :: GroupHeader ( SegmentGroupVisibility :: Shown )
466+ {
467+ columns
439468 . iter ( )
440469 . map ( |column| {
441470 column_state (
442471 column,
443472 timer,
444473 layout_settings,
445- segment,
446- i ,
474+ segment. segment ,
475+ segment . index ,
447476 current_split,
448477 method,
449478 )
450479 } )
451- . collect ( ) ;
480+ . collect ( )
481+ } else {
482+ Vec :: new ( )
483+ } ;
452484
453- if let Some ( icon_change) = icon_id. update_with ( Some ( segment. icon ( ) ) ) {
454- icon_changes. push ( IconChange {
455- segment_index : i,
456- icon : icon_change. to_owned ( ) ,
457- } ) ;
458- }
485+ if let Some ( icon_change) =
486+ self . icon_ids [ segment. index ] . update_with ( Some ( segment. segment . icon ( ) ) )
487+ {
488+ icon_changes. push ( IconChange {
489+ segment_index : segment. index ,
490+ icon : icon_change. to_owned ( ) ,
491+ } ) ;
492+ }
459493
460- SplitState {
461- name : segment. name ( ) . to_string ( ) ,
462- columns,
463- is_current_split : Some ( i) == current_split,
464- index : i,
465- }
466- } )
467- . collect ( ) ;
494+ splits. push ( SplitState {
495+ name : segment. name . to_string ( ) ,
496+ columns,
497+ is_current_split : segment. in_focus ,
498+ is_subsplit : segment. kind == FlattenedSegmentGroupItemKind :: Subsplit ,
499+ is_even : flattened_index % 2 == 0 ,
500+ index : segment. index ,
501+ } ) ;
502+ }
468503
469504 if fill_with_blank_space && splits. len ( ) < visual_split_count {
470505 let blank_split_count = visual_split_count - splits. len ( ) ;
@@ -473,6 +508,8 @@ impl Component {
473508 name : String :: new ( ) ,
474509 columns : Vec :: new ( ) ,
475510 is_current_split : false ,
511+ is_subsplit : false ,
512+ is_even : true ,
476513 index : ( usize:: max_value ( ) ^ 1 ) - 2 * i,
477514 } ) ;
478515 }
@@ -808,3 +845,63 @@ impl ColumnUpdateWith {
808845 }
809846 }
810847}
848+
849+ #[ derive( Copy , Clone , PartialEq ) ]
850+ enum SegmentGroupVisibility {
851+ Collapsed ,
852+ Shown ,
853+ }
854+
855+ #[ derive( Copy , Clone , PartialEq ) ]
856+ enum FlattenedSegmentGroupItemKind {
857+ GroupHeader ( SegmentGroupVisibility ) ,
858+ Subsplit ,
859+ }
860+
861+ struct FlattenedSegmentGroupItem < ' groups_or_segments , ' segments > {
862+ segment : & ' segments Segment ,
863+ name : & ' groups_or_segments str ,
864+ index : usize ,
865+ kind : FlattenedSegmentGroupItemKind ,
866+ in_focus : bool ,
867+ }
868+
869+ fn flatten < ' groups_or_segments , ' segments : ' groups_or_segments > (
870+ iter : SegmentGroupsIter < ' groups_or_segments , ' segments > ,
871+ focus_segment_index : usize ,
872+ ) -> impl Iterator < Item = FlattenedSegmentGroupItem < ' groups_or_segments , ' segments > > {
873+ iter. flat_map ( move |group| {
874+ let start_index = group. start_index ( ) ;
875+ let ( children, visibility) =
876+ if group. contains ( focus_segment_index) && group. len ( ) > 1 {
877+ (
878+ Some ( group. segments ( ) . iter ( ) . enumerate ( ) . map (
879+ move |( local_index, subsplit) | {
880+ let index = start_index + local_index;
881+ FlattenedSegmentGroupItem {
882+ segment : subsplit,
883+ name : subsplit. name ( ) ,
884+ index,
885+ kind : FlattenedSegmentGroupItemKind :: Subsplit ,
886+ in_focus : index == focus_segment_index,
887+ }
888+ } ,
889+ ) ) ,
890+ SegmentGroupVisibility :: Shown ,
891+ )
892+ } else {
893+ ( None , SegmentGroupVisibility :: Collapsed )
894+ } ;
895+
896+ let header_index = start_index + group. len ( ) - 1 ;
897+ iter:: once ( FlattenedSegmentGroupItem {
898+ segment : group. ending_segment ( ) ,
899+ name : group. name_or_default ( ) ,
900+ index : header_index,
901+ kind : FlattenedSegmentGroupItemKind :: GroupHeader ( visibility) ,
902+ in_focus : visibility == SegmentGroupVisibility :: Collapsed
903+ && header_index == focus_segment_index,
904+ } )
905+ . chain ( children. into_iter ( ) . flatten ( ) )
906+ } )
907+ }
0 commit comments