fix: scroll tile bar to keep selected tile visible
When there are more tiles than fit in the available width, the tile bar now auto-scrolls to ensure the selected tile is always visible. Overflow indicators (◀ ▶) show when tiles exist beyond the visible area. Scroll offset is computed fresh each frame from tile_cat_idx. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -52,16 +52,71 @@ impl<'a> Widget for TileBar<'a> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut x = area.x + 1;
|
let prefix = " Tiles: ";
|
||||||
buf.set_string(area.x, area.y, " Tiles: ", Style::default().fg(Color::Gray));
|
let prefix_w = prefix.width() as u16;
|
||||||
x += 8;
|
buf.set_string(area.x, area.y, prefix, Style::default().fg(Color::Gray));
|
||||||
|
|
||||||
let cat_names: Vec<&str> = self.model.category_names();
|
let cat_names: Vec<&str> = self.model.category_names();
|
||||||
for (i, cat_name) in cat_names.iter().enumerate() {
|
|
||||||
let (axis_symbol, axis_color) = TileBar::axis_display(view.axis_of(cat_name));
|
|
||||||
let label = format!(" [{cat_name} {axis_symbol}] ");
|
|
||||||
let is_selected = selected_cat_idx == Some(i);
|
|
||||||
|
|
||||||
|
// Compute label widths for all tiles
|
||||||
|
let labels: Vec<String> = cat_names
|
||||||
|
.iter()
|
||||||
|
.map(|cat_name| {
|
||||||
|
let (axis_symbol, _) = TileBar::axis_display(view.axis_of(cat_name));
|
||||||
|
format!(" [{cat_name} {axis_symbol}] ")
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let widths: Vec<u16> = labels.iter().map(|l| l.width() as u16).collect();
|
||||||
|
|
||||||
|
// Available space for tiles (after prefix)
|
||||||
|
let avail = area.width.saturating_sub(prefix_w);
|
||||||
|
|
||||||
|
// Find the minimal starting index so the selected tile is fully visible.
|
||||||
|
// We scroll by whole tiles: find the first tile to draw such that the
|
||||||
|
// selected tile fits within the available width.
|
||||||
|
let sel = selected_cat_idx.unwrap_or(0);
|
||||||
|
let mut start = 0;
|
||||||
|
loop {
|
||||||
|
// Check if selected tile is visible when starting from `start`
|
||||||
|
let mut used: u16 = 0;
|
||||||
|
let mut sel_visible = false;
|
||||||
|
for i in start..labels.len() {
|
||||||
|
if used + widths[i] > avail {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
used += widths[i];
|
||||||
|
if i == sel {
|
||||||
|
sel_visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sel_visible || start >= sel {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw an overflow indicator if we scrolled past the beginning
|
||||||
|
let mut x = area.x + prefix_w + 1;
|
||||||
|
if start > 0 {
|
||||||
|
buf.set_string(
|
||||||
|
area.x + prefix_w,
|
||||||
|
area.y,
|
||||||
|
"◀",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
);
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render tiles from `start`
|
||||||
|
let mut last_drawn = start;
|
||||||
|
for i in start..labels.len() {
|
||||||
|
let label_w = widths[i];
|
||||||
|
if x + label_w > area.x + area.width {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, axis_color) = TileBar::axis_display(view.axis_of(cat_names[i]));
|
||||||
|
let is_selected = selected_cat_idx == Some(i);
|
||||||
let style = if is_selected {
|
let style = if is_selected {
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(Color::Black)
|
.fg(Color::Black)
|
||||||
@ -71,12 +126,15 @@ impl<'a> Widget for TileBar<'a> {
|
|||||||
Style::default().fg(axis_color)
|
Style::default().fg(axis_color)
|
||||||
};
|
};
|
||||||
|
|
||||||
let label_w = label.width() as u16;
|
buf.set_string(x, area.y, &labels[i], style);
|
||||||
if x + label_w > area.x + area.width {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buf.set_string(x, area.y, &label, style);
|
|
||||||
x += label_w;
|
x += label_w;
|
||||||
|
last_drawn = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw overflow indicator if tiles remain after the visible area
|
||||||
|
if last_drawn + 1 < labels.len() && x < area.x + area.width {
|
||||||
|
buf.set_string(x, area.y, "▶", Style::default().fg(Color::DarkGray));
|
||||||
|
x += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hint
|
// Hint
|
||||||
|
|||||||
Reference in New Issue
Block a user