tui: Split widgets into separate modules
This commit is contained in:
parent
6346931ce2
commit
969219c51d
|
|
@ -18,7 +18,7 @@ use radicle_tui::ui::components::workspace::{
|
|||
};
|
||||
use radicle_tui::ui::layout;
|
||||
use radicle_tui::ui::theme::{self, Theme};
|
||||
use radicle_tui::ui::widget::Widget;
|
||||
use radicle_tui::ui::widget::{self, Widget};
|
||||
|
||||
use radicle_tui::Tui;
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ impl Tui<Cid, Message> for App {
|
|||
self.mount_home_view(app, &self.theme.clone())?;
|
||||
|
||||
// Add global key listener and subscribe to key events
|
||||
let global = ui::global_listener().to_boxed();
|
||||
let global = ui::widget::common::global_listener().to_boxed();
|
||||
app.mount(Cid::GlobalListener, global, subs::global())?;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -223,11 +223,12 @@ impl ViewPage for Home {
|
|||
context: &Context,
|
||||
theme: &Theme,
|
||||
) -> Result<()> {
|
||||
let navigation = ui::home_navigation(theme).to_boxed();
|
||||
let navigation = widget::home::navigation(theme).to_boxed();
|
||||
|
||||
let dashboard = ui::dashboard(theme, &context.id, &context.project).to_boxed();
|
||||
let issue_browser = ui::issue_browser(theme).to_boxed();
|
||||
let patch_browser = ui::patch_browser(theme, &context.patches, &context.profile).to_boxed();
|
||||
let dashboard = widget::home::dashboard(theme, &context.id, &context.project).to_boxed();
|
||||
let issue_browser = widget::home::issues(theme).to_boxed();
|
||||
let patch_browser =
|
||||
widget::home::patches(theme, &context.patches, &context.profile).to_boxed();
|
||||
|
||||
app.remount(
|
||||
Cid::Home(HomeCid::Navigation),
|
||||
|
|
@ -291,9 +292,10 @@ impl ViewPage for PatchView {
|
|||
theme: &Theme,
|
||||
) -> Result<()> {
|
||||
if let Some((id, patch)) = context.patches.get(context.selected_patch) {
|
||||
let navigation = ui::patch_navigation(theme).to_boxed();
|
||||
let activity = ui::patch_activity(theme, (*id, patch), &context.profile).to_boxed();
|
||||
let files = ui::patch_files(theme, (*id, patch), &context.profile).to_boxed();
|
||||
let navigation = widget::patch::navigation(theme).to_boxed();
|
||||
let activity =
|
||||
widget::patch::activity(theme, (*id, patch), &context.profile).to_boxed();
|
||||
let files = widget::patch::files(theme, (*id, patch), &context.profile).to_boxed();
|
||||
|
||||
app.remount(
|
||||
Cid::Patch(PatchCid::Navigation),
|
||||
|
|
@ -338,11 +340,11 @@ impl ViewPage for PatchView {
|
|||
impl tuirealm::Component<Message, NoUserEvent> for Widget<GlobalListener> {
|
||||
fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
|
||||
match event {
|
||||
Event::WindowResize(_, _) => Some(Message::Tick),
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Char('q'),
|
||||
..
|
||||
}) => Some(Message::Quit),
|
||||
Event::WindowResize(_, _) => Some(Message::Tick),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,14 @@ where
|
|||
Id: Clone + Hash + Eq + PartialEq,
|
||||
UserEvent: Clone + Eq + PartialEq + PartialOrd,
|
||||
{
|
||||
vec![Sub::new(
|
||||
SubEventClause::Keyboard(KeyEvent {
|
||||
code: Key::Char('q'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}),
|
||||
SubClause::Always,
|
||||
)]
|
||||
vec![
|
||||
Sub::new(
|
||||
SubEventClause::Keyboard(KeyEvent {
|
||||
code: Key::Char('q'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}),
|
||||
SubClause::Always,
|
||||
),
|
||||
Sub::new(SubEventClause::WindowResize, SubClause::Always),
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,284 +4,3 @@ pub mod layout;
|
|||
pub mod state;
|
||||
pub mod theme;
|
||||
pub mod widget;
|
||||
|
||||
use radicle::prelude::{Id, Project};
|
||||
use radicle::Profile;
|
||||
use tuirealm::props::{AttrValue, Attribute, Color, PropPayload, PropValue, TextSpan};
|
||||
use tuirealm::MockComponent;
|
||||
|
||||
use radicle::cob::patch::{Patch, PatchId};
|
||||
|
||||
use components::container::{GlobalListener, LabeledContainer, Tabs};
|
||||
use components::context::{Shortcut, Shortcuts};
|
||||
use components::label::Label;
|
||||
use components::list::{Property, PropertyList};
|
||||
|
||||
use widget::Widget;
|
||||
|
||||
use self::cob::patch;
|
||||
use self::components::context::ContextBar;
|
||||
use self::components::list::{List, Table};
|
||||
use self::components::workspace::{Browser, Dashboard, IssueBrowser, PatchActivity, PatchFiles};
|
||||
use self::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::default())
|
||||
.content(AttrValue::String(content.to_string()))
|
||||
.height(1)
|
||||
.width(width)
|
||||
}
|
||||
|
||||
pub fn labeled_container(
|
||||
theme: &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);
|
||||
|
||||
Widget::new(container).background(theme.colors.labeled_container_bg)
|
||||
}
|
||||
|
||||
pub fn shortcut(theme: &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::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::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, divider, value);
|
||||
|
||||
Widget::new(property).height(1).width(width)
|
||||
}
|
||||
|
||||
pub fn property_list(
|
||||
_theme: &theme::Theme,
|
||||
properties: Vec<Widget<Property>>,
|
||||
) -> Widget<PropertyList> {
|
||||
let property_list = PropertyList::new(properties);
|
||||
|
||||
Widget::new(property_list)
|
||||
}
|
||||
|
||||
pub fn tabs(theme: &theme::Theme, tabs: Vec<Widget<Label>>) -> Widget<Tabs> {
|
||||
let divider = label(&theme.icons.tab_divider.to_string());
|
||||
let tabs = Tabs::new(tabs, divider);
|
||||
|
||||
Widget::new(tabs)
|
||||
.height(1)
|
||||
.foreground(theme.colors.tabs_fg)
|
||||
.highlight(theme.colors.tabs_highlighted_fg)
|
||||
}
|
||||
|
||||
pub fn table(theme: &theme::Theme, items: &[impl List], profile: &Profile) -> Widget<Table> {
|
||||
let table = Table::default();
|
||||
let items = items.iter().map(|item| item.row(theme, profile)).collect();
|
||||
|
||||
Widget::new(table)
|
||||
.content(AttrValue::Table(items))
|
||||
.background(theme.colors.labeled_container_bg)
|
||||
.highlight(theme.colors.item_list_highlighted_bg)
|
||||
}
|
||||
|
||||
pub fn issue_browser(theme: &theme::Theme) -> Widget<IssueBrowser> {
|
||||
let shortcuts = shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
shortcut(theme, "tab", "section"),
|
||||
shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
|
||||
let not_implemented = label("not implemented").foreground(theme.colors.default_fg);
|
||||
let browser = IssueBrowser::new(not_implemented, shortcuts);
|
||||
|
||||
Widget::new(browser)
|
||||
}
|
||||
|
||||
pub fn patch_browser(
|
||||
theme: &theme::Theme,
|
||||
items: &[(PatchId, Patch)],
|
||||
profile: &Profile,
|
||||
) -> Widget<Browser<(PatchId, Patch)>> {
|
||||
let shortcuts = shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
shortcut(theme, "tab", "section"),
|
||||
shortcut(theme, "↑/↓", "navigate"),
|
||||
shortcut(theme, "enter", "show"),
|
||||
shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
|
||||
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 table = table(theme, items, profile)
|
||||
.custom("widths", widths)
|
||||
.custom("header", header);
|
||||
let browser: Browser<(PatchId, Patch)> = Browser::new(table, shortcuts);
|
||||
|
||||
Widget::new(browser)
|
||||
}
|
||||
|
||||
pub fn patch_activity(
|
||||
theme: &theme::Theme,
|
||||
patch: (PatchId, &Patch),
|
||||
profile: &Profile,
|
||||
) -> Widget<PatchActivity> {
|
||||
let (id, patch) = patch;
|
||||
let shortcuts = shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
shortcut(theme, "esc", "back"),
|
||||
shortcut(theme, "tab", "section"),
|
||||
shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
let context = patch_context(theme, (id, patch), profile);
|
||||
|
||||
let not_implemented = label("not implemented").foreground(theme.colors.default_fg);
|
||||
let activity = PatchActivity::new(not_implemented, context, shortcuts);
|
||||
|
||||
Widget::new(activity)
|
||||
}
|
||||
|
||||
pub fn patch_files(
|
||||
theme: &theme::Theme,
|
||||
patch: (PatchId, &Patch),
|
||||
profile: &Profile,
|
||||
) -> Widget<PatchFiles> {
|
||||
let (id, patch) = patch;
|
||||
let shortcuts = shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
shortcut(theme, "esc", "back"),
|
||||
shortcut(theme, "tab", "section"),
|
||||
shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
let context = patch_context(theme, (id, patch), profile);
|
||||
|
||||
let not_implemented = label("not implemented").foreground(theme.colors.default_fg);
|
||||
let files = PatchFiles::new(not_implemented, context, shortcuts);
|
||||
|
||||
Widget::new(files)
|
||||
}
|
||||
|
||||
pub fn patch_context(
|
||||
_theme: &Theme,
|
||||
patch: (PatchId, &Patch),
|
||||
profile: &Profile,
|
||||
) -> Widget<ContextBar> {
|
||||
let (id, patch) = patch;
|
||||
let id = patch::format_id(id);
|
||||
let title = patch.title();
|
||||
let author = patch::format_author(patch, profile);
|
||||
let comments = patch::format_comments(patch);
|
||||
|
||||
let context = label(" patch ").background(Color::Rgb(238, 111, 248));
|
||||
let id = label(&format!(" {id} "))
|
||||
.foreground(Color::Rgb(117, 113, 249))
|
||||
.background(Color::Rgb(40, 40, 40));
|
||||
let title = label(&format!(" {title} "))
|
||||
.foreground(Color::Rgb(70, 70, 70))
|
||||
.background(Color::Rgb(40, 40, 40));
|
||||
let author = label(&format!(" {author} "))
|
||||
.foreground(Color::Rgb(117, 113, 249))
|
||||
.background(Color::Rgb(40, 40, 40));
|
||||
let comments = label(&format!(" {comments} "))
|
||||
.foreground(Color::Rgb(70, 70, 70))
|
||||
.background(Color::Rgb(50, 50, 50));
|
||||
|
||||
let context_bar = ContextBar::new(context, id, author, title, comments);
|
||||
Widget::new(context_bar).height(1)
|
||||
}
|
||||
|
||||
pub fn home_navigation(theme: &theme::Theme) -> Widget<Tabs> {
|
||||
tabs(
|
||||
theme,
|
||||
vec![label("dashboard"), label("issues"), label("patches")],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn patch_navigation(theme: &theme::Theme) -> Widget<Tabs> {
|
||||
tabs(theme, vec![label("activity"), label("files")])
|
||||
}
|
||||
|
||||
pub fn dashboard(theme: &theme::Theme, id: &Id, project: &Project) -> Widget<Dashboard> {
|
||||
let about = labeled_container(
|
||||
theme,
|
||||
"about",
|
||||
property_list(
|
||||
theme,
|
||||
vec![
|
||||
property(theme, "id", &id.to_string()),
|
||||
property(theme, "name", project.name()),
|
||||
property(theme, "description", project.description()),
|
||||
],
|
||||
)
|
||||
.to_boxed(),
|
||||
);
|
||||
let shortcuts = shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
shortcut(theme, "tab", "section"),
|
||||
shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
let dashboard = Dashboard::new(about, shortcuts);
|
||||
|
||||
Widget::new(dashboard)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
pub mod common;
|
||||
pub mod home;
|
||||
pub mod patch;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use tuirealm::command::{Cmd, CmdResult};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
use radicle::Profile;
|
||||
use tuirealm::props::{AttrValue, Attribute};
|
||||
use tuirealm::MockComponent;
|
||||
|
||||
use crate::ui::components::container::{GlobalListener, LabeledContainer, Tabs};
|
||||
use crate::ui::components::context::{Shortcut, Shortcuts};
|
||||
use crate::ui::components::label::Label;
|
||||
use crate::ui::components::list::{Property, PropertyList};
|
||||
|
||||
use super::Widget;
|
||||
|
||||
use crate::ui::components::list::{List, Table};
|
||||
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::default())
|
||||
.content(AttrValue::String(content.to_string()))
|
||||
.height(1)
|
||||
.width(width)
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Widget::new(container).background(theme.colors.labeled_container_bg)
|
||||
}
|
||||
|
||||
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, divider, value);
|
||||
|
||||
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 tabs(theme: &Theme, tabs: Vec<Widget<Label>>) -> Widget<Tabs> {
|
||||
let divider = label(&theme.icons.tab_divider.to_string());
|
||||
let tabs = Tabs::new(tabs, divider);
|
||||
|
||||
Widget::new(tabs)
|
||||
.height(1)
|
||||
.foreground(theme.colors.tabs_fg)
|
||||
.highlight(theme.colors.tabs_highlighted_fg)
|
||||
}
|
||||
|
||||
pub fn table(theme: &Theme, items: &[impl List], profile: &Profile) -> Widget<Table> {
|
||||
let table = Table::default();
|
||||
let items = items.iter().map(|item| item.row(theme, profile)).collect();
|
||||
|
||||
Widget::new(table)
|
||||
.content(AttrValue::Table(items))
|
||||
.background(theme.colors.labeled_container_bg)
|
||||
.highlight(theme.colors.item_list_highlighted_bg)
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
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::container::Tabs;
|
||||
use crate::ui::components::workspace::{Browser, Dashboard, IssueBrowser};
|
||||
use crate::ui::theme::Theme;
|
||||
|
||||
use super::{common, Widget};
|
||||
|
||||
pub fn navigation(theme: &Theme) -> Widget<Tabs> {
|
||||
common::tabs(
|
||||
theme,
|
||||
vec![
|
||||
common::label("dashboard"),
|
||||
common::label("issues"),
|
||||
common::label("patches"),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dashboard(theme: &Theme, id: &Id, project: &Project) -> Widget<Dashboard> {
|
||||
let about = common::labeled_container(
|
||||
theme,
|
||||
"about",
|
||||
common::property_list(
|
||||
theme,
|
||||
vec![
|
||||
common::property(theme, "id", &id.to_string()),
|
||||
common::property(theme, "name", project.name()),
|
||||
common::property(theme, "description", project.description()),
|
||||
],
|
||||
)
|
||||
.to_boxed(),
|
||||
);
|
||||
let shortcuts = common::shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
common::shortcut(theme, "tab", "section"),
|
||||
common::shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
let dashboard = Dashboard::new(about, shortcuts);
|
||||
|
||||
Widget::new(dashboard)
|
||||
}
|
||||
|
||||
pub fn patches(
|
||||
theme: &Theme,
|
||||
items: &[(PatchId, Patch)],
|
||||
profile: &Profile,
|
||||
) -> Widget<Browser<(PatchId, Patch)>> {
|
||||
let shortcuts = common::shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
common::shortcut(theme, "tab", "section"),
|
||||
common::shortcut(theme, "↑/↓", "navigate"),
|
||||
common::shortcut(theme, "enter", "show"),
|
||||
common::shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
|
||||
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 table = common::table(theme, items, profile)
|
||||
.custom("widths", widths)
|
||||
.custom("header", header);
|
||||
let browser: Browser<(PatchId, Patch)> = Browser::new(table, shortcuts);
|
||||
|
||||
Widget::new(browser)
|
||||
}
|
||||
|
||||
pub fn issues(theme: &Theme) -> Widget<IssueBrowser> {
|
||||
let shortcuts = common::shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
common::shortcut(theme, "tab", "section"),
|
||||
common::shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
|
||||
let not_implemented = common::label("not implemented").foreground(theme.colors.default_fg);
|
||||
let browser = IssueBrowser::new(not_implemented, shortcuts);
|
||||
|
||||
Widget::new(browser)
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
use radicle::Profile;
|
||||
use tuirealm::props::Color;
|
||||
|
||||
use radicle::cob::patch::{Patch, PatchId};
|
||||
|
||||
use super::common;
|
||||
use super::Widget;
|
||||
|
||||
use crate::ui::cob::patch;
|
||||
use crate::ui::components::container::Tabs;
|
||||
use crate::ui::components::context::ContextBar;
|
||||
use crate::ui::components::workspace::{PatchActivity, PatchFiles};
|
||||
use crate::ui::theme::Theme;
|
||||
|
||||
pub fn navigation(theme: &Theme) -> Widget<Tabs> {
|
||||
common::tabs(
|
||||
theme,
|
||||
vec![common::label("activity"), common::label("files")],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn activity(
|
||||
theme: &Theme,
|
||||
patch: (PatchId, &Patch),
|
||||
profile: &Profile,
|
||||
) -> Widget<PatchActivity> {
|
||||
let (id, patch) = patch;
|
||||
let shortcuts = common::shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
common::shortcut(theme, "esc", "back"),
|
||||
common::shortcut(theme, "tab", "section"),
|
||||
common::shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
let context = context(theme, (id, patch), profile);
|
||||
|
||||
let not_implemented = common::label("not implemented").foreground(theme.colors.default_fg);
|
||||
let activity = PatchActivity::new(not_implemented, context, shortcuts);
|
||||
|
||||
Widget::new(activity)
|
||||
}
|
||||
|
||||
pub fn files(theme: &Theme, patch: (PatchId, &Patch), profile: &Profile) -> Widget<PatchFiles> {
|
||||
let (id, patch) = patch;
|
||||
let shortcuts = common::shortcuts(
|
||||
theme,
|
||||
vec![
|
||||
common::shortcut(theme, "esc", "back"),
|
||||
common::shortcut(theme, "tab", "section"),
|
||||
common::shortcut(theme, "q", "quit"),
|
||||
],
|
||||
);
|
||||
let context = context(theme, (id, patch), profile);
|
||||
|
||||
let not_implemented = common::label("not implemented").foreground(theme.colors.default_fg);
|
||||
let files = PatchFiles::new(not_implemented, context, shortcuts);
|
||||
|
||||
Widget::new(files)
|
||||
}
|
||||
|
||||
pub fn context(_theme: &Theme, patch: (PatchId, &Patch), profile: &Profile) -> Widget<ContextBar> {
|
||||
let (id, patch) = patch;
|
||||
let id = patch::format_id(id);
|
||||
let title = patch.title();
|
||||
let author = patch::format_author(patch, profile);
|
||||
let comments = patch::format_comments(patch);
|
||||
|
||||
let context = common::label(" patch ").background(Color::Rgb(238, 111, 248));
|
||||
let id = common::label(&format!(" {} ", id))
|
||||
.foreground(Color::Rgb(117, 113, 249))
|
||||
.background(Color::Rgb(40, 40, 40));
|
||||
let title = common::label(&format!(" {} ", title))
|
||||
.foreground(Color::Rgb(70, 70, 70))
|
||||
.background(Color::Rgb(40, 40, 40));
|
||||
let author = common::label(&format!(" {} ", author))
|
||||
.foreground(Color::Rgb(117, 113, 249))
|
||||
.background(Color::Rgb(40, 40, 40));
|
||||
let comments = common::label(&format!(" {} ", comments))
|
||||
.foreground(Color::Rgb(70, 70, 70))
|
||||
.background(Color::Rgb(50, 50, 50));
|
||||
|
||||
let context_bar = ContextBar::new(context, id, author, title, comments);
|
||||
|
||||
Widget::new(context_bar).height(1)
|
||||
}
|
||||
Loading…
Reference in New Issue