radicle-heartwood-lfs/radicle-tui/src/ui/layout.rs

174 lines
4.8 KiB
Rust

use tuirealm::props::{AttrValue, Attribute};
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::MockComponent;
pub struct IssuePreview {
pub left: Rect,
pub details: Rect,
pub discussion: Rect,
pub shortcuts: Rect,
}
pub fn v_stack(
widgets: Vec<Box<dyn MockComponent>>,
area: Rect,
) -> Vec<(Box<dyn MockComponent>, Rect)> {
let constraints = widgets
.iter()
.map(|w| {
Constraint::Length(
w.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size(),
)
})
.collect::<Vec<_>>();
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(area);
widgets.into_iter().zip(layout.into_iter()).collect()
}
pub fn h_stack(
widgets: Vec<Box<dyn MockComponent>>,
area: Rect,
) -> Vec<(Box<dyn MockComponent>, Rect)> {
let constraints = widgets
.iter()
.map(|w| {
Constraint::Length(
w.query(Attribute::Width)
.unwrap_or(AttrValue::Size(0))
.unwrap_size(),
)
})
.collect::<Vec<_>>();
let layout = Layout::default()
.direction(Direction::Horizontal)
.constraints(constraints)
.split(area);
widgets.into_iter().zip(layout.into_iter()).collect()
}
pub fn default_page(area: Rect) -> Vec<Rect> {
let nav_h = 3u16;
let margin_h = 1u16;
let content_h = area.height.saturating_sub(nav_h.saturating_add(margin_h));
Layout::default()
.direction(Direction::Vertical)
.horizontal_margin(margin_h)
.constraints([Constraint::Length(nav_h), Constraint::Length(content_h)].as_ref())
.split(area)
}
pub fn headerless_page(area: Rect) -> Vec<Rect> {
let margin_h = 1u16;
let content_h = area.height.saturating_sub(margin_h);
Layout::default()
.direction(Direction::Vertical)
.horizontal_margin(margin_h)
.constraints([Constraint::Length(content_h)].as_ref())
.split(area)
}
pub fn root_component(area: Rect, shortcuts_h: u16) -> Vec<Rect> {
let content_h = area.height.saturating_sub(shortcuts_h);
Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(content_h),
Constraint::Length(shortcuts_h),
]
.as_ref(),
)
.split(area)
}
pub fn root_component_with_context(area: Rect, context_h: u16, shortcuts_h: u16) -> Vec<Rect> {
let content_h = area
.height
.saturating_sub(shortcuts_h.saturating_add(context_h));
Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(content_h),
Constraint::Length(context_h),
Constraint::Length(shortcuts_h),
]
.as_ref(),
)
.split(area)
}
pub fn centered_label(label_w: u16, area: Rect) -> Rect {
let label_h = 1u16;
let spacer_w = area.width.saturating_sub(label_w).saturating_div(2);
let spacer_h = area.height.saturating_sub(label_h).saturating_div(2);
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(spacer_h),
Constraint::Length(label_h),
Constraint::Length(spacer_h),
]
.as_ref(),
)
.split(area);
Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Length(spacer_w),
Constraint::Length(label_w),
Constraint::Length(spacer_w),
]
.as_ref(),
)
.split(layout[1])[1]
}
pub fn issue_preview(area: Rect, shortcuts_h: u16) -> IssuePreview {
let content_h = area.height.saturating_sub(shortcuts_h);
let root = Layout::default()
.direction(Direction::Vertical)
.horizontal_margin(1)
.constraints(
[
Constraint::Length(content_h),
Constraint::Length(shortcuts_h),
]
.as_ref(),
)
.split(area);
let split = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(root[0]);
let right = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(6), Constraint::Min(0)].as_ref())
.split(split[1]);
IssuePreview {
left: split[0],
details: right[0],
discussion: right[1],
shortcuts: root[1],
}
}