Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
tui: New labeled container design
Erik Kundt committed 3 years ago
commit b1c92f4a0d3c5e545010012c9f3f11c8146a86bb
parent e268e75280ef54929e9cf4427c455b958980638e
2 files changed +81 -35
modified radicle-tui/src/ui/components/common/container.rs
@@ -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]);
        }
    }

modified radicle-tui/src/ui/widget/common.rs
@@ -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> {