1use crate::_private::NonExhaustive;
2use crate::event::PagerOutcome;
3use crate::layout::GenericLayout;
4use crate::pager::{PageNavigation, PageNavigationState, Pager, PagerBuffer, PagerStyle};
5use rat_event::{HandleEvent, MouseOnly, Regular};
6use rat_reloc::RelocatableState;
7use ratatui::buffer::Buffer;
8use ratatui::layout::{Alignment, Rect, Size};
9use ratatui::prelude::{StatefulWidget, Style};
10use ratatui::widgets::{Block, Widget};
11use std::borrow::Cow;
12use std::cell::{Ref, RefCell, RefMut};
13use std::hash::Hash;
14use std::rc::Rc;
15
16#[derive(Debug, Clone)]
18pub struct SinglePager<'a, W>
19where
20 W: Eq + Hash + Clone,
21{
22 layout: Option<GenericLayout<W>>,
23 pager: Pager<W>,
24 page_nav: PageNavigation<'a>,
25}
26
27#[derive(Debug)]
34pub struct SinglePagerBuffer<'a, W>
35where
36 W: Eq + Hash + Clone,
37{
38 pager: PagerBuffer<'a, W>,
39}
40
41#[derive(Debug, Clone)]
43pub struct SinglePagerState<W>
44where
45 W: Eq + Hash + Clone,
46{
47 pub layout: Rc<RefCell<GenericLayout<W>>>,
50
51 pub nav: PageNavigationState,
54
55 pub non_exhaustive: NonExhaustive,
57}
58
59impl<W> Default for SinglePager<'_, W>
60where
61 W: Eq + Hash + Clone,
62{
63 fn default() -> Self {
64 Self {
65 layout: Default::default(),
66 pager: Default::default(),
67 page_nav: Default::default(),
68 }
69 }
70}
71
72impl<'a, W> SinglePager<'a, W>
73where
74 W: Eq + Hash + Clone,
75{
76 pub fn new() -> Self {
78 Self::default()
79 }
80
81 pub fn layout(mut self, layout: GenericLayout<W>) -> Self {
84 self.layout = Some(layout);
85 self
86 }
87
88 pub fn style(mut self, style: Style) -> Self {
90 self.pager = self.pager.style(style);
91 self.page_nav = self.page_nav.style(style);
92 self
93 }
94
95 pub fn label_style(mut self, style: Style) -> Self {
97 self.pager = self.pager.label_style(style);
98 self
99 }
100
101 pub fn label_alignment(mut self, alignment: Alignment) -> Self {
103 self.pager = self.pager.label_alignment(alignment);
104 self
105 }
106
107 pub fn nav_style(mut self, nav_style: Style) -> Self {
109 self.page_nav = self.page_nav.nav_style(nav_style);
110 self
111 }
112
113 pub fn title_style(mut self, title_style: Style) -> Self {
115 self.page_nav = self.page_nav.title_style(title_style);
116 self
117 }
118
119 pub fn block(mut self, block: Block<'a>) -> Self {
121 self.page_nav = self.page_nav.block(block);
122 self
123 }
124
125 pub fn next_page_mark(mut self, txt: &'a str) -> Self {
126 self.page_nav = self.page_nav.next_page_mark(txt);
127 self
128 }
129
130 pub fn prev_page_mark(mut self, txt: &'a str) -> Self {
131 self.page_nav = self.page_nav.prev_page_mark(txt);
132 self
133 }
134
135 pub fn first_page_mark(mut self, txt: &'a str) -> Self {
136 self.page_nav = self.page_nav.first_page_mark(txt);
137 self
138 }
139
140 pub fn last_page_mark(mut self, txt: &'a str) -> Self {
141 self.page_nav = self.page_nav.last_page_mark(txt);
142 self
143 }
144
145 pub fn styles(mut self, styles: PagerStyle) -> Self {
147 self.pager = self.pager.styles(styles.clone());
148 self.page_nav = self.page_nav.styles(styles);
149 self
150 }
151
152 pub fn layout_size(&self, area: Rect) -> Size {
154 self.page_nav.layout_size(area)
155 }
156
157 pub fn inner(&self, area: Rect) -> Rect {
159 self.page_nav.inner(area)
160 }
161
162 pub fn into_buffer(
165 self,
166 area: Rect,
167 buf: &'a mut Buffer,
168 state: &'a mut SinglePagerState<W>,
169 ) -> SinglePagerBuffer<'a, W> {
170 if let Some(layout) = self.layout {
172 state.layout = Rc::new(RefCell::new(layout));
173 }
174
175 state.nav.page_count = state.layout.borrow().page_count();
176 state.nav.set_page(state.nav.page);
177
178 self.page_nav.render(area, buf, &mut state.nav);
179
180 SinglePagerBuffer {
181 pager: self
182 .pager .layout(state.layout.clone())
184 .page(state.nav.page)
185 .into_buffer(state.nav.widget_areas[0], Rc::new(RefCell::new(buf))),
186 }
187 }
188}
189
190impl<'a, W> SinglePagerBuffer<'a, W>
191where
192 W: Eq + Hash + Clone,
193{
194 pub fn is_visible(&self, widget: W) -> bool {
196 if let Some(idx) = self.pager.widget_idx(widget) {
197 self.pager.is_visible(idx)
198 } else {
199 false
200 }
201 }
202
203 pub fn render_block(&mut self) {
205 self.pager.render_block()
206 }
207
208 #[inline(always)]
210 pub fn render_label<FN, WW>(&mut self, widget: W, render_fn: FN) -> bool
211 where
212 FN: FnOnce(&Option<Cow<'static, str>>) -> WW,
213 WW: Widget,
214 {
215 let Some(idx) = self.pager.widget_idx(widget) else {
216 return false;
217 };
218 self.pager.render_label(idx, render_fn)
219 }
220
221 #[inline(always)]
223 pub fn render_widget<FN, WW>(&mut self, widget: W, render_fn: FN) -> bool
224 where
225 FN: FnOnce() -> WW,
226 WW: Widget,
227 {
228 let Some(idx) = self.pager.widget_idx(widget) else {
229 return false;
230 };
231 self.pager.render_auto_label(idx);
232 self.pager.render_widget(idx, render_fn)
233 }
234
235 #[inline(always)]
237 pub fn render_opt<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
238 where
239 FN: FnOnce() -> Option<WW>,
240 WW: StatefulWidget<State = SS>,
241 SS: RelocatableState,
242 {
243 let Some(idx) = self.pager.widget_idx(widget) else {
244 return false;
245 };
246 self.pager.render_auto_label(idx);
247 if !self.pager.render_opt(idx, render_fn, state) {
248 self.hidden(state);
249 false
250 } else {
251 true
252 }
253 }
254
255 #[inline(always)]
257 pub fn render<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
258 where
259 FN: FnOnce() -> WW,
260 WW: StatefulWidget<State = SS>,
261 SS: RelocatableState,
262 {
263 let Some(idx) = self.pager.widget_idx(widget) else {
264 return false;
265 };
266 self.pager.render_auto_label(idx);
267 if !self.pager.render(idx, render_fn, state) {
268 self.hidden(state);
269 false
270 } else {
271 true
272 }
273 }
274
275 #[inline(always)]
279 #[allow(clippy::question_mark)]
280 pub fn render2<FN, WW, SS, R>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> Option<R>
281 where
282 FN: FnOnce() -> (WW, R),
283 WW: StatefulWidget<State = SS>,
284 SS: RelocatableState,
285 {
286 let Some(idx) = self.pager.widget_idx(widget) else {
287 return None;
288 };
289 self.pager.render_auto_label(idx);
290 if let Some(remainder) = self.pager.render2(idx, render_fn, state) {
291 Some(remainder)
292 } else {
293 self.hidden(state);
294 None
295 }
296 }
297
298 pub fn shift(&self) -> (i16, i16) {
304 (0, 0)
305 }
306
307 #[allow(clippy::question_mark)]
311 pub fn locate_widget(&self, widget: W) -> Option<Rect> {
312 let Some(idx) = self.pager.widget_idx(widget) else {
313 return None;
314 };
315 self.pager.locate_widget(idx)
316 }
317
318 #[allow(clippy::question_mark)]
322 pub fn locate_label(&self, widget: W) -> Option<Rect> {
323 let Some(idx) = self.pager.widget_idx(widget) else {
324 return None;
325 };
326 self.pager.locate_label(idx)
327 }
328
329 pub fn locate_area(&self, area: Rect) -> Option<Rect> {
334 self.pager.locate_area(area)
335 }
336
337 pub fn relocate<S>(&self, _state: &mut S)
340 where
341 S: RelocatableState,
342 {
343 }
344
345 pub fn hidden<S>(&self, state: &mut S)
348 where
349 S: RelocatableState,
350 {
351 state.relocate((0, 0), Rect::default())
352 }
353
354 pub fn buffer<'b>(&'b mut self) -> RefMut<'b, &'a mut Buffer> {
356 self.pager.buffer()
357 }
358}
359
360impl<W> Default for SinglePagerState<W>
361where
362 W: Eq + Hash + Clone,
363{
364 fn default() -> Self {
365 Self {
366 layout: Default::default(),
367 nav: Default::default(),
368 non_exhaustive: NonExhaustive,
369 }
370 }
371}
372
373impl<W> SinglePagerState<W>
374where
375 W: Eq + Hash + Clone,
376{
377 pub fn new() -> Self {
378 Self::default()
379 }
380
381 pub fn valid_layout(&self, size: Size) -> bool {
383 let layout = self.layout.borrow();
384 !layout.size_changed(size) && !layout.is_empty()
385 }
386
387 pub fn set_layout(&mut self, layout: GenericLayout<W>) {
389 self.layout = Rc::new(RefCell::new(layout));
390 }
391
392 pub fn layout(&self) -> Ref<'_, GenericLayout<W>> {
394 self.layout.borrow()
395 }
396
397 pub fn clear(&mut self) {
399 self.layout.borrow_mut().clear();
400 self.nav.clear();
401 }
402
403 pub fn show(&mut self, widget: W) {
405 if let Some(page) = self.layout.borrow().page_of(widget) {
406 self.nav.set_page(page);
407 }
408 }
409
410 pub fn first(&self, page: usize) -> Option<W> {
412 self.layout.borrow().first(page)
413 }
414
415 pub fn page_of(&self, widget: W) -> Option<usize> {
417 self.layout.borrow().page_of(widget)
418 }
419
420 pub fn set_page(&mut self, page: usize) -> bool {
422 self.nav.set_page(page)
423 }
424
425 pub fn page(&self) -> usize {
427 self.nav.page()
428 }
429
430 pub fn next_page(&mut self) -> bool {
432 self.nav.next_page()
433 }
434
435 pub fn prev_page(&mut self) -> bool {
437 self.nav.prev_page()
438 }
439}
440
441impl<W> HandleEvent<crossterm::event::Event, Regular, PagerOutcome> for SinglePagerState<W>
442where
443 W: Eq + Hash + Clone,
444{
445 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> PagerOutcome {
446 self.nav.handle(event, Regular)
447 }
448}
449
450impl<W> HandleEvent<crossterm::event::Event, MouseOnly, PagerOutcome> for SinglePagerState<W>
451where
452 W: Eq + Hash + Clone,
453{
454 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> PagerOutcome {
455 self.nav.handle(event, MouseOnly)
456 }
457}