Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Rework page shortcuts
Erik Kundt committed 2 years ago
commit aee8b37caaa578b9d1f6c854928343ba4f645563
parent 1c8f4d2b224a302120af935c5248ec41345e05ce
4 files changed +110 -129
modified bin/commands/inbox/select/ui.rs
@@ -17,7 +17,7 @@ use tui::ui::span;
use tui::ui::widget::container::{Footer, FooterProps, Header};
use tui::ui::widget::input::{TextField, TextFieldProps};
use tui::ui::widget::text::{Paragraph, ParagraphProps};
-
use tui::ui::widget::{Column, Render, Shortcut, Shortcuts, ShortcutsProps, Table, Widget};
+
use tui::ui::widget::{Column, Render, Shortcuts, Table, Widget};
use tui::Selection;

use crate::tui_inbox::common::{InboxOperation, Mode, RepositoryMode, SelectionMode};
@@ -25,7 +25,6 @@ use crate::tui_inbox::common::{InboxOperation, Mode, RepositoryMode, SelectionMo
use super::{Action, State};

pub struct ListPageProps {
-
    mode: Mode,
    show_search: bool,
    show_help: bool,
}
@@ -33,7 +32,6 @@ pub struct ListPageProps {
impl From<&State> for ListPageProps {
    fn from(state: &State) -> Self {
        Self {
-
            mode: state.mode.clone(),
            show_search: state.ui.show_search,
            show_help: state.ui.show_help,
        }
@@ -66,7 +64,7 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
            notifications: Notifications::new(state, action_tx.clone()),
            search: Search::new(state, action_tx.clone()),
            help: Help::new(state, action_tx.clone()),
-
            shortcuts: Shortcuts::new(state, action_tx.clone()),
+
            shortcuts: Shortcuts::new(&(), action_tx.clone()),
        }
        .move_with_state(state)
    }
@@ -75,9 +73,28 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
    where
        Self: Sized,
    {
+
        let shorts = if state.ui.show_search {
+
            vec![("esc", "cancel"), ("enter", "apply")]
+
        } else if state.ui.show_help {
+
            vec![("?", "close")]
+
        } else {
+
            match state.mode.selection() {
+
                SelectionMode::Id => vec![("enter", "select"), ("/", "search")],
+
                SelectionMode::Operation => vec![
+
                    ("enter", "show"),
+
                    ("c", "clear"),
+
                    ("/", "search"),
+
                    ("?", "help"),
+
                ],
+
            }
+
        };
+

+
        let shortcuts = self.shortcuts.move_with_state(&());
+
        let shortcuts = shortcuts.shortcuts(&shorts);
+

        ListPage {
            notifications: self.notifications.move_with_state(state),
-
            shortcuts: self.shortcuts.move_with_state(state),
+
            shortcuts,
            help: self.help.move_with_state(state),
            props: ListPageProps::from(state),
            ..self
@@ -116,28 +133,6 @@ impl<'a> Render<()> for ListPage<'a> {
        let area = frame.size();
        let layout = tui::ui::layout::default_page(area, 0u16, 1u16);

-
        let shortcuts = if self.props.show_search {
-
            vec![
-
                Shortcut::new("esc", "cancel"),
-
                Shortcut::new("enter", "apply"),
-
            ]
-
        } else if self.props.show_help {
-
            vec![Shortcut::new("?", "close")]
-
        } else {
-
            match self.props.mode.selection() {
-
                SelectionMode::Id => vec![
-
                    Shortcut::new("enter", "select"),
-
                    Shortcut::new("/", "search"),
-
                ],
-
                SelectionMode::Operation => vec![
-
                    Shortcut::new("enter", "show"),
-
                    Shortcut::new("c", "clear"),
-
                    Shortcut::new("/", "search"),
-
                    Shortcut::new("?", "help"),
-
                ],
-
            }
-
        };
-

        if self.props.show_search {
            let component_layout = Layout::vertical([Constraint::Min(1), Constraint::Length(2)])
                .split(layout.component);
@@ -152,14 +147,7 @@ impl<'a> Render<()> for ListPage<'a> {
            self.notifications.render::<B>(frame, layout.component, ());
        }

-
        self.shortcuts.render::<B>(
-
            frame,
-
            layout.shortcuts,
-
            ShortcutsProps {
-
                shortcuts,
-
                divider: '∙',
-
            },
-
        );
+
        self.shortcuts.render::<B>(frame, layout.shortcuts, ());
    }
}

modified bin/commands/issue/select/ui.rs
@@ -19,7 +19,7 @@ use tui::ui::span;
use tui::ui::widget::container::{Footer, FooterProps, Header};
use tui::ui::widget::input::{TextField, TextFieldProps};
use tui::ui::widget::text::{Paragraph, ParagraphProps};
-
use tui::ui::widget::{Column, Render, Shortcut, Shortcuts, ShortcutsProps, Table, Widget};
+
use tui::ui::widget::{Column, Render, Shortcuts, Table, Widget};
use tui::Selection;

use crate::tui_issue::common::IssueOperation;
@@ -28,7 +28,6 @@ use crate::tui_issue::common::Mode;
use super::{Action, State};

pub struct ListPageProps {
-
    mode: Mode,
    show_search: bool,
    show_help: bool,
}
@@ -36,7 +35,6 @@ pub struct ListPageProps {
impl From<&State> for ListPageProps {
    fn from(state: &State) -> Self {
        Self {
-
            mode: state.mode.clone(),
            show_search: state.ui.show_search,
            show_help: state.ui.show_help,
        }
@@ -69,7 +67,7 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
            issues: Issues::new(state, action_tx.clone()),
            search: Search::new(state, action_tx.clone()),
            help: Help::new(state, action_tx.clone()),
-
            shortcuts: Shortcuts::new(state, action_tx.clone()),
+
            shortcuts: Shortcuts::new(&(), action_tx.clone()),
        }
        .move_with_state(state)
    }
@@ -78,9 +76,28 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
    where
        Self: Sized,
    {
+
        let shorts = if state.ui.show_search {
+
            vec![("esc", "cancel"), ("enter", "apply")]
+
        } else if state.ui.show_help {
+
            vec![("?", "close")]
+
        } else {
+
            match state.mode {
+
                Mode::Id => vec![("enter", "select"), ("/", "search")],
+
                Mode::Operation => vec![
+
                    ("enter", "show"),
+
                    ("e", "edit"),
+
                    ("/", "search"),
+
                    ("?", "help"),
+
                ],
+
            }
+
        };
+

+
        let shortcuts = self.shortcuts.move_with_state(&());
+
        let shortcuts = shortcuts.shortcuts(&shorts);
+

        ListPage {
            issues: self.issues.move_with_state(state),
-
            shortcuts: self.shortcuts.move_with_state(state),
+
            shortcuts,
            help: self.help.move_with_state(state),
            props: ListPageProps::from(state),
            ..self
@@ -116,28 +133,6 @@ impl<'a> Render<()> for ListPage<'a> {
        let area = frame.size();
        let layout = tui::ui::layout::default_page(area, 0u16, 1u16);

-
        let shortcuts = if self.props.show_search {
-
            vec![
-
                Shortcut::new("esc", "cancel"),
-
                Shortcut::new("enter", "apply"),
-
            ]
-
        } else if self.props.show_help {
-
            vec![Shortcut::new("?", "close")]
-
        } else {
-
            match self.props.mode {
-
                Mode::Id => vec![
-
                    Shortcut::new("enter", "select"),
-
                    Shortcut::new("/", "search"),
-
                ],
-
                Mode::Operation => vec![
-
                    Shortcut::new("enter", "show"),
-
                    Shortcut::new("e", "edit"),
-
                    Shortcut::new("/", "search"),
-
                    Shortcut::new("?", "help"),
-
                ],
-
            }
-
        };
-

        if self.props.show_search {
            let component_layout = Layout::vertical([Constraint::Min(1), Constraint::Length(2)])
                .split(layout.component);
@@ -151,14 +146,7 @@ impl<'a> Render<()> for ListPage<'a> {
            self.issues.render::<B>(frame, layout.component, ());
        }

-
        self.shortcuts.render::<B>(
-
            frame,
-
            layout.shortcuts,
-
            ShortcutsProps {
-
                shortcuts,
-
                divider: '∙',
-
            },
-
        );
+
        self.shortcuts.render::<B>(frame, layout.shortcuts, ());
    }
}

modified bin/commands/patch/select/ui.rs
@@ -20,7 +20,7 @@ use tui::ui::span;
use tui::ui::widget::container::{Footer, FooterProps, Header};
use tui::ui::widget::input::{TextField, TextFieldProps};
use tui::ui::widget::text::{Paragraph, ParagraphProps};
-
use tui::ui::widget::{Column, Render, Shortcut, Shortcuts, ShortcutsProps, Table, Widget};
+
use tui::ui::widget::{Column, Render, Shortcuts, Table, Widget};
use tui::Selection;

use crate::tui_patch::common::Mode;
@@ -29,7 +29,6 @@ use crate::tui_patch::common::PatchOperation;
use super::{Action, State};

pub struct ListPageProps {
-
    mode: Mode,
    show_search: bool,
    show_help: bool,
}
@@ -37,7 +36,6 @@ pub struct ListPageProps {
impl From<&State> for ListPageProps {
    fn from(state: &State) -> Self {
        Self {
-
            mode: state.mode.clone(),
            show_search: state.ui.show_search,
            show_help: state.ui.show_help,
        }
@@ -70,7 +68,7 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
            patches: Patches::new(state, action_tx.clone()),
            search: Search::new(state, action_tx.clone()),
            help: Help::new(state, action_tx.clone()),
-
            shortcuts: Shortcuts::new(state, action_tx),
+
            shortcuts: Shortcuts::new(&(), action_tx),
        }
        .move_with_state(state)
    }
@@ -79,10 +77,30 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
    where
        Self: Sized,
    {
+
        let shorts = if state.ui.show_search {
+
            vec![("esc", "cancel"), ("enter", "apply")]
+
        } else if state.ui.show_help {
+
            vec![("?", "close")]
+
        } else {
+
            match state.mode {
+
                Mode::Id => vec![("enter", "select"), ("/", "search")],
+
                Mode::Operation => vec![
+
                    ("enter", "show"),
+
                    ("c", "checkout"),
+
                    ("d", "diff"),
+
                    ("/", "search"),
+
                    ("?", "help"),
+
                ],
+
            }
+
        };
+

+
        let shortcuts = self.shortcuts.move_with_state(&());
+
        let shortcuts = shortcuts.shortcuts(&shorts);
+

        ListPage {
            patches: self.patches.move_with_state(state),
            search: self.search.move_with_state(state),
-
            shortcuts: self.shortcuts.move_with_state(state),
+
            shortcuts,
            help: self.help.move_with_state(state),
            props: ListPageProps::from(state),
            ..self
@@ -118,29 +136,6 @@ impl<'a> Render<()> for ListPage<'a> {
        let area = frame.size();
        let layout = tui::ui::layout::default_page(area, 0u16, 1u16);

-
        let shortcuts = if self.props.show_search {
-
            vec![
-
                Shortcut::new("esc", "cancel"),
-
                Shortcut::new("enter", "apply"),
-
            ]
-
        } else if self.props.show_help {
-
            vec![Shortcut::new("?", "close")]
-
        } else {
-
            match self.props.mode {
-
                Mode::Id => vec![
-
                    Shortcut::new("enter", "select"),
-
                    Shortcut::new("/", "search"),
-
                ],
-
                Mode::Operation => vec![
-
                    Shortcut::new("enter", "show"),
-
                    Shortcut::new("c", "checkout"),
-
                    Shortcut::new("d", "diff"),
-
                    Shortcut::new("/", "search"),
-
                    Shortcut::new("?", "help"),
-
                ],
-
            }
-
        };
-

        if self.props.show_search {
            let component_layout = Layout::vertical([Constraint::Min(1), Constraint::Length(2)])
                .split(layout.component);
@@ -154,14 +149,7 @@ impl<'a> Render<()> for ListPage<'a> {
            self.patches.render::<B>(frame, layout.component, ());
        }

-
        self.shortcuts.render::<B>(
-
            frame,
-
            layout.shortcuts,
-
            ShortcutsProps {
-
                shortcuts,
-
                divider: '∙',
-
            },
-
        );
+
        self.shortcuts.render::<B>(frame, layout.shortcuts, ());
    }
}

modified src/ui/widget.rs
@@ -37,41 +37,57 @@ pub trait ToRow {
    fn to_row(&self) -> Vec<Cell>;
}

-
pub struct Shortcut {
-
    pub short: String,
-
    pub long: String,
+
pub struct ShortcutsProps {
+
    pub shortcuts: Vec<(String, String)>,
+
    pub divider: char,
}

-
impl Shortcut {
-
    pub fn new(short: &str, long: &str) -> Self {
+
impl Default for ShortcutsProps {
+
    fn default() -> Self {
        Self {
-
            short: short.to_string(),
-
            long: long.to_string(),
+
            shortcuts: vec![],
+
            divider: '∙',
        }
    }
}

-
pub struct ShortcutsProps {
-
    pub shortcuts: Vec<Shortcut>,
-
    pub divider: char,
-
}
-

pub struct Shortcuts<A> {
+
    /// Message sender
    pub action_tx: UnboundedSender<A>,
+
    /// Internal properties
+
    props: ShortcutsProps,
}

-
impl<S, A> Widget<S, A> for Shortcuts<A> {
-
    fn new(state: &S, action_tx: UnboundedSender<A>) -> Self
+
impl<A> Shortcuts<A> {
+
    pub fn divider(mut self, divider: char) -> Self {
+
        self.props.divider = divider;
+
        self
+
    }
+

+
    pub fn shortcuts(mut self, shortcuts: &[(&str, &str)]) -> Self {
+
        self.props.shortcuts.clear();
+
        for (short, long) in shortcuts {
+
            self.props
+
                .shortcuts
+
                .push((short.to_string(), long.to_string()));
+
        }
+
        self
+
    }
+
}
+

+
impl<A> Widget<(), A> for Shortcuts<A> {
+
    fn new(state: &(), action_tx: UnboundedSender<A>) -> Self
    where
        Self: Sized,
    {
        Self {
            action_tx: action_tx.clone(),
+
            props: ShortcutsProps::default(),
        }
        .move_with_state(state)
    }

-
    fn move_with_state(self, _state: &S) -> Self
+
    fn move_with_state(self, _state: &()) -> Self
    where
        Self: Sized,
    {
@@ -81,22 +97,23 @@ impl<S, A> Widget<S, A> for Shortcuts<A> {
    fn handle_key_event(&mut self, _key: termion::event::Key) {}
}

-
impl<A> Render<ShortcutsProps> for Shortcuts<A> {
-
    fn render<B: Backend>(&self, frame: &mut ratatui::Frame, area: Rect, props: ShortcutsProps) {
+
impl<A> Render<()> for Shortcuts<A> {
+
    fn render<B: Backend>(&self, frame: &mut ratatui::Frame, area: Rect, _props: ()) {
        use ratatui::widgets::Table;

-
        let mut shortcuts = props.shortcuts.iter().peekable();
+
        let mut shortcuts = self.props.shortcuts.iter().peekable();
        let mut row = vec![];

        while let Some(shortcut) = shortcuts.next() {
-
            let short = Text::from(shortcut.short.clone()).style(style::gray());
-
            let long = Text::from(shortcut.long.clone()).style(style::gray().dim());
+
            let short = Text::from(shortcut.0.clone()).style(style::gray());
+
            let long = Text::from(shortcut.1.clone()).style(style::gray().dim());
            let spacer = Text::from(String::new());
-
            let divider = Text::from(format!(" {} ", props.divider)).style(style::gray().dim());
+
            let divider =
+
                Text::from(format!(" {} ", self.props.divider)).style(style::gray().dim());

-
            row.push((shortcut.short.chars().count(), short));
+
            row.push((shortcut.0.chars().count(), short));
            row.push((1, spacer));
-
            row.push((shortcut.long.chars().count(), long));
+
            row.push((shortcut.1.chars().count(), long));

            if shortcuts.peek().is_some() {
                row.push((3, divider));