tui: New labeled container design
This commit is contained in:
parent
e268e75280
commit
b1c92f4a0d
|
|
@ -1,12 +1,14 @@
|
|||
use tuirealm::command::{Cmd, CmdResult};
|
||||
use tuirealm::props::{AttrValue, Attribute, Color, Props, Style};
|
||||
use tuirealm::props::{
|
||||
AttrValue, Attribute, BorderSides, BorderType, Color, PropPayload, PropValue, Props, Style,
|
||||
};
|
||||
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
|
||||
use tuirealm::tui::text::{Span, Spans};
|
||||
use tuirealm::tui::widgets::Block;
|
||||
use tuirealm::tui::widgets::{Block, Cell, Row};
|
||||
use tuirealm::{Frame, MockComponent, State, StateValue};
|
||||
|
||||
use crate::ui::components::common::label::Label;
|
||||
use crate::ui::layout;
|
||||
use crate::ui::ext::HeaderBlock;
|
||||
use crate::ui::state::TabState;
|
||||
use crate::ui::widget::{Widget, WidgetComponent};
|
||||
|
||||
|
|
@ -104,14 +106,24 @@ impl WidgetComponent for Tabs {
|
|||
}
|
||||
|
||||
/// A labeled container header.
|
||||
#[derive(Clone)]
|
||||
struct Header {
|
||||
content: Widget<Label>,
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct Header;
|
||||
|
||||
impl Header {
|
||||
pub fn new(content: Widget<Label>) -> Self {
|
||||
Self { content }
|
||||
fn content<'a>(spans: Vec<PropValue>) -> Row<'a> {
|
||||
Row::new(
|
||||
spans
|
||||
.iter()
|
||||
.map(|span| Cell::from(span.clone().unwrap_text_span().content))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
fn widths(widths: Vec<PropValue>) -> Vec<Constraint> {
|
||||
widths
|
||||
.iter()
|
||||
.map(|prop| Constraint::Percentage(prop.clone().unwrap_u16()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,17 +132,43 @@ impl WidgetComponent for Header {
|
|||
let display = properties
|
||||
.get_or(Attribute::Display, AttrValue::Flag(true))
|
||||
.unwrap_flag();
|
||||
let spacer = Widget::new(Label::default())
|
||||
.content(AttrValue::String(String::default()))
|
||||
.to_boxed();
|
||||
let content = properties
|
||||
.get_or(
|
||||
Attribute::Content,
|
||||
AttrValue::Payload(PropPayload::Vec(vec![])),
|
||||
)
|
||||
.unwrap_payload()
|
||||
.unwrap_vec();
|
||||
let widths = properties
|
||||
.get_or(
|
||||
Attribute::Custom("widths"),
|
||||
AttrValue::Payload(PropPayload::Vec(vec![])),
|
||||
)
|
||||
.unwrap_payload()
|
||||
.unwrap_vec();
|
||||
|
||||
if display {
|
||||
let labels: Vec<Box<dyn MockComponent>> = vec![self.content.clone().to_boxed(), spacer];
|
||||
let block = HeaderBlock::default()
|
||||
.borders(BorderSides::all())
|
||||
.border_style(Style::default().fg(Color::Rgb(48, 48, 48)))
|
||||
.border_type(BorderType::Rounded);
|
||||
frame.render_widget(block, area);
|
||||
|
||||
let layout = layout::h_stack(labels, area);
|
||||
for (mut shortcut, area) in layout {
|
||||
shortcut.view(frame, area);
|
||||
}
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(vec![Constraint::Min(1)])
|
||||
.vertical_margin(1)
|
||||
.horizontal_margin(1)
|
||||
.split(area);
|
||||
|
||||
let header = Self::content(content);
|
||||
let widths = Self::widths(widths);
|
||||
|
||||
let table = tuirealm::tui::widgets::Table::new(vec![])
|
||||
.column_spacing(3u16)
|
||||
.header(header)
|
||||
.widths(&widths);
|
||||
frame.render_widget(table, layout[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -149,11 +187,8 @@ pub struct LabeledContainer {
|
|||
}
|
||||
|
||||
impl LabeledContainer {
|
||||
pub fn new(content: Widget<Label>, component: Box<dyn MockComponent>) -> Self {
|
||||
Self {
|
||||
header: Widget::new(Header::new(content)),
|
||||
component,
|
||||
}
|
||||
pub fn new(header: Widget<Header>, component: Box<dyn MockComponent>) -> Self {
|
||||
Self { header, component }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -162,13 +197,10 @@ impl WidgetComponent for LabeledContainer {
|
|||
let display = properties
|
||||
.get_or(Attribute::Display, AttrValue::Flag(true))
|
||||
.unwrap_flag();
|
||||
let background = properties
|
||||
.get_or(Attribute::Background, AttrValue::Color(Color::Reset))
|
||||
.unwrap_color();
|
||||
let header_height = self
|
||||
.header
|
||||
.query(Attribute::Height)
|
||||
.unwrap_or(AttrValue::Size(1))
|
||||
.unwrap_or(AttrValue::Size(3))
|
||||
.unwrap_size();
|
||||
|
||||
if display {
|
||||
|
|
@ -177,18 +209,22 @@ impl WidgetComponent for LabeledContainer {
|
|||
.constraints([Constraint::Length(header_height), Constraint::Length(0)].as_ref())
|
||||
.split(area);
|
||||
|
||||
self.header.view(frame, layout[0]);
|
||||
|
||||
// Make some space on the left
|
||||
let inner_layout = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.horizontal_margin(1)
|
||||
.constraints(vec![Constraint::Length(1), Constraint::Min(0)].as_ref())
|
||||
.split(layout[1]);
|
||||
// reverse draw order: child needs to be drawn first?
|
||||
self.component.view(frame, inner_layout[1]);
|
||||
|
||||
let block = Block::default().style(Style::default().bg(background));
|
||||
let block = Block::default()
|
||||
.borders(BorderSides::BOTTOM | BorderSides::LEFT | BorderSides::RIGHT)
|
||||
.border_style(Style::default().fg(Color::Rgb(48, 48, 48)))
|
||||
.border_type(BorderType::Rounded);
|
||||
frame.render_widget(block, layout[1]);
|
||||
|
||||
self.header.view(frame, layout[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use radicle::Profile;
|
||||
use tuirealm::props::{AttrValue, Attribute};
|
||||
use tuirealm::props::{AttrValue, Attribute, PropPayload, PropValue, TextSpan};
|
||||
use tuirealm::MockComponent;
|
||||
|
||||
use crate::ui;
|
||||
use crate::ui::components::common::container::Header;
|
||||
|
||||
use ui::components::common::container::{GlobalListener, LabeledContainer, Tabs};
|
||||
use ui::components::common::context::{Shortcut, Shortcuts};
|
||||
|
|
@ -26,17 +27,26 @@ pub fn label(content: &str) -> Widget<Label> {
|
|||
.width(width)
|
||||
}
|
||||
|
||||
pub fn container_header(theme: &Theme, label: &str) -> Widget<Header> {
|
||||
let content = AttrValue::Payload(PropPayload::Vec(vec![PropValue::TextSpan(
|
||||
TextSpan::from(&format!(" {label} ")).fg(theme.colors.default_fg),
|
||||
)]));
|
||||
let widths = AttrValue::Payload(PropPayload::Vec(vec![PropValue::U16(100)]));
|
||||
|
||||
Widget::new(Header::default())
|
||||
.content(content)
|
||||
.custom("widths", widths)
|
||||
}
|
||||
|
||||
pub fn labeled_container(
|
||||
theme: &Theme,
|
||||
title: &str,
|
||||
component: Box<dyn MockComponent>,
|
||||
) -> Widget<LabeledContainer> {
|
||||
let title = label(&format!(" {title} "))
|
||||
.foreground(theme.colors.default_fg)
|
||||
.background(theme.colors.labeled_container_bg);
|
||||
let container = LabeledContainer::new(title, component);
|
||||
let header = container_header(theme, title);
|
||||
let container = LabeledContainer::new(header, component);
|
||||
|
||||
Widget::new(container).background(theme.colors.labeled_container_bg)
|
||||
Widget::new(container)
|
||||
}
|
||||
|
||||
pub fn shortcut(theme: &Theme, short: &str, long: &str) -> Widget<Shortcut> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue