@@ -11,13 +11,16 @@ use crate::settings::{
11
11
} ;
12
12
use crate :: timing:: formatter:: { Delta , Regular , TimeFormatter } ;
13
13
use crate :: {
14
- analysis, comparison, CachedImageId , GeneralLayoutSettings , Segment , TimeSpan , Timer ,
15
- TimingMethod ,
14
+ analysis, comparison, run :: SegmentGroupsIter , CachedImageId , GeneralLayoutSettings , Segment ,
15
+ TimeSpan , Timer , TimingMethod ,
16
16
} ;
17
17
use 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
+ } ;
21
24
22
25
#[ cfg( test) ]
23
26
mod tests;
@@ -197,10 +200,17 @@ pub struct SplitState {
197
200
/// Describes if this segment is the segment the active attempt is currently
198
201
/// on.
199
202
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 ,
200
208
/// The index of the segment based on all the segments of the run. This may
201
209
/// 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.
204
214
pub index : usize ,
205
215
}
206
216
@@ -379,12 +389,27 @@ impl Component {
379
389
let run = timer. run ( ) ;
380
390
self . icon_ids . resize ( run. len ( ) , CachedImageId :: default ( ) ) ;
381
391
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
+
382
408
let mut visual_split_count = self . settings . visual_split_count ;
383
409
if visual_split_count == 0 {
384
- visual_split_count = run . len ( ) ;
410
+ visual_split_count = flattened_count ;
385
411
}
386
412
387
- let current_split = timer. current_split_index ( ) ;
388
413
let method = timer. current_timing_method ( ) ;
389
414
390
415
let always_show_last_split = if self . settings . always_show_last_split {
@@ -393,27 +418,27 @@ impl Component {
393
418
1
394
419
} ;
395
420
let skip_count = min (
396
- current_split . map_or ( 0 , |c_s| {
421
+ index_of_segment_in_focus . map_or ( 0 , |c_s| {
397
422
c_s. saturating_sub (
398
423
visual_split_count
399
424
. saturating_sub ( 2 )
400
425
. saturating_sub ( self . settings . split_preview_count )
401
426
. saturating_add ( always_show_last_split) ,
402
427
) as isize
403
428
} ) ,
404
- run . len ( ) as isize - visual_split_count as isize ,
429
+ flattened_count as isize - visual_split_count as isize ,
405
430
) ;
406
431
self . scroll_offset = min (
407
432
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 ,
409
434
) ;
410
435
let skip_count = max ( 0 , skip_count + self . scroll_offset ) as usize ;
411
436
let take_count = visual_split_count + always_show_last_split as usize - 1 ;
412
437
let always_show_last_split = self . settings . always_show_last_split ;
413
438
414
439
let show_final_separator = self . settings . separator_last_split
415
440
&& always_show_last_split
416
- && skip_count + take_count + 1 < run . len ( ) ;
441
+ && skip_count + take_count + 1 < flattened_count ;
417
442
418
443
let Settings {
419
444
show_thin_separators,
@@ -425,46 +450,56 @@ impl Component {
425
450
426
451
let mut icon_changes = Vec :: new ( ) ;
427
452
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
439
468
. iter ( )
440
469
. map ( |column| {
441
470
column_state (
442
471
column,
443
472
timer,
444
473
layout_settings,
445
- segment,
446
- i ,
474
+ segment. segment ,
475
+ segment . index ,
447
476
current_split,
448
477
method,
449
478
)
450
479
} )
451
- . collect ( ) ;
480
+ . collect ( )
481
+ } else {
482
+ Vec :: new ( )
483
+ } ;
452
484
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
+ }
459
493
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
+ }
468
503
469
504
if fill_with_blank_space && splits. len ( ) < visual_split_count {
470
505
let blank_split_count = visual_split_count - splits. len ( ) ;
@@ -473,6 +508,8 @@ impl Component {
473
508
name : String :: new ( ) ,
474
509
columns : Vec :: new ( ) ,
475
510
is_current_split : false ,
511
+ is_subsplit : false ,
512
+ is_even : true ,
476
513
index : ( usize:: max_value ( ) ^ 1 ) - 2 * i,
477
514
} ) ;
478
515
}
@@ -808,3 +845,63 @@ impl ColumnUpdateWith {
808
845
}
809
846
}
810
847
}
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