WIP: menu

This commit is contained in:
2026-01-25 21:37:53 +01:00
parent ac83ceb2cb
commit 73470ded79
14 changed files with 635 additions and 335 deletions

View File

@@ -3,6 +3,7 @@ mod editor;
mod file_browser;
mod list_select;
mod modal;
mod nav_minimap;
mod sample_browser;
mod scope;
mod spectrum;
@@ -14,6 +15,7 @@ pub use editor::{CompletionCandidate, Editor};
pub use file_browser::FileBrowserModal;
pub use list_select::ListSelect;
pub use modal::ModalFrame;
pub use nav_minimap::{NavMinimap, NavTile};
pub use sample_browser::{SampleBrowser, TreeLine, TreeLineKind};
pub use scope::{Orientation, Scope};
pub use spectrum::Spectrum;

View File

@@ -0,0 +1,86 @@
use ratatui::layout::{Alignment, Rect};
use ratatui::style::{Color, Style};
use ratatui::widgets::{Clear, Paragraph};
use ratatui::Frame;
/// A tile in the navigation grid
pub struct NavTile {
pub col: i8,
pub row: i8,
pub name: &'static str,
}
/// Navigation minimap widget that renders a grid of page tiles
pub struct NavMinimap<'a> {
tiles: &'a [NavTile],
selected: (i8, i8),
}
impl<'a> NavMinimap<'a> {
pub fn new(tiles: &'a [NavTile], selected: (i8, i8)) -> Self {
Self { tiles, selected }
}
pub fn render_centered(self, frame: &mut Frame, term: Rect) {
if self.tiles.is_empty() {
return;
}
// Compute grid bounds from tiles
let max_col = self.tiles.iter().map(|t| t.col).max().unwrap_or(0);
let max_row = self.tiles.iter().map(|t| t.row).max().unwrap_or(0);
let cols = (max_col + 1) as u16;
let rows = (max_row + 1) as u16;
let tile_w: u16 = 12;
let tile_h: u16 = 3;
let gap: u16 = 1;
let pad: u16 = 2;
let content_w = tile_w * cols + gap * (cols.saturating_sub(1));
let content_h = tile_h * rows + gap * (rows.saturating_sub(1));
let modal_w = content_w + pad * 2;
let modal_h = content_h + pad * 2;
let x = term.x + (term.width.saturating_sub(modal_w)) / 2;
let y = term.y + (term.height.saturating_sub(modal_h)) / 2;
let area = Rect::new(x, y, modal_w, modal_h);
frame.render_widget(Clear, area);
let inner_x = area.x + pad;
let inner_y = area.y + pad;
for tile in self.tiles {
let tile_x = inner_x + (tile.col as u16) * (tile_w + gap);
let tile_y = inner_y + (tile.row as u16) * (tile_h + gap);
let tile_area = Rect::new(tile_x, tile_y, tile_w, tile_h);
let is_selected = (tile.col, tile.row) == self.selected;
self.render_tile(frame, tile_area, tile.name, is_selected);
}
}
fn render_tile(&self, frame: &mut Frame, area: Rect, label: &str, is_selected: bool) {
let (bg, fg) = if is_selected {
(Color::Rgb(50, 90, 110), Color::White)
} else {
(Color::Rgb(30, 35, 45), Color::Rgb(100, 105, 115))
};
// Fill background
for row in 0..area.height {
let line_area = Rect::new(area.x, area.y + row, area.width, 1);
let fill = " ".repeat(area.width as usize);
frame.render_widget(Paragraph::new(fill).style(Style::new().bg(bg)), line_area);
}
// Center text vertically
let text_y = area.y + area.height / 2;
let text_area = Rect::new(area.x, text_y, area.width, 1);
let paragraph = Paragraph::new(label)
.style(Style::new().bg(bg).fg(fg))
.alignment(Alignment::Center);
frame.render_widget(paragraph, text_area);
}
}