tui: Move shortcuts to root widget

This commit is contained in:
Erik Kundt 2023-04-18 18:06:46 +02:00 committed by Alexis Sellier
parent 95b0029267
commit e13bf0b75b
No known key found for this signature in database
4 changed files with 172 additions and 92 deletions

View File

@ -4,7 +4,6 @@ use tui_realm_stdlib::Phantom;
use tuirealm::application::PollStrategy;
use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
use tuirealm::event::{Event, Key, KeyEvent};
use tuirealm::props::{AttrValue, Attribute};
use tuirealm::{Application, Frame, MockComponent, NoUserEvent, State, StateValue};
use radicle_tui::cob::patch::{self};
@ -40,7 +39,6 @@ pub enum PatchCid {
Navigation,
Activity,
Files,
Context,
}
/// All component ids known to this application.
@ -48,7 +46,6 @@ pub enum PatchCid {
pub enum Cid {
Home(HomeCid),
Patch(PatchCid),
Shortcuts,
GlobalListener,
}
@ -232,15 +229,6 @@ impl ViewPage for Home {
let issue_browser = ui::issue_browser(theme).to_boxed();
let patch_browser = ui::patch_browser(theme, &context.patches, &context.profile).to_boxed();
let shortcuts = ui::shortcuts(
theme,
vec![
ui::shortcut(theme, "tab", "section"),
ui::shortcut(theme, "q", "quit"),
],
)
.to_boxed();
app.remount(
Cid::Home(HomeCid::Navigation),
navigation,
@ -251,7 +239,6 @@ impl ViewPage for Home {
app.remount(Cid::Home(HomeCid::IssueBrowser), issue_browser, vec![])?;
app.remount(Cid::Home(HomeCid::PatchBrowser), patch_browser, vec![])?;
app.remount(Cid::Shortcuts, shortcuts, vec![])?;
Ok(())
}
@ -269,17 +256,10 @@ 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 shortcuts_h = app
.query(&Cid::Shortcuts, Attribute::Height)
.ok()
.flatten()
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::default_page(area, navigation_h, shortcuts_h);
let layout = layout::default_page(area, navigation_h);
app.view(&Cid::Home(HomeCid::Navigation), frame, layout[0]);
app.view(&self.active_component, frame, layout[1]);
app.view(&Cid::Shortcuts, frame, layout[2]);
}
fn activate(&self, app: &mut Application<Cid, Message, NoUserEvent>) -> Result<()> {
@ -312,18 +292,8 @@ impl ViewPage for PatchView {
) -> 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).to_boxed();
let files = ui::patch_files(theme).to_boxed();
let context = ui::patch_context(theme, (*id, patch), &context.profile).to_boxed();
let shortcuts = ui::shortcuts(
theme,
vec![
ui::shortcut(theme, "esc", "back"),
ui::shortcut(theme, "tab", "section"),
ui::shortcut(theme, "q", "quit"),
],
)
.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();
app.remount(
Cid::Patch(PatchCid::Navigation),
@ -332,8 +302,6 @@ impl ViewPage for PatchView {
)?;
app.remount(Cid::Patch(PatchCid::Activity), activity, vec![])?;
app.remount(Cid::Patch(PatchCid::Files), files, vec![])?;
app.remount(Cid::Patch(PatchCid::Context), context, vec![])?;
app.remount(Cid::Shortcuts, shortcuts, vec![])?;
}
Ok(())
}
@ -351,19 +319,11 @@ impl ViewPage for PatchView {
fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame) {
let area = frame.size();
let navigation_h = 2u16;
let context_h = 1u16;
let shortcuts_h = app
.query(&Cid::Shortcuts, Attribute::Height)
.ok()
.flatten()
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::page_with_context(area, navigation_h, context_h, shortcuts_h);
let layout = layout::default_page(area, navigation_h);
app.view(&Cid::Patch(PatchCid::Navigation), frame, layout[0]);
app.view(&self.active_component, frame, layout[1]);
app.view(&Cid::Patch(PatchCid::Context), frame, layout[2]);
app.view(&Cid::Shortcuts, frame, layout[3]);
}
fn activate(&self, app: &mut Application<Cid, Message, NoUserEvent>) -> Result<()> {

View File

@ -122,8 +122,16 @@ pub fn table(theme: &theme::Theme, items: &[impl List], profile: &Profile) -> Wi
}
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);
let browser = IssueBrowser::new(not_implemented, shortcuts);
Widget::new(browser)
}
@ -133,6 +141,16 @@ pub fn patch_browser(
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),
@ -153,21 +171,51 @@ pub fn patch_browser(
let table = table(theme, items, profile)
.custom("widths", widths)
.custom("header", header);
let browser: Browser<(PatchId, Patch)> = Browser::new(table);
let browser: Browser<(PatchId, Patch)> = Browser::new(table, shortcuts);
Widget::new(browser)
}
pub fn patch_activity(theme: &theme::Theme) -> Widget<PatchActivity> {
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);
let activity = PatchActivity::new(not_implemented, context, shortcuts);
Widget::new(activity)
}
pub fn patch_files(theme: &theme::Theme) -> Widget<PatchFiles> {
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);
let files = PatchFiles::new(not_implemented, context, shortcuts);
Widget::new(files)
}
@ -198,7 +246,7 @@ pub fn patch_context(
.background(Color::Rgb(50, 50, 50));
let context_bar = ContextBar::new(context, id, author, title, comments);
Widget::new(context_bar)
Widget::new(context_bar).height(1)
}
pub fn home_navigation(theme: &theme::Theme) -> Widget<Tabs> {
@ -226,7 +274,14 @@ pub fn dashboard(theme: &theme::Theme, id: &Id, project: &Project) -> Widget<Das
)
.to_boxed(),
);
let dashboard = Dashboard::new(about);
let shortcuts = shortcuts(
theme,
vec![
shortcut(theme, "tab", "section"),
shortcut(theme, "q", "quit"),
],
);
let dashboard = Dashboard::new(about, shortcuts);
Widget::new(dashboard)
}

View File

@ -2,25 +2,28 @@ use std::marker::PhantomData;
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::Props;
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::tui::layout::Rect;
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, State};
use crate::ui::layout;
use crate::ui::widget::{Widget, WidgetComponent};
use super::container::LabeledContainer;
use super::context::{ContextBar, Shortcuts};
use super::label::Label;
use super::list::{List, Table};
pub struct Browser<T> {
list: Widget<Table>,
shortcuts: Widget<Shortcuts>,
phantom: PhantomData<T>,
}
impl<T: List> Browser<T> {
pub fn new(list: Widget<Table>) -> Self {
pub fn new(list: Widget<Table>, shortcuts: Widget<Shortcuts>) -> Self {
Self {
list,
shortcuts,
phantom: PhantomData,
}
}
@ -28,12 +31,15 @@ impl<T: List> Browser<T> {
impl<T: List> WidgetComponent for Browser<T> {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![Constraint::Min(1)].as_ref())
.split(area);
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component(area, shortcuts_h);
self.list.view(frame, layout[0]);
self.shortcuts.view(frame, layout[1]);
}
fn state(&self) -> State {
@ -47,20 +53,25 @@ impl<T: List> WidgetComponent for Browser<T> {
pub struct Dashboard {
about: Widget<LabeledContainer>,
shortcuts: Widget<Shortcuts>,
}
impl Dashboard {
pub fn new(about: Widget<LabeledContainer>) -> Self {
Self { about }
pub fn new(about: Widget<LabeledContainer>, shortcuts: Widget<Shortcuts>) -> Self {
Self { about, shortcuts }
}
}
impl WidgetComponent for Dashboard {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![Constraint::Length(4)].as_ref())
.split(area);
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component(area, shortcuts_h);
self.about.view(frame, layout[0]);
self.shortcuts.view(frame, layout[1]);
}
fn state(&self) -> State {
@ -74,11 +85,12 @@ impl WidgetComponent for Dashboard {
pub struct IssueBrowser {
label: Widget<Label>,
shortcuts: Widget<Shortcuts>,
}
impl IssueBrowser {
pub fn new(label: Widget<Label>) -> Self {
Self { label }
pub fn new(label: Widget<Label>, shortcuts: Widget<Shortcuts>) -> Self {
Self { label, shortcuts }
}
}
@ -89,9 +101,16 @@ impl WidgetComponent for IssueBrowser {
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
let rect = layout::centered_label(label_w, area);
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component(area, shortcuts_h);
self.label.view(frame, rect);
self.label
.view(frame, layout::centered_label(label_w, layout[0]));
self.shortcuts.view(frame, layout[1])
}
fn state(&self) -> State {
@ -105,11 +124,21 @@ impl WidgetComponent for IssueBrowser {
pub struct PatchActivity {
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
}
impl PatchActivity {
pub fn new(label: Widget<Label>) -> Self {
Self { label }
pub fn new(
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
) -> Self {
Self {
label,
context,
shortcuts,
}
}
}
@ -120,9 +149,22 @@ impl WidgetComponent for PatchActivity {
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
let rect = layout::centered_label(label_w, area);
let context_h = self
.context
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component_with_context(area, context_h, shortcuts_h);
self.label.view(frame, rect);
self.label
.view(frame, layout::centered_label(label_w, layout[0]));
self.context.view(frame, layout[1]);
self.shortcuts.view(frame, layout[2]);
}
fn state(&self) -> State {
@ -136,11 +178,21 @@ impl WidgetComponent for PatchActivity {
pub struct PatchFiles {
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
}
impl PatchFiles {
pub fn new(label: Widget<Label>) -> Self {
Self { label }
pub fn new(
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
) -> Self {
Self {
label,
context,
shortcuts,
}
}
}
@ -151,9 +203,22 @@ impl WidgetComponent for PatchFiles {
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
let rect = layout::centered_label(label_w, area);
let context_h = self
.context
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component_with_context(area, context_h, shortcuts_h);
self.label.view(frame, rect);
self.label
.view(frame, layout::centered_label(label_w, layout[0]));
self.context.view(frame, layout[1]);
self.shortcuts.view(frame, layout[2]);
}
fn state(&self) -> State {

View File

@ -46,18 +46,24 @@ pub fn h_stack(
widgets.into_iter().zip(layout.into_iter()).collect()
}
pub fn default_page(area: Rect, nav_h: u16, shortcuts_h: u16) -> Vec<Rect> {
pub fn default_page(area: Rect, nav_h: u16) -> Vec<Rect> {
let margin_h = 1u16;
let content_h = area
.height
.saturating_sub(shortcuts_h.saturating_add(nav_h).saturating_add(margin_h));
let content_h = area.height.saturating_sub(nav_h.saturating_add(margin_h));
Layout::default()
.direction(Direction::Vertical)
.horizontal_margin(margin_h)
.constraints([Constraint::Length(nav_h), Constraint::Length(content_h)].as_ref())
.split(area)
}
pub fn root_component(area: Rect, shortcuts_h: u16) -> Vec<Rect> {
let content_h = area.height.saturating_sub(shortcuts_h);
Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(nav_h),
Constraint::Length(content_h),
Constraint::Length(shortcuts_h),
]
@ -66,21 +72,15 @@ pub fn default_page(area: Rect, nav_h: u16, shortcuts_h: u16) -> Vec<Rect> {
.split(area)
}
pub fn page_with_context(area: Rect, nav_h: u16, context_h: u16, shortcuts_h: u16) -> Vec<Rect> {
let margin_h = 1u16;
let content_h = area.height.saturating_sub(
shortcuts_h
.saturating_add(nav_h)
.saturating_add(context_h)
.saturating_add(margin_h),
);
pub fn root_component_with_context(area: Rect, context_h: u16, shortcuts_h: u16) -> Vec<Rect> {
let content_h = area
.height
.saturating_sub(shortcuts_h.saturating_add(context_h));
Layout::default()
.direction(Direction::Vertical)
.horizontal_margin(margin_h)
.constraints(
[
Constraint::Length(nav_h),
Constraint::Length(content_h),
Constraint::Length(context_h),
Constraint::Length(shortcuts_h),