Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Move label builders to widget module
Erik Kundt committed 2 years ago
commit ea91de73cc3f8f77fe480bbf789e6bbf68494a28
parent ddc10a45e7f916923654c26154e42b2f1af2f4d6
8 files changed +126 -125
modified bin/commands/issue/suite/ui.rs
@@ -22,7 +22,7 @@ use tui::ui::widget::{Widget, WidgetComponent};
use tui::ui::widget::container::{Container, Tabs};
use tui::ui::widget::context::{ContextBar, Progress};
use tui::ui::widget::form::{Form, TextArea, TextField};
-
use tui::ui::widget::label::Textarea;
+
use tui::ui::widget::label::{self, Textarea};
use tui::ui::widget::list::{ColumnWidth, List, Property, Table};

pub const FORM_ID_EDIT: &str = "edit-form";
@@ -35,13 +35,13 @@ pub struct IssueBrowser {
impl IssueBrowser {
    pub fn new(context: &Context, theme: &Theme, selected: Option<(IssueId, Issue)>) -> Self {
        let header = [
-
            tui::ui::label(" ● "),
-
            tui::ui::label("ID"),
-
            tui::ui::label("Title"),
-
            tui::ui::label("Author"),
-
            tui::ui::label("Tags"),
-
            tui::ui::label("Assignees"),
-
            tui::ui::label("Opened"),
+
            label::default(" ● ").style(style::reset_dim()),
+
            label::default("ID").style(style::reset_dim()),
+
            label::default("Title").style(style::reset_dim()),
+
            label::default("Author").style(style::reset_dim()),
+
            label::default("Tags").style(style::reset_dim()),
+
            label::default("Assignees").style(style::reset_dim()),
+
            label::default("Opened").style(style::reset_dim()),
        ];

        let widths = [
@@ -172,8 +172,8 @@ impl IssueHeader {
        let item = IssueItem::from((context.profile(), repo, id, issue.clone()));

        let title = Property::new(
-
            tui::ui::label("Title").style(style::cyan()),
-
            tui::ui::label(item.title()).style(style::reset()),
+
            label::default("Title").style(style::cyan()),
+
            label::default(item.title()).style(style::reset()),
        );

        let author_style = match alias {
@@ -181,23 +181,23 @@ impl IssueHeader {
            None => style::magenta_dim(),
        };

-
        let author = tui::ui::label(&cob::format_author(issue.author().id(), &alias, by_you))
+
        let author = label::default(&cob::format_author(issue.author().id(), &alias, by_you))
            .style(author_style);
-
        let author = Property::new(tui::ui::label("Author").style(style::cyan()), author);
+
        let author = Property::new(label::default("Author").style(style::cyan()), author);

        let issue_id = Property::new(
-
            tui::ui::label("Issue").style(style::cyan()),
-
            tui::ui::label(&id.to_string()).style(style::gray()),
+
            label::default("Issue").style(style::cyan()),
+
            label::default(&id.to_string()).style(style::gray()),
        );

        let labels = Property::new(
-
            tui::ui::label("Labels").style(style::cyan()),
-
            tui::ui::label(&cob::format_labels(item.labels())).style(style::lightblue()),
+
            label::default("Labels").style(style::cyan()),
+
            label::default(&cob::format_labels(item.labels())).style(style::lightblue()),
        );

        let assignees = Property::new(
-
            tui::ui::label("Assignees").style(style::cyan()),
-
            tui::ui::label(&cob::format_assignees(
+
            label::default("Assignees").style(style::cyan()),
+
            label::default(&cob::format_assignees(
                &item
                    .assignees()
                    .iter()
@@ -208,8 +208,8 @@ impl IssueHeader {
        );

        let state = Property::new(
-
            tui::ui::label("Status").style(style::cyan()),
-
            tui::ui::label(&item.state().to_string()).style(style::reset()),
+
            label::default("Status").style(style::cyan()),
+
            label::default(&item.state().to_string()).style(style::reset()),
        );

        let table = tui::ui::property_table(
@@ -330,7 +330,7 @@ impl WidgetComponent for CommentBody {
pub fn list_navigation(theme: &Theme) -> Widget<Tabs> {
    tui::ui::tabs(
        theme,
-
        vec![tui::ui::reversable_label("Issues").style(style::magenta())],
+
        vec![label::reversable("Issues").style(style::magenta())],
    )
}

modified bin/commands/patch/list/ui.rs
@@ -13,6 +13,7 @@ use tui::ui::widget::{Widget, WidgetComponent};

use tui::ui::widget::container::Tabs;
use tui::ui::widget::context::{ContextBar, Progress};
+
use tui::ui::widget::label::{self};
use tui::ui::widget::list::{ColumnWidth, Table};

pub struct PatchBrowser {
@@ -23,14 +24,14 @@ pub struct PatchBrowser {
impl PatchBrowser {
    pub fn new(context: &Context, theme: &Theme, selected: Option<(PatchId, Patch)>) -> Self {
        let header = [
-
            tui::ui::label(" ● ").style(style::reset_dim()),
-
            tui::ui::label("ID").style(style::reset_dim()),
-
            tui::ui::label("Title").style(style::reset_dim()),
-
            tui::ui::label("Author").style(style::reset_dim()),
-
            tui::ui::label("Head").style(style::reset_dim()),
-
            tui::ui::label("+").style(style::reset_dim()),
-
            tui::ui::label("-").style(style::reset_dim()),
-
            tui::ui::label("Updated").style(style::reset_dim()),
+
            label::default(" ● ").style(style::reset_dim()),
+
            label::default("ID").style(style::reset_dim()),
+
            label::default("Title").style(style::reset_dim()),
+
            label::default("Author").style(style::reset_dim()),
+
            label::default("Head").style(style::reset_dim()),
+
            label::default("+").style(style::reset_dim()),
+
            label::default("-").style(style::reset_dim()),
+
            label::default("Updated").style(style::reset_dim()),
        ];

        let widths = [
@@ -96,7 +97,7 @@ impl WidgetComponent for PatchBrowser {
pub fn list_navigation(theme: &Theme) -> Widget<Tabs> {
    tui::ui::tabs(
        theme,
-
        vec![tui::ui::reversable_label("Patches").style(style::cyan())],
+
        vec![label::reversable("Patches").style(style::cyan())],
    )
}

@@ -110,7 +111,6 @@ pub fn patches(

pub fn browse_context(context: &Context, _theme: &Theme, progress: Progress) -> Widget<ContextBar> {
    use radicle::cob::patch::State;
-
    use tui::ui::{label, label_group};

    let mut draft = 0;
    let mut open = 0;
@@ -130,29 +130,30 @@ pub fn browse_context(context: &Context, _theme: &Theme, progress: Progress) ->
        }
    }

-
    let context = label(" Patches ").style(style::magenta_reversed());
-
    let divider = label(" | ").style(style::default_reversed());
+
    let context = label::default(" Patches ").style(style::magenta_reversed());
+
    let divider = label::default(" | ").style(style::default_reversed());

-
    let draft_n = label(&format!("{draft}")).style(style::default_reversed());
-
    let draft = label(" Draft").style(style::default_reversed());
+
    let draft_n = label::default(&format!("{draft}")).style(style::default_reversed());
+
    let draft = label::default(" Draft").style(style::default_reversed());

-
    let open_n = label(&format!("{open}")).style(style::green_default_reversed());
-
    let open = label(" Open").style(style::default_reversed());
+
    let open_n = label::default(&format!("{open}")).style(style::green_default_reversed());
+
    let open = label::default(" Open").style(style::default_reversed());

-
    let archived_n = label(&format!("{archived}")).style(style::yellow_default_reversed());
-
    let archived = label(" Archived").style(style::default_reversed());
+
    let archived_n = label::default(&format!("{archived}")).style(style::yellow_default_reversed());
+
    let archived = label::default(" Archived").style(style::default_reversed());

-
    let merged_n = label(&format!("{merged}")).style(style::cyan_default_reversed());
-
    let merged = label(" Merged ").style(style::default_reversed());
+
    let merged_n = label::default(&format!("{merged}")).style(style::cyan_default_reversed());
+
    let merged = label::default(" Merged ").style(style::default_reversed());

-
    let progress = label(&format!(" {} ", progress.to_string())).style(style::magenta_reversed());
-
    let spacer = label("").style(style::default_reversed());
+
    let progress =
+
        label::default(&format!(" {} ", progress.to_string())).style(style::magenta_reversed());
+
    let spacer = label::default("").style(style::default_reversed());

    let context_bar = ContextBar::new(
-
        label_group(&[context]),
-
        label_group(&[spacer.clone()]),
-
        label_group(&[spacer]),
-
        label_group(&[
+
        label::group(&[context]),
+
        label::group(&[spacer.clone()]),
+
        label::group(&[spacer]),
+
        label::group(&[
            draft_n,
            draft,
            divider.clone(),
@@ -165,7 +166,7 @@ pub fn browse_context(context: &Context, _theme: &Theme, progress: Progress) ->
            merged_n,
            merged,
        ]),
-
        label_group(&[progress]),
+
        label::group(&[progress]),
    );

    Widget::new(context_bar).height(1)
modified bin/commands/patch/suite/ui.rs
@@ -16,7 +16,7 @@ use tui::ui::widget::{Widget, WidgetComponent};

use tui::ui::widget::container::Tabs;
use tui::ui::widget::context::{ContextBar, Progress};
-
use tui::ui::widget::label::Label;
+
use tui::ui::widget::label::{self, Label};
use tui::ui::widget::list::{ColumnWidth, Table};

pub struct PatchBrowser {
@@ -27,14 +27,14 @@ pub struct PatchBrowser {
impl PatchBrowser {
    pub fn new(context: &Context, theme: &Theme, selected: Option<(PatchId, Patch)>) -> Self {
        let header = [
-
            tui::ui::label(" ● "),
-
            tui::ui::label("ID"),
-
            tui::ui::label("Title"),
-
            tui::ui::label("Author"),
-
            tui::ui::label("Head"),
-
            tui::ui::label("+"),
-
            tui::ui::label("-"),
-
            tui::ui::label("Updated"),
+
            label::default(" ● ").style(style::reset_dim()),
+
            label::default("ID").style(style::reset_dim()),
+
            label::default("Title").style(style::reset_dim()),
+
            label::default("Author").style(style::reset_dim()),
+
            label::default("Head").style(style::reset_dim()),
+
            label::default("+").style(style::reset_dim()),
+
            label::default("-").style(style::reset_dim()),
+
            label::default("Updated").style(style::reset_dim()),
        ];

        let widths = [
@@ -162,7 +162,7 @@ impl WidgetComponent for Files {
pub fn list_navigation(theme: &Theme) -> Widget<Tabs> {
    tui::ui::tabs(
        theme,
-
        vec![tui::ui::reversable_label("Patches").style(style::magenta())],
+
        vec![label::reversable("Patches").style(style::magenta())],
    )
}

@@ -170,8 +170,8 @@ pub fn navigation(theme: &Theme) -> Widget<Tabs> {
    tui::ui::tabs(
        theme,
        vec![
-
            tui::ui::reversable_label("Activity").style(style::magenta()),
-
            tui::ui::reversable_label("Files").style(style::magenta()),
+
            label::reversable("Activity").style(style::magenta()),
+
            label::reversable("Files").style(style::magenta()),
        ],
    )
}
@@ -185,14 +185,14 @@ pub fn patches(
}

pub fn activity(_theme: &Theme) -> Widget<Activity> {
-
    let not_implemented = tui::ui::label("not implemented").style(style::reset());
+
    let not_implemented = label::default("not implemented").style(style::reset());
    let activity = Activity::new(not_implemented);

    Widget::new(activity)
}

pub fn files(_theme: &Theme) -> Widget<Files> {
-
    let not_implemented = tui::ui::label("not implemented").style(style::reset());
+
    let not_implemented = label::default("not implemented").style(style::reset());
    let files = Files::new(not_implemented);

    Widget::new(files)
modified src/ui.rs
@@ -14,13 +14,11 @@ use widget::container::{
    VerticalLine,
};
use widget::context::{Shortcut, Shortcuts};
-
use widget::label::{Label, Textarea};
+
use widget::label::{self, Label, Textarea};
use widget::list::{ColumnWidth, Property, PropertyList, PropertyTable};
-

use widget::Widget;

-
use self::theme::{style, Theme};
-
use self::widget::label::LabelGroup;
+
use theme::{style, Theme};

use super::context::Context;

@@ -28,35 +26,6 @@ pub fn global_listener() -> Widget<GlobalListener> {
    Widget::new(GlobalListener::default())
}

-
pub fn label(content: &str) -> Widget<Label> {
-
    // TODO: Remove when size constraints are implemented
-
    let width = content.chars().count() as u16;
-

-
    Widget::new(Label)
-
        .content(AttrValue::String(content.to_string()))
-
        .height(1)
-
        .width(width)
-
}
-

-
pub fn label_group(labels: &[Widget<Label>]) -> Widget<LabelGroup> {
-
    let group = LabelGroup::new(labels);
-
    let width = labels.iter().fold(0, |total, label| {
-
        total
-
            + label
-
                .query(Attribute::Width)
-
                .unwrap_or(AttrValue::Size(0))
-
                .unwrap_size()
-
    });
-

-
    Widget::new(group).width(width)
-
}
-

-
pub fn reversable_label(content: &str) -> Widget<Label> {
-
    let content = &format!(" {content} ");
-

-
    label(content)
-
}
-

pub fn container_header(theme: &Theme, label: Widget<Label>) -> Widget<Header<1>> {
    let header = Header::new([label], [ColumnWidth::Grow], theme.clone());

@@ -73,16 +42,19 @@ pub fn labeled_container(
    title: &str,
    component: Box<dyn MockComponent>,
) -> Widget<LabeledContainer> {
-
    let header = container_header(theme, label(&format!(" {title} ")).style(style::reset()));
+
    let header = container_header(
+
        theme,
+
        label::default(&format!(" {title} ")).style(style::reset()),
+
    );
    let container = LabeledContainer::new(header, component, theme.clone());

    Widget::new(container)
}

pub fn shortcut(theme: &Theme, short: &str, long: &str) -> Widget<Shortcut> {
-
    let short = label(short).style(style::gray());
-
    let long = label(long).style(style::gray_dim());
-
    let divider = label(&theme.icons.whitespace.to_string());
+
    let short = label::default(short).style(style::gray());
+
    let long = label::default(long).style(style::gray_dim());
+
    let divider = label::default(&theme.icons.whitespace.to_string());

    // TODO: Remove when size constraints are implemented
    let short_w = short.query(Attribute::Width).unwrap().unwrap_size();
@@ -96,16 +68,17 @@ pub fn shortcut(theme: &Theme, short: &str, long: &str) -> Widget<Shortcut> {
}

pub fn shortcuts(theme: &Theme, shortcuts: Vec<Widget<Shortcut>>) -> Widget<Shortcuts> {
-
    let divider = label(&format!(" {} ", theme.icons.shortcutbar_divider)).style(style::gray_dim());
+
    let divider =
+
        label::default(&format!(" {} ", theme.icons.shortcutbar_divider)).style(style::gray_dim());
    let shortcut_bar = Shortcuts::new(shortcuts, divider);

    Widget::new(shortcut_bar).height(1)
}

pub fn property(theme: &Theme, name: &str, value: &str) -> Widget<Property> {
-
    let name = label(name).style(style::cyan());
-
    let divider = label(&format!(" {} ", theme.icons.property_divider));
-
    let value = label(value).style(style::reset());
+
    let name = label::default(name).style(style::cyan());
+
    let divider = label::default(&format!(" {} ", theme.icons.property_divider));
+
    let value = label::default(value).style(style::reset());

    // TODO: Remove when size constraints are implemented
    let name_w = name.query(Attribute::Width).unwrap().unwrap_size();
@@ -137,8 +110,8 @@ pub fn tabs(_theme: &Theme, tabs: Vec<Widget<Label>>) -> Widget<Tabs> {
}

pub fn app_info(context: &Context) -> Widget<AppInfo> {
-
    let project = label(context.project().name()).style(style::cyan());
-
    let rid = label(&format!(" ({})", context.id())).style(style::yellow());
+
    let project = label::default(context.project().name()).style(style::cyan());
+
    let rid = label::default(&format!(" ({})", context.id())).style(style::yellow());

    let project_w = project
        .query(Attribute::Width)
@@ -158,7 +131,7 @@ pub fn app_header(
    theme: &Theme,
    nav: Option<Widget<Tabs>>,
) -> Widget<AppHeader> {
-
    let line = label(&theme.icons.tab_overline.to_string()).style(style::magenta());
+
    let line = label::default(&theme.icons.tab_overline.to_string()).style(style::magenta());
    let line = Widget::new(VerticalLine::new(line));
    let info = app_info(context);
    let header = AppHeader::new(nav, info, line);
modified src/ui/widget/context.rs
@@ -3,7 +3,7 @@ use tuirealm::props::{AttrValue, Attribute, Props};
use tuirealm::tui::layout::Rect;
use tuirealm::{Frame, MockComponent, State};

-
use super::label::{Label, LabelGroup};
+
use super::label::{self, Label, LabelGroup};

use crate::ui::layout;
use crate::ui::theme::{style, Theme};
@@ -215,19 +215,17 @@ pub fn bar(
    label_3: &str,
    label_4: &str,
) -> Widget<ContextBar> {
-
    use crate::ui::{label, label_group};
-

-
    let label_0 = label(&format!(" {label_0} ")).style(style::magenta_reversed());
-
    let label_1 = label(&format!(" {label_1} ")).style(style::default_reversed());
-
    let label_2 = label(&format!(" {label_2} ")).style(style::default_reversed());
-
    let label_3 = label(&format!(" {label_3} ")).style(style::default_reversed());
-
    let label_4 = label(&format!(" {label_4} ")).style(style::default_reversed());
-

-
    let label_0 = label_group(&[label_0]);
-
    let label_1 = label_group(&[label_1]);
-
    let label_2 = label_group(&[label_2]);
-
    let label_3 = label_group(&[label_3]);
-
    let label_4 = label_group(&[label_4]);
+
    let label_0 = label::default(&format!(" {label_0} ")).style(style::magenta_reversed());
+
    let label_1 = label::default(&format!(" {label_1} ")).style(style::default_reversed());
+
    let label_2 = label::default(&format!(" {label_2} ")).style(style::default_reversed());
+
    let label_3 = label::default(&format!(" {label_3} ")).style(style::default_reversed());
+
    let label_4 = label::default(&format!(" {label_4} ")).style(style::default_reversed());
+

+
    let label_0 = label::group(&[label_0]);
+
    let label_1 = label::group(&[label_1]);
+
    let label_2 = label::group(&[label_2]);
+
    let label_3 = label::group(&[label_3]);
+
    let label_4 = label::group(&[label_4]);

    let context_bar = ContextBar::new(label_0, label_1, label_2, label_3, label_4);

modified src/ui/widget/form.rs
@@ -10,7 +10,7 @@ use crate::ui::theme::{style, Theme};
use crate::ui::widget::{Widget, WidgetComponent};

use super::container::Container;
-
use super::label::Label;
+
use super::label::{self, Label};

pub struct TextField {
    input: Widget<Container>,
@@ -29,7 +29,7 @@ impl TextField {

        Self {
            input: container,
-
            placeholder: crate::ui::label(title).style(style::gray_dim()),
+
            placeholder: label::default(title).style(style::gray_dim()),
            show_placeholder: true,
        }
    }
@@ -103,7 +103,7 @@ impl TextArea {

        Self {
            input: container,
-
            placeholder: crate::ui::label(title).style(style::gray_dim()),
+
            placeholder: label::default(title).style(style::gray_dim()),
            show_placeholder: true,
        }
    }
modified src/ui/widget/label.rs
@@ -8,6 +8,35 @@ use crate::ui::layout;
use crate::ui::theme::style;
use crate::ui::widget::{Widget, WidgetComponent};

+
pub fn default(content: &str) -> Widget<Label> {
+
    // TODO: Remove when size constraints are implemented
+
    let width = content.chars().count() as u16;
+

+
    Widget::new(Label)
+
        .content(AttrValue::String(content.to_string()))
+
        .height(1)
+
        .width(width)
+
}
+

+
pub fn group(labels: &[Widget<Label>]) -> Widget<LabelGroup> {
+
    let group = LabelGroup::new(labels);
+
    let width = labels.iter().fold(0, |total, label| {
+
        total
+
            + label
+
                .query(Attribute::Width)
+
                .unwrap_or(AttrValue::Size(0))
+
                .unwrap_size()
+
    });
+

+
    Widget::new(group).width(width)
+
}
+

+
pub fn reversable(content: &str) -> Widget<Label> {
+
    let content = &format!(" {content} ");
+

+
    default(content)
+
}
+

/// A label that can be styled using a foreground color and text modifiers.
/// Its height is fixed, its width depends on the length of the text it displays.
#[derive(Clone, Default)]
modified src/ui/widget/list.rs
@@ -10,7 +10,7 @@ use crate::ui::theme::{style, Theme};
use crate::ui::widget::{utils, Widget, WidgetComponent};

use super::container::Header;
-
use super::label::Label;
+
use super::label::{self, Label};

/// A generic item that can be displayed in a table with [`const W: usize`] columns.
pub trait TableItem<const W: usize> {
@@ -47,7 +47,7 @@ pub struct Property {

impl Property {
    pub fn new(name: Widget<Label>, value: Widget<Label>) -> Self {
-
        let divider = crate::ui::label("");
+
        let divider = label::default("");
        Self {
            name,
            divider,