Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
lib: Pass render props by reference
Merged did:key:z6MkgFq6...nBGz opened 2 years ago

Performance improvement: pass render props by reference and don’t clone internal ones, if render props are none.

7 files changed +58 -106 222da206 3aa7e70f
modified bin/commands/inbox/select/ui.rs
@@ -256,10 +256,10 @@ impl<'a: 'static> Widget for BrowsePage<'a> {
        self.shortcuts.update(state);
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(BrowsePageProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<BrowsePageProps>())
+
            .unwrap_or(&self.props);

        let page_size = area.height.saturating_sub(6) as usize;

@@ -273,33 +273,21 @@ impl<'a: 'static> Widget for BrowsePage<'a> {
            self.notifications.render(
                frame,
                table_area,
-
                Some(
-
                    ContainerProps::default()
-
                        .hide_footer(props.show_search)
-
                        .to_boxed(),
-
                ),
+
                Some(&ContainerProps::default().hide_footer(props.show_search)),
            );
            self.search.render(frame, search_area, None);
        } else {
            self.notifications.render(
                frame,
                content_area,
-
                Some(
-
                    ContainerProps::default()
-
                        .hide_footer(props.show_search)
-
                        .to_boxed(),
-
                ),
+
                Some(&ContainerProps::default().hide_footer(props.show_search)),
            );
        }

        self.shortcuts.render(
            frame,
            shortcuts_area,
-
            Some(
-
                ShortcutsProps::default()
-
                    .shortcuts(&props.shortcuts)
-
                    .to_boxed(),
-
            ),
+
            Some(&ShortcutsProps::default().shortcuts(&props.shortcuts)),
        );

        if page_size != props.page_size {
@@ -380,7 +368,7 @@ impl Widget for Search {
        self.input.update(state);
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<&dyn Any>) {
        let layout = Layout::horizontal(Constraint::from_mins([0]))
            .horizontal_margin(1)
            .split(area);
@@ -523,10 +511,10 @@ impl<'a: 'static> Widget for HelpPage<'a> {
        self.content.update(state);
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(HelpPageProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<HelpPageProps>())
+
            .unwrap_or(&self.props);

        let page_size = area.height.saturating_sub(6) as usize;

@@ -537,11 +525,7 @@ impl<'a: 'static> Widget for HelpPage<'a> {
        self.shortcuts.render(
            frame,
            shortcuts_area,
-
            Some(
-
                ShortcutsProps::default()
-
                    .shortcuts(&props.shortcuts)
-
                    .to_boxed(),
-
            ),
+
            Some(&ShortcutsProps::default().shortcuts(&props.shortcuts)),
        );

        if page_size != props.page_size {
modified bin/commands/issue/select/ui.rs
@@ -263,10 +263,10 @@ impl<'a: 'static> Widget for BrowsePage<'a> {
        self.shortcuts.update(state);
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(BrowsePageProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<BrowsePageProps>())
+
            .unwrap_or(&self.props);

        let page_size = area.height.saturating_sub(6) as usize;

@@ -280,33 +280,21 @@ impl<'a: 'static> Widget for BrowsePage<'a> {
            self.issues.render(
                frame,
                table_area,
-
                Some(
-
                    ContainerProps::default()
-
                        .hide_footer(props.show_search)
-
                        .to_boxed(),
-
                ),
+
                Some(&ContainerProps::default().hide_footer(props.show_search)),
            );
            self.search.render(frame, search_area, None);
        } else {
            self.issues.render(
                frame,
                content_area,
-
                Some(
-
                    ContainerProps::default()
-
                        .hide_footer(props.show_search)
-
                        .to_boxed(),
-
                ),
+
                Some(&ContainerProps::default().hide_footer(props.show_search)),
            );
        }

        self.shortcuts.render(
            frame,
            shortcuts_area,
-
            Some(
-
                ShortcutsProps::default()
-
                    .shortcuts(&props.shortcuts)
-
                    .to_boxed(),
-
            ),
+
            Some(&ShortcutsProps::default().shortcuts(&props.shortcuts)),
        );

        if page_size != props.page_size {
@@ -387,7 +375,7 @@ impl Widget for Search {
        self.input.update(state);
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<&dyn Any>) {
        let layout = Layout::horizontal(Constraint::from_mins([0]))
            .horizontal_margin(1)
            .split(area);
@@ -530,10 +518,10 @@ impl<'a: 'static> Widget for HelpPage<'a> {
        self.content.update(state);
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(HelpPageProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<HelpPageProps>())
+
            .unwrap_or(&self.props);

        let page_size = area.height.saturating_sub(6) as usize;

@@ -544,11 +532,7 @@ impl<'a: 'static> Widget for HelpPage<'a> {
        self.shortcuts.render(
            frame,
            shortcuts_area,
-
            Some(
-
                ShortcutsProps::default()
-
                    .shortcuts(&props.shortcuts)
-
                    .to_boxed(),
-
            ),
+
            Some(&ShortcutsProps::default().shortcuts(&props.shortcuts)),
        );

        if page_size != props.page_size {
modified bin/commands/patch/select/ui.rs
@@ -279,10 +279,10 @@ impl<'a: 'static> Widget for BrowsePage<'a> {
        self.shortcuts.update(state);
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(BrowsePageProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<BrowsePageProps>())
+
            .unwrap_or(&self.props);

        let page_size = area.height.saturating_sub(6) as usize;

@@ -296,33 +296,21 @@ impl<'a: 'static> Widget for BrowsePage<'a> {
            self.patches.render(
                frame,
                table_area,
-
                Some(
-
                    ContainerProps::default()
-
                        .hide_footer(props.show_search)
-
                        .to_boxed(),
-
                ),
+
                Some(&ContainerProps::default().hide_footer(props.show_search)),
            );
            self.search.render(frame, search_area, None);
        } else {
            self.patches.render(
                frame,
                content_area,
-
                Some(
-
                    ContainerProps::default()
-
                        .hide_footer(props.show_search)
-
                        .to_boxed(),
-
                ),
+
                Some(&ContainerProps::default().hide_footer(props.show_search)),
            );
        }

        self.shortcuts.render(
            frame,
            shortcuts_area,
-
            Some(
-
                ShortcutsProps::default()
-
                    .shortcuts(&props.shortcuts)
-
                    .to_boxed(),
-
            ),
+
            Some(&ShortcutsProps::default().shortcuts(&props.shortcuts)),
        );

        if page_size != props.page_size {
@@ -403,7 +391,7 @@ impl Widget for Search {
        self.input.update(state);
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, _props: Option<&dyn Any>) {
        let layout = Layout::horizontal(Constraint::from_mins([0]))
            .horizontal_margin(1)
            .split(area);
@@ -546,10 +534,10 @@ impl<'a: 'static> Widget for HelpPage<'a> {
        self.content.update(state);
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(HelpPageProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<HelpPageProps>())
+
            .unwrap_or(&self.props);

        let page_size = area.height.saturating_sub(6) as usize;

@@ -560,11 +548,7 @@ impl<'a: 'static> Widget for HelpPage<'a> {
        self.shortcuts.render(
            frame,
            shortcuts_area,
-
            Some(
-
                ShortcutsProps::default()
-
                    .shortcuts(&props.shortcuts)
-
                    .to_boxed(),
-
            ),
+
            Some(&ShortcutsProps::default().shortcuts(&props.shortcuts)),
        );

        if page_size != props.page_size {
modified src/ui/widget.rs
@@ -67,7 +67,7 @@ pub trait Widget {
    /// Renders a widget to the given frame in the given area.
    ///
    /// Optional props take precedence over the internal ones.
-
    fn render(&self, frame: &mut Frame, area: Rect, props: Option<Box<dyn Any>>);
+
    fn render(&self, frame: &mut Frame, area: Rect, props: Option<&dyn Any>);

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

-
    fn render(&self, frame: &mut ratatui::Frame, _area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, _area: Rect, props: Option<&dyn Any>) {
        let _props = props
-
            .and_then(WindowProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<WindowProps<Id>>())
+
            .unwrap_or(&self.props);

        let area = frame.size();

@@ -335,12 +335,12 @@ 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<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        use ratatui::widgets::Table;

        let props = props
-
            .and_then(ShortcutsProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<ShortcutsProps>())
+
            .unwrap_or(&self.props);

        let mut shortcuts = props.shortcuts.iter().peekable();
        let mut row = vec![];
@@ -606,10 +606,10 @@ where
        }
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(TableProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<TableProps<R, W>>())
+
            .unwrap_or(&self.props);

        let widths: Vec<Constraint> = self
            .props
modified src/ui/widget/container.rs
@@ -102,10 +102,10 @@ 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<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(HeaderProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<HeaderProps>())
+
            .unwrap_or(&self.props);

        let widths: Vec<Constraint> = props
            .columns
@@ -275,10 +275,10 @@ 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<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(FooterProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<FooterProps>())
+
            .unwrap_or(&self.props);

        let widths = props
            .columns
@@ -411,10 +411,10 @@ impl<S, A> Widget for Container<S, A> {
        }
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(ContainerProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<ContainerProps>())
+
            .unwrap_or(&self.props);

        let header_h = if self.header.is_some() { 3 } else { 0 };
        let footer_h = if self.footer.is_some() && !props.hide_footer {
modified src/ui/widget/input.rs
@@ -191,10 +191,10 @@ impl<S, A> Widget for TextField<S, A> {
        }
    }

-
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(TextFieldProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<TextFieldProps>())
+
            .unwrap_or(&self.props);

        let layout = Layout::vertical(Constraint::from_lengths([1, 1])).split(area);

modified src/ui/widget/text.rs
@@ -205,10 +205,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<Box<dyn Any>>) {
+
    fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
        let props = props
-
            .and_then(ParagraphProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
+
            .and_then(|props| props.downcast_ref::<ParagraphProps>())
+
            .unwrap_or(&self.props);

        let [content_area] = Layout::horizontal([Constraint::Min(1)])
            .horizontal_margin(1)