Skip to main content

git_plumber/tui/widget/
scrollable_text.rs

1use crate::tui::helpers::render_styled_paragraph_with_scrollbar;
2use ratatui::text::Text;
3
4/// A unified widget for displaying scrollable text content.
5/// This eliminates code duplication across LooseObjectWidget, PackIndexWidget, and PackObjectWidget.
6#[derive(Debug, Clone)]
7pub struct ScrollableTextWidget {
8    text_cache: Option<Text<'static>>,
9    scroll_position: usize,
10    max_scroll: usize,
11}
12
13impl ScrollableTextWidget {
14    /// Create a new scrollable text widget
15    pub fn new() -> Self {
16        Self {
17            text_cache: None,
18            scroll_position: 0,
19            max_scroll: 0,
20        }
21    }
22
23    /// Set the text content for the widget
24    pub fn set_text(&mut self, text: Text<'static>) {
25        self.text_cache = Some(text);
26        // Reset scroll position when content changes
27        self.scroll_position = 0;
28        self.max_scroll = 0;
29    }
30
31    /// Get the cached text content, or return a default if not set
32    pub fn text(&self) -> Text<'static> {
33        self.text_cache
34            .as_ref()
35            .cloned()
36            .unwrap_or_else(|| Text::from("Loading..."))
37    }
38
39    /// Scroll up by one line
40    pub fn scroll_up(&mut self) {
41        self.scroll_position = self.scroll_position.saturating_sub(1);
42    }
43
44    /// Scroll down by one line
45    pub fn scroll_down(&mut self) {
46        self.scroll_position = (self.scroll_position + 1).min(self.max_scroll);
47    }
48
49    /// Scroll to the top of the content
50    pub fn scroll_to_top(&mut self) {
51        self.scroll_position = 0;
52    }
53
54    /// Scroll to the bottom of the content
55    pub fn scroll_to_bottom(&mut self) {
56        self.scroll_position = self.max_scroll;
57    }
58
59    /// Get the current scroll position
60    pub fn scroll_position(&self) -> usize {
61        self.scroll_position
62    }
63
64    /// Render the widget with scrollbar
65    pub fn render(
66        &mut self,
67        f: &mut ratatui::Frame,
68        area: ratatui::layout::Rect,
69        title: &str,
70        is_focused: bool,
71    ) {
72        let content = self.text();
73
74        // Update max_scroll based on current content and area
75        let total_lines = content.lines.len();
76        let visible_height = area.height.saturating_sub(2) as usize; // Account for borders
77        self.max_scroll = total_lines.saturating_sub(visible_height);
78
79        render_styled_paragraph_with_scrollbar(
80            f,
81            area,
82            content,
83            self.scroll_position,
84            title,
85            is_focused,
86        );
87    }
88
89    /// Check if the widget has content
90    pub fn has_content(&self) -> bool {
91        self.text_cache.is_some()
92    }
93
94    /// Clear the cached content
95    pub fn clear(&mut self) {
96        self.text_cache = None;
97        self.scroll_position = 0;
98        self.max_scroll = 0;
99    }
100}
101
102impl Default for ScrollableTextWidget {
103    fn default() -> Self {
104        Self::new()
105    }
106}