Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Move key handling to paragraph widget
Erik Kundt committed 2 years ago
commit f02369894247aab8d648f6659e1cc0c78350db2b
parent 853c39f105b1f316ec1a8a8128839b756c6ab632
4 files changed +86 -110
modified bin/commands/inbox/select/ui.rs
@@ -16,7 +16,7 @@ use tui::ui::items::{Filter, NotificationItem, NotificationItemFilter, Notificat
use tui::ui::span;
use tui::ui::widget::container::{Footer, Header};
use tui::ui::widget::input::TextField;
-
use tui::ui::widget::text::{Paragraph, ParagraphProps};
+
use tui::ui::widget::text::Paragraph;
use tui::ui::widget::{Column, Render, Shortcuts, Table, Widget};
use tui::Selection;

@@ -647,7 +647,7 @@ pub struct Help<'a> {
    /// Container header
    header: Header<'a, Action>,
    /// Content widget
-
    content: Paragraph<Action>,
+
    content: Paragraph<'a, Action>,
    /// Container footer
    footer: Footer<'a, Action>,
}
@@ -681,6 +681,8 @@ impl<'a> Widget<State, Action> for Help<'a> {
            .focus(props.focus);

        let content = self.content.move_with_state(state);
+
        let content = content.text(&props.content).page_size(props.page_size);
+

        let progress = span::default(format!("{}%", content.progress())).dim();

        let footer = self.footer.move_with_state(&());
@@ -704,8 +706,6 @@ impl<'a> Widget<State, Action> for Help<'a> {
    }

    fn handle_key_event(&mut self, key: termion::event::Key) {
-
        let len = self.props.content.lines.len() + 1;
-
        let page_size = self.props.page_size;
        match key {
            Key::Esc => {
                let _ = self.action_tx.send(Action::Exit { selection: None });
@@ -713,25 +713,9 @@ impl<'a> Widget<State, Action> for Help<'a> {
            Key::Char('?') => {
                let _ = self.action_tx.send(Action::CloseHelp);
            }
-
            Key::Up | Key::Char('k') => {
-
                self.content.prev(len, page_size);
-
            }
-
            Key::Down | Key::Char('j') => {
-
                self.content.next(len, page_size);
-
            }
-
            Key::PageUp => {
-
                self.content.prev_page(len, page_size);
-
            }
-
            Key::PageDown => {
-
                self.content.next_page(len, page_size);
-
            }
-
            Key::Home => {
-
                self.content.begin(len, page_size);
-
            }
-
            Key::End => {
-
                self.content.end(len, page_size);
+
            _ => {
+
                <Paragraph<_> as Widget<(), _>>::handle_key_event(&mut self.content, key);
            }
-
            _ => {}
        }
    }
}
@@ -746,16 +730,7 @@ impl<'a> Render<()> for Help<'a> {
        .areas(area);

        self.header.render::<B>(frame, header_area, ());
-
        self.content.render::<B>(
-
            frame,
-
            content_area,
-
            ParagraphProps {
-
                content: self.props.content.clone(),
-
                focus: self.props.focus,
-
                has_footer: true,
-
                has_header: true,
-
            },
-
        );
+
        self.content.render::<B>(frame, content_area, ());
        self.footer.render::<B>(frame, footer_area, ());

        let page_size = content_area.height as usize;
modified bin/commands/issue/select/ui.rs
@@ -18,7 +18,7 @@ use tui::ui::items::{Filter, IssueItem, IssueItemFilter};
use tui::ui::span;
use tui::ui::widget::container::{Footer, Header};
use tui::ui::widget::input::TextField;
-
use tui::ui::widget::text::{Paragraph, ParagraphProps};
+
use tui::ui::widget::text::Paragraph;
use tui::ui::widget::{Column, Render, Shortcuts, Table, Widget};
use tui::Selection;

@@ -667,7 +667,7 @@ pub struct Help<'a> {
    /// Container header
    header: Header<'a, Action>,
    /// Content widget
-
    content: Paragraph<Action>,
+
    content: Paragraph<'a, Action>,
    /// Container footer
    footer: Footer<'a, Action>,
}
@@ -701,6 +701,8 @@ impl<'a> Widget<State, Action> for Help<'a> {
            .focus(props.focus);

        let content = self.content.move_with_state(state);
+
        let content = content.text(&props.content).page_size(props.page_size);
+

        let progress = span::default(format!("{}%", content.progress())).dim();

        let footer = self.footer.move_with_state(&());
@@ -724,8 +726,6 @@ impl<'a> Widget<State, Action> for Help<'a> {
    }

    fn handle_key_event(&mut self, key: termion::event::Key) {
-
        let len = self.props.content.lines.len() + 1;
-
        let page_size = self.props.page_size;
        match key {
            Key::Esc => {
                let _ = self.action_tx.send(Action::Exit { selection: None });
@@ -733,25 +733,9 @@ impl<'a> Widget<State, Action> for Help<'a> {
            Key::Char('?') => {
                let _ = self.action_tx.send(Action::CloseHelp);
            }
-
            Key::Up | Key::Char('k') => {
-
                self.content.prev(len, page_size);
-
            }
-
            Key::Down | Key::Char('j') => {
-
                self.content.next(len, page_size);
-
            }
-
            Key::PageUp => {
-
                self.content.prev_page(len, page_size);
-
            }
-
            Key::PageDown => {
-
                self.content.next_page(len, page_size);
-
            }
-
            Key::Home => {
-
                self.content.begin(len, page_size);
-
            }
-
            Key::End => {
-
                self.content.end(len, page_size);
+
            _ => {
+
                <Paragraph<_> as Widget<(), _>>::handle_key_event(&mut self.content, key);
            }
-
            _ => {}
        }
    }
}
@@ -766,16 +750,7 @@ impl<'a> Render<()> for Help<'a> {
        .areas(area);

        self.header.render::<B>(frame, header_area, ());
-
        self.content.render::<B>(
-
            frame,
-
            content_area,
-
            ParagraphProps {
-
                content: self.props.content.clone(),
-
                focus: self.props.focus,
-
                has_footer: true,
-
                has_header: true,
-
            },
-
        );
+
        self.content.render::<B>(frame, content_area, ());
        self.footer.render::<B>(frame, footer_area, ());

        let page_size = content_area.height as usize;
modified bin/commands/patch/select/ui.rs
@@ -19,7 +19,7 @@ use tui::ui::items::{Filter, PatchItem, PatchItemFilter};
use tui::ui::span;
use tui::ui::widget::container::{Footer, Header};
use tui::ui::widget::input::TextField;
-
use tui::ui::widget::text::{Paragraph, ParagraphProps};
+
use tui::ui::widget::text::Paragraph;
use tui::ui::widget::{Column, Render, Shortcuts, Table, Widget};
use tui::Selection;

@@ -709,7 +709,7 @@ pub struct Help<'a> {
    /// Container header
    header: Header<'a, Action>,
    /// Content widget
-
    content: Paragraph<Action>,
+
    content: Paragraph<'a, Action>,
    /// Container footer
    footer: Footer<'a, Action>,
}
@@ -743,6 +743,8 @@ impl<'a> Widget<State, Action> for Help<'a> {
            .focus(props.focus);

        let content = self.content.move_with_state(state);
+
        let content = content.text(&props.content).page_size(props.page_size);
+

        let progress = span::default(format!("{}%", content.progress())).dim();

        let footer = self.footer.move_with_state(&());
@@ -766,8 +768,6 @@ impl<'a> Widget<State, Action> for Help<'a> {
    }

    fn handle_key_event(&mut self, key: termion::event::Key) {
-
        let len = self.props.content.lines.len() + 1;
-
        let page_size = self.props.page_size;
        match key {
            Key::Esc => {
                let _ = self.action_tx.send(Action::Exit { selection: None });
@@ -775,25 +775,9 @@ impl<'a> Widget<State, Action> for Help<'a> {
            Key::Char('?') => {
                let _ = self.action_tx.send(Action::CloseHelp);
            }
-
            Key::Up | Key::Char('k') => {
-
                self.content.prev(len, page_size);
-
            }
-
            Key::Down | Key::Char('j') => {
-
                self.content.next(len, page_size);
-
            }
-
            Key::PageUp => {
-
                self.content.prev_page(len, page_size);
-
            }
-
            Key::PageDown => {
-
                self.content.next_page(len, page_size);
-
            }
-
            Key::Home => {
-
                self.content.begin(len, page_size);
-
            }
-
            Key::End => {
-
                self.content.end(len, page_size);
+
            _ => {
+
                <Paragraph<_> as Widget<(), _>>::handle_key_event(&mut self.content, key);
            }
-
            _ => {}
        }
    }
}
@@ -808,16 +792,7 @@ impl<'a> Render<()> for Help<'a> {
        .areas(area);

        self.header.render::<B>(frame, header_area, ());
-
        self.content.render::<B>(
-
            frame,
-
            content_area,
-
            ParagraphProps {
-
                content: self.props.content.clone(),
-
                focus: self.props.focus,
-
                has_footer: true,
-
                has_header: true,
-
            },
-
        );
+
        self.content.render::<B>(frame, content_area, ());
        self.footer.render::<B>(frame, footer_area, ());

        let page_size = content_area.height as usize;
modified src/ui/widget/text.rs
@@ -16,29 +16,54 @@ pub struct ParagraphProps<'a> {
    pub focus: bool,
    pub has_header: bool,
    pub has_footer: bool,
+
    pub page_size: usize,
}

-
pub struct Paragraph<A> {
+
impl<'a> Default for ParagraphProps<'a> {
+
    fn default() -> Self {
+
        Self {
+
            content: Text::raw(""),
+
            focus: false,
+
            has_header: false,
+
            has_footer: false,
+
            page_size: 1,
+
        }
+
    }
+
}
+

+
pub struct Paragraph<'a, A> {
    /// Sending actions to the state store
    pub action_tx: UnboundedSender<A>,
    /// Internal offset
    offset: usize,
    /// Internal progress
    progress: usize,
+
    /// Internal properties
+
    props: ParagraphProps<'a>,
}

-
impl<A> Paragraph<A> {
+
impl<'a, A> Paragraph<'a, A> {
    pub fn scroll(&self) -> (u16, u16) {
        (self.offset as u16, 0)
    }

-
    pub fn prev(&mut self, len: usize, page_size: usize) -> (u16, u16) {
+
    pub fn page_size(mut self, page_size: usize) -> Self {
+
        self.props.page_size = page_size;
+
        self
+
    }
+

+
    pub fn text(mut self, text: &Text<'a>) -> Self {
+
        self.props.content = text.clone();
+
        self
+
    }
+

+
    fn prev(&mut self, len: usize, page_size: usize) -> (u16, u16) {
        self.offset = self.offset.saturating_sub(1);
        self.progress = Self::scroll_percent(self.offset, len, page_size);
        self.scroll()
    }

-
    pub fn next(&mut self, len: usize, page_size: usize) -> (u16, u16) {
+
    fn next(&mut self, len: usize, page_size: usize) -> (u16, u16) {
        if self.progress < 100 {
            self.offset = self.offset.saturating_add(1);
            self.progress = Self::scroll_percent(self.offset, len, page_size);
@@ -47,13 +72,13 @@ impl<A> Paragraph<A> {
        self.scroll()
    }

-
    pub fn prev_page(&mut self, len: usize, page_size: usize) -> (u16, u16) {
+
    fn prev_page(&mut self, len: usize, page_size: usize) -> (u16, u16) {
        self.offset = self.offset.saturating_sub(page_size);
        self.progress = Self::scroll_percent(self.offset, len, page_size);
        self.scroll()
    }

-
    pub fn next_page(&mut self, len: usize, page_size: usize) -> (u16, u16) {
+
    fn next_page(&mut self, len: usize, page_size: usize) -> (u16, u16) {
        let end = len.saturating_sub(page_size);

        self.offset = std::cmp::min(self.offset.saturating_add(page_size), end);
@@ -61,13 +86,13 @@ impl<A> Paragraph<A> {
        self.scroll()
    }

-
    pub fn begin(&mut self, len: usize, page_size: usize) -> (u16, u16) {
+
    fn begin(&mut self, len: usize, page_size: usize) -> (u16, u16) {
        self.offset = 0;
        self.progress = Self::scroll_percent(self.offset, len, page_size);
        self.scroll()
    }

-
    pub fn end(&mut self, len: usize, page_size: usize) -> (u16, u16) {
+
    fn end(&mut self, len: usize, page_size: usize) -> (u16, u16) {
        self.offset = len.saturating_sub(page_size);
        self.progress = Self::scroll_percent(self.offset, len, page_size);
        self.scroll()
@@ -91,7 +116,7 @@ impl<A> Paragraph<A> {
    }
}

-
impl<S, A> Widget<S, A> for Paragraph<A> {
+
impl<'a, S, A> Widget<S, A> for Paragraph<'a, A> {
    fn new(state: &S, action_tx: UnboundedSender<A>) -> Self
    where
        Self: Sized,
@@ -100,6 +125,7 @@ impl<S, A> Widget<S, A> for Paragraph<A> {
            action_tx: action_tx.clone(),
            offset: 0,
            progress: 0,
+
            props: ParagraphProps::default(),
        }
        .move_with_state(state)
    }
@@ -111,22 +137,47 @@ impl<S, A> Widget<S, A> for Paragraph<A> {
        Self { ..self }
    }

-
    fn handle_key_event(&mut self, _key: Key) {}
+
    fn handle_key_event(&mut self, key: Key) {
+
        let len = self.props.content.lines.len() + 1;
+
        let page_size = self.props.page_size;
+

+
        match key {
+
            Key::Up | Key::Char('k') => {
+
                self.prev(len, page_size);
+
            }
+
            Key::Down | Key::Char('j') => {
+
                self.next(len, page_size);
+
            }
+
            Key::PageUp => {
+
                self.prev_page(len, page_size);
+
            }
+
            Key::PageDown => {
+
                self.next_page(len, page_size);
+
            }
+
            Key::Home => {
+
                self.begin(len, page_size);
+
            }
+
            Key::End => {
+
                self.end(len, page_size);
+
            }
+
            _ => {}
+
        }
+
    }
}

-
impl<'a, A> Render<ParagraphProps<'a>> for Paragraph<A> {
-
    fn render<B: Backend>(&self, frame: &mut ratatui::Frame, area: Rect, props: ParagraphProps) {
+
impl<'a, A> Render<()> for Paragraph<'a, A> {
+
    fn render<B: Backend>(&self, frame: &mut ratatui::Frame, area: Rect, _props: ()) {
        let block = Block::default()
            .borders(Borders::LEFT | Borders::RIGHT)
            .border_type(BorderType::Rounded)
-
            .border_style(style::border(props.focus));
+
            .border_style(style::border(self.props.focus));
        frame.render_widget(block, area);

        let [content_area] = Layout::horizontal([Constraint::Min(1)])
            .horizontal_margin(2)
            .areas(area);
        let content =
-
            ratatui::widgets::Paragraph::new(props.content.clone()).scroll((self.offset as u16, 0));
+
            ratatui::widgets::Paragraph::new(self.props.content.clone()).scroll((self.offset as u16, 0));

        frame.render_widget(content, content_area);
    }