Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
Fix text area and text view issues
Merged did:key:z6MkgFq6...nBGz opened 1 year ago
4 files changed +63 -23 087e7bff 24da1d01
modified CHANGELOG.md
@@ -9,9 +9,10 @@
- Widgets can be mutated in their render function
- Scrollable widgets calculate their state by using a stored render height
- Per-column visibility for tables depending on their render width
-
- Vertically split container
- Predefined layouts for section groups
-
- `TextView`: Scrollable text viewer widget
+
- New widgets:
+
- `SplitContainer`: Vertically split container
+
- `TextView`: Scrollable text view widget
- `TextArea`: Non-editable text area widget

### Changed
modified bin/commands/issue/select.rs
@@ -315,7 +315,6 @@ fn help_page(_state: &State, channel: &Channel<Message>) -> Widget<State, Messag
                        .content(help_text())
                        .cursor(state.help.cursor)
                        .show_scroll_progress(true)
-
                        .show_column_progress(true)
                        .to_boxed_any()
                        .into()
                }),
modified src/ui/widget.rs
@@ -61,6 +61,7 @@ impl From<&'static dyn Any> for ViewProps {

/// A `ViewState` is the representation of a `View`s internal state. e.g. current
/// table selection or contents of a text field.
+
#[derive(Debug)]
pub enum ViewState {
    USize(usize),
    String(String),
modified src/ui/widget/input.rs
@@ -283,7 +283,7 @@ where
}

/// The state of a `TextArea`.
-
#[derive(Clone, Default)]
+
#[derive(Clone, Default, Debug)]
pub struct TextAreaState {
    /// Current vertical scroll position.
    pub scroll: usize,
@@ -445,14 +445,17 @@ impl<'a, S, M> View for TextArea<'a, S, M> {
            .horizontal_margin(1)
            .areas(render.area);

-
        let progress_height = if props.show_scroll_progress || props.show_column_progress {
-
            1
-
        } else {
-
            0
-
        };
-

-
        let [content_area, progress_area] =
-
            Layout::vertical([Constraint::Min(1), Constraint::Length(progress_height)]).areas(area);
+
        let [content_area, progress_area] = Layout::vertical([
+
            Constraint::Min(1),
+
            Constraint::Length(
+
                if props.show_scroll_progress || props.show_column_progress {
+
                    1
+
                } else {
+
                    0
+
                },
+
            ),
+
        ])
+
        .areas(area);

        let cursor_line_style = Style::default();
        let cursor_style = if render.focus {
@@ -526,7 +529,7 @@ impl<'a, S, M> View for TextArea<'a, S, M> {
}

/// State of a `TextView`.
-
#[derive(Clone, Default)]
+
#[derive(Clone, Default, Debug)]
pub struct TextViewState {
    /// Current vertical scroll position.
    pub scroll: usize,
@@ -545,8 +548,6 @@ pub struct TextViewProps<'a> {
    handle_keys: bool,
    /// If this widget should render its scroll progress. Default: `false`.
    show_scroll_progress: bool,
-
    /// If this widget should render its cursor progress. Default: `false`.
-
    show_column_progress: bool,
}

impl<'a> TextViewProps<'a> {
@@ -568,11 +569,6 @@ impl<'a> TextViewProps<'a> {
        self
    }

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

    pub fn handle_keys(mut self, handle_keys: bool) -> Self {
        self.handle_keys = handle_keys;
        self
@@ -586,7 +582,6 @@ impl<'a> Default for TextViewProps<'a> {
            cursor: (0, 0),
            handle_keys: true,
            show_scroll_progress: false,
-
            show_column_progress: false,
        }
    }
}
@@ -718,20 +713,64 @@ where
        None
    }

+
    fn update(&mut self, props: Option<&ViewProps>, _state: &Self::State) {
+
        let default = TextViewProps::default();
+
        let props = props
+
            .and_then(|props| props.inner_ref::<TextViewProps>())
+
            .unwrap_or(&default);
+

+
        if props.cursor != self.state.cursor {
+
            self.state.cursor = props.cursor;
+
        }
+
    }
+

    fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
        let default = TextViewProps::default();
        let props = props
            .and_then(|props| props.inner_ref::<TextViewProps>())
            .unwrap_or(&default);

-
        let [content_area] = Layout::horizontal([Constraint::Min(1)])
+
        let [area] = Layout::default()
+
            .constraints([Constraint::Min(1)])
            .horizontal_margin(1)
            .areas(render.area);
+

+
        let [content_area, progress_area] = Layout::vertical([
+
            Constraint::Min(1),
+
            Constraint::Length(if props.show_scroll_progress { 1 } else { 0 }),
+
        ])
+
        .areas(area);
+

+
        let style = if render.focus {
+
            Style::default()
+
        } else {
+
            Style::default().dim()
+
        };
+

        let content = ratatui::widgets::Paragraph::new(props.content.clone())
-
            .style(props.content.style)
+
            .style(style)
            .scroll((self.state.cursor.0 as u16, self.state.cursor.1 as u16));

+
        let scroll_progress = utils::scroll::percent_absolute(
+
            self.state.cursor.0,
+
            props.content.lines.len(),
+
            content_area.height.into(),
+
        );
+

+
        let progress_info = if props.show_scroll_progress {
+
            vec![Span::styled(
+
                format!("{}%", scroll_progress),
+
                Style::default().dim(),
+
            )]
+
        } else {
+
            vec![]
+
        };
+

        frame.render_widget(content, content_area);
+
        frame.render_widget(
+
            Line::from(progress_info).alignment(Alignment::Right),
+
            progress_area,
+
        );

        self.area = (content_area.height, content_area.width);
    }