1use crate::_private::NonExhaustive;
2use crate::event::PagerOutcome;
3use crate::layout::GenericLayout;
4use crate::pager::{PageNavigation, PageNavigationState, Pager, PagerBuffer, PagerStyle};
5use log::debug;
6use rat_event::{HandleEvent, MouseOnly, Regular};
7use rat_reloc::RelocatableState;
8use ratatui::buffer::Buffer;
9use ratatui::layout::{Alignment, Rect, Size};
10use ratatui::style::Style;
11use ratatui::widgets::StatefulWidget;
12use ratatui::widgets::{Block, Widget};
13use std::borrow::Cow;
14use std::cell::{Ref, RefCell, RefMut};
15use std::hash::Hash;
16use std::rc::Rc;
17
18#[derive(Debug, Clone)]
20pub struct SinglePager<'a, W>
21where
22 W: Eq + Hash + Clone,
23{
24 layout: Option<GenericLayout<W>>,
25 pager: Pager<W>,
26 page_nav: PageNavigation<'a>,
27 auto_label: bool,
28}
29
30#[derive(Debug)]
37pub struct SinglePagerBuffer<'a, W>
38where
39 W: Eq + Hash + Clone,
40{
41 pager: PagerBuffer<'a, W>,
42 auto_label: bool,
43}
44
45#[derive(Debug, Clone)]
47pub struct SinglePagerState<W>
48where
49 W: Eq + Hash + Clone,
50{
51 pub layout: Rc<RefCell<GenericLayout<W>>>,
54
55 pub nav: PageNavigationState,
58
59 pub non_exhaustive: NonExhaustive,
61}
62
63impl<W> Default for SinglePager<'_, W>
64where
65 W: Eq + Hash + Clone,
66{
67 fn default() -> Self {
68 Self {
69 layout: Default::default(),
70 pager: Default::default(),
71 page_nav: Default::default(),
72 auto_label: true,
73 }
74 }
75}
76
77impl<'a, W> SinglePager<'a, W>
78where
79 W: Eq + Hash + Clone,
80{
81 pub fn new() -> Self {
83 Self::default()
84 }
85
86 pub fn layout(mut self, layout: GenericLayout<W>) -> Self {
89 self.layout = Some(layout);
90 self
91 }
92
93 pub fn style(mut self, style: Style) -> Self {
95 self.pager = self.pager.style(style);
96 self.page_nav = self.page_nav.style(style);
97 self
98 }
99
100 pub fn auto_label(mut self, auto: bool) -> Self {
104 self.auto_label = auto;
105 self
106 }
107
108 pub fn label_style(mut self, style: Style) -> Self {
110 self.pager = self.pager.label_style(style);
111 self
112 }
113
114 pub fn label_alignment(mut self, alignment: Alignment) -> Self {
116 self.pager = self.pager.label_alignment(alignment);
117 self
118 }
119
120 pub fn nav_style(mut self, nav_style: Style) -> Self {
122 self.page_nav = self.page_nav.nav_style(nav_style);
123 self
124 }
125
126 pub fn title_style(mut self, title_style: Style) -> Self {
128 self.page_nav = self.page_nav.title_style(title_style);
129 self
130 }
131
132 pub fn block(mut self, block: Block<'a>) -> Self {
134 self.page_nav = self.page_nav.block(block);
135 self
136 }
137
138 pub fn next_page_mark(mut self, txt: &'a str) -> Self {
139 self.page_nav = self.page_nav.next_page_mark(txt);
140 self
141 }
142
143 pub fn prev_page_mark(mut self, txt: &'a str) -> Self {
144 self.page_nav = self.page_nav.prev_page_mark(txt);
145 self
146 }
147
148 pub fn first_page_mark(mut self, txt: &'a str) -> Self {
149 self.page_nav = self.page_nav.first_page_mark(txt);
150 self
151 }
152
153 pub fn last_page_mark(mut self, txt: &'a str) -> Self {
154 self.page_nav = self.page_nav.last_page_mark(txt);
155 self
156 }
157
158 pub fn styles(mut self, styles: PagerStyle) -> Self {
160 self.pager = self.pager.styles(styles.clone());
161 self.page_nav = self.page_nav.styles(styles);
162 self
163 }
164
165 pub fn layout_size(&self, area: Rect) -> Size {
167 self.page_nav.layout_size(area)
168 }
169
170 pub fn inner(&self, area: Rect) -> Rect {
172 self.page_nav.inner(area)
173 }
174
175 pub fn into_buffer(
178 self,
179 area: Rect,
180 buf: &'a mut Buffer,
181 state: &'a mut SinglePagerState<W>,
182 ) -> SinglePagerBuffer<'a, W> {
183 if let Some(layout) = self.layout {
185 state.layout = Rc::new(RefCell::new(layout));
186 }
187
188 state.nav.page_count = state.layout.borrow().page_count();
189 state.nav.set_page(state.nav.page);
190
191 self.page_nav.render(area, buf, &mut state.nav);
192
193 SinglePagerBuffer {
194 pager: self
195 .pager .layout(state.layout.clone())
197 .page(state.nav.page)
198 .into_buffer(state.nav.widget_areas[0], Rc::new(RefCell::new(buf))),
199 auto_label: self.auto_label,
200 }
201 }
202}
203
204impl<'a, W> SinglePagerBuffer<'a, W>
205where
206 W: Eq + Hash + Clone,
207{
208 pub fn is_visible(&self, widget: W) -> bool {
210 if let Some(idx) = self.pager.widget_idx(widget) {
211 self.pager.is_visible(idx)
212 } else {
213 false
214 }
215 }
216
217 pub fn render_block(&mut self) {
219 self.pager.render_block()
220 }
221
222 #[inline(always)]
224 pub fn render_label<FN>(&mut self, widget: W, render_fn: FN) -> bool
225 where
226 FN: FnOnce(&Cow<'static, str>, Rect, &mut Buffer),
227 {
228 let Some(idx) = self.pager.widget_idx(widget) else {
229 debug!("no widget");
230 return false;
231 };
232 self.pager.render_label(idx, render_fn)
233 }
234
235 #[inline(always)]
237 pub fn render_widget<FN, WW>(&mut self, widget: W, render_fn: FN) -> bool
238 where
239 FN: FnOnce() -> WW,
240 WW: Widget,
241 {
242 let Some(idx) = self.pager.widget_idx(widget) else {
243 return false;
244 };
245 if self.auto_label {
246 self.pager.render_auto_label(idx);
247 }
248 self.pager.render_widget(idx, render_fn)
249 }
250
251 #[inline(always)]
253 pub fn render_opt<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
254 where
255 FN: FnOnce() -> Option<WW>,
256 WW: StatefulWidget<State = SS>,
257 SS: RelocatableState,
258 {
259 let Some(idx) = self.pager.widget_idx(widget) else {
260 return false;
261 };
262 if self.auto_label {
263 self.pager.render_auto_label(idx);
264 }
265 if !self.pager.render_opt(idx, render_fn, state) {
266 self.hidden(state);
267 false
268 } else {
269 true
270 }
271 }
272
273 #[inline(always)]
275 pub fn render<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
276 where
277 FN: FnOnce() -> WW,
278 WW: StatefulWidget<State = SS>,
279 SS: RelocatableState,
280 {
281 let Some(idx) = self.pager.widget_idx(widget) else {
282 return false;
283 };
284 if self.auto_label {
285 self.pager.render_auto_label(idx);
286 }
287 if self.pager.render(idx, render_fn, state) {
288 true
289 } else {
290 self.hidden(state);
291 false
292 }
293 }
294
295 #[inline(always)]
299 pub fn render2<FN, WW, SS, R>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> Option<R>
300 where
301 FN: FnOnce() -> (WW, R),
302 WW: StatefulWidget<State = SS>,
303 SS: RelocatableState,
304 {
305 let Some(idx) = self.pager.widget_idx(widget) else {
306 return None;
307 };
308 if self.auto_label {
309 self.pager.render_auto_label(idx);
310 }
311 if let Some(remainder) = self.pager.render2(idx, render_fn, state) {
312 Some(remainder)
313 } else {
314 self.hidden(state);
315 None
316 }
317 }
318
319 pub fn shift(&self) -> (i16, i16) {
325 (0, 0)
326 }
327
328 #[allow(clippy::question_mark)]
332 pub fn locate_widget(&self, widget: W) -> Option<Rect> {
333 let Some(idx) = self.pager.widget_idx(widget) else {
334 return None;
335 };
336 self.pager.locate_widget(idx)
337 }
338
339 #[allow(clippy::question_mark)]
343 pub fn locate_label(&self, widget: W) -> Option<Rect> {
344 let Some(idx) = self.pager.widget_idx(widget) else {
345 return None;
346 };
347 self.pager.locate_label(idx)
348 }
349
350 pub fn locate_area(&self, area: Rect) -> Option<Rect> {
355 self.pager.locate_area(area)
356 }
357
358 pub fn relocate<S>(&self, _state: &mut S)
361 where
362 S: RelocatableState,
363 {
364 }
365
366 pub fn hidden<S>(&self, state: &mut S)
369 where
370 S: RelocatableState,
371 {
372 state.relocate((0, 0), Rect::default())
373 }
374
375 pub fn buffer<'b>(&'b mut self) -> RefMut<'b, &'a mut Buffer> {
377 self.pager.buffer()
378 }
379}
380
381impl<W> Default for SinglePagerState<W>
382where
383 W: Eq + Hash + Clone,
384{
385 fn default() -> Self {
386 Self {
387 layout: Default::default(),
388 nav: Default::default(),
389 non_exhaustive: NonExhaustive,
390 }
391 }
392}
393
394impl<W> SinglePagerState<W>
395where
396 W: Eq + Hash + Clone,
397{
398 pub fn new() -> Self {
399 Self::default()
400 }
401
402 pub fn clear(&mut self) {
404 self.layout.borrow_mut().clear();
405 self.nav.clear();
406 }
407
408 pub fn valid_layout(&self, size: Size) -> bool {
410 let layout = self.layout.borrow();
411 !layout.size_changed(size) && !layout.is_empty()
412 }
413
414 pub fn set_layout(&mut self, layout: GenericLayout<W>) {
416 self.layout = Rc::new(RefCell::new(layout));
417 }
418
419 pub fn layout(&self) -> Ref<'_, GenericLayout<W>> {
421 self.layout.borrow()
422 }
423
424 pub fn show(&mut self, widget: W) {
428 if let Some(page) = self.layout.borrow().page_of(widget) {
429 self.nav.set_page(page);
430 } else {
431 self.nav.set_page(0);
432 }
433 }
434
435 pub fn first(&self, page: usize) -> Option<W> {
437 self.layout.borrow().first(page)
438 }
439
440 pub fn page_of(&self, widget: W) -> Option<usize> {
442 self.layout.borrow().page_of(widget)
443 }
444
445 pub fn set_page(&mut self, page: usize) -> bool {
447 self.nav.set_page(page)
448 }
449
450 pub fn page(&self) -> usize {
452 self.nav.page()
453 }
454
455 pub fn next_page(&mut self) -> bool {
457 self.nav.next_page()
458 }
459
460 pub fn prev_page(&mut self) -> bool {
462 self.nav.prev_page()
463 }
464}
465
466impl<W> HandleEvent<crossterm::event::Event, Regular, PagerOutcome> for SinglePagerState<W>
467where
468 W: Eq + Hash + Clone,
469{
470 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> PagerOutcome {
471 self.nav.handle(event, Regular)
472 }
473}
474
475impl<W> HandleEvent<crossterm::event::Event, MouseOnly, PagerOutcome> for SinglePagerState<W>
476where
477 W: Eq + Hash + Clone,
478{
479 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> PagerOutcome {
480 self.nav.handle(event, MouseOnly)
481 }
482}