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#[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#[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#[derive(Debug, Clone)]
48pub struct SinglePagerState<W>
49where
50 W: Eq + Hash + Clone,
51{
52 pub layout: Rc<RefCell<GenericLayout<W>>>,
55
56 pub nav: PageNavigationState,
59
60 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 pub fn new() -> Self {
84 Self::default()
85 }
86
87 pub fn layout(mut self, layout: GenericLayout<W>) -> Self {
90 self.layout = Some(layout);
91 self
92 }
93
94 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 pub fn auto_label(mut self, auto: bool) -> Self {
105 self.auto_label = auto;
106 self
107 }
108
109 pub fn label_style(mut self, style: Style) -> Self {
111 self.pager = self.pager.label_style(style);
112 self
113 }
114
115 pub fn label_alignment(mut self, alignment: Alignment) -> Self {
117 self.pager = self.pager.label_alignment(alignment);
118 self
119 }
120
121 pub fn caption_style(mut self, style: CaptionStyle) -> Self {
123 self.pager = self.pager.caption_style(style);
124 self
125 }
126
127 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 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 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 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 pub fn layout_size(&self, area: Rect) -> Size {
174 self.page_nav.layout_size(area)
175 }
176
177 pub fn inner(&self, area: Rect) -> Rect {
179 self.page_nav.inner(area)
180 }
181
182 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 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 .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 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 pub fn render_block(&mut self) {
226 self.pager.render_block()
227 }
228
229 #[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 #[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 #[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 #[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 #[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 #[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 pub fn shift(&self) -> (i16, i16) {
345 (0, 0)
346 }
347
348 #[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 #[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 pub fn locate_area(&self, area: Rect) -> Option<Rect> {
375 self.pager.locate_area(area)
376 }
377
378 pub fn relocate<S>(&self, _state: &mut S)
381 where
382 S: RelocatableState,
383 {
384 }
385
386 pub fn hidden<S>(&self, state: &mut S)
389 where
390 S: RelocatableState,
391 {
392 state.relocate((0, 0), Rect::default())
393 }
394
395 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 pub fn clear(&mut self) {
424 self.layout.borrow_mut().clear();
425 self.nav.clear();
426 }
427
428 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 pub fn set_layout(&mut self, layout: GenericLayout<W>) {
436 self.layout = Rc::new(RefCell::new(layout));
437 }
438
439 pub fn layout(&self) -> Ref<'_, GenericLayout<W>> {
441 self.layout.borrow()
442 }
443
444 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 pub fn first(&self, page: usize) -> Option<W> {
457 self.layout.borrow().first(page)
458 }
459
460 pub fn page_of(&self, widget: W) -> Option<usize> {
462 self.layout.borrow().page_of(widget)
463 }
464
465 pub fn set_page(&mut self, page: usize) -> bool {
467 self.nav.set_page(page)
468 }
469
470 pub fn page(&self) -> usize {
472 self.nav.page()
473 }
474
475 pub fn next_page(&mut self) -> bool {
477 self.nav.next_page()
478 }
479
480 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}