use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use crate::primitives::sidebar_panel::{SidebarPanel, SidebarPanelLayout, SidebarPanelMeasure};
use crate::primitives::toolbar::ToolbarItemMeasure;
use crate::theme::Theme;
use crate::types::WidgetId;
fn tui_item_measure(btn: &crate::primitives::toolbar::ToolbarButton) -> ToolbarItemMeasure {
ToolbarItemMeasure::new(super::toolbar::tui_item_width(btn))
}
pub fn tui_sidebar_panel_layout(panel: &SidebarPanel, area: Rect) -> SidebarPanelLayout {
let bounds = crate::event::Rect::new(
area.x as f32,
area.y as f32,
area.width as f32,
area.height as f32,
);
panel.layout(bounds, SidebarPanelMeasure::new(1.0, 1.0), tui_item_measure)
}
pub fn draw_sidebar_panel(
buf: &mut Buffer,
area: Rect,
panel: &SidebarPanel,
theme: &Theme,
hovered_toolbar_id: Option<&WidgetId>,
pressed_toolbar_id: Option<&WidgetId>,
) -> SidebarPanelLayout {
let layout = tui_sidebar_panel_layout(panel, area);
if area.width == 0 || area.height == 0 {
return layout;
}
if let (Some(bar), Some(tb_layout), Some(tb_bounds)) = (
&panel.toolbar,
layout.toolbar_layout.as_ref(),
layout.toolbar_bounds,
) {
let _ = tb_layout; let tb_rect = Rect::new(
tb_bounds.x.round() as u16,
tb_bounds.y.round() as u16,
tb_bounds.width.round() as u16,
tb_bounds.height.round() as u16,
);
let _ = super::draw_toolbar(
buf,
tb_rect,
bar,
theme,
hovered_toolbar_id,
pressed_toolbar_id,
);
}
layout
}
#[cfg(test)]
mod tests {
use super::*;
use crate::primitives::sidebar_panel::SidebarPanelHit;
use crate::primitives::toolbar::{Toolbar, ToolbarButton};
use crate::types::WidgetId;
fn cell_char(buf: &Buffer, x: u16, y: u16) -> char {
buf[(x, y)].symbol().chars().next().unwrap_or(' ')
}
fn panel_with_toolbar() -> SidebarPanel {
SidebarPanel {
id: WidgetId::new("sb"),
toolbar: Some(Toolbar {
id: WidgetId::new("sb:toolbar"),
buttons: vec![ToolbarButton::Action {
id: WidgetId::new("refine"),
label: "Refine".into(),
icon: None,
key_hint: None,
enabled: true,
is_active: false,
tooltip: String::new(),
}],
bg: None,
focused_index: None,
}),
toolbar_height: None,
}
}
#[test]
fn paints_toolbar_in_top_row() {
let area = Rect::new(0, 0, 40, 10);
let mut buf = Buffer::empty(area);
let panel = panel_with_toolbar();
let layout = draw_sidebar_panel(&mut buf, area, &panel, &Theme::default(), None, None);
assert_eq!(cell_char(&buf, 0, 0), '[');
assert_eq!(cell_char(&buf, 0, 1), ' ');
assert_eq!(layout.content_bounds.y, 1.0);
assert_eq!(layout.content_bounds.height, 9.0);
}
fn click_in_header_round_trip_at(origin_x: u16, origin_y: u16) {
let area = Rect::new(origin_x, origin_y, 40, 10);
let mut buf = Buffer::empty(area);
let panel = panel_with_toolbar();
let layout = draw_sidebar_panel(&mut buf, area, &panel, &Theme::default(), None, None);
match layout.hit_test(origin_x as f32 + 2.0, origin_y as f32) {
SidebarPanelHit::ToolbarButton(id) => assert_eq!(id.as_str(), "refine"),
other => panic!("expected ToolbarButton, got {other:?}"),
}
}
#[test]
fn click_in_header_resolves_to_toolbar_button() {
click_in_header_round_trip_at(0, 0);
}
#[test]
fn click_in_header_resolves_to_toolbar_button_at_nonzero_origin() {
click_in_header_round_trip_at(7, 13);
}
#[test]
fn click_in_content_returns_content_local_coords() {
let area = Rect::new(0, 0, 40, 10);
let mut buf = Buffer::empty(area);
let panel = panel_with_toolbar();
let layout = draw_sidebar_panel(&mut buf, area, &panel, &Theme::default(), None, None);
match layout.hit_test(5.0, 4.0) {
SidebarPanelHit::Content { x, y } => {
assert_eq!(x, 5.0);
assert_eq!(y, 3.0);
}
other => panic!("expected Content, got {other:?}"),
}
}
#[test]
fn no_toolbar_gives_full_rect_to_content() {
let area = Rect::new(0, 0, 40, 10);
let mut buf = Buffer::empty(area);
let panel = SidebarPanel {
id: WidgetId::new("sb"),
toolbar: None,
toolbar_height: None,
};
let layout = draw_sidebar_panel(&mut buf, area, &panel, &Theme::default(), None, None);
assert!(layout.toolbar_bounds.is_none());
assert_eq!(layout.content_bounds.y, 0.0);
assert_eq!(layout.content_bounds.height, 10.0);
}
}