Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Replace page size by render height
Erik Kundt committed 1 year ago
commit 294e93e72392acd08c17abf81854271f05f8bfd4
parent c2b3a874fdd378233034b4938d3531c1ea7d0502
5 files changed +55 -48
modified src/ui/widget.rs
@@ -63,6 +63,7 @@ impl From<&'static dyn Any> for ViewProps {
pub enum ViewState {
    USize(usize),
    String(String),
+
    Table { selected: usize, scroll: usize },
}

impl ViewState {
@@ -79,6 +80,13 @@ impl ViewState {
            _ => None,
        }
    }
+

+
    pub fn unwrap_table(&self) -> Option<(usize, usize)> {
+
        match self {
+
            ViewState::Table { selected, scroll } => Some((*selected, *scroll)),
+
            _ => None,
+
        }
+
    }
}

#[derive(Clone, Default)]
modified src/ui/widget/list.rs
@@ -14,7 +14,7 @@ use crate::ui::theme::style;
use crate::ui::{layout, span};

use super::{container::Column, RenderProps, View};
-
use super::{ViewProps, ViewState};
+
use super::{utils, ViewProps, ViewState};

/// Needs to be implemented for items that are supposed to be rendered in tables.
pub trait ToRow<const W: usize> {
@@ -30,7 +30,6 @@ where
    pub selected: Option<usize>,
    pub columns: Vec<Column<'a>>,
    pub has_footer: bool,
-
    pub page_size: usize,
}

impl<'a, R, const W: usize> Default for TableProps<'a, R, W>
@@ -42,7 +41,6 @@ where
            items: vec![],
            columns: vec![],
            has_footer: false,
-
            page_size: 1,
            selected: Some(0),
        }
    }
@@ -71,11 +69,6 @@ where
        self.has_footer = has_footer;
        self
    }
-

-
    pub fn page_size(mut self, page_size: usize) -> Self {
-
        self.page_size = page_size;
-
        self
-
    }
}

pub struct Table<S, M, R, const W: usize>
@@ -83,9 +76,11 @@ where
    R: ToRow<W>,
{
    /// Internal selection and offset state
-
    state: TableState,
+
    state: (TableState, usize),
    /// Phantom
    phantom: PhantomData<(S, M, R)>,
+
    /// Current render height
+
    height: u16,
}

impl<S, M, R, const W: usize> Default for Table<S, M, R, W>
@@ -94,8 +89,9 @@ where
{
    fn default() -> Self {
        Self {
-
            state: TableState::default().with_selected(Some(0)),
+
            state: (TableState::default().with_selected(Some(0)), 0),
            phantom: PhantomData,
+
            height: 1,
        }
    }
}
@@ -107,51 +103,53 @@ where
    fn prev(&mut self) -> Option<usize> {
        let selected = self
            .state
+
            .0
            .selected()
            .map(|current| current.saturating_sub(1));
-
        self.state.select(selected);
+
        self.state.0.select(selected);
        selected
    }

    fn next(&mut self, len: usize) -> Option<usize> {
-
        let selected = self.state.selected().map(|current| {
+
        let selected = self.state.0.selected().map(|current| {
            if current < len.saturating_sub(1) {
                current.saturating_add(1)
            } else {
                current
            }
        });
-
        self.state.select(selected);
+
        self.state.0.select(selected);
        selected
    }

    fn prev_page(&mut self, page_size: usize) -> Option<usize> {
        let selected = self
            .state
+
            .0
            .selected()
            .map(|current| current.saturating_sub(page_size));
-
        self.state.select(selected);
+
        self.state.0.select(selected);
        selected
    }

    fn next_page(&mut self, len: usize, page_size: usize) -> Option<usize> {
-
        let selected = self.state.selected().map(|current| {
+
        let selected = self.state.0.selected().map(|current| {
            if current < len.saturating_sub(1) {
                cmp::min(current.saturating_add(page_size), len.saturating_sub(1))
            } else {
                current
            }
        });
-
        self.state.select(selected);
+
        self.state.0.select(selected);
        selected
    }

    fn begin(&mut self) {
-
        self.state.select(Some(0));
+
        self.state.0.select(Some(0));
    }

    fn end(&mut self, len: usize) {
-
        self.state.select(Some(len.saturating_sub(1)));
+
        self.state.0.select(Some(len.saturating_sub(1)));
    }
}

@@ -170,6 +168,8 @@ where
            .and_then(|props| props.inner_ref::<TableProps<R, W>>())
            .unwrap_or(&default);

+
        let page_size = self.height;
+

        match key {
            Key::Up | Key::Char('k') => {
                self.prev();
@@ -178,10 +178,10 @@ where
                self.next(props.items.len());
            }
            Key::PageUp => {
-
                self.prev_page(props.page_size);
+
                self.prev_page(page_size as usize);
            }
            Key::PageDown => {
-
                self.next_page(props.items.len(), props.page_size);
+
                self.next_page(props.items.len(), page_size as usize);
            }
            Key::Home => {
                self.begin();
@@ -201,9 +201,10 @@ where
            .and_then(|props| props.inner_ref::<TableProps<R, W>>())
            .unwrap_or(&default);

-
        if props.selected != self.state.selected() {
-
            self.state.select(props.selected);
+
        if props.selected != self.state.0.selected() {
+
            self.state.0.select(props.selected);
        }
+
        self.state.1 = props.items.len();
    }

    fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
@@ -251,7 +252,7 @@ where
                .column_spacing(1)
                .highlight_style(style::highlight(render.focus));

-
            frame.render_stateful_widget(rows, render.area, &mut self.state);
+
            frame.render_stateful_widget(rows, render.area, &mut self.state.0);
        } else {
            let center = layout::centered_rect(render.area, 50, 10);
            let hint = Text::from(span::default("Nothing to show"))
@@ -261,9 +262,20 @@ where

            frame.render_widget(hint, center);
        }
+

+
        self.height = render.area.height;
    }

    fn view_state(&self) -> Option<ViewState> {
-
        self.state.selected().map(ViewState::USize)
+
        let selected = self.state.0.selected().unwrap_or_default();
+

+
        Some(ViewState::Table {
+
            selected,
+
            scroll: utils::scroll::percent_absolute(
+
                selected.saturating_sub(self.height.into()),
+
                self.state.1,
+
                self.height.into(),
+
            ),
+
        })
    }
}
modified src/ui/widget/text.rs
@@ -14,17 +14,11 @@ pub struct TextAreaProps<'a> {
    pub content: Text<'a>,
    pub has_header: bool,
    pub has_footer: bool,
-
    pub page_size: usize,
    pub progress: usize,
    pub can_scroll: bool,
}

impl<'a> TextAreaProps<'a> {
-
    pub fn page_size(mut self, page_size: usize) -> Self {
-
        self.page_size = page_size;
-
        self
-
    }
-

    pub fn text(mut self, text: &Text<'a>) -> Self {
        self.content = text.clone();
        self
@@ -42,7 +36,6 @@ impl<'a> Default for TextAreaProps<'a> {
            content: Text::raw(""),
            has_header: false,
            has_footer: false,
-
            page_size: 1,
            progress: 0,
            can_scroll: true,
        }
@@ -62,6 +55,8 @@ pub struct TextArea<S, M> {
    state: TextAreaState,
    /// Phantom
    phantom: PhantomData<(S, M)>,
+
    /// Current render height
+
    height: u16,
}

impl<S, M> Default for TextArea<S, M> {
@@ -72,6 +67,7 @@ impl<S, M> Default for TextArea<S, M> {
                progress: 0,
            },
            phantom: PhantomData,
+
            height: 1,
        }
    }
}
@@ -139,7 +135,7 @@ where
            .unwrap_or(&default);

        let len = props.content.lines.len() + 1;
-
        let page_size = props.page_size;
+
        let page_size = self.height as usize;

        if props.can_scroll {
            match key {
@@ -169,6 +165,8 @@ where
    }

    fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
+
        self.height = render.area.height;
+

        let default = TextAreaProps::default();
        let props = props
            .and_then(|props| props.inner_ref::<TextAreaProps>())
modified src/ui/widget/utils.rs
@@ -15,16 +15,12 @@ pub mod scroll {
    }

    pub fn percent_absolute(offset: usize, len: usize, height: usize) -> usize {
-
        if height >= len {
-
            100
-
        } else {
-
            let y = offset as f64;
-
            let h = height as f64;
-
            let t = len.saturating_sub(1) as f64;
-
            let v = y / (t - h) * 100_f64;
+
        let y = offset as f64;
+
        let h = height as f64;
+
        let t = len.saturating_sub(1) as f64;
+
        let v = y / (t - h) * 100_f64;

-
            std::cmp::max(0, std::cmp::min(100, v as usize))
-
        }
+
        std::cmp::max(0, std::cmp::min(100, v as usize))
    }

    fn map_range(from: (f64, f64), to: (f64, f64), value: f64) -> f64 {
modified src/ui/widget/window.rs
@@ -119,18 +119,11 @@ where

#[derive(Clone, Default)]
pub struct PageProps {
-
    /// Current page size (height of table content etc.).
-
    pub page_size: usize,
    /// If this view's should handle keys
    pub handle_keys: bool,
}

impl PageProps {
-
    pub fn page_size(mut self, page_size: usize) -> Self {
-
        self.page_size = page_size;
-
        self
-
    }
-

    pub fn handle_keys(mut self, handle_keys: bool) -> Self {
        self.handle_keys = handle_keys;
        self