rat_widget/pager/
single_pager.rs

1use crate::_private::NonExhaustive;
2use crate::caption::{CaptionState, CaptionStyle};
3use crate::event::PagerOutcome;
4use crate::layout::GenericLayout;
5use crate::pager::{PageNavigation, PageNavigationState, Pager, PagerBuffer, PagerStyle};
6use rat_event::{HandleEvent, MouseOnly, Regular};
7use rat_focus::HasFocus;
8use rat_reloc::RelocatableState;
9use ratatui::buffer::Buffer;
10use ratatui::layout::{Alignment, Rect, Size};
11use ratatui::style::Style;
12use ratatui::widgets::StatefulWidget;
13use ratatui::widgets::{Block, Widget};
14use std::borrow::Cow;
15use std::cell::{Ref, RefCell, RefMut};
16use std::hash::Hash;
17use std::rc::Rc;
18
19/// This widget renders a single page of a [GenericLayout].
20#[derive(Debug, Clone)]
21pub struct SinglePager<'a, W>
22where
23    W: Eq + Hash + Clone,
24{
25    layout: Option<GenericLayout<W>>,
26    pager: Pager<W>,
27    page_nav: PageNavigation<'a>,
28    auto_label: bool,
29}
30
31/// Renders directly to the frame buffer.
32///
33/// * It maps your widget area from layout coordinates
34///   to screen coordinates before rendering.
35/// * It helps with cleanup of the widget state if your
36///   widget is currently invisible.
37#[derive(Debug)]
38pub struct SinglePagerBuffer<'a, W>
39where
40    W: Eq + Hash + Clone,
41{
42    pager: PagerBuffer<'a, W>,
43    auto_label: bool,
44}
45
46/// Widget state.
47#[derive(Debug, Clone)]
48pub struct SinglePagerState<W>
49where
50    W: Eq + Hash + Clone,
51{
52    /// Page layout
53    /// __read+write__ might be overwritten from widget.
54    pub layout: Rc<RefCell<GenericLayout<W>>>,
55
56    /// PageNavigationState holds most of our state.
57    /// __read+write__
58    pub nav: PageNavigationState,
59
60    /// Only construct with `..Default::default()`.
61    pub non_exhaustive: NonExhaustive,
62}
63
64impl<W> Default for SinglePager<'_, W>
65where
66    W: Eq + Hash + Clone,
67{
68    fn default() -> Self {
69        Self {
70            layout: Default::default(),
71            pager: Default::default(),
72            page_nav: Default::default(),
73            auto_label: true,
74        }
75    }
76}
77
78impl<'a, W> SinglePager<'a, W>
79where
80    W: Eq + Hash + Clone,
81{
82    /// New SinglePage.
83    pub fn new() -> Self {
84        Self::default()
85    }
86
87    /// Set the layout. If no layout is set here the layout is
88    /// taken from the state.
89    pub fn layout(mut self, layout: GenericLayout<W>) -> Self {
90        self.layout = Some(layout);
91        self
92    }
93
94    /// Base style.
95    pub fn style(mut self, style: Style) -> Self {
96        self.pager = self.pager.style(style);
97        self.page_nav = self.page_nav.style(style);
98        self
99    }
100
101    /// Render the label automatically when rendering the widget.
102    ///
103    /// Default: true
104    pub fn auto_label(mut self, auto: bool) -> Self {
105        self.auto_label = auto;
106        self
107    }
108
109    /// Style for text labels.
110    pub fn label_style(mut self, style: Style) -> Self {
111        self.pager = self.pager.label_style(style);
112        self
113    }
114
115    /// Alignment for text labels.
116    pub fn label_alignment(mut self, alignment: Alignment) -> Self {
117        self.pager = self.pager.label_alignment(alignment);
118        self
119    }
120
121    /// Styles for caption labels.
122    pub fn caption_style(mut self, style: CaptionStyle) -> Self {
123        self.pager = self.pager.caption_style(style);
124        self
125    }
126
127    /// Style for navigation.
128    pub fn nav_style(mut self, nav_style: Style) -> Self {
129        self.page_nav = self.page_nav.nav_style(nav_style);
130        self
131    }
132
133    /// Style for the title.
134    pub fn title_style(mut self, title_style: Style) -> Self {
135        self.page_nav = self.page_nav.title_style(title_style);
136        self
137    }
138
139    /// Block for border
140    pub fn block(mut self, block: Block<'a>) -> Self {
141        self.page_nav = self.page_nav.block(block);
142        self
143    }
144
145    pub fn next_page_mark(mut self, txt: &'a str) -> Self {
146        self.page_nav = self.page_nav.next_page_mark(txt);
147        self
148    }
149
150    pub fn prev_page_mark(mut self, txt: &'a str) -> Self {
151        self.page_nav = self.page_nav.prev_page_mark(txt);
152        self
153    }
154
155    pub fn first_page_mark(mut self, txt: &'a str) -> Self {
156        self.page_nav = self.page_nav.first_page_mark(txt);
157        self
158    }
159
160    pub fn last_page_mark(mut self, txt: &'a str) -> Self {
161        self.page_nav = self.page_nav.last_page_mark(txt);
162        self
163    }
164
165    /// Set all styles.
166    pub fn styles(mut self, styles: PagerStyle) -> Self {
167        self.pager = self.pager.styles(styles.clone());
168        self.page_nav = self.page_nav.styles(styles);
169        self
170    }
171
172    /// Calculate the layout page size.
173    pub fn layout_size(&self, area: Rect) -> Size {
174        self.page_nav.layout_size(area)
175    }
176
177    // Calculate the view area for all columns.
178    pub fn inner(&self, area: Rect) -> Rect {
179        self.page_nav.inner(area)
180    }
181
182    /// Render the page navigation and create the SinglePagerBuffer
183    /// that will do the actual rendering.
184    pub fn into_buffer(
185        self,
186        area: Rect,
187        buf: &'a mut Buffer,
188        state: &'a mut SinglePagerState<W>,
189    ) -> SinglePagerBuffer<'a, W> {
190        // set layout
191        if let Some(layout) = self.layout {
192            state.layout = Rc::new(RefCell::new(layout));
193        }
194
195        state.nav.page_count = state.layout.borrow().page_count();
196        state.nav.set_page(state.nav.page);
197
198        self.page_nav.render(area, buf, &mut state.nav);
199
200        SinglePagerBuffer {
201            pager: self
202                .pager //
203                .layout(state.layout.clone())
204                .page(state.nav.page)
205                .into_buffer(state.nav.widget_areas[0], Rc::new(RefCell::new(buf))),
206            auto_label: self.auto_label,
207        }
208    }
209}
210
211impl<'a, W> SinglePagerBuffer<'a, W>
212where
213    W: Eq + Hash + Clone,
214{
215    /// Is the given area visible?
216    pub fn is_visible(&self, widget: W) -> bool {
217        if let Some(idx) = self.pager.widget_idx(widget) {
218            self.pager.is_visible(idx)
219        } else {
220            false
221        }
222    }
223
224    /// Render all blocks for the current page.
225    pub fn render_block(&mut self) {
226        self.pager.render_block()
227    }
228
229    /// Render a manual label.
230    #[inline(always)]
231    pub fn render_label<FN>(&mut self, widget: W, render_fn: FN) -> bool
232    where
233        FN: FnOnce(&Cow<'static, str>, Rect, &mut Buffer),
234    {
235        let Some(idx) = self.pager.widget_idx(widget) else {
236            return false;
237        };
238        self.pager.render_label(idx, render_fn)
239    }
240
241    /// Render the label as a caption with the set style.
242    #[inline(always)]
243    pub fn render_caption(
244        &mut self,
245        widget: W,
246        link: &impl HasFocus,
247        state: &mut CaptionState,
248    ) -> bool {
249        let Some(idx) = self.pager.widget_idx(widget) else {
250            return false;
251        };
252        self.pager.render_caption(idx, &link.focus(), state)
253    }
254
255    /// Render a stateless widget and its label, if any.
256    #[inline(always)]
257    pub fn render_widget<FN, WW>(&mut self, widget: W, render_fn: FN) -> bool
258    where
259        FN: FnOnce() -> WW,
260        WW: Widget,
261    {
262        let Some(idx) = self.pager.widget_idx(widget) else {
263            return false;
264        };
265        if self.auto_label {
266            self.pager.render_auto_label(idx);
267        }
268        self.pager.render_widget(idx, render_fn)
269    }
270
271    /// Render an optional stateful widget and its label, if any.
272    #[inline(always)]
273    pub fn render_opt<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
274    where
275        FN: FnOnce() -> Option<WW>,
276        WW: StatefulWidget<State = SS>,
277        SS: RelocatableState,
278    {
279        let Some(idx) = self.pager.widget_idx(widget) else {
280            return false;
281        };
282        if self.auto_label {
283            self.pager.render_auto_label(idx);
284        }
285        if !self.pager.render_opt(idx, render_fn, state) {
286            self.hidden(state);
287            false
288        } else {
289            true
290        }
291    }
292
293    /// Render a stateful widget and its label, if any.
294    #[inline(always)]
295    pub fn render<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
296    where
297        FN: FnOnce() -> WW,
298        WW: StatefulWidget<State = SS>,
299        SS: RelocatableState,
300    {
301        let Some(idx) = self.pager.widget_idx(widget) else {
302            return false;
303        };
304        if self.auto_label {
305            self.pager.render_auto_label(idx);
306        }
307        if self.pager.render(idx, render_fn, state) {
308            true
309        } else {
310            self.hidden(state);
311            false
312        }
313    }
314
315    /// Render a stateful widget and its label, if any.
316    /// The closure can return a second value, which will be forwarded
317    /// if the widget is visible.
318    #[inline(always)]
319    pub fn render2<FN, WW, SS, R>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> Option<R>
320    where
321        FN: FnOnce() -> (WW, R),
322        WW: StatefulWidget<State = SS>,
323        SS: RelocatableState,
324    {
325        let Some(idx) = self.pager.widget_idx(widget) else {
326            return None;
327        };
328        if self.auto_label {
329            self.pager.render_auto_label(idx);
330        }
331        if let Some(remainder) = self.pager.render2(idx, render_fn, state) {
332            Some(remainder)
333        } else {
334            self.hidden(state);
335            None
336        }
337    }
338
339    /// Calculate the necessary shift from view to screen.
340    /// This does nothing as pager always places the widgets
341    /// in screen coordinates.
342    ///
343    /// Just to keep the api in sync with [Clipper](crate::clipper::Clipper).
344    pub fn shift(&self) -> (i16, i16) {
345        (0, 0)
346    }
347
348    /// Relocate the widget area to screen coordinates.
349    /// Returns None if the widget is not visible.
350    /// This clips the area to page_area.
351    #[allow(clippy::question_mark)]
352    pub fn locate_widget(&self, widget: W) -> Option<Rect> {
353        let Some(idx) = self.pager.widget_idx(widget) else {
354            return None;
355        };
356        self.pager.locate_widget(idx)
357    }
358
359    /// Relocate the label area to screen coordinates.
360    /// Returns None if the widget is not visible.
361    /// This clips the area to page_area.
362    #[allow(clippy::question_mark)]
363    pub fn locate_label(&self, widget: W) -> Option<Rect> {
364        let Some(idx) = self.pager.widget_idx(widget) else {
365            return None;
366        };
367        self.pager.locate_label(idx)
368    }
369
370    /// Relocate an area from layout coordinates to screen coordinates.
371    /// A result None indicates that the area is invisible.
372    ///
373    /// This will clip the area to the page_area.
374    pub fn locate_area(&self, area: Rect) -> Option<Rect> {
375        self.pager.locate_area(area)
376    }
377
378    /// Does nothing for pager.
379    /// Just to keep the api in sync with [Clipper](crate::clipper::Clipper).
380    pub fn relocate<S>(&self, _state: &mut S)
381    where
382        S: RelocatableState,
383    {
384    }
385
386    /// Clear the areas in the widget-state.
387    /// This is called by render_xx whenever a widget is invisible.
388    pub fn hidden<S>(&self, state: &mut S)
389    where
390        S: RelocatableState,
391    {
392        state.relocate((0, 0), Rect::default())
393    }
394
395    /// Get access to the buffer during rendering a page.
396    pub fn buffer<'b>(&'b mut self) -> RefMut<'b, &'a mut Buffer> {
397        self.pager.buffer()
398    }
399}
400
401impl<W> Default for SinglePagerState<W>
402where
403    W: Eq + Hash + Clone,
404{
405    fn default() -> Self {
406        Self {
407            layout: Default::default(),
408            nav: Default::default(),
409            non_exhaustive: NonExhaustive,
410        }
411    }
412}
413
414impl<W> SinglePagerState<W>
415where
416    W: Eq + Hash + Clone,
417{
418    pub fn new() -> Self {
419        Self::default()
420    }
421
422    /// Clear the layout data and reset the page/page-count.
423    pub fn clear(&mut self) {
424        self.layout.borrow_mut().clear();
425        self.nav.clear();
426    }
427
428    /// Layout needs to change?
429    pub fn valid_layout(&self, size: Size) -> bool {
430        let layout = self.layout.borrow();
431        !layout.size_changed(size) && !layout.is_empty()
432    }
433
434    /// Set the layout.
435    pub fn set_layout(&mut self, layout: GenericLayout<W>) {
436        self.layout = Rc::new(RefCell::new(layout));
437    }
438
439    /// Layout.
440    pub fn layout(&self) -> Ref<'_, GenericLayout<W>> {
441        self.layout.borrow()
442    }
443
444    /// Show the page for this widget.
445    /// If there is no widget for the given identifier, this
446    /// will set the page to 0.
447    pub fn show(&mut self, widget: W) {
448        if let Some(page) = self.layout.borrow().page_of(widget) {
449            self.nav.set_page(page);
450        } else {
451            self.nav.set_page(0);
452        }
453    }
454
455    /// Returns the first widget for the given page.
456    pub fn first(&self, page: usize) -> Option<W> {
457        self.layout.borrow().first(page)
458    }
459
460    /// Calculates the page of the widget.
461    pub fn page_of(&self, widget: W) -> Option<usize> {
462        self.layout.borrow().page_of(widget)
463    }
464
465    /// Set the visible page.
466    pub fn set_page(&mut self, page: usize) -> bool {
467        self.nav.set_page(page)
468    }
469
470    /// Visible page
471    pub fn page(&self) -> usize {
472        self.nav.page()
473    }
474
475    /// Select next page. Keeps the page in bounds.
476    pub fn next_page(&mut self) -> bool {
477        self.nav.next_page()
478    }
479
480    /// Select prev page.
481    pub fn prev_page(&mut self) -> bool {
482        self.nav.prev_page()
483    }
484}
485
486impl<W> HandleEvent<crossterm::event::Event, Regular, PagerOutcome> for SinglePagerState<W>
487where
488    W: Eq + Hash + Clone,
489{
490    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> PagerOutcome {
491        self.nav.handle(event, Regular)
492    }
493}
494
495impl<W> HandleEvent<crossterm::event::Event, MouseOnly, PagerOutcome> for SinglePagerState<W>
496where
497    W: Eq + Hash + Clone,
498{
499    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> PagerOutcome {
500        self.nav.handle(event, MouseOnly)
501    }
502}