1use crate::_private::NonExhaustive;
81use crate::layout::GenericLayout;
82use crate::util::revert_style;
83use event::FormOutcome;
84use rat_event::util::MouseFlagsN;
85use rat_event::{ConsumedEvent, HandleEvent, MouseOnly, Regular, ct_event};
86use rat_focus::{Focus, FocusBuilder, FocusFlag, HasFocus};
87use rat_reloc::RelocatableState;
88use ratatui::buffer::Buffer;
89use ratatui::layout::{Alignment, Rect, Size};
90use ratatui::prelude::BlockExt;
91use ratatui::style::Style;
92use ratatui::text::{Line, Span};
93use ratatui::widgets::{Block, StatefulWidget, Widget};
94use std::borrow::Cow;
95use std::cmp::min;
96use std::hash::Hash;
97use std::rc::Rc;
98use unicode_display_width::width as unicode_width;
99
100#[derive(Debug, Clone)]
107pub struct Form<'a, W = usize>
108where
109 W: Eq + Hash + Clone,
110{
111 layout: Option<GenericLayout<W>>,
112
113 style: Style,
114 block: Option<Block<'a>>,
115 nav_style: Option<Style>,
116 title_style: Option<Style>,
117 navigation: bool,
118 next_page: &'a str,
119 prev_page: &'a str,
120 first_page: &'a str,
121 last_page: &'a str,
122
123 auto_label: bool,
124 label_style: Option<Style>,
125 label_alignment: Option<Alignment>,
126}
127
128#[derive(Debug)]
133#[must_use]
134pub struct FormBuffer<'b, W>
135where
136 W: Eq + Hash + Clone,
137{
138 layout: Rc<GenericLayout<W>>,
139
140 page_area: Rect,
141 widget_area: Rect,
142 buffer: &'b mut Buffer,
143
144 auto_label: bool,
145 label_style: Option<Style>,
146 label_alignment: Option<Alignment>,
147}
148
149#[derive(Debug, Clone)]
151pub struct FormStyle {
152 pub style: Style,
154 pub label_style: Option<Style>,
156 pub label_alignment: Option<Alignment>,
158 pub navigation: Option<Style>,
160 pub show_navigation: Option<bool>,
162 pub title: Option<Style>,
164 pub block: Option<Block<'static>>,
166 pub next_page_mark: Option<&'static str>,
168 pub prev_page_mark: Option<&'static str>,
170 pub first_page_mark: Option<&'static str>,
172 pub last_page_mark: Option<&'static str>,
174
175 pub non_exhaustive: NonExhaustive,
176}
177
178#[derive(Debug, Clone)]
180pub struct FormState<W = usize>
181where
182 W: Eq + Hash + Clone,
183{
184 pub layout: Rc<GenericLayout<W>>,
187 pub area: Rect,
190 pub widget_area: Rect,
193 pub prev_area: Rect,
196 pub next_area: Rect,
199
200 pub page: usize,
201
202 pub container: FocusFlag,
205
206 pub mouse: MouseFlagsN,
208
209 pub non_exhaustive: NonExhaustive,
211}
212
213pub(crate) mod event {
214 use rat_event::{ConsumedEvent, Outcome};
215
216 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
218 pub enum FormOutcome {
219 Continue,
221 Unchanged,
224 Changed,
229 Page,
231 }
232
233 impl ConsumedEvent for FormOutcome {
234 fn is_consumed(&self) -> bool {
235 *self != FormOutcome::Continue
236 }
237 }
238
239 impl From<Outcome> for FormOutcome {
240 fn from(value: Outcome) -> Self {
241 match value {
242 Outcome::Continue => FormOutcome::Continue,
243 Outcome::Unchanged => FormOutcome::Unchanged,
244 Outcome::Changed => FormOutcome::Changed,
245 }
246 }
247 }
248
249 impl From<FormOutcome> for Outcome {
250 fn from(value: FormOutcome) -> Self {
251 match value {
252 FormOutcome::Continue => Outcome::Continue,
253 FormOutcome::Unchanged => Outcome::Unchanged,
254 FormOutcome::Changed => Outcome::Changed,
255 FormOutcome::Page => Outcome::Changed,
256 }
257 }
258 }
259}
260
261impl<W> Default for Form<'_, W>
262where
263 W: Eq + Hash + Clone,
264{
265 fn default() -> Self {
266 Self {
267 layout: Default::default(),
268 style: Default::default(),
269 block: Default::default(),
270 nav_style: Default::default(),
271 title_style: Default::default(),
272 navigation: true,
273 next_page: ">>>",
274 prev_page: "<<<",
275 first_page: " [ ",
276 last_page: " ] ",
277 auto_label: true,
278 label_style: Default::default(),
279 label_alignment: Default::default(),
280 }
281 }
282}
283
284impl<'a, W> Form<'a, W>
285where
286 W: Eq + Hash + Clone,
287{
288 pub fn new() -> Self {
290 Self::default()
291 }
292
293 pub fn layout(mut self, layout: GenericLayout<W>) -> Self {
296 self.layout = Some(layout);
297 self
298 }
299
300 pub fn auto_label(mut self, auto: bool) -> Self {
304 self.auto_label = auto;
305 self
306 }
307
308 pub fn style(mut self, style: Style) -> Self {
310 self.style = style;
311 self.block = self.block.map(|v| v.style(style));
312 self
313 }
314
315 pub fn nav_style(mut self, nav_style: Style) -> Self {
317 self.nav_style = Some(nav_style);
318 self
319 }
320
321 pub fn show_navigation(mut self, show: bool) -> Self {
323 self.navigation = show;
324 self
325 }
326
327 pub fn title_style(mut self, title_style: Style) -> Self {
329 self.title_style = Some(title_style);
330 self
331 }
332
333 pub fn block(mut self, block: Block<'a>) -> Self {
335 self.block = Some(block.style(self.style));
336 self
337 }
338
339 pub fn next_page_mark(mut self, txt: &'a str) -> Self {
340 self.next_page = txt;
341 self
342 }
343
344 pub fn prev_page_mark(mut self, txt: &'a str) -> Self {
345 self.prev_page = txt;
346 self
347 }
348
349 pub fn first_page_mark(mut self, txt: &'a str) -> Self {
350 self.first_page = txt;
351 self
352 }
353
354 pub fn last_page_mark(mut self, txt: &'a str) -> Self {
355 self.last_page = txt;
356 self
357 }
358
359 pub fn label_style(mut self, style: Style) -> Self {
361 self.label_style = Some(style);
362 self
363 }
364
365 pub fn label_alignment(mut self, alignment: Alignment) -> Self {
367 self.label_alignment = Some(alignment);
368 self
369 }
370
371 pub fn styles(mut self, styles: FormStyle) -> Self {
373 self.style = styles.style;
374 if let Some(nav) = styles.navigation {
375 self.nav_style = Some(nav);
376 }
377 if let Some(navigation) = styles.show_navigation {
378 self.navigation = navigation;
379 }
380 if let Some(title) = styles.title {
381 self.title_style = Some(title);
382 }
383 if let Some(block) = styles.block {
384 self.block = Some(block);
385 }
386 if let Some(txt) = styles.next_page_mark {
387 self.next_page = txt;
388 }
389 if let Some(txt) = styles.prev_page_mark {
390 self.prev_page = txt;
391 }
392 if let Some(txt) = styles.first_page_mark {
393 self.first_page = txt;
394 }
395 if let Some(txt) = styles.last_page_mark {
396 self.last_page = txt;
397 }
398 self.block = self.block.map(|v| v.style(styles.style));
399
400 if let Some(label) = styles.label_style {
401 self.label_style = Some(label);
402 }
403 if let Some(alignment) = styles.label_alignment {
404 self.label_alignment = Some(alignment);
405 }
406
407 self
408 }
409
410 pub fn layout_size(&self, area: Rect) -> Size {
412 self.block.inner_if_some(area).as_size()
413 }
414
415 pub fn layout_area(&self, area: Rect) -> Rect {
417 if let Some(block) = &self.block
418 && self.navigation
419 {
420 block.inner(area)
421 } else {
422 area
423 }
424 }
425
426 #[allow(clippy::needless_lifetimes)]
429 pub fn into_buffer<'b, 's>(
430 mut self,
431 area: Rect,
432 buf: &'b mut Buffer,
433 state: &'s mut FormState<W>,
434 ) -> FormBuffer<'b, W> {
435 state.area = area;
436 state.widget_area = self.layout_area(area);
437
438 if let Some(layout) = self.layout.take() {
439 state.layout = Rc::new(layout);
440 }
441
442 let page_size = state.layout.page_size();
443 assert!(page_size.height < u16::MAX || page_size.height == u16::MAX && state.page == 0);
444 let page_area = Rect::new(
445 0,
446 (state.page as u16).saturating_mul(page_size.height),
447 page_size.width,
448 page_size.height,
449 );
450
451 if self.navigation {
452 self.render_navigation(area, buf, state);
453 } else {
454 buf.set_style(area, self.style);
455 }
456
457 let mut form_buf = FormBuffer {
458 layout: state.layout.clone(),
459 page_area,
460 widget_area: state.widget_area,
461 buffer: buf,
462
463 auto_label: true,
464 label_style: self.label_style,
465 label_alignment: self.label_alignment,
466 };
467 form_buf.render_block();
468 form_buf
469 }
470
471 fn render_navigation(&self, area: Rect, buf: &mut Buffer, state: &mut FormState<W>) {
472 let page_count = state.layout.page_count();
473
474 if !state.layout.is_endless() {
475 if state.page > 0 {
476 state.prev_area =
477 Rect::new(area.x, area.y, unicode_width(self.prev_page) as u16, 1);
478 } else {
479 state.prev_area =
480 Rect::new(area.x, area.y, unicode_width(self.first_page) as u16, 1);
481 }
482 if (state.page + 1) < page_count {
483 let p = unicode_width(self.next_page) as u16;
484 state.next_area = Rect::new(area.x + area.width.saturating_sub(p), area.y, p, 1);
485 } else {
486 let p = unicode_width(self.last_page) as u16;
487 state.next_area = Rect::new(area.x + area.width.saturating_sub(p), area.y, p, 1);
488 }
489 } else {
490 state.prev_area = Default::default();
491 state.next_area = Default::default();
492 }
493
494 let block = if page_count > 1 {
495 let title = format!(" {}/{} ", state.page + 1, page_count);
496 let block = self
497 .block
498 .clone()
499 .unwrap_or_else(|| Block::new().style(self.style))
500 .title_bottom(title)
501 .title_alignment(Alignment::Right);
502 if let Some(title_style) = self.title_style {
503 block.title_style(title_style)
504 } else {
505 block
506 }
507 } else {
508 self.block
509 .clone()
510 .unwrap_or_else(|| Block::new().style(self.style))
511 };
512 block.render(area, buf);
513
514 if !state.layout.is_endless() {
515 let nav_style = self.nav_style.unwrap_or(self.style);
517 if matches!(state.mouse.hover.get(), Some(0)) {
518 buf.set_style(state.prev_area, revert_style(nav_style));
519 } else {
520 buf.set_style(state.prev_area, nav_style);
521 }
522 if state.page > 0 {
523 Span::from(self.prev_page).render(state.prev_area, buf);
524 } else {
525 Span::from(self.first_page).render(state.prev_area, buf);
526 }
527 if matches!(state.mouse.hover.get(), Some(1)) {
528 buf.set_style(state.next_area, revert_style(nav_style));
529 } else {
530 buf.set_style(state.next_area, nav_style);
531 }
532 if (state.page + 1) < page_count {
533 Span::from(self.next_page).render(state.next_area, buf);
534 } else {
535 Span::from(self.last_page).render(state.next_area, buf);
536 }
537 }
538 }
539}
540
541impl<'b, W> FormBuffer<'b, W>
542where
543 W: Eq + Hash + Clone,
544{
545 pub fn is_visible(&self, widget: W) -> bool {
547 if let Some(idx) = self.layout.try_index_of(widget) {
548 self.locate_area(self.layout.widget(idx)).is_some()
549 } else {
550 false
551 }
552 }
553
554 fn render_block(&mut self) {
556 for (idx, block_area) in self.layout.block_area_iter().enumerate() {
557 if let Some(block_area) = self.locate_area(*block_area) {
558 if let Some(block) = self.layout.block(idx) {
559 block.render(block_area, self.buffer);
560 }
561 }
562 }
563 }
564
565 #[inline(always)]
567 pub fn render_label<FN>(&mut self, widget: W, render_fn: FN) -> bool
568 where
569 FN: FnOnce(&Cow<'static, str>, Rect, &mut Buffer),
570 {
571 let Some(idx) = self.layout.try_index_of(widget) else {
572 return false;
573 };
574 let Some(label_area) = self.locate_area(self.layout.label(idx)) else {
575 return false;
576 };
577 if let Some(label_str) = self.layout.try_label_str(idx) {
578 render_fn(label_str, label_area, self.buffer);
579 } else {
580 render_fn(&Cow::default(), label_area, self.buffer);
581 }
582 true
583 }
584
585 #[inline(always)]
587 pub fn render_widget<FN, WW>(&mut self, widget: W, render_fn: FN) -> bool
588 where
589 FN: FnOnce() -> WW,
590 WW: Widget,
591 {
592 let Some(idx) = self.layout.try_index_of(widget) else {
593 return false;
594 };
595 if self.auto_label {
596 self.render_auto_label(idx);
597 }
598
599 let Some(widget_area) = self.locate_area(self.layout.widget(idx)) else {
600 return false;
601 };
602 render_fn().render(widget_area, self.buffer);
603 true
604 }
605
606 #[inline(always)]
608 pub fn render<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
609 where
610 FN: FnOnce() -> WW,
611 WW: StatefulWidget<State = SS>,
612 SS: RelocatableState,
613 {
614 let Some(idx) = self.layout.try_index_of(widget) else {
615 return false;
616 };
617 if self.auto_label {
618 self.render_auto_label(idx);
619 }
620 let Some(widget_area) = self.locate_area(self.layout.widget(idx)) else {
621 state.relocate_hidden();
622 return false;
623 };
624 let widget = render_fn();
625 widget.render(widget_area, self.buffer, state);
626 true
627 }
628
629 #[inline(always)]
638 #[allow(clippy::question_mark)]
639 pub fn render2<FN, WW, SS, R>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> Option<R>
640 where
641 FN: FnOnce() -> (WW, R),
642 WW: StatefulWidget<State = SS>,
643 SS: RelocatableState,
644 {
645 let Some(idx) = self.layout.try_index_of(widget) else {
646 return None;
647 };
648 if self.auto_label {
649 self.render_auto_label(idx);
650 }
651 let Some(widget_area) = self.locate_area(self.layout.widget(idx)) else {
652 state.relocate_hidden();
653 return None;
654 };
655 let (widget, remainder) = render_fn();
656 widget.render(widget_area, self.buffer, state);
657
658 Some(remainder)
659 }
660
661 #[inline(always)]
663 #[deprecated(since = "2.3.0", note = "use render_popup() for popups")]
664 pub fn render_opt<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
665 where
666 FN: FnOnce() -> Option<WW>,
667 WW: StatefulWidget<State = SS>,
668 SS: RelocatableState,
669 {
670 let Some(idx) = self.layout.try_index_of(widget) else {
671 return false;
672 };
673 if self.auto_label {
674 self.render_auto_label(idx);
675 }
676 let Some(widget_area) = self.locate_area(self.layout.widget(idx)) else {
677 state.relocate_hidden();
678 return false;
679 };
680 let widget = render_fn();
681 if let Some(widget) = widget {
682 widget.render(widget_area, self.buffer, state);
683 true
684 } else {
685 state.relocate_hidden();
686 false
687 }
688 }
689
690 #[inline(always)]
694 pub fn render_popup<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
695 where
696 FN: FnOnce() -> Option<WW>,
697 WW: StatefulWidget<State = SS>,
698 SS: RelocatableState,
699 {
700 let Some(idx) = self.layout.try_index_of(widget) else {
701 return false;
702 };
703 let Some(widget_area) = self.locate_area(self.layout.widget(idx)) else {
704 state.relocate_popup_hidden();
705 return false;
706 };
707 let widget = render_fn();
708 if let Some(widget) = widget {
709 widget.render(widget_area, self.buffer, state);
710 true
711 } else {
712 state.relocate_popup_hidden();
713 false
714 }
715 }
716
717 pub fn buffer(&mut self) -> &mut Buffer {
719 self.buffer
720 }
721
722 #[inline(always)]
724 fn render_auto_label(&mut self, idx: usize) -> bool {
725 let Some(label_area) = self.locate_area(self.layout.label(idx)) else {
726 return false;
727 };
728 let Some(label_str) = self.layout.try_label_str(idx) else {
729 return false;
730 };
731 let mut label = Line::from(label_str.as_ref());
732 if let Some(style) = self.label_style {
733 label = label.style(style)
734 };
735 if let Some(align) = self.label_alignment {
736 label = label.alignment(align);
737 }
738 label.render(label_area, self.buffer);
739
740 true
741 }
742
743 pub fn locate_widget(&self, widget: W) -> Option<Rect> {
745 let Some(idx) = self.layout.try_index_of(widget) else {
746 return None;
747 };
748 self.locate_area(self.layout.widget(idx))
749 }
750
751 pub fn locate_label(&self, widget: W) -> Option<Rect> {
753 let Some(idx) = self.layout.try_index_of(widget) else {
754 return None;
755 };
756 self.locate_area(self.layout.label(idx))
757 }
758
759 #[inline]
761 pub fn locate_area(&self, area: Rect) -> Option<Rect> {
762 let area = self.page_area.intersection(area);
764 if self.page_area.intersects(area) {
765 let located = Rect::new(
766 area.x - self.page_area.x + self.widget_area.x,
767 area.y - self.page_area.y + self.widget_area.y,
768 area.width,
769 area.height,
770 );
771 let located = self.widget_area.intersection(located);
773 if self.widget_area.intersects(located) {
774 Some(located)
775 } else {
776 None
777 }
778 } else {
779 None
780 }
781 }
782}
783
784impl Default for FormStyle {
785 fn default() -> Self {
786 Self {
787 style: Default::default(),
788 label_style: None,
789 label_alignment: None,
790 navigation: None,
791 show_navigation: None,
792 title: None,
793 block: None,
794 next_page_mark: None,
795 prev_page_mark: None,
796 first_page_mark: None,
797 last_page_mark: None,
798 non_exhaustive: NonExhaustive,
799 }
800 }
801}
802
803impl<W> Default for FormState<W>
804where
805 W: Eq + Hash + Clone,
806{
807 fn default() -> Self {
808 Self {
809 layout: Default::default(),
810 area: Default::default(),
811 widget_area: Default::default(),
812 prev_area: Default::default(),
813 next_area: Default::default(),
814 page: 0,
815 container: Default::default(),
816 mouse: Default::default(),
817 non_exhaustive: NonExhaustive,
818 }
819 }
820}
821
822impl<W> HasFocus for FormState<W>
823where
824 W: Eq + Hash + Clone,
825{
826 fn build(&self, _builder: &mut FocusBuilder) {
827 }
829
830 fn focus(&self) -> FocusFlag {
831 self.container.clone()
832 }
833
834 fn area(&self) -> Rect {
835 self.area
836 }
837}
838
839impl<W> FormState<W>
840where
841 W: Eq + Hash + Clone,
842{
843 pub fn new() -> Self {
844 Self::default()
845 }
846
847 pub fn named(name: &str) -> Self {
848 let mut z = Self::default();
849 z.container = z.container.with_name(name);
850 z
851 }
852
853 pub fn clear(&mut self) {
855 self.layout = Default::default();
856 self.page = 0;
857 }
858
859 pub fn valid_layout(&self, size: Size) -> bool {
861 !self.layout.size_changed(size) && !self.layout.is_empty()
862 }
863
864 pub fn set_layout(&mut self, layout: GenericLayout<W>) {
866 self.layout = Rc::new(layout);
867 }
868
869 pub fn layout(&self) -> Rc<GenericLayout<W>> {
871 self.layout.clone()
872 }
873
874 pub fn show(&mut self, widget: W) {
878 let page = self.layout.page_of(widget).unwrap_or_default();
879 self.set_page(page);
880 }
881
882 pub fn page_count(&self) -> usize {
884 self.layout.page_count()
885 }
886
887 pub fn first(&self, page: usize) -> Option<W> {
889 self.layout.first(page)
890 }
891
892 pub fn page_of(&self, widget: W) -> Option<usize> {
894 self.layout.page_of(widget)
895 }
896
897 pub fn set_page(&mut self, page: usize) -> bool {
899 let old_page = self.page;
900 self.page = min(page, self.page_count().saturating_sub(1));
901 old_page != self.page
902 }
903
904 pub fn page(&self) -> usize {
906 self.page
907 }
908
909 pub fn next_page(&mut self) -> bool {
911 let old_page = self.page;
912
913 if self.page + 1 == self.page_count() {
914 } else if self.page + 1 > self.page_count() {
916 self.page = self.page_count().saturating_sub(1);
917 } else {
918 self.page += 1;
919 }
920
921 old_page != self.page
922 }
923
924 pub fn prev_page(&mut self) -> bool {
926 if self.page >= 1 {
927 self.page -= 1;
928 true
929 } else if self.page > 0 {
930 self.page = 0;
931 true
932 } else {
933 false
934 }
935 }
936}
937
938impl FormState<usize> {
939 pub fn focus_first(&self, focus: &Focus) -> bool {
942 if let Some(w) = self.first(self.page) {
943 focus.by_widget_id(w);
944 true
945 } else {
946 false
947 }
948 }
949
950 pub fn show_focused(&mut self, focus: &Focus) -> bool {
954 let Some(focused) = focus.focused() else {
955 return false;
956 };
957 let focused = focused.widget_id();
958 let page = self.layout.page_of(focused);
959 if let Some(page) = page {
960 self.set_page(page);
961 true
962 } else {
963 false
964 }
965 }
966}
967
968impl FormState<FocusFlag> {
969 pub fn focus_first(&self, focus: &Focus) -> bool {
971 if let Some(w) = self.first(self.page) {
972 focus.focus(&w);
973 true
974 } else {
975 false
976 }
977 }
978
979 pub fn show_focused(&mut self, focus: &Focus) -> bool {
982 let Some(focused) = focus.focused() else {
983 return false;
984 };
985 let page = self.layout.page_of(focused);
986 if let Some(page) = page {
987 self.set_page(page);
988 true
989 } else {
990 false
991 }
992 }
993}
994
995impl<W> HandleEvent<crossterm::event::Event, Regular, FormOutcome> for FormState<W>
996where
997 W: Eq + Hash + Clone,
998{
999 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> FormOutcome {
1000 let r = if self.container.is_focused() && !self.layout.is_endless() {
1001 match event {
1002 ct_event!(keycode press ALT-PageUp) => {
1003 if self.prev_page() {
1004 FormOutcome::Page
1005 } else {
1006 FormOutcome::Continue
1007 }
1008 }
1009 ct_event!(keycode press ALT-PageDown) => {
1010 if self.next_page() {
1011 FormOutcome::Page
1012 } else {
1013 FormOutcome::Continue
1014 }
1015 }
1016 _ => FormOutcome::Continue,
1017 }
1018 } else {
1019 FormOutcome::Continue
1020 };
1021
1022 r.or_else(|| self.handle(event, MouseOnly))
1023 }
1024}
1025
1026impl<W> HandleEvent<crossterm::event::Event, MouseOnly, FormOutcome> for FormState<W>
1027where
1028 W: Eq + Hash + Clone,
1029{
1030 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> FormOutcome {
1031 if !self.layout.is_endless() {
1032 match event {
1033 ct_event!(mouse down Left for x,y) if self.prev_area.contains((*x, *y).into()) => {
1034 if self.prev_page() {
1035 FormOutcome::Page
1036 } else {
1037 FormOutcome::Unchanged
1038 }
1039 }
1040 ct_event!(mouse down Left for x,y) if self.next_area.contains((*x, *y).into()) => {
1041 if self.next_page() {
1042 FormOutcome::Page
1043 } else {
1044 FormOutcome::Unchanged
1045 }
1046 }
1047 ct_event!(scroll down for x,y) => {
1048 if self.area.contains((*x, *y).into()) {
1049 if self.next_page() {
1050 FormOutcome::Page
1051 } else {
1052 FormOutcome::Continue
1053 }
1054 } else {
1055 FormOutcome::Continue
1056 }
1057 }
1058 ct_event!(scroll up for x,y) => {
1059 if self.area.contains((*x, *y).into()) {
1060 if self.prev_page() {
1061 FormOutcome::Page
1062 } else {
1063 FormOutcome::Continue
1064 }
1065 } else {
1066 FormOutcome::Continue
1067 }
1068 }
1069 ct_event!(mouse any for m)
1070 if self.mouse.hover(&[self.prev_area, self.next_area], m) =>
1071 {
1072 FormOutcome::Changed
1073 }
1074 _ => FormOutcome::Continue,
1075 }
1076 } else {
1077 FormOutcome::Continue
1078 }
1079 }
1080}
1081
1082pub fn handle_events<W>(
1086 state: &mut FormState<W>,
1087 _focus: bool,
1088 event: &crossterm::event::Event,
1089) -> FormOutcome
1090where
1091 W: Eq + Clone + Hash,
1092{
1093 HandleEvent::handle(state, event, Regular)
1094}
1095
1096pub fn handle_mouse_events<W>(
1098 state: &mut FormState<W>,
1099 event: &crossterm::event::Event,
1100) -> FormOutcome
1101where
1102 W: Eq + Clone + Hash,
1103{
1104 HandleEvent::handle(state, event, MouseOnly)
1105}