Minimap
Group: Data
Code
const VIEWPORT_ROWS: usize = 10;
const LINES_PER_ROW: usize = 4;
pub struct MinimapDemo {
buffer: Vec<String>,
scroll_offset: usize,
}
impl MinimapDemo {
pub fn new() -> Self {
let buffer = (0..120)
.map(|i| match i % 6 {
0 => format!("fn function_{i}() {{"),
1 => " let value = compute();".to_string(),
2 => " // a short comment explaining it".to_string(),
3 => String::new(),
4 => " value.process()".to_string(),
_ => "}".to_string(),
})
.collect();
Self {
buffer,
scroll_offset: 0,
}
}
fn scale(variant: usize) -> MinimapScale {
if variant == 1 {
MinimapScale::Two
} else {
MinimapScale::One
}
}
fn minimap(&self, area: Rect, backend: &dyn Backend) -> Minimap {
let buffer = &self.buffer;
let lines = sample_blocks(buffer.len(), buffer.len(), |i| buffer[i].clone());
let visible_row_start = lines
.iter()
.position(|l| l.line_idx >= self.scroll_offset)
.unwrap_or(0);
let visible_row_end = lines
.iter()
.position(|l| l.line_idx >= self.scroll_offset + VIEWPORT_ROWS)
.unwrap_or(lines.len());
let mut minimap = Minimap {
id: WidgetId::new("gallery:minimap"),
lines,
syntax_spans: Vec::new(),
visible_row_start,
visible_row_count: visible_row_end.saturating_sub(visible_row_start).max(1),
total_buffer_lines: self.buffer.len(),
};
let minimap_rect = Self::minimap_rect(area, backend);
let cols_per_cell = backend.minimap_layout(minimap_rect, &minimap).cols_per_cell;
let raw_spans: Vec<MinimapSpan> = minimap
.lines
.iter()
.enumerate()
.filter_map(|(idx, l)| {
let trimmed = l.text.trim_start();
if trimmed.starts_with("fn") {
Some(MinimapSpan {
line_idx: idx,
start_col: 0,
end_col: 2,
color: Color::rgb(80, 160, 255),
})
} else if trimmed.starts_with("//") {
Some(MinimapSpan {
line_idx: idx,
start_col: 4,
end_col: l.text.len(),
color: Color::rgb(100, 180, 100),
})
} else {
None
}
})
.collect();
let grid = MinimapGrid {
rows: minimap.lines.len().div_ceil(LINES_PER_ROW).max(1),
cols: 200,
lines_per_row: LINES_PER_ROW,
cols_per_cell,
};
minimap.syntax_spans = aggregate_spans(&raw_spans, grid);
minimap
}
fn minimap_rect(area: Rect, backend: &dyn Backend) -> Rect {
let lh = backend.line_height();
let minimap_w = (backend.char_width() * 14.0).min(area.width);
Rect::new(
area.x + area.width - minimap_w,
area.y,
minimap_w,
(area.height - lh).max(0.0),
)
}
fn status_rect(area: Rect, backend: &dyn Backend) -> Rect {
let lh = backend.line_height();
Rect::new(area.x, area.y + area.height - lh, area.width, lh)
}
fn status(&self, variant: usize) -> StatusBar {
let scale_label = if variant == 1 { "2 (2x4)" } else { "1 (1x2)" };
StatusBar {
id: WidgetId::new("gallery:minimap:status"),
left_segments: vec![StatusBarSegment {
text: format!(" line {} — scale {scale_label} ", self.scroll_offset),
fg: Color::rgb(255, 255, 255),
bg: Color::rgb(40, 80, 120),
bold: false,
action_id: None,
}],
right_segments: vec![StatusBarSegment {
text: " up/down=scroll click=seek ".into(),
fg: Color::rgb(220, 220, 220),
bg: Color::rgb(40, 80, 120),
bold: false,
action_id: None,
}],
}
}
fn seek(&mut self, fraction: f32) {
let max_start = self.buffer.len().saturating_sub(VIEWPORT_ROWS);
let target = (fraction as f64 * max_start as f64).round() as usize;
self.scroll_offset = target.min(max_start);
}
}
impl Default for MinimapDemo {
fn default() -> Self {
Self::new()
}
}
impl Demo for MinimapDemo {
fn name(&self) -> &'static str {
"Minimap"
}
fn group(&self) -> &'static str {
"Data"
}
fn variants(&self) -> &'static [&'static str] {
&["Scale 1 (1x2)", "Scale 2 (2x4)"]
}
fn render(&self, variant: usize, backend: &mut dyn Backend, area: Rect) {
backend.set_minimap_scale(Self::scale(variant));
let minimap_rect = Self::minimap_rect(area, backend);
let minimap = self.minimap(area, backend);
let _ = backend.draw_minimap(minimap_rect, &minimap);
let status_rect = Self::status_rect(area, backend);
let _ = backend.draw_status_bar_interactive(
status_rect,
&self.status(variant),
&InteractionState::new(),
);
}
fn handle(
&mut self,
variant: usize,
event: &UiEvent,
backend: &mut dyn Backend,
area: Rect,
) -> Reaction {
match event {
UiEvent::KeyPressed {
key: Key::Named(quadraui::NamedKey::Down),
..
} => {
let max_start = self.buffer.len().saturating_sub(VIEWPORT_ROWS);
self.scroll_offset = (self.scroll_offset + 1).min(max_start);
Reaction::Redraw
}
UiEvent::KeyPressed {
key: Key::Named(quadraui::NamedKey::Up),
..
} => {
self.scroll_offset = self.scroll_offset.saturating_sub(1);
Reaction::Redraw
}
UiEvent::MouseDown {
button: MouseButton::Left,
position,
..
} => {
backend.set_minimap_scale(Self::scale(variant));
let minimap_rect = Self::minimap_rect(area, backend);
let minimap = self.minimap(area, backend);
let layout = backend.minimap_layout(minimap_rect, &minimap);
if let MinimapHit::Seek { fraction } = layout.hit_test(position.x, position.y) {
self.seek(fraction);
}
Reaction::Redraw
}
_ => Reaction::Continue,
}
}
fn source(&self) -> &'static str {
extract_region(SOURCE)
}
fn data(&self, variant: usize) -> serde_json::Value {
serde_json::json!({
"scale": format!("{:?}", Self::scale(variant)),
"scroll_offset": self.scroll_offset,
"total_lines": self.buffer.len(),
})
}
fn caps_note(&self, _variant: usize, _caps: &BackendCaps) -> Option<String> {
None
}
}
Backends
| Variant | TUI | GTK | macOS | Windows |
|---|---|---|---|---|
| Scale 1 (1x2) | ||||
| Scale 2 (2x4) |