Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
tui: Implement new table design for patches
Erik Kundt committed 3 years ago
commit 7c7379c7333880c0bb6e0f264ea1d5a5d71187dd
parent b1c92f4a0d3c5e545010012c9f3f11c8146a86bb
5 files changed +67 -65
modified radicle-tui/src/app/page.rs
@@ -81,7 +81,7 @@ impl ViewPage for Home {

    fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame) {
        let area = frame.size();
-
        let navigation_h = 2u16;
+
        let navigation_h = 1u16;
        let layout = layout::default_page(area, navigation_h);

        app.view(&Cid::Home(HomeCid::Navigation), frame, layout[0]);
modified radicle-tui/src/ui/cob/patch.rs
@@ -69,15 +69,15 @@ impl List for (PatchId, Patch) {
        let author = format_author(patch, profile);
        let author = TextSpan::from(author).fg(theme.colors.browser_patch_list_author);

-
        let tags = format_tags(patch);
-
        let tags = TextSpan::from(tags).fg(theme.colors.browser_patch_list_tags);
+
        let timestamp = format_timestamp(patch);
+
        let timestamp = TextSpan::from(timestamp).fg(theme.colors.browser_patch_list_timestamp);

        let comments = format_comments(patch);
        let comments = TextSpan::from(comments).fg(theme.colors.browser_patch_list_comments);

-
        let timestamp = format_timestamp(patch);
-
        let timestamp = TextSpan::from(timestamp).fg(theme.colors.browser_patch_list_timestamp);
+
        let tags = format_tags(patch);
+
        let tags = TextSpan::from(tags).fg(theme.colors.browser_patch_list_tags);

-
        vec![status, title, author, tags, comments, timestamp]
+
        vec![status, title, author, timestamp, comments, tags]
    }
}
modified radicle-tui/src/ui/components/common/list.rs
@@ -1,10 +1,11 @@
use radicle::Profile;
-
use tuirealm::command::{Cmd, CmdResult, Direction};
+
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::{
-
    AttrValue, Attribute, Color, PropPayload, PropValue, Props, Style, TextModifiers, TextSpan,
+
    AttrValue, Attribute, BorderSides, BorderType, Color, PropPayload, PropValue, Props, Style,
+
    TextSpan,
};
-
use tuirealm::tui::layout::{Constraint, Rect};
-
use tuirealm::tui::widgets::{Cell, Row, TableState};
+
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
+
use tuirealm::tui::widgets::{Block, Cell, Row, TableState};
use tuirealm::{Frame, MockComponent, State, StateValue};

use crate::ui::components::common::label::Label;
@@ -12,6 +13,8 @@ use crate::ui::layout;
use crate::ui::theme::Theme;
use crate::ui::widget::{Widget, WidgetComponent};

+
use super::container::Header;
+

pub trait List {
    fn row(&self, theme: &Theme, profile: &Profile) -> Vec<TextSpan>;
}
@@ -105,18 +108,17 @@ impl WidgetComponent for PropertyList {
}

pub struct Table {
+
    header: Widget<Header>,
    state: TableState,
}

-
impl Default for Table {
-
    fn default() -> Self {
+
impl Table {
+
    pub fn new(header: Widget<Header>) -> Self {
        let mut state = TableState::default();
        state.select(Some(0));
-
        Self { state }
+
        Self { header, state }
    }
-
}

-
impl Table {
    fn select_previous(&mut self) {
        let index = match self.state.selected() {
            Some(selected) if selected == 0 => 0,
@@ -135,18 +137,6 @@ impl Table {
        self.state.select(Some(index));
    }

-
    fn header<'a>(spans: Vec<PropValue>) -> Row<'a> {
-
        Row::new(
-
            spans
-
                .iter()
-
                .map(|span| {
-
                    Cell::from(span.clone().unwrap_text_span().content)
-
                        .style(Style::default().add_modifier(TextModifiers::BOLD))
-
                })
-
                .collect::<Vec<_>>(),
-
        )
-
    }
-

    fn rows<'a>(spans: Vec<Vec<TextSpan>>) -> Vec<Row<'a>> {
        spans
            .iter()
@@ -173,19 +163,9 @@ impl WidgetComponent for Table {
        let content = properties
            .get_or(Attribute::Content, AttrValue::Table(vec![]))
            .unwrap_table();
-
        let background = properties
-
            .get_or(Attribute::Background, AttrValue::Color(Color::Reset))
-
            .unwrap_color();
        let highlight = properties
            .get_or(Attribute::HighlightedColor, AttrValue::Color(Color::Reset))
            .unwrap_color();
-
        let header = properties
-
            .get_or(
-
                Attribute::Custom("header"),
-
                AttrValue::Payload(PropPayload::Vec(vec![])),
-
            )
-
            .unwrap_payload()
-
            .unwrap_vec();
        let widths = properties
            .get_or(
                Attribute::Custom("widths"),
@@ -194,18 +174,27 @@ impl WidgetComponent for Table {
            .unwrap_payload()
            .unwrap_vec();

-
        let header = Self::header(header);
+
        let layout = Layout::default()
+
            .direction(Direction::Vertical)
+
            .constraints(vec![Constraint::Length(3), Constraint::Min(1)])
+
            .split(area);
+

        let rows = Self::rows(content);
        let widths = Self::widths(widths);

-
        let table = tuirealm::tui::widgets::Table::new(rows)
+
        let rows = tuirealm::tui::widgets::Table::new(rows)
+
            .block(
+
                Block::default()
+
                    .borders(BorderSides::BOTTOM | BorderSides::LEFT | BorderSides::RIGHT)
+
                    .border_style(Style::default().fg(Color::Rgb(48, 48, 48)))
+
                    .border_type(BorderType::Rounded),
+
            )
            .highlight_style(Style::default().bg(highlight))
-
            .style(Style::default().bg(background))
            .column_spacing(3u16)
-
            .header(header)
            .widths(&widths);

-
        frame.render_stateful_widget(table, area, &mut self.state);
+
        self.header.view(frame, layout[0]);
+
        frame.render_stateful_widget(rows, layout[1], &mut self.state);
    }

    fn state(&self) -> State {
@@ -213,6 +202,8 @@ impl WidgetComponent for Table {
    }

    fn perform(&mut self, properties: &Props, cmd: Cmd) -> CmdResult {
+
        use tuirealm::command::Direction;
+

        let content = properties
            .get_or(Attribute::Content, AttrValue::Table(vec![]))
            .unwrap_table();
modified radicle-tui/src/ui/widget/common.rs
@@ -38,6 +38,22 @@ pub fn container_header(theme: &Theme, label: &str) -> Widget<Header> {
        .custom("widths", widths)
}

+
pub fn table_header(theme: &Theme, labels: &[&str], widths: &[u16]) -> Widget<Header> {
+
    let content = labels
+
        .iter()
+
        .map(|label| {
+
            PropValue::TextSpan(TextSpan::from(label.to_string()).fg(theme.colors.default_fg))
+
        })
+
        .collect::<Vec<_>>();
+
    let widths = AttrValue::Payload(PropPayload::Vec(
+
        widths.iter().map(|w| PropValue::U16(*w)).collect(),
+
    ));
+

+
    Widget::new(Header::default())
+
        .content(AttrValue::Payload(PropPayload::Vec(content)))
+
        .custom("widths", widths)
+
}
+

pub fn labeled_container(
    theme: &Theme,
    title: &str,
@@ -105,12 +121,25 @@ pub fn tabs(theme: &Theme, tabs: Vec<Widget<Label>>) -> Widget<Tabs> {
        .highlight(theme.colors.tabs_highlighted_fg)
}

-
pub fn table(theme: &Theme, items: &[impl List], profile: &Profile) -> Widget<Table> {
-
    let table = Table::default();
+
pub fn table(
+
    theme: &Theme,
+
    labels: &[&str],
+
    widths: &[u16],
+
    items: &[impl List],
+
    profile: &Profile,
+
) -> Widget<Table> {
    let items = items.iter().map(|item| item.row(theme, profile)).collect();

+
    let header = table_header(theme, labels, widths);
+
    let table = Table::new(header);
+

+
    let widths = AttrValue::Payload(PropPayload::Vec(
+
        widths.iter().map(|w| PropValue::U16(*w)).collect(),
+
    ));
+

    Widget::new(table)
        .content(AttrValue::Table(items))
+
        .custom("widths", widths)
        .background(theme.colors.labeled_container_bg)
        .highlight(theme.colors.item_list_highlighted_bg)
}
modified radicle-tui/src/ui/widget/home.rs
@@ -1,8 +1,6 @@
use radicle::cob::patch::{Patch, PatchId};
use radicle::identity::{Id, Project};
use radicle::Profile;
-
use tuirealm::props::{PropPayload, PropValue, TextSpan};
-
use tuirealm::AttrValue;

use crate::ui::components::common::container::Tabs;
use crate::ui::components::common::Browser;
@@ -63,26 +61,10 @@ pub fn patches(
        ],
    );

-
    let widths = AttrValue::Payload(PropPayload::Vec(vec![
-
        PropValue::U16(2),
-
        PropValue::U16(43),
-
        PropValue::U16(15),
-
        PropValue::U16(15),
-
        PropValue::U16(5),
-
        PropValue::U16(20),
-
    ]));
-
    let header = AttrValue::Payload(PropPayload::Vec(vec![
-
        PropValue::TextSpan(TextSpan::from("")),
-
        PropValue::TextSpan(TextSpan::from("title")),
-
        PropValue::TextSpan(TextSpan::from("author")),
-
        PropValue::TextSpan(TextSpan::from("tags")),
-
        PropValue::TextSpan(TextSpan::from("comments")),
-
        PropValue::TextSpan(TextSpan::from("date")),
-
    ]));
+
    let labels = vec!["", "title", "author", "time", "comments", "tags"];
+
    let widths = vec![3u16, 42, 15, 10, 5, 25];

-
    let table = common::table(theme, items, profile)
-
        .custom("widths", widths)
-
        .custom("header", header);
+
    let table = common::table(theme, &labels, &widths, items, profile);
    let browser: Browser<(PatchId, Patch)> = Browser::new(table, shortcuts);

    Widget::new(browser)