Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Move render area to render props
Erik Kundt committed 2 years ago
commit 314883e8c23ae10b36083278d4cce1c4cac2b36e
parent c5a9d55da8e5575e0d8fe1515ec174542841d750
5 files changed +42 -51
modified src/ui.rs
@@ -12,6 +12,8 @@ use std::time::Duration;
use tokio::sync::broadcast;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};

+
use crate::ui::widget::RenderProps;
+

use super::event::Event;
use super::store::State;
use super::task::Interrupted;
@@ -106,7 +108,7 @@ impl<A> Frontend<A> {
                    break Ok(interrupted);
                }
            }
-
            terminal.draw(|frame| root.render(frame, frame.size(), None))?;
+
            terminal.draw(|frame| root.render(frame, RenderProps::from(frame.size())))?;
        };

        terminal::restore(&mut terminal)?;
modified src/ui/widget.rs
@@ -37,31 +37,23 @@ pub struct BaseView<S, A> {
/// They can be passed to a widgets' `render` function.
#[derive(Clone, Default)]
pub struct RenderProps {
+
    /// Area of the render props
+
    pub area: Rect,
    /// Focus of the render props.
    pub focus: bool,
}

impl RenderProps {
-
    /// Creates render props with focus.
-
    pub fn focused() -> Self {
-
        Self { focus: true }
-
    }
-

-
    /// Creates render props with no focus.
-
    pub fn blurred() -> Self {
-
        Self { focus: false }
-
    }
-

    /// Sets the focus of these render props.
-
    pub fn focus(mut self) -> Self {
-
        self.focus = true;
+
    pub fn focus(mut self, focus: bool) -> Self {
+
        self.focus = focus;
        self
    }
+
}

-
    /// Removes the focus from these render props.
-
    pub fn blur(mut self) -> Self {
-
        self.focus = false;
-
        self
+
impl From<Rect> for RenderProps {
+
    fn from(area: Rect) -> Self {
+
        Self { area, focus: false }
    }
}

@@ -99,7 +91,7 @@ pub trait Widget {
    /// Renders a widget to the given frame in the given area.
    ///
    /// Optional render props can be given.
-
    fn render(&self, frame: &mut Frame, area: Rect, props: Option<RenderProps>);
+
    fn render(&self, frame: &mut Frame, props: RenderProps);

    /// Return a mutable reference to this widgets' base view.
    fn base_mut(&mut self) -> &mut BaseView<Self::State, Self::Action>;
@@ -266,7 +258,7 @@ where
        }
    }

-
    fn render(&self, frame: &mut ratatui::Frame, _area: Rect, _props: Option<RenderProps>) {
+
    fn render(&self, frame: &mut ratatui::Frame, _props: RenderProps) {
        let area = frame.size();

        let page = self
@@ -276,7 +268,7 @@ where
            .and_then(|id| self.pages.get(id));

        if let Some(page) = page {
-
            page.render(frame, area, Some(RenderProps { focus: true }));
+
            page.render(frame, RenderProps::from(area).focus(true));
        }
    }

@@ -363,7 +355,7 @@ impl<S, A> Widget for Shortcuts<S, A> {
            ShortcutsProps::from_callback(self.base.on_update, state).unwrap_or(self.props.clone());
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<RenderProps>) {
+
    fn render(&self, frame: &mut ratatui::Frame, props: RenderProps) {
        use ratatui::widgets::Table;

        let mut shortcuts = self.props.shortcuts.iter().peekable();
@@ -398,7 +390,7 @@ impl<S, A> Widget for Shortcuts<S, A> {
            .collect();

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

    fn base_mut(&mut self) -> &mut BaseView<S, A> {
@@ -629,7 +621,7 @@ where
        }
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<RenderProps>) {
+
    fn render(&self, frame: &mut ratatui::Frame, props: RenderProps) {
        let widths: Vec<Constraint> = self
            .props
            .columns
@@ -637,7 +629,7 @@ where
            .filter_map(|c| if !c.skip { Some(c.width) } else { None })
            .collect();

-
        let widths = if area.width < self.props.cutoff as u16 {
+
        let widths = if props.area.width < self.props.cutoff as u16 {
            widths
                .iter()
                .take(self.props.cutoff_after)
@@ -674,9 +666,9 @@ where
                .column_spacing(1)
                .highlight_style(style::highlight());

-
            frame.render_stateful_widget(rows, area, &mut self.state.clone());
+
            frame.render_stateful_widget(rows, props.area, &mut self.state.clone());
        } else {
-
            let center = layout::centered_rect(area, 50, 10);
+
            let center = layout::centered_rect(props.area, 50, 10);
            let hint = Text::from(span::default("Nothing to show"))
                .centered()
                .light_magenta()
modified src/ui/widget/container.rs
@@ -89,7 +89,7 @@ impl<'a: 'static, S, A> Widget for Header<'a, S, A> {
            .unwrap_or(self.props.clone());
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<RenderProps>) {
+
    fn render(&self, frame: &mut ratatui::Frame, props: RenderProps) {
        let widths: Vec<Constraint> = self
            .props
            .columns
@@ -115,7 +115,7 @@ impl<'a: 'static, S, A> Widget for Header<'a, S, A> {
            })
            .collect::<Vec<_>>();

-
        let widths = if area.width < self.props.cutoff as u16 {
+
        let widths = if props.area.width < self.props.cutoff as u16 {
            widths
                .iter()
                .take(self.props.cutoff_after)
@@ -124,12 +124,10 @@ impl<'a: 'static, S, A> Widget for Header<'a, S, A> {
            widths.iter().collect::<Vec<_>>()
        };

-
        let focus = props.map(|props| props.focus).unwrap_or(false);
-

        // Render header
        let block = HeaderBlock::default()
            .borders(Borders::ALL)
-
            .border_style(style::border(focus))
+
            .border_style(style::border(props.focus))
            .border_type(BorderType::Rounded);

        let header_layout = Layout::default()
@@ -137,7 +135,7 @@ impl<'a: 'static, S, A> Widget for Header<'a, S, A> {
            .constraints(vec![Constraint::Min(1)])
            .vertical_margin(1)
            .horizontal_margin(1)
-
            .split(area);
+
            .split(props.area);

        let header = Row::new(cells).style(style::reset().bold());
        let header = ratatui::widgets::Table::default()
@@ -145,7 +143,7 @@ impl<'a: 'static, S, A> Widget for Header<'a, S, A> {
            .header(header)
            .widths(widths.clone());

-
        frame.render_widget(block, area);
+
        frame.render_widget(block, props.area);
        frame.render_widget(header, header_layout[0]);
    }

@@ -253,7 +251,7 @@ impl<'a: 'static, S, A> Widget for Footer<'a, S, A> {
            .unwrap_or(self.props.clone());
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<RenderProps>) {
+
    fn render(&self, frame: &mut ratatui::Frame, props: RenderProps) {
        let widths = self
            .props
            .columns
@@ -264,7 +262,7 @@ impl<'a: 'static, S, A> Widget for Footer<'a, S, A> {
            })
            .collect::<Vec<_>>();

-
        let layout = Layout::horizontal(widths).split(area);
+
        let layout = Layout::horizontal(widths).split(props.area);
        let cells = self
            .props
            .columns
@@ -276,8 +274,6 @@ impl<'a: 'static, S, A> Widget for Footer<'a, S, A> {
        let last = cells.len().saturating_sub(1);
        let len = cells.len();

-
        let focus = props.as_ref().map(|props| props.focus).unwrap_or_default();
-

        for (i, (cell, area)) in cells.into_iter().enumerate() {
            let block_type = match i {
                0 if len == 1 => FooterBlockType::Single,
@@ -285,7 +281,7 @@ impl<'a: 'static, S, A> Widget for Footer<'a, S, A> {
                _ if i == last => FooterBlockType::End,
                _ => FooterBlockType::Repeat,
            };
-
            self.render_cell(frame, *area, block_type, cell.clone(), focus);
+
            self.render_cell(frame, *area, block_type, cell.clone(), props.focus);
        }
    }

@@ -383,7 +379,7 @@ impl<S, A> Widget for Container<S, A> {
        }
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<RenderProps>) {
+
    fn render(&self, frame: &mut ratatui::Frame, props: RenderProps) {
        let header_h = if self.header.is_some() { 3 } else { 0 };
        let footer_h = if self.footer.is_some() && !self.props.hide_footer {
            3
@@ -396,7 +392,7 @@ impl<S, A> Widget for Container<S, A> {
            Constraint::Min(1),
            Constraint::Length(footer_h),
        ])
-
        .areas(area);
+
        .areas(props.area);

        let borders = match (
            self.header.is_some(),
@@ -408,24 +404,25 @@ impl<S, A> Widget for Container<S, A> {
            (true, true) => Borders::LEFT | Borders::RIGHT,
        };

-
        let focus = props.as_ref().map(|props| props.focus).unwrap_or_default();
-

        let block = Block::default()
-
            .border_style(style::border(focus))
+
            .border_style(style::border(props.focus))
            .border_type(BorderType::Rounded)
            .borders(borders);
        frame.render_widget(block.clone(), content_area);

        if let Some(header) = &self.header {
-
            header.render(frame, header_area, props.clone());
+
            header.render(frame, RenderProps::from(header_area).focus(props.focus));
        }

        if let Some(content) = &self.content {
-
            content.render(frame, block.inner(content_area), props.clone());
+
            content.render(
+
                frame,
+
                RenderProps::from(block.inner(content_area)).focus(props.focus),
+
            );
        }

        if let Some(footer) = &self.footer {
-
            footer.render(frame, footer_area, props);
+
            footer.render(frame, RenderProps::from(footer_area).focus(props.focus));
        }
    }

modified src/ui/widget/input.rs
@@ -3,7 +3,6 @@ use termion::event::Key;
use tokio::sync::mpsc::UnboundedSender;

use ratatui::layout::{Constraint, Layout};
-
use ratatui::prelude::Rect;
use ratatui::style::Stylize;
use ratatui::text::{Line, Span};

@@ -189,7 +188,8 @@ impl<S, A> Widget for TextField<S, A> {
        }
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<RenderProps>) {
+
    fn render(&self, frame: &mut ratatui::Frame, props: RenderProps) {
+
        let area = props.area;
        let layout = Layout::vertical(Constraint::from_lengths([1, 1])).split(area);

        let text = self.state.text.clone().unwrap_or_default();
modified src/ui/widget/text.rs
@@ -2,7 +2,7 @@ use tokio::sync::mpsc::UnboundedSender;

use termion::event::Key;

-
use ratatui::layout::{Constraint, Layout, Rect};
+
use ratatui::layout::{Constraint, Layout};
use ratatui::text::Text;

use super::{BaseView, Properties, RenderProps, Widget, WidgetState};
@@ -196,10 +196,10 @@ impl<'a: 'static, S, A> Widget for Paragraph<'a, S, A> {
            ParagraphProps::from_callback(self.base.on_update, state).unwrap_or(self.props.clone());
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<RenderProps>) {
+
    fn render(&self, frame: &mut ratatui::Frame, props: RenderProps) {
        let [content_area] = Layout::horizontal([Constraint::Min(1)])
            .horizontal_margin(1)
-
            .areas(area);
+
            .areas(props.area);
        let content = ratatui::widgets::Paragraph::new(self.props.content.clone())
            .scroll((self.state.offset as u16, 0));