refactor: Simplify library ui and widget modules

This commit is contained in:
Erik Kundt
2023-10-06 10:55:37 +02:00
parent e47a0401fa
commit f4eaf5c17e
17 changed files with 351 additions and 168 deletions
+12 -11
View File
@@ -9,16 +9,17 @@ use radicle::identity::{Id, Project};
use radicle::prelude::Signer;
use radicle::profile::Profile;
use radicle_tui::ui::subscription;
use radicle_tui::ui::widget;
use tuirealm::application::PollStrategy;
use tuirealm::{Application, Frame, NoUserEvent, Sub, SubClause};
use radicle_tui::cob;
use radicle_tui::ui::context::Context;
use radicle_tui::ui::theme::{self, Theme};
use radicle_tui::PageStack;
use radicle_tui::Tui;
use radicle_tui as tui;
use tui::cob;
use tui::ui::context::Context;
use tui::ui::subscription;
use tui::ui::theme::{self, Theme};
use tui::PageStack;
use tui::Tui;
use page::{IssuePage, ListPage};
@@ -235,7 +236,7 @@ impl App {
theme: &Theme,
message: &str,
) -> Result<()> {
let popup = widget::common::info(theme, message);
let popup = tui::ui::info(theme, message);
app.remount(Cid::Popup, popup.to_boxed(), vec![])?;
app.active(&Cid::Popup)?;
@@ -248,7 +249,7 @@ impl App {
theme: &Theme,
message: &str,
) -> Result<()> {
let popup = widget::common::warning(theme, message);
let popup = tui::ui::warning(theme, message);
app.remount(Cid::Popup, popup.to_boxed(), vec![])?;
app.active(&Cid::Popup)?;
@@ -261,7 +262,7 @@ impl App {
theme: &Theme,
message: &str,
) -> Result<()> {
let popup = widget::common::error(theme, message);
let popup = tui::ui::error(theme, message);
app.remount(Cid::Popup, popup.to_boxed(), vec![])?;
app.active(&Cid::Popup)?;
@@ -304,7 +305,7 @@ impl Tui<Cid, Message> for App {
self.view_list(app, &self.theme.clone())?;
// Add global key listener and subscribe to key events
let global = widget::common::global_listener().to_boxed();
let global = tui::ui::global_listener().to_boxed();
app.mount(
Cid::GlobalListener,
global,
+7 -7
View File
@@ -3,14 +3,14 @@ use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection, Position};
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers};
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
use radicle_tui::ui::widget::common::container::{
AppHeader, GlobalListener, LabeledContainer, Popup,
};
use radicle_tui::ui::widget::common::context::{ContextBar, Shortcuts};
use radicle_tui::ui::widget::common::form::Form;
use radicle_tui::ui::widget::common::list::PropertyList;
use radicle_tui as tui;
use radicle_tui::ui::widget::Widget;
use tui::ui::widget::container::{AppHeader, GlobalListener, LabeledContainer, Popup};
use tui::ui::widget::context::{ContextBar, Shortcuts};
use tui::ui::widget::form::Form;
use tui::ui::widget::list::PropertyList;
use tui::ui::widget::Widget;
use super::ui;
use super::{IssueCid, IssueMessage, Message, PopupMessage};
+32 -29
View File
@@ -3,15 +3,18 @@ use std::collections::HashMap;
use anyhow::Result;
use radicle::cob::issue::{Issue, IssueId};
use radicle_tui::cob;
use radicle_tui::ui::widget::common::context::{Progress, Shortcuts};
use tuirealm::{AttrValue, Attribute, Frame, NoUserEvent, State, StateValue, Sub, SubClause};
use radicle_tui::ui::context::Context;
use radicle_tui::ui::layout;
use radicle_tui::ui::theme::Theme;
use radicle_tui::ui::widget::{self, Widget};
use radicle_tui::ViewPage;
use radicle_tui as tui;
use tui::cob;
use tui::ui::context::Context;
use tui::ui::layout;
use tui::ui::theme::Theme;
use tui::ui::widget::context::{Progress, Shortcuts};
use tui::ui::widget::Widget;
use tui::ViewPage;
use super::{
Application, Cid, IssueCid, IssueCobMessage, IssueMessage, ListCid, Message, PopupMessage,
@@ -53,14 +56,14 @@ impl ListPage {
fn build_shortcuts(theme: &Theme) -> HashMap<ListCid, Widget<Shortcuts>> {
[(
ListCid::IssueBrowser,
widget::common::shortcuts(
tui::ui::shortcuts(
theme,
vec![
widget::common::shortcut(theme, "tab", "section"),
widget::common::shortcut(theme, "↑/↓", "navigate"),
widget::common::shortcut(theme, "enter", "show"),
widget::common::shortcut(theme, "o", "open"),
widget::common::shortcut(theme, "q", "quit"),
tui::ui::shortcut(theme, "tab", "section"),
tui::ui::shortcut(theme, "↑/↓", "navigate"),
tui::ui::shortcut(theme, "enter", "show"),
tui::ui::shortcut(theme, "o", "open"),
tui::ui::shortcut(theme, "q", "quit"),
],
),
)]
@@ -112,7 +115,7 @@ impl ViewPage<Cid, Message> for ListPage {
theme: &Theme,
) -> Result<()> {
let navigation = ui::list_navigation(theme);
let header = widget::common::app_header(context, theme, Some(navigation)).to_boxed();
let header = tui::ui::app_header(context, theme, Some(navigation)).to_boxed();
let issue_browser = ui::issues(context, theme, None).to_boxed();
app.remount(Cid::List(ListCid::Header), header, vec![])?;
@@ -229,36 +232,36 @@ impl IssuePage {
[
(
IssueCid::List,
widget::common::shortcuts(
tui::ui::shortcuts(
theme,
vec![
widget::common::shortcut(theme, "esc", "back"),
widget::common::shortcut(theme, "↑/↓", "navigate"),
widget::common::shortcut(theme, "enter", "show"),
widget::common::shortcut(theme, "o", "open"),
widget::common::shortcut(theme, "q", "quit"),
tui::ui::shortcut(theme, "esc", "back"),
tui::ui::shortcut(theme, "↑/↓", "navigate"),
tui::ui::shortcut(theme, "enter", "show"),
tui::ui::shortcut(theme, "o", "open"),
tui::ui::shortcut(theme, "q", "quit"),
],
),
),
(
IssueCid::Details,
widget::common::shortcuts(
tui::ui::shortcuts(
theme,
vec![
widget::common::shortcut(theme, "esc", "back"),
widget::common::shortcut(theme, "↑/↓", "scroll"),
widget::common::shortcut(theme, "q", "quit"),
tui::ui::shortcut(theme, "esc", "back"),
tui::ui::shortcut(theme, "↑/↓", "scroll"),
tui::ui::shortcut(theme, "q", "quit"),
],
),
),
(
IssueCid::Form,
widget::common::shortcuts(
tui::ui::shortcuts(
theme,
vec![
widget::common::shortcut(theme, "esc", "back"),
widget::common::shortcut(theme, "shift + tab / tab", "navigate"),
widget::common::shortcut(theme, "ctrl + s", "submit"),
tui::ui::shortcut(theme, "esc", "back"),
tui::ui::shortcut(theme, "shift + tab / tab", "navigate"),
tui::ui::shortcut(theme, "ctrl + s", "submit"),
],
),
),
@@ -334,7 +337,7 @@ impl ViewPage<Cid, Message> for IssuePage {
theme: &Theme,
) -> Result<()> {
let navigation = ui::list_navigation(theme);
let header = widget::common::app_header(context, theme, Some(navigation)).to_boxed();
let header = tui::ui::app_header(context, theme, Some(navigation)).to_boxed();
let list = ui::list(context, theme, self.issue.clone()).to_boxed();
app.remount(Cid::Issue(IssueCid::Header), header, vec![])?;
+40 -39
View File
@@ -8,18 +8,19 @@ use tuirealm::command::{Cmd, CmdResult};
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, Props, State};
use radicle_tui::ui::cob;
use radicle_tui::ui::cob::IssueItem;
use radicle_tui::ui::context::Context;
use radicle_tui::ui::theme::Theme;
use radicle_tui::ui::widget::common;
use radicle_tui::ui::widget::{Widget, WidgetComponent};
use radicle_tui as tui;
use common::container::{Container, Tabs};
use common::context::{ContextBar, Progress};
use common::form::{Form, TextArea, TextField};
use common::label::Textarea;
use common::list::{ColumnWidth, List, Property, Table};
use tui::ui::cob;
use tui::ui::cob::IssueItem;
use tui::ui::context::Context;
use tui::ui::theme::Theme;
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::list::{ColumnWidth, List, Property, Table};
pub const FORM_ID_EDIT: &str = "edit-form";
@@ -31,13 +32,13 @@ pub struct IssueBrowser {
impl IssueBrowser {
pub fn new(context: &Context, theme: &Theme, selected: Option<(IssueId, Issue)>) -> Self {
let header = [
common::label(" ● "),
common::label("ID"),
common::label("Title"),
common::label("Author"),
common::label("Tags"),
common::label("Assignees"),
common::label("Opened"),
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"),
];
let widths = [
@@ -121,7 +122,7 @@ impl LargeList {
let list = Widget::new(List::new(&items, selected, theme.clone()))
.highlight(theme.colors.item_list_highlighted_bg);
let container = common::container(theme, list.to_boxed());
let container = tui::ui::container(theme, list.to_boxed());
Self {
items,
@@ -166,30 +167,30 @@ impl IssueHeader {
let item = IssueItem::from((context.profile(), repo, id, issue.clone()));
let title = Property::new(
common::label("Title").foreground(theme.colors.property_name_fg),
common::label(item.title()).foreground(theme.colors.browser_list_title),
tui::ui::label("Title").foreground(theme.colors.property_name_fg),
tui::ui::label(item.title()).foreground(theme.colors.browser_list_title),
);
let author = Property::new(
common::label("Author").foreground(theme.colors.property_name_fg),
common::label(&cob::format_author(issue.author().id(), by_you))
tui::ui::label("Author").foreground(theme.colors.property_name_fg),
tui::ui::label(&cob::format_author(issue.author().id(), by_you))
.foreground(theme.colors.browser_list_author),
);
let issue_id = Property::new(
common::label("Issue").foreground(theme.colors.property_name_fg),
common::label(&id.to_string()).foreground(theme.colors.browser_list_description),
tui::ui::label("Issue").foreground(theme.colors.property_name_fg),
tui::ui::label(&id.to_string()).foreground(theme.colors.browser_list_description),
);
let labels = Property::new(
common::label("Labels").foreground(theme.colors.property_name_fg),
common::label(&cob::format_labels(item.labels()))
tui::ui::label("Labels").foreground(theme.colors.property_name_fg),
tui::ui::label(&cob::format_labels(item.labels()))
.foreground(theme.colors.browser_list_labels),
);
let assignees = Property::new(
common::label("Assignees").foreground(theme.colors.property_name_fg),
common::label(&cob::format_assignees(
tui::ui::label("Assignees").foreground(theme.colors.property_name_fg),
tui::ui::label(&cob::format_assignees(
&item
.assignees()
.iter()
@@ -200,11 +201,11 @@ impl IssueHeader {
);
let state = Property::new(
common::label("Status").foreground(theme.colors.property_name_fg),
common::label(&item.state().to_string()).foreground(theme.colors.browser_list_title),
tui::ui::label("Status").foreground(theme.colors.property_name_fg),
tui::ui::label(&item.state().to_string()).foreground(theme.colors.browser_list_title),
);
let table = common::property_table(
let table = tui::ui::property_table(
theme,
vec![
Widget::new(title),
@@ -215,7 +216,7 @@ impl IssueHeader {
Widget::new(state),
],
);
let container = common::container(theme, table.to_boxed());
let container = tui::ui::container(theme, table.to_boxed());
Self { container }
}
@@ -294,7 +295,7 @@ impl CommentBody {
.content(AttrValue::String(content))
.foreground(theme.colors.default_fg);
let textarea = common::container(theme, textarea.to_boxed());
let textarea = tui::ui::container(theme, textarea.to_boxed());
Self { textarea }
}
@@ -320,9 +321,9 @@ impl WidgetComponent for CommentBody {
}
pub fn list_navigation(theme: &Theme) -> Widget<Tabs> {
common::tabs(
tui::ui::tabs(
theme,
vec![common::reversable_label("Issues").foreground(theme.colors.tabs_highlighted_fg)],
vec![tui::ui::reversable_label("Issues").foreground(theme.colors.tabs_highlighted_fg)],
)
}
@@ -403,7 +404,7 @@ pub fn browse_context(context: &Context, theme: &Theme, progress: Progress) -> W
.collect::<Vec<_>>()
.len();
common::context::bar(
tui::ui::widget::context::bar(
theme,
"Browse",
"",
@@ -418,11 +419,11 @@ pub fn description_context(
theme: &Theme,
progress: Progress,
) -> Widget<ContextBar> {
common::context::bar(theme, "Show", "", "", "", &progress.to_string())
tui::ui::widget::context::bar(theme, "Show", "", "", "", &progress.to_string())
}
pub fn form_context(_context: &Context, theme: &Theme, progress: Progress) -> Widget<ContextBar> {
common::context::bar(theme, "Open", "", "", "", &progress.to_string())
tui::ui::widget::context::bar(theme, "Open", "", "", "", &progress.to_string())
.custom(ContextBar::PROP_EDIT_MODE, AttrValue::Flag(true))
}
+5 -5
View File
@@ -11,10 +11,10 @@ use radicle::identity::{Id, Project};
use radicle::prelude::Signer;
use radicle::profile::Profile;
use radicle_tui::ui::widget;
use tuirealm::application::PollStrategy;
use tuirealm::{Application, Frame, NoUserEvent, Sub, SubClause};
use radicle_tui as tui;
use radicle_tui::cob;
use radicle_tui::ui::context::Context;
use radicle_tui::ui::subscription;
@@ -189,7 +189,7 @@ impl App {
theme: &Theme,
message: &str,
) -> Result<()> {
let popup = widget::common::info(theme, message);
let popup = tui::ui::info(theme, message);
app.remount(Cid::Popup, popup.to_boxed(), vec![])?;
app.active(&Cid::Popup)?;
@@ -202,7 +202,7 @@ impl App {
theme: &Theme,
message: &str,
) -> Result<()> {
let popup = widget::common::warning(theme, message);
let popup = tui::ui::warning(theme, message);
app.remount(Cid::Popup, popup.to_boxed(), vec![])?;
app.active(&Cid::Popup)?;
@@ -215,7 +215,7 @@ impl App {
theme: &Theme,
message: &str,
) -> Result<()> {
let popup = widget::common::error(theme, message);
let popup = tui::ui::error(theme, message);
app.remount(Cid::Popup, popup.to_boxed(), vec![])?;
app.active(&Cid::Popup)?;
@@ -235,7 +235,7 @@ impl Tui<Cid, Message> for App {
self.view_list(app, &self.theme.clone())?;
// Add global key listener and subscribe to key events
let global = widget::common::global_listener().to_boxed();
let global = tui::ui::global_listener().to_boxed();
app.mount(
Cid::GlobalListener,
global,
+3 -5
View File
@@ -2,11 +2,9 @@ use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
use tuirealm::event::{Event, Key, KeyEvent};
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
use radicle_tui::ui::widget::common::container::{
AppHeader, GlobalListener, LabeledContainer, Popup,
};
use radicle_tui::ui::widget::common::context::{ContextBar, Shortcuts};
use radicle_tui::ui::widget::common::list::PropertyList;
use radicle_tui::ui::widget::container::{AppHeader, GlobalListener, LabeledContainer, Popup};
use radicle_tui::ui::widget::context::{ContextBar, Shortcuts};
use radicle_tui::ui::widget::list::PropertyList;
use radicle_tui::ui::widget::Widget;
+23 -22
View File
@@ -4,15 +4,16 @@ use anyhow::Result;
use radicle::cob::patch::{Patch, PatchId};
use radicle_tui::ui::widget::common::context::{Progress, Shortcuts};
use tuirealm::{Frame, NoUserEvent, State, StateValue, Sub, SubClause};
use radicle_tui::ui::context::Context;
use radicle_tui::ui::layout;
use radicle_tui::ui::subscription;
use radicle_tui::ui::theme::Theme;
use radicle_tui::ui::widget::{self, Widget};
use radicle_tui::ViewPage;
use radicle_tui as tui;
use tui::ui::context::Context;
use tui::ui::theme::Theme;
use tui::ui::widget::context::{Progress, Shortcuts};
use tui::ui::widget::Widget;
use tui::ui::{layout, subscription};
use tui::ViewPage;
use super::{ui, Application, Cid, ListCid, Message, PatchCid};
@@ -36,13 +37,13 @@ impl ListView {
fn build_shortcuts(theme: &Theme) -> HashMap<ListCid, Widget<Shortcuts>> {
[(
ListCid::PatchBrowser,
widget::common::shortcuts(
tui::ui::shortcuts(
theme,
vec![
widget::common::shortcut(theme, "tab", "section"),
widget::common::shortcut(theme, "↑/↓", "navigate"),
widget::common::shortcut(theme, "enter", "show"),
widget::common::shortcut(theme, "q", "quit"),
tui::ui::shortcut(theme, "tab", "section"),
tui::ui::shortcut(theme, "↑/↓", "navigate"),
tui::ui::shortcut(theme, "enter", "show"),
tui::ui::shortcut(theme, "q", "quit"),
],
),
)]
@@ -95,7 +96,7 @@ impl ViewPage<Cid, Message> for ListView {
theme: &Theme,
) -> Result<()> {
let navigation = ui::list_navigation(theme);
let header = widget::common::app_header(context, theme, Some(navigation)).to_boxed();
let header = tui::ui::app_header(context, theme, Some(navigation)).to_boxed();
let patch_browser = ui::patches(context, theme, None).to_boxed();
app.remount(Cid::List(ListCid::Header), header, vec![])?;
@@ -186,23 +187,23 @@ impl PatchView {
[
(
PatchCid::Activity,
widget::common::shortcuts(
tui::ui::shortcuts(
theme,
vec![
widget::common::shortcut(theme, "esc", "back"),
widget::common::shortcut(theme, "tab", "section"),
widget::common::shortcut(theme, "q", "quit"),
tui::ui::shortcut(theme, "esc", "back"),
tui::ui::shortcut(theme, "tab", "section"),
tui::ui::shortcut(theme, "q", "quit"),
],
),
),
(
PatchCid::Files,
widget::common::shortcuts(
tui::ui::shortcuts(
theme,
vec![
widget::common::shortcut(theme, "esc", "back"),
widget::common::shortcut(theme, "tab", "section"),
widget::common::shortcut(theme, "q", "quit"),
tui::ui::shortcut(theme, "esc", "back"),
tui::ui::shortcut(theme, "tab", "section"),
tui::ui::shortcut(theme, "q", "quit"),
],
),
),
@@ -236,7 +237,7 @@ impl ViewPage<Cid, Message> for PatchView {
theme: &Theme,
) -> Result<()> {
let navigation = ui::navigation(theme);
let header = widget::common::app_header(context, theme, Some(navigation)).to_boxed();
let header = tui::ui::app_header(context, theme, Some(navigation)).to_boxed();
let activity = ui::activity(theme).to_boxed();
let files = ui::files(theme).to_boxed();
let context = ui::context(context, theme, self.patch.clone()).to_boxed();
+29 -28
View File
@@ -4,18 +4,19 @@ use tuirealm::command::{Cmd, CmdResult};
use tuirealm::tui::layout::Rect;
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, Props, State};
use radicle_tui::ui::cob;
use radicle_tui::ui::cob::PatchItem;
use radicle_tui::ui::context::Context;
use radicle_tui::ui::layout;
use radicle_tui::ui::theme::Theme;
use radicle_tui::ui::widget::common;
use radicle_tui::ui::widget::{Widget, WidgetComponent};
use radicle_tui as tui;
use common::container::Tabs;
use common::context::{ContextBar, Progress};
use common::label::Label;
use common::list::{ColumnWidth, Table};
use tui::ui::cob;
use tui::ui::cob::PatchItem;
use tui::ui::context::Context;
use tui::ui::layout;
use tui::ui::theme::Theme;
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::list::{ColumnWidth, Table};
pub struct PatchBrowser {
items: Vec<PatchItem>,
@@ -25,14 +26,14 @@ pub struct PatchBrowser {
impl PatchBrowser {
pub fn new(context: &Context, theme: &Theme, selected: Option<(PatchId, Patch)>) -> Self {
let header = [
common::label(" ● "),
common::label("ID"),
common::label("Title"),
common::label("Author"),
common::label("Head"),
common::label("+"),
common::label("-"),
common::label("Updated"),
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"),
];
let widths = [
@@ -158,18 +159,18 @@ impl WidgetComponent for Files {
}
pub fn list_navigation(theme: &Theme) -> Widget<Tabs> {
common::tabs(
tui::ui::tabs(
theme,
vec![common::reversable_label("Patches").foreground(theme.colors.tabs_highlighted_fg)],
vec![tui::ui::reversable_label("Patches").foreground(theme.colors.tabs_highlighted_fg)],
)
}
pub fn navigation(theme: &Theme) -> Widget<Tabs> {
common::tabs(
tui::ui::tabs(
theme,
vec![
common::reversable_label("Activity").foreground(theme.colors.tabs_highlighted_fg),
common::reversable_label("Files").foreground(theme.colors.tabs_highlighted_fg),
tui::ui::reversable_label("Activity").foreground(theme.colors.tabs_highlighted_fg),
tui::ui::reversable_label("Files").foreground(theme.colors.tabs_highlighted_fg),
],
)
}
@@ -183,14 +184,14 @@ pub fn patches(
}
pub fn activity(theme: &Theme) -> Widget<Activity> {
let not_implemented = common::label("not implemented").foreground(theme.colors.default_fg);
let not_implemented = tui::ui::label("not implemented").foreground(theme.colors.default_fg);
let activity = Activity::new(not_implemented);
Widget::new(activity)
}
pub fn files(theme: &Theme) -> Widget<Files> {
let not_implemented = common::label("not implemented").foreground(theme.colors.default_fg);
let not_implemented = tui::ui::label("not implemented").foreground(theme.colors.default_fg);
let files = Files::new(not_implemented);
Widget::new(files)
@@ -206,7 +207,7 @@ pub fn context(context: &Context, theme: &Theme, patch: (PatchId, Patch)) -> Wid
let author = cob::format_author(patch.author().id(), is_you);
let comments = rev.discussion().len();
common::context::bar(theme, "Patch", &id, title, &author, &comments.to_string())
tui::ui::widget::context::bar(theme, "Patch", &id, title, &author, &comments.to_string())
}
pub fn browse_context(context: &Context, theme: &Theme, progress: Progress) -> Widget<ContextBar> {
@@ -230,7 +231,7 @@ pub fn browse_context(context: &Context, theme: &Theme, progress: Progress) -> W
}
}
common::context::bar(
tui::ui::widget::context::bar(
theme,
"Browse",
"",
+180
View File
@@ -6,3 +6,183 @@ pub mod state;
pub mod subscription;
pub mod theme;
pub mod widget;
use tuirealm::props::{AttrValue, Attribute};
use tuirealm::MockComponent;
use widget::container::{
AppHeader, AppInfo, Container, GlobalListener, Header, LabeledContainer, Popup, Tabs,
VerticalLine,
};
use widget::context::{Shortcut, Shortcuts};
use widget::label::{Label, Textarea};
use widget::list::{ColumnWidth, Property, PropertyList, PropertyTable};
use widget::Widget;
use crate::ui::context::Context;
use crate::ui::theme::Theme;
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 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());
Widget::new(header)
}
pub fn container(theme: &Theme, component: Box<dyn MockComponent>) -> Widget<Container> {
let container = Container::new(component, theme.clone());
Widget::new(container)
}
pub fn labeled_container(
theme: &Theme,
title: &str,
component: Box<dyn MockComponent>,
) -> Widget<LabeledContainer> {
let header = container_header(
theme,
label(&format!(" {title} ")).foreground(theme.colors.default_fg),
);
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).foreground(theme.colors.shortcut_short_fg);
let divider = label(&theme.icons.whitespace.to_string());
let long = label(long).foreground(theme.colors.shortcut_long_fg);
// TODO: Remove when size constraints are implemented
let short_w = short.query(Attribute::Width).unwrap().unwrap_size();
let divider_w = divider.query(Attribute::Width).unwrap().unwrap_size();
let long_w = long.query(Attribute::Width).unwrap().unwrap_size();
let width = short_w.saturating_add(divider_w).saturating_add(long_w);
let shortcut = Shortcut::new(short, divider, long);
Widget::new(shortcut).height(1).width(width)
}
pub fn shortcuts(theme: &Theme, shortcuts: Vec<Widget<Shortcut>>) -> Widget<Shortcuts> {
let divider = label(&format!(" {} ", theme.icons.shortcutbar_divider))
.foreground(theme.colors.shortcutbar_divider_fg);
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).foreground(theme.colors.property_name_fg);
let divider = label(&format!(" {} ", theme.icons.property_divider));
let value = label(value).foreground(theme.colors.default_fg);
// TODO: Remove when size constraints are implemented
let name_w = name.query(Attribute::Width).unwrap().unwrap_size();
let divider_w = divider.query(Attribute::Width).unwrap().unwrap_size();
let value_w = value.query(Attribute::Width).unwrap().unwrap_size();
let width = name_w.saturating_add(divider_w).saturating_add(value_w);
let property = Property::new(name, value).with_divider(divider);
Widget::new(property).height(1).width(width)
}
pub fn property_list(_theme: &Theme, properties: Vec<Widget<Property>>) -> Widget<PropertyList> {
let property_list = PropertyList::new(properties);
Widget::new(property_list)
}
pub fn property_table(_theme: &Theme, properties: Vec<Widget<Property>>) -> Widget<PropertyTable> {
let table = PropertyTable::new(properties);
Widget::new(table)
}
pub fn tabs(_theme: &Theme, tabs: Vec<Widget<Label>>) -> Widget<Tabs> {
let tabs = Tabs::new(tabs);
Widget::new(tabs).height(2)
}
pub fn app_info(context: &Context, theme: &Theme) -> Widget<AppInfo> {
let project = label(context.project().name()).foreground(theme.colors.app_header_project_fg);
let rid = label(&format!(" ({})", context.id())).foreground(theme.colors.app_header_rid_fg);
let project_w = project
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let rid_w = rid
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let info = AppInfo::new(project, rid);
Widget::new(info).width(project_w.saturating_add(rid_w))
}
pub fn app_header(
context: &Context,
theme: &Theme,
nav: Option<Widget<Tabs>>,
) -> Widget<AppHeader> {
let line =
label(&theme.icons.tab_overline.to_string()).foreground(theme.colors.tabs_highlighted_fg);
let line = Widget::new(VerticalLine::new(line));
let info = app_info(context, theme);
let header = AppHeader::new(nav, info, line);
Widget::new(header)
}
pub fn info(theme: &Theme, message: &str) -> Widget<Popup> {
let textarea =
Widget::new(Textarea::new(theme.clone())).content(AttrValue::String(message.to_owned()));
let container = labeled_container(theme, "Info", textarea.to_boxed());
Widget::new(Popup::new(theme.clone(), container))
.width(50)
.height(20)
}
pub fn warning(theme: &Theme, message: &str) -> Widget<Popup> {
let textarea =
Widget::new(Textarea::new(theme.clone())).content(AttrValue::String(message.to_owned()));
let container = labeled_container(theme, "Warning", textarea.to_boxed());
Widget::new(Popup::new(theme.clone(), container))
.width(50)
.height(20)
}
pub fn error(theme: &Theme, message: &str) -> Widget<Popup> {
let textarea =
Widget::new(Textarea::new(theme.clone())).content(AttrValue::String(message.to_owned()));
let container = labeled_container(theme, "Error", textarea.to_boxed());
Widget::new(Popup::new(theme.clone(), container))
.width(50)
.height(20)
}
+4 -9
View File
@@ -2,8 +2,9 @@ pub mod format;
use radicle_surf;
// use cli::terminal::format;
// use radicle_cli as cli;
use tuirealm::props::{Color, Style};
use tuirealm::tui::text::{Span, Spans};
use tuirealm::tui::widgets::Cell;
use radicle::prelude::Did;
use radicle::storage::git::Repository;
@@ -14,14 +15,8 @@ use radicle::cob::issue::{Issue, IssueId, State as IssueState};
use radicle::cob::patch::{Patch, PatchId, State as PatchState};
use radicle::cob::{Label, Timestamp};
use tuirealm::props::{Color, Style};
use tuirealm::tui::text::{Span, Spans};
use tuirealm::tui::widgets::Cell;
use crate::ui::theme::Theme;
use crate::ui::widget::common::list::TableItem;
use super::widget::common::list::ListItem;
use crate::ui::widget::list::{ListItem, TableItem};
/// An author item that can be used in tables, list or trees.
///
+5 -1
View File
@@ -1,4 +1,8 @@
pub mod common;
pub mod container;
pub mod context;
pub mod form;
pub mod label;
pub mod list;
mod utils;
use std::ops::Deref;
@@ -218,19 +218,19 @@ pub fn bar(
label_3: &str,
label_4: &str,
) -> Widget<ContextBar> {
let context = super::label(&format!(" {label_0} "))
let context = crate::ui::label(&format!(" {label_0} "))
.foreground(theme.colors.context_bg)
.background(theme.colors.context_badge_bg);
let id = super::label(&format!(" {label_1} "))
let id = crate::ui::label(&format!(" {label_1} "))
.foreground(theme.colors.context_color_fg)
.background(theme.colors.context_bg);
let title = super::label(&format!(" {label_2} "))
let title = crate::ui::label(&format!(" {label_2} "))
.foreground(theme.colors.default_fg)
.background(theme.colors.context_bg);
let author = super::label(&format!(" {label_3} "))
let author = crate::ui::label(&format!(" {label_3} "))
.foreground(theme.colors.context_light)
.background(theme.colors.context_bg);
let comments = super::label(&format!(" {label_4} "))
let comments = crate::ui::label(&format!(" {label_4} "))
.foreground(theme.colors.context_light)
.background(theme.colors.context_bg);
@@ -25,11 +25,11 @@ impl TextField {
.single_line(true)
.cursor_line_style(Style::reset())
.style(Style::default().fg(theme.colors.default_fg));
let container = super::container(&theme, Box::new(input));
let container = crate::ui::container(&theme, Box::new(input));
Self {
input: container,
placeholder: super::label(title).foreground(theme.colors.input_placeholder_fg),
placeholder: crate::ui::label(title).foreground(theme.colors.input_placeholder_fg),
show_placeholder: true,
}
}
@@ -99,11 +99,11 @@ impl TextArea {
.single_line(false)
.cursor_line_style(Style::reset())
.style(Style::default().fg(theme.colors.default_fg));
let container = super::container(&theme, Box::new(input));
let container = crate::ui::container(&theme, Box::new(input));
Self {
input: container,
placeholder: super::label(title).foreground(theme.colors.input_placeholder_fg),
placeholder: crate::ui::label(title).foreground(theme.colors.input_placeholder_fg),
show_placeholder: true,
}
}
@@ -11,7 +11,6 @@ use crate::ui::widget::{utils, Widget, WidgetComponent};
use super::container::Header;
use super::label::Label;
use super::*;
/// A generic item that can be displayed in a table with [`const W: usize`] columns.
pub trait TableItem<const W: usize> {
@@ -48,7 +47,7 @@ pub struct Property {
impl Property {
pub fn new(name: Widget<Label>, value: Widget<Label>) -> Self {
let divider = label("");
let divider = crate::ui::label("");
Self {
name,
divider,
+1 -1
View File
@@ -1,6 +1,6 @@
use tuirealm::tui::layout::{Constraint, Rect};
use super::common::list::ColumnWidth;
use super::list::ColumnWidth;
/// Calculates `Constraint::Percentage` for each fixed column width in `widths`,
/// taking into account the available width in `area` and the column spacing given by `spacing`.