Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
lib: Add support for shortcut groups
Merged did:key:z6MkgFq6...nBGz opened 4 months ago
7 files changed +85 -30 27a96ab6 a0e9939a
modified bin/commands/inbox/list.rs
@@ -317,7 +317,7 @@ impl Show<Message> for App {
                        self.show_help_text(frame, ui);
                        self.show_help_context(frame, ui);

-
                        ui.shortcuts(frame, &[("?", "close")], '∙');
+
                        ui.shortcuts(frame, &[("?", "close")], '∙', Alignment::Left);
                    });

                    if ui.has_input(|key| key == Key::Char('?')) {
@@ -564,6 +564,7 @@ impl App {
                ("?", "help"),
            ],
            '∙',
+
            Alignment::Left,
        );
    }

modified bin/commands/patch/list.rs
@@ -8,7 +8,7 @@ use radicle::patch::PatchId;
use radicle::storage::git::Repository;
use radicle::Profile;

-
use ratatui::layout::{Constraint, Layout, Position};
+
use ratatui::layout::{Alignment, Constraint, Layout, Position};
use ratatui::style::Stylize;
use ratatui::text::Span;
use ratatui::{Frame, Viewport};
@@ -289,7 +289,7 @@ impl Show<Message> for App {
                        self.show_help_text(frame, ui);
                        self.show_help_context(frame, ui);

-
                        ui.shortcuts(frame, &[("?", "close")], '∙');
+
                        ui.shortcuts(frame, &[("?", "close")], '∙', Alignment::Left);
                    });

                    if ui.has_input(|key| key == Key::Char('?')) {
@@ -570,6 +570,7 @@ impl App {
                ("?", "help"),
            ],
            '∙',
+
            Alignment::Left,
        );
    }

modified bin/commands/patch/review.rs
@@ -8,7 +8,7 @@ use anyhow::Result;

use serde::{Deserialize, Serialize};

-
use ratatui::layout::{Constraint, Layout, Position};
+
use ratatui::layout::{Alignment, Constraint, Layout, Position};
use ratatui::style::{Style, Stylize};
use ratatui::text::Text;
use ratatui::{Frame, Viewport};
@@ -486,6 +486,7 @@ impl App<'_> {
                        ("q", "quit"),
                    ],
                    '∙',
+
                    Alignment::Left,
                );

                if ui.has_input(|key| key == Key::Char('?')) {
@@ -502,7 +503,7 @@ impl App<'_> {
                }
            }
            ReviewMode::Show => {
-
                ui.shortcuts(frame, &[("?", "help"), ("q", "quit")], '∙');
+
                ui.shortcuts(frame, &[("?", "help"), ("q", "quit")], '∙', Alignment::Left);

                if ui.has_input(|key| key == Key::Char('?')) {
                    ui.send_message(Message::ShowHelp);
@@ -567,7 +568,12 @@ impl Show<Message> for App<'_> {

                        self.show_context_bar(ui, frame);

-
                        ui.shortcuts(frame, &[("?", "close"), ("q", "quit")], '∙');
+
                        ui.shortcuts(
+
                            frame,
+
                            &[("?", "close"), ("q", "quit")],
+
                            '∙',
+
                            Alignment::Left,
+
                        );
                    });

                    if ui.has_input(|key| key == Key::Char('?')) {
modified examples/selection.rs
@@ -2,7 +2,7 @@ use std::time::{SystemTime, UNIX_EPOCH};

use anyhow::Result;

-
use ratatui::layout::{Constraint, Layout};
+
use ratatui::layout::{Alignment, Constraint, Layout};
use ratatui::style::Stylize;
use ratatui::text::Span;
use ratatui::widgets::Cell;
@@ -117,7 +117,7 @@ impl Show<Message> for App {
                        })
                    }

-
                    ui.shortcuts(frame, &[("q", "quit")], '|');
+
                    ui.shortcuts(frame, &[("q", "quit")], '|', Alignment::Left);

                    if ui.has_input(|key| key == Key::Enter) {
                        ui.send_message(Message::Return);
modified src/ui/im.rs
@@ -12,7 +12,7 @@ use ratatui::text::{Span, Text};
use tokio::sync::broadcast;
use tokio::sync::mpsc::UnboundedReceiver;

-
use ratatui::layout::{Constraint, Flex, Position, Rect};
+
use ratatui::layout::{Alignment, Constraint, Flex, Position, Rect};
use ratatui::{Frame, Viewport};

use crate::event::{Event, Key};
@@ -571,8 +571,9 @@ where
        frame: &mut Frame,
        shortcuts: &[(&str, &str)],
        divider: char,
+
        alignment: Alignment,
    ) -> Response {
-
        widget::Shortcuts::new(shortcuts, divider).ui(self, frame)
+
        widget::Shortcuts::new(shortcuts, divider, alignment).ui(self, frame)
    }

    pub fn column_bar(
modified src/ui/im/widget.rs
@@ -2,7 +2,7 @@ use std::cmp;

use serde::{Deserialize, Serialize};

-
use ratatui::layout::{Direction, Layout, Position, Rect};
+
use ratatui::layout::{Alignment, Direction, Layout, Position, Rect};
use ratatui::style::{Style, Stylize};
use ratatui::text::{Line, Span, Text};
use ratatui::widgets::{Block, BorderType, Row, Scrollbar, ScrollbarState};
@@ -1043,16 +1043,18 @@ impl Widget for TextEdit<'_> {
pub struct Shortcuts {
    pub shortcuts: Vec<(String, String)>,
    pub divider: char,
+
    pub alignment: Alignment,
}

impl Shortcuts {
-
    pub fn new(shortcuts: &[(&str, &str)], divider: char) -> Self {
+
    pub fn new(shortcuts: &[(&str, &str)], divider: char, alignment: Alignment) -> Self {
        Self {
            shortcuts: shortcuts
                .iter()
                .map(|(s, a)| (s.to_string(), a.to_string()))
                .collect(),
            divider,
+
            alignment,
        }
    }
}
@@ -1095,6 +1097,24 @@ impl Widget for Shortcuts {
            .iter()
            .map(|(width, _)| Constraint::Length(*width as u16))
            .collect();
+

+
        let (row, widths) = match self.alignment {
+
            Alignment::Left => ([row.as_slice(), &[Text::from("")]].concat(), widths),
+
            Alignment::Center => (
+
                [&[Text::from("")], row.as_slice(), &[Text::from("")]].concat(),
+
                [
+
                    &[Constraint::Fill(1)],
+
                    widths.as_slice(),
+
                    &[Constraint::Fill(1)],
+
                ]
+
                .concat(),
+
            ),
+
            Alignment::Right => (
+
                [&[Text::from("")], row.as_slice()].concat(),
+
                [&[Constraint::Fill(1)], widths.as_slice()].concat(),
+
            ),
+
        };
+

        let table = Table::new([Row::new(row)], widths).column_spacing(0);

        frame.render_widget(table, area);
modified src/ui/rm/widget/window.rs
@@ -203,6 +203,7 @@ where
#[derive(Clone)]
pub struct ShortcutsProps {
    pub shortcuts: Vec<(String, String)>,
+
    pub global_shortcuts: Vec<(String, String)>,
    pub divider: char,
    pub shortcuts_keys_style: Style,
    pub shortcuts_action_style: Style,
@@ -215,12 +216,18 @@ impl ShortcutsProps {
    }

    pub fn shortcuts(mut self, shortcuts: &[(&str, &str)]) -> Self {
-
        self.shortcuts.clear();
-
        self.shortcuts.extend(
-
            shortcuts
-
                .iter()
-
                .map(|(s, l)| (s.to_string(), l.to_string())),
-
        );
+
        self.shortcuts = shortcuts
+
            .iter()
+
            .map(|(s, l)| (s.to_string(), l.to_string()))
+
            .collect();
+
        self
+
    }
+

+
    pub fn global_shortcuts(mut self, shortcuts: &[(&str, &str)]) -> Self {
+
        self.global_shortcuts = shortcuts
+
            .iter()
+
            .map(|(s, l)| (s.to_string(), l.to_string()))
+
            .collect();
        self
    }

@@ -241,6 +248,7 @@ impl Default for ShortcutsProps {

        Self {
            shortcuts: vec![],
+
            global_shortcuts: vec![],
            divider: '∙',
            shortcuts_keys_style: theme.shortcuts_keys_style,
            shortcuts_action_style: theme.shortcuts_action_style,
@@ -273,21 +281,43 @@ impl<S, M> View for Shortcuts<S, M> {
            .and_then(|props| props.inner_ref::<ShortcutsProps>())
            .unwrap_or(&default);

+
        let spacer = Text::from(String::new());
+
        let divider = Text::from(format!(" {} ", props.divider)).style(style::gray().dim());
+

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

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

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

+
        while let Some(shortcut) = shortcuts.next() {
+
            let (long, short) = action_texts(shortcut);
+

+
            row.push((Constraint::Length(shortcut.0.chars().count() as u16), short));
+
            row.push((Constraint::Length(1), spacer.clone()));
+
            row.push((Constraint::Length(shortcut.1.chars().count() as u16), long));

            if shortcuts.peek().is_some() {
-
                row.push((3, divider));
+
                row.push((Constraint::Length(3), divider.clone()));
+
            }
+
        }
+

+
        row.push((Constraint::Fill(1), Text::from(String::new())));
+

+
        let mut global_shortcuts = props.global_shortcuts.iter().peekable();
+
        while let Some(shortcut) = global_shortcuts.next() {
+
            let (long, short) = action_texts(shortcut);
+

+
            row.push((Constraint::Length(shortcut.0.chars().count() as u16), short));
+
            row.push((Constraint::Length(1), spacer.clone()));
+
            row.push((Constraint::Length(shortcut.1.chars().count() as u16), long));
+

+
            if global_shortcuts.peek().is_some() {
+
                row.push((Constraint::Length(3), divider.clone()));
            }
        }

@@ -297,11 +327,7 @@ impl<S, M> View for Shortcuts<S, M> {
            .iter()
            .map(|(_, text)| text.clone())
            .collect();
-
        let widths: Vec<Constraint> = row_copy
-
            .clone()
-
            .iter()
-
            .map(|(width, _)| Constraint::Length(*width as u16))
-
            .collect();
+
        let widths: Vec<Constraint> = row_copy.clone().iter().map(|(width, _)| *width).collect();

        let table = Table::new([Row::new(row)], widths).column_spacing(0);
        frame.render_widget(table, render.area);