use ratatui::layout::{Constraint, Direction, Layout};
use super::standard::{self, StandardOptions};
use super::types::{LayoutContext, LayoutResult, WidgetSizes};
#[derive(Clone)]
pub struct SidebarOptions {
pub main_options: StandardOptions,
pub sidebar_widget_id: &'static str,
pub sidebar_width: SidebarWidth,
pub sidebar_position: SidebarPosition,
}
#[derive(Clone)]
pub enum SidebarWidth {
Fixed(u16),
Percentage(u16),
Min(u16),
}
impl From<u16> for SidebarWidth {
fn from(width: u16) -> Self {
Self::Fixed(width)
}
}
#[derive(Clone, Copy, Default)]
pub enum SidebarPosition {
Left,
#[default]
Right,
}
impl Default for SidebarOptions {
fn default() -> Self {
Self {
main_options: StandardOptions::default(),
sidebar_widget_id: "sidebar",
sidebar_width: SidebarWidth::Fixed(30),
sidebar_position: SidebarPosition::Right,
}
}
}
pub fn compute(ctx: &LayoutContext, sizes: &WidgetSizes, opts: &SidebarOptions) -> LayoutResult {
let area = ctx.frame_area;
let sidebar_constraint = match opts.sidebar_width {
SidebarWidth::Fixed(w) => Constraint::Length(w),
SidebarWidth::Percentage(p) => Constraint::Percentage(p),
SidebarWidth::Min(w) => Constraint::Min(w),
};
let h_constraints = match opts.sidebar_position {
SidebarPosition::Left => vec![sidebar_constraint, Constraint::Min(1)],
SidebarPosition::Right => vec![Constraint::Min(1), sidebar_constraint],
};
let h_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints(h_constraints)
.split(area);
let (main_area, sidebar_area) = match opts.sidebar_position {
SidebarPosition::Left => (h_chunks[1], h_chunks[0]),
SidebarPosition::Right => (h_chunks[0], h_chunks[1]),
};
let main_ctx = LayoutContext {
frame_area: main_area,
show_throbber: ctx.show_throbber,
input_visual_lines: ctx.input_visual_lines,
theme: ctx.theme,
active_widgets: ctx.active_widgets.clone(),
};
let mut result = standard::compute(&main_ctx, sizes, &opts.main_options);
result.widget_areas.insert(opts.sidebar_widget_id, sidebar_area);
result.render_order.insert(0, opts.sidebar_widget_id);
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::themes::Theme;
use crate::widgets::widget_ids;
use ratatui::layout::Rect;
use std::collections::{HashMap, HashSet};
fn test_context(area: Rect) -> LayoutContext<'static> {
static THEME: std::sync::LazyLock<Theme> = std::sync::LazyLock::new(Theme::default);
LayoutContext {
frame_area: area,
show_throbber: false,
input_visual_lines: 1,
theme: &THEME,
active_widgets: HashSet::new(),
}
}
fn test_sizes() -> WidgetSizes {
WidgetSizes {
heights: HashMap::new(),
is_active: HashMap::new(),
}
}
#[test]
fn test_sidebar_layout() {
let area = Rect::new(0, 0, 100, 24);
let ctx = test_context(area);
let sizes = test_sizes();
let opts = SidebarOptions {
sidebar_widget_id: "file_browser",
sidebar_width: SidebarWidth::Fixed(30),
..Default::default()
};
let result = compute(&ctx, &sizes, &opts);
assert!(result.widget_areas.contains_key(widget_ids::CHAT_VIEW));
assert!(result.widget_areas.contains_key("file_browser"));
let sidebar_area = result.widget_areas.get("file_browser").unwrap();
assert_eq!(sidebar_area.width, 30);
}
}