Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion capi/bind_gen/src/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,20 @@ export interface SplitStateJson {
* on.
*/
is_current_split: boolean,
/** Describes whether the segment is part of a segment group. */
is_subsplit: boolean,
/**
* Describes whether the row should be considered an even or an odd row.
* This is useful for visualizing the rows with alternating colors.
*/
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This talks about rows, but in horizontal, we don't even use rows.

is_even: boolean,
/**
* The index of the segment based on all the segments of the run. This may
* differ from the index of this `SplitStateJson` in the
* `SplitsComponentStateJson` object, as there can be a scrolling window,
* showing only a subset of segments. Each index is guaranteed to be unique.
* showing only a subset of segments. Indices are not guaranteed to be
* unique, as they may appear in both group headers and in segments within
* the groups. Only the pair of index and `is_subsplit` is unique.
*/
index: number,
}
Expand Down
183 changes: 141 additions & 42 deletions src/component/splits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
use crate::platform::prelude::*;
use crate::{
clear_vec::{Clear, ClearVec},
run::SegmentGroupsIter,
settings::{
CachedImageId, Color, Field, Gradient, ImageData, ListGradient, SettingsDescription, Value,
},
timing::Snapshot,
GeneralLayoutSettings,
GeneralLayoutSettings, Segment,
};
use core::{
cmp::{max, min},
iter,
};
use core::cmp::{max, min};
use serde::{Deserialize, Serialize};

#[cfg(test)]
Expand Down Expand Up @@ -102,10 +106,17 @@ pub struct SplitState {
/// Describes if this segment is the segment the active attempt is currently
/// on.
pub is_current_split: bool,
/// Describes whether the segment is part of a segment group.
pub is_subsplit: bool,
/// Describes whether the row should be considered an even or an odd row.
/// This is useful for visualizing the rows with alternating colors.
pub is_even: bool,
/// The index of the segment based on all the segments of the run. This may
/// differ from the index of this `SplitState` in the `State` object, as
/// there can be a scrolling window, showing only a subset of segments. Each
/// index is guaranteed to be unique.
/// there can be a scrolling window, showing only a subset of segments.
/// Indices are not guaranteed to be unique, as they may appear in both
/// group headers and in segments within the groups. Only the pair of index
/// and `is_subsplit` is unique.
pub index: usize,
}

Expand Down Expand Up @@ -286,12 +297,27 @@ impl Component {
let run = timer.run();
self.icon_ids.resize(run.len(), CachedImageId::default());

let current_split = timer.current_split_index();

let mut index_of_segment_in_focus = None;
let mut flattened_count = 0;
for (flattened_index, segment) in flatten(
run.segment_groups_iter(),
current_split.unwrap_or_else(|| run.len()),
)
.enumerate()
{
if segment.in_focus {
index_of_segment_in_focus = Some(flattened_index);
}
flattened_count += 1;
}

let mut visual_split_count = self.settings.visual_split_count;
if visual_split_count == 0 {
visual_split_count = run.len();
visual_split_count = flattened_count;
}

let current_split = timer.current_split_index();
let method = timer.current_timing_method();

let locked_last_split = if self.settings.always_show_last_split {
Expand All @@ -300,29 +326,29 @@ impl Component {
0
};
let skip_count = min(
current_split.map_or(0, |current_split| {
index_of_segment_in_focus.map_or(0, |current_split| {
max(
0,
current_split as isize
+ self.settings.split_preview_count as isize
+ locked_last_split
+ 1
- visual_split_count as isize
- visual_split_count as isize,
)
}),
run.len() as isize - visual_split_count as isize,
flattened_count as isize - visual_split_count as isize,
);
self.scroll_offset = min(
max(self.scroll_offset, -skip_count),
run.len() as isize - skip_count - visual_split_count as isize,
flattened_count as isize - skip_count - visual_split_count as isize,
);
let skip_count = max(0, skip_count + self.scroll_offset) as usize;
let take_count = visual_split_count - locked_last_split as usize;
let always_show_last_split = self.settings.always_show_last_split;

let show_final_separator = self.settings.separator_last_split
&& always_show_last_split
&& skip_count + take_count + 1 < run.len();
&& skip_count + take_count + 1 < flattened_count;

let Settings {
show_thin_separators,
Expand All @@ -348,51 +374,60 @@ impl Component {
icon_changes.clear();

state.splits.clear();
for ((i, segment), icon_id) in run
.segments()
.iter()
.enumerate()
.zip(self.icon_ids.iter_mut())
.skip(skip_count)
.filter(|&((i, _), _)| {
i - skip_count < take_count || (always_show_last_split && i + 1 == run.len())
})
{
for (flattened_index, segment) in flatten(
run.segment_groups_iter(),
current_split.unwrap_or_else(|| run.len()),
)
.enumerate()
.skip(skip_count)
.filter(|&(i, _)| {
i - skip_count < take_count || (always_show_last_split && i + 1 == flattened_count)
}) {
let state = state.splits.push_with(|| SplitState {
name: String::new(),
columns: ClearVec::new(),
is_current_split: false,
index: 0,
is_subsplit: false,
is_even: false,
});

if let Some(icon_change) = icon_id.update_with(Some(segment.icon())) {
if let Some(icon_change) =
self.icon_ids[segment.index].update_with(Some(segment.segment.icon()))
{
icon_changes.push(IconChange {
segment_index: i,
segment_index: segment.index,
icon: icon_change.into(),
});
}

state.name.push_str(segment.name());

for column in columns {
column::update_state(
state.columns.push_with(|| ColumnState {
value: String::new(),
semantic_color: Default::default(),
visual_color: Color::transparent(),
}),
column,
timer,
layout_settings,
segment,
i,
current_split,
method,
);
state.name.push_str(segment.name);

if segment.kind
!= FlattenedSegmentGroupItemKind::GroupHeader(SegmentGroupVisibility::Shown)
{
for column in columns {
column::update_state(
state.columns.push_with(|| ColumnState {
value: String::new(),
semantic_color: Default::default(),
visual_color: Color::transparent(),
}),
column,
timer,
layout_settings,
segment.segment,
segment.index,
current_split,
method,
);
}
}

state.is_current_split = Some(i) == current_split;
state.index = i;
state.is_current_split = segment.in_focus;
state.is_subsplit = segment.kind == FlattenedSegmentGroupItemKind::Subsplit;
state.is_even = flattened_index % 2 == 0;
state.index = segment.index;
}

if fill_with_blank_space && state.splits.len() < visual_split_count {
Expand All @@ -402,9 +437,13 @@ impl Component {
name: String::new(),
columns: ClearVec::new(),
is_current_split: false,
is_subsplit: false,
is_even: true,
index: 0,
});
state.is_current_split = false;
state.is_subsplit = false;
state.is_even = true;
state.index = (usize::max_value() ^ 1) - 2 * i;
}
}
Expand Down Expand Up @@ -550,3 +589,63 @@ impl Component {
}
}
}

#[derive(Copy, Clone, PartialEq)]
enum SegmentGroupVisibility {
Collapsed,
Shown,
}

#[derive(Copy, Clone, PartialEq)]
enum FlattenedSegmentGroupItemKind {
GroupHeader(SegmentGroupVisibility),
Subsplit,
}

struct FlattenedSegmentGroupItem<'groups_or_segments, 'segments> {
segment: &'segments Segment,
name: &'groups_or_segments str,
index: usize,
kind: FlattenedSegmentGroupItemKind,
in_focus: bool,
}

fn flatten<'groups_or_segments, 'segments: 'groups_or_segments>(
iter: SegmentGroupsIter<'groups_or_segments, 'segments>,
focus_segment_index: usize,
) -> impl Iterator<Item = FlattenedSegmentGroupItem<'groups_or_segments, 'segments>> {
iter.flat_map(move |group| {
let start_index = group.start_index();
let (children, visibility) =
if group.contains(focus_segment_index) && group.len() > 1 {
(
Some(group.segments().iter().enumerate().map(
move |(local_index, subsplit)| {
let index = start_index + local_index;
FlattenedSegmentGroupItem {
segment: subsplit,
name: subsplit.name(),
index,
kind: FlattenedSegmentGroupItemKind::Subsplit,
in_focus: index == focus_segment_index,
}
},
)),
SegmentGroupVisibility::Shown,
)
} else {
(None, SegmentGroupVisibility::Collapsed)
};

let header_index = start_index + group.len() - 1;
iter::once(FlattenedSegmentGroupItem {
segment: group.ending_segment(),
name: group.name_or_default(),
index: header_index,
kind: FlattenedSegmentGroupItemKind::GroupHeader(visibility),
in_focus: visibility == SegmentGroupVisibility::Collapsed
&& header_index == focus_segment_index,
})
.chain(children.into_iter().flatten())
})
}
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
#![allow(
clippy::blocks_in_if_conditions,
clippy::redundant_closure_call,
clippy::new_ret_no_self
clippy::new_ret_no_self,
clippy::single_char_pattern, // https://github.com/rust-lang/rust-clippy/issues/3813
)]
#![cfg_attr(not(feature = "std"), no_std)]

Expand Down
14 changes: 6 additions & 8 deletions src/rendering/component/splits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
component::splits::State,
layout::{LayoutDirection, LayoutState},
rendering::{
icon::Icon, vertical_padding, Backend, RenderContext, BOTH_PADDINGS,
icon::Icon, vertical_padding, Backend, RenderContext,
DEFAULT_COMPONENT_HEIGHT, DEFAULT_TEXT_SIZE, PADDING, TEXT_ALIGN_BOTTOM, TEXT_ALIGN_TOP,
THIN_SEPARATOR_THICKNESS, TWO_ROW_HEIGHT,
},
Expand Down Expand Up @@ -94,13 +94,11 @@ pub(in crate::rendering) fn render<B: Backend>(
}

let icon_size = split_height - 2.0 * vertical_padding;
let icon_right = if component.has_icons {
BOTH_PADDINGS + icon_size
} else {
PADDING
};

for (i, split) in component.splits.iter().enumerate() {
let icon_left = if split.is_subsplit { 2.5 * PADDING } else { PADDING };
let icon_right = if component.has_icons { icon_left + PADDING + icon_size } else { icon_left };

if component.show_thin_separators && i + 1 != component.splits.len() {
context.render_rectangle(
separator_pos,
Expand All @@ -116,13 +114,13 @@ pub(in crate::rendering) fn render<B: Backend>(
&component.current_split_gradient,
);
} else if let Some((even, odd)) = &split_background {
let color = if split.index % 2 == 0 { even } else { odd };
let color = if split.is_even { even } else { odd };
context.render_rectangle([0.0, 0.0], split_background_bottom_right, color);
}

{
if let Some(Some(icon)) = split_icons.get(split.index) {
context.render_icon([PADDING, icon_y], [icon_size, icon_size], icon);
context.render_icon([icon_left, icon_y], [icon_size, icon_size], icon);
}

let mut left_x = split_width - PADDING;
Expand Down
Loading