1use crate::_private::NonExhaustive;
2use crate::choice::{
3 Choice, ChoiceClose, ChoiceFocus, ChoicePopup, ChoiceSelect, ChoiceState, ChoiceStyle,
4 ChoiceWidget,
5};
6use crate::combobox::event::ComboboxOutcome;
7use crate::event::ChoiceOutcome;
8use crate::text::HasScreenCursor;
9use rat_event::util::{MouseFlags, item_at, mouse_trap};
10use rat_event::{ConsumedEvent, HandleEvent, MouseOnly, Popup, Regular, ct_event};
11use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
12use rat_popup::Placement;
13use rat_popup::event::PopupOutcome;
14use rat_reloc::{RelocatableState, relocate_area};
15use rat_scrolled::event::ScrollOutcome;
16use rat_scrolled::{Scroll, ScrollAreaState};
17use rat_text::TextStyle;
18use rat_text::event::TextOutcome;
19use rat_text::text_input::{TextInput, TextInputState};
20use ratatui::buffer::Buffer;
21use ratatui::layout::{Alignment, Rect};
22use ratatui::style::Style;
23use ratatui::text::Line;
24use ratatui::widgets::{Block, StatefulWidget};
25use std::cmp::max;
26
27#[derive(Debug)]
28pub struct Combobox<'a> {
29 choice: Choice<'a, String>,
30 text: TextInput<'a>,
31}
32
33#[derive(Debug)]
34pub struct ComboboxWidget<'a> {
35 choice: ChoiceWidget<'a, String>,
36 text: TextInput<'a>,
37}
38
39#[derive(Debug)]
40pub struct ComboboxPopup<'a> {
41 choice: ChoicePopup<'a, String>,
42}
43
44#[derive(Debug, Clone)]
45pub struct ComboboxStyle {
46 pub choice: ChoiceStyle,
47 pub text: TextStyle,
48
49 pub non_exhaustive: NonExhaustive,
50}
51
52#[derive(Debug)]
53pub struct ComboboxState {
54 pub area: Rect,
57 pub choice: ChoiceState<String>,
59 pub text: TextInputState,
61
62 pub focus: FocusFlag,
65 pub mouse: MouseFlags,
67
68 pub non_exhaustive: NonExhaustive,
69}
70
71pub(crate) mod event {
72 use rat_event::{ConsumedEvent, Outcome};
73 use rat_popup::event::PopupOutcome;
74
75 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
77 pub enum ComboboxOutcome {
78 Continue,
80 Unchanged,
82 Changed,
84 Value,
86 TextChanged,
88 }
89
90 impl ConsumedEvent for ComboboxOutcome {
91 fn is_consumed(&self) -> bool {
92 *self != ComboboxOutcome::Continue
93 }
94 }
95
96 impl From<Outcome> for ComboboxOutcome {
97 fn from(value: Outcome) -> Self {
98 match value {
99 Outcome::Continue => ComboboxOutcome::Continue,
100 Outcome::Unchanged => ComboboxOutcome::Unchanged,
101 Outcome::Changed => ComboboxOutcome::Changed,
102 }
103 }
104 }
105
106 impl From<ComboboxOutcome> for Outcome {
107 fn from(value: ComboboxOutcome) -> Self {
108 match value {
109 ComboboxOutcome::Continue => Outcome::Continue,
110 ComboboxOutcome::Unchanged => Outcome::Unchanged,
111 ComboboxOutcome::Changed => Outcome::Changed,
112 ComboboxOutcome::Value => Outcome::Changed,
113 ComboboxOutcome::TextChanged => Outcome::Changed,
114 }
115 }
116 }
117
118 impl From<PopupOutcome> for ComboboxOutcome {
119 fn from(value: PopupOutcome) -> Self {
120 match value {
121 PopupOutcome::Continue => ComboboxOutcome::Continue,
122 PopupOutcome::Unchanged => ComboboxOutcome::Unchanged,
123 PopupOutcome::Changed => ComboboxOutcome::Changed,
124 PopupOutcome::Hide => ComboboxOutcome::Changed,
125 }
126 }
127 }
128}
129
130impl Default for ComboboxStyle {
131 fn default() -> Self {
132 Self {
133 choice: Default::default(),
134 text: Default::default(),
135 non_exhaustive: NonExhaustive,
136 }
137 }
138}
139
140impl Default for Combobox<'_> {
141 fn default() -> Self {
142 Self {
143 choice: Choice::default().skip_item_render(true),
144 text: Default::default(),
145 }
146 }
147}
148
149impl<'a> Combobox<'a> {
150 pub fn new() -> Self {
151 Self::default()
152 }
153
154 #[inline]
156 pub fn items<V: Into<Line<'a>>>(
157 mut self,
158 items: impl IntoIterator<Item = (String, V)>,
159 ) -> Self {
160 self.choice = self.choice.items(items);
161 self
162 }
163
164 pub fn item(mut self, value: String, item: impl Into<Line<'a>>) -> Self {
166 self.choice = self.choice.item(value, item);
167 self
168 }
169
170 pub fn default_value(mut self, default: String) -> Self {
172 self.choice = self.choice.default_value(default);
173 self
174 }
175
176 pub fn styles(mut self, styles: ComboboxStyle) -> Self {
178 self.choice = self.choice.styles(styles.choice);
179 self.text = self.text.styles(styles.text);
180 self
181 }
182
183 pub fn style(mut self, style: Style) -> Self {
185 self.choice = self.choice.style(style);
186 self.text = self.text.style(style);
187 self
188 }
189
190 pub fn button_style(mut self, style: Style) -> Self {
192 self.choice = self.choice.button_style(style);
193 self
194 }
195
196 pub fn select_style(mut self, style: Style) -> Self {
198 self.choice = self.choice.select_style(style);
199 self
200 }
201
202 pub fn focus_style(mut self, style: Style) -> Self {
204 self.choice = self.choice.focus_style(style);
205 self
206 }
207
208 pub fn text_style(mut self, style: TextStyle) -> Self {
210 self.text = self.text.styles(style);
211 self
212 }
213
214 pub fn block(mut self, block: Block<'a>) -> Self {
216 self.choice = self.choice.block(block);
217 self
218 }
219
220 pub fn popup_alignment(mut self, alignment: Alignment) -> Self {
225 self.choice = self.choice.popup_alignment(alignment);
226 self
227 }
228
229 pub fn popup_placement(mut self, placement: Placement) -> Self {
234 self.choice = self.choice.popup_placement(placement);
235 self
236 }
237
238 pub fn popup_boundary(mut self, boundary: Rect) -> Self {
240 self.choice = self.choice.popup_boundary(boundary);
241 self
242 }
243
244 pub fn popup_len(mut self, len: u16) -> Self {
249 self.choice = self.choice.popup_len(len);
250 self
251 }
252
253 pub fn popup_style(mut self, style: Style) -> Self {
255 self.choice = self.choice.popup_style(style);
256 self
257 }
258
259 pub fn popup_block(mut self, block: Block<'a>) -> Self {
261 self.choice = self.choice.popup_block(block);
262 self
263 }
264
265 pub fn popup_scroll(mut self, scroll: Scroll<'a>) -> Self {
267 self.choice = self.choice.popup_scroll(scroll);
268 self
269 }
270
271 pub fn popup_offset(mut self, offset: (i16, i16)) -> Self {
278 self.choice = self.choice.popup_offset(offset);
279 self
280 }
281
282 pub fn popup_x_offset(mut self, offset: i16) -> Self {
285 self.choice = self.choice.popup_x_offset(offset);
286 self
287 }
288
289 pub fn popup_y_offset(mut self, offset: i16) -> Self {
292 self.choice = self.choice.popup_y_offset(offset);
293 self
294 }
295
296 pub fn behave_focus(mut self, focus: ChoiceFocus) -> Self {
298 self.choice = self.choice.behave_focus(focus);
299 self
300 }
301
302 pub fn behave_select(mut self, select: ChoiceSelect) -> Self {
304 self.choice = self.choice.behave_select(select);
305 self
306 }
307
308 pub fn behave_close(mut self, close: ChoiceClose) -> Self {
310 self.choice = self.choice.behave_close(close);
311 self
312 }
313
314 pub fn width(&self) -> u16 {
316 self.choice.width()
317 }
318
319 pub fn height(&self) -> u16 {
321 self.choice.height()
322 }
323
324 pub fn into_widgets(self) -> (ComboboxWidget<'a>, ComboboxPopup<'a>) {
328 let (choice, choice_popup) = self.choice.into_widgets();
329 (
330 ComboboxWidget {
331 choice,
332 text: self.text,
333 },
334 ComboboxPopup {
335 choice: choice_popup,
336 },
337 )
338 }
339}
340
341impl<'a> ComboboxWidget<'a> {
342 pub fn width(&self) -> u16 {
344 self.choice.width()
345 }
346
347 pub fn height(&self) -> u16 {
349 self.choice.height()
350 }
351}
352
353impl<'a> StatefulWidget for &ComboboxWidget<'a> {
354 type State = ComboboxState;
355
356 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
357 render_combobox(self, area, buf, state);
358 }
359}
360
361impl StatefulWidget for ComboboxWidget<'_> {
362 type State = ComboboxState;
363
364 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
365 render_combobox(&self, area, buf, state);
366 }
367}
368
369fn render_combobox(
370 widget: &ComboboxWidget<'_>,
371 area: Rect,
372 buf: &mut Buffer,
373 state: &mut ComboboxState,
374) {
375 state.area = area;
376 (&widget.choice).render(area, buf, &mut state.choice);
377 (&widget.text).render(state.choice.item_area, buf, &mut state.text);
378}
379
380impl ComboboxPopup<'_> {
381 pub fn layout(&self, area: Rect, buf: &mut Buffer, state: &mut ComboboxState) -> Rect {
384 self.choice.layout(area, buf, &mut state.choice)
385 }
386}
387
388impl StatefulWidget for &ComboboxPopup<'_> {
389 type State = ComboboxState;
390
391 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
392 render_popup(self, area, buf, state);
393 }
394}
395
396impl StatefulWidget for ComboboxPopup<'_> {
397 type State = ComboboxState;
398
399 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
400 render_popup(&self, area, buf, state);
401 }
402}
403
404fn render_popup(
405 widget: &ComboboxPopup<'_>,
406 area: Rect,
407 buf: &mut Buffer,
408 state: &mut ComboboxState,
409) {
410 (&widget.choice).render(area, buf, &mut state.choice);
411}
412
413impl Clone for ComboboxState {
414 fn clone(&self) -> Self {
415 let mut text = self.text.clone();
416 let mut choice = self.choice.clone();
417 let focus = focus_cb("", text.focus, choice.focus);
418 text.focus = focus.clone();
419 choice.focus = focus.clone();
420
421 Self {
422 area: self.area,
423 choice,
424 text,
425 focus,
426 mouse: Default::default(),
427 non_exhaustive: NonExhaustive,
428 }
429 }
430}
431
432impl Default for ComboboxState {
433 fn default() -> Self {
434 let mut text = TextInputState::default();
435 let mut choice = ChoiceState::default();
436 let focus = focus_cb("", text.focus, choice.focus);
437 text.focus = focus.clone();
438 choice.focus = focus.clone();
439
440 Self {
441 area: Default::default(),
442 choice,
443 text,
444 focus,
445 mouse: Default::default(),
446 non_exhaustive: NonExhaustive,
447 }
448 }
449}
450
451fn focus_cb(name: &str, choice: FocusFlag, text: FocusFlag) -> FocusFlag {
452 let flag = FocusFlag::named(name);
453
454 let choice_clone = choice.clone();
455 let text_clone = text.clone();
456 flag.on_lost(move || {
457 choice_clone.call_on_lost();
458 text_clone.call_on_lost();
459 });
460 let choice_clone = choice.clone();
461 let text_clone = text.clone();
462 flag.on_gained(move || {
463 choice_clone.call_on_gained();
464 text_clone.call_on_gained();
465 });
466 flag
467}
468
469impl HasScreenCursor for ComboboxState {
470 fn screen_cursor(&self) -> Option<(u16, u16)> {
471 self.text.screen_cursor()
472 }
473}
474
475impl HasFocus for ComboboxState {
476 fn build(&self, builder: &mut FocusBuilder) {
477 builder.leaf_widget(self);
478 }
479
480 fn focus(&self) -> FocusFlag {
481 self.focus.clone()
482 }
483
484 fn area(&self) -> Rect {
485 self.area
486 }
487}
488
489impl RelocatableState for ComboboxState {
490 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
491 self.area = relocate_area(self.area, shift, clip);
492 self.choice.relocate(shift, clip);
493 self.text.relocate(shift, clip);
494 }
495}
496
497impl ComboboxState {
498 pub fn new() -> Self {
499 Self::default()
500 }
501
502 pub fn named(name: &str) -> Self {
503 let mut text = TextInputState::default();
504 let mut choice = ChoiceState::default();
505 let focus = focus_cb(name, text.focus, choice.focus);
506 text.focus = focus.clone();
507 choice.focus = focus.clone();
508
509 Self {
510 area: Default::default(),
511 choice,
512 text,
513 focus,
514 mouse: Default::default(),
515 non_exhaustive: NonExhaustive,
516 }
517 }
518
519 pub fn is_popup_active(&self) -> bool {
521 self.choice.is_popup_active()
522 }
523
524 pub fn flip_popup_active(&mut self) {
526 self.choice.flip_popup_active();
527 }
528
529 pub fn set_popup_active(&mut self, active: bool) -> bool {
531 self.choice.set_popup_active(active)
532 }
533
534 pub fn set_default_value(&mut self, default_value: Option<String>) {
543 self.choice.set_default_value(default_value);
544 }
545
546 pub fn default_value(&self) -> &Option<String> {
548 self.choice.default_value()
549 }
550
551 pub fn set_value(&mut self, value: impl Into<String>) -> bool {
562 let value = value.into();
563 self.text.set_value(value.clone());
564 self.choice.set_value(value)
565 }
566
567 pub fn value(&self) -> String {
569 self.text.value()
570 }
571
572 pub fn clear(&mut self) -> bool {
574 self.text.clear() || self.choice.clear()
575 }
576
577 pub fn select(&mut self, select: usize) -> bool {
589 if self.choice.select(select) {
590 self.text.set_value(self.choice.value());
591 true
592 } else {
593 false
594 }
595 }
596
597 pub fn selected(&self) -> Option<usize> {
602 self.choice.selected()
603 }
604
605 pub fn is_empty(&self) -> bool {
607 self.choice.is_empty()
608 }
609
610 pub fn len(&self) -> usize {
612 self.choice.len()
613 }
614
615 pub fn clear_offset(&mut self) {
617 self.choice.set_offset(0);
618 }
619
620 pub fn set_offset(&mut self, offset: usize) -> bool {
622 self.choice.set_offset(offset)
623 }
624
625 pub fn offset(&self) -> usize {
627 self.choice.offset()
628 }
629
630 pub fn max_offset(&self) -> usize {
632 self.choice.max_offset()
633 }
634
635 pub fn page_len(&self) -> usize {
637 self.choice.page_len()
638 }
639
640 pub fn scroll_by(&self) -> usize {
642 self.choice.scroll_by()
643 }
644
645 pub fn scroll_to_selected(&mut self) -> bool {
647 self.choice.scroll_to_selected()
648 }
649}
650
651impl ComboboxState {
652 pub fn select_by_char(&mut self, c: char) -> bool {
654 if self.choice.select_by_char(c) {
655 self.text.set_value(self.choice.value());
656 true
657 } else {
658 false
659 }
660 }
661
662 pub fn reverse_select_by_char(&mut self, c: char) -> bool {
664 if self.choice.reverse_select_by_char(c) {
665 self.text.set_value(self.choice.value());
666 true
667 } else {
668 false
669 }
670 }
671
672 pub fn move_to(&mut self, n: usize) -> ComboboxOutcome {
674 match self.choice.move_to(n) {
675 ChoiceOutcome::Continue => ComboboxOutcome::Continue,
676 ChoiceOutcome::Unchanged => ComboboxOutcome::Unchanged,
677 ChoiceOutcome::Changed => ComboboxOutcome::Changed,
678 ChoiceOutcome::Value => {
679 self.text.set_value(self.choice.value());
680 ComboboxOutcome::Value
681 }
682 }
683 }
684
685 pub fn move_down(&mut self, n: usize) -> ComboboxOutcome {
687 match self.choice.move_down(n) {
688 ChoiceOutcome::Continue => ComboboxOutcome::Continue,
689 ChoiceOutcome::Unchanged => ComboboxOutcome::Unchanged,
690 ChoiceOutcome::Changed => ComboboxOutcome::Changed,
691 ChoiceOutcome::Value => {
692 self.text.set_value(self.choice.value());
693 ComboboxOutcome::Value
694 }
695 }
696 }
697
698 pub fn move_up(&mut self, n: usize) -> ComboboxOutcome {
700 match self.choice.move_up(n) {
701 ChoiceOutcome::Continue => ComboboxOutcome::Continue,
702 ChoiceOutcome::Unchanged => ComboboxOutcome::Unchanged,
703 ChoiceOutcome::Changed => ComboboxOutcome::Changed,
704 ChoiceOutcome::Value => {
705 self.text.set_value(self.choice.value());
706 ComboboxOutcome::Value
707 }
708 }
709 }
710}
711
712impl HandleEvent<crossterm::event::Event, Popup, ComboboxOutcome> for ComboboxState {
713 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Popup) -> ComboboxOutcome {
714 let r = if self.is_focused() {
715 match event {
716 ct_event!(keycode press Esc) => {
717 if self.set_popup_active(false) {
718 ComboboxOutcome::Changed
719 } else {
720 ComboboxOutcome::Continue
721 }
722 }
723 ct_event!(keycode press Down) => self.move_down(1),
724 ct_event!(keycode press Up) => self.move_up(1),
725 ct_event!(keycode press PageUp) => self.move_up(self.page_len()),
726 ct_event!(keycode press PageDown) => self.move_down(self.page_len()),
727 ct_event!(keycode press ALT-Home) => self.move_to(0),
728 ct_event!(keycode press ALT-End) => self.move_to(self.len().saturating_sub(1)),
729 crossterm::event::Event::Key(_) => match self.text.handle(event, Regular) {
730 TextOutcome::Continue => ComboboxOutcome::Continue,
731 TextOutcome::Unchanged => ComboboxOutcome::Unchanged,
732 TextOutcome::Changed => ComboboxOutcome::Changed,
733 TextOutcome::TextChanged => ComboboxOutcome::TextChanged,
734 },
735 _ => ComboboxOutcome::Continue,
736 }
737 } else {
738 ComboboxOutcome::Continue
739 };
740
741 if !r.is_consumed() {
742 self.handle(event, MouseOnly)
743 } else {
744 r
745 }
746 }
747}
748
749impl HandleEvent<crossterm::event::Event, MouseOnly, ComboboxOutcome> for ComboboxState {
750 fn handle(
751 &mut self,
752 event: &crossterm::event::Event,
753 _qualifier: MouseOnly,
754 ) -> ComboboxOutcome {
755 let r0 = handle_mouse(self, event);
756 let r1 = handle_select(self, event);
757 let r2 = handle_close(self, event);
758 let mut r = max(r0, max(r1, r2));
759
760 r = r.or_else(|| match self.text.handle(event, MouseOnly) {
761 TextOutcome::Continue => ComboboxOutcome::Continue,
762 TextOutcome::Unchanged => ComboboxOutcome::Unchanged,
763 TextOutcome::Changed => ComboboxOutcome::Changed,
764 TextOutcome::TextChanged => ComboboxOutcome::TextChanged,
765 });
766 r = r.or_else(|| mouse_trap(event, self.choice.popup.area).into());
767
768 r
769 }
770}
771
772fn handle_mouse(state: &mut ComboboxState, event: &crossterm::event::Event) -> ComboboxOutcome {
773 match event {
774 ct_event!(mouse down Left for x,y)
775 if state.choice.button_area.contains((*x, *y).into()) =>
776 {
777 if !state.gained_focus() {
778 state.flip_popup_active();
779 ComboboxOutcome::Changed
780 } else {
781 ComboboxOutcome::Continue
784 }
785 }
786 ct_event!(mouse down Left for x,y)
787 | ct_event!(mouse down Right for x,y)
788 | ct_event!(mouse down Middle for x,y)
789 if !state.choice.item_area.contains((*x, *y).into())
790 && !state.choice.button_area.contains((*x, *y).into()) =>
791 {
792 match state.choice.popup.handle(event, Popup) {
793 PopupOutcome::Hide => {
794 state.set_popup_active(false);
795 ComboboxOutcome::Changed
796 }
797 r => r.into(),
798 }
799 }
800 _ => ComboboxOutcome::Continue,
801 }
802}
803
804fn handle_select(state: &mut ComboboxState, event: &crossterm::event::Event) -> ComboboxOutcome {
805 match state.choice.behave_select {
806 ChoiceSelect::MouseScroll => {
807 let mut sas = ScrollAreaState::new()
808 .area(state.choice.popup.area)
809 .v_scroll(&mut state.choice.popup_scroll);
810 let mut r = match sas.handle(event, MouseOnly) {
811 ScrollOutcome::Up(n) => state.move_up(n),
812 ScrollOutcome::Down(n) => state.move_down(n),
813 ScrollOutcome::VPos(n) => state.move_to(n),
814 _ => ComboboxOutcome::Continue,
815 };
816
817 r = r.or_else(|| match event {
818 ct_event!(mouse down Left for x,y)
819 if state.choice.popup.area.contains((*x, *y).into()) =>
820 {
821 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
822 state.move_to(state.offset() + n)
823 } else {
824 ComboboxOutcome::Unchanged
825 }
826 }
827 ct_event!(mouse drag Left for x,y)
828 if state.choice.popup.area.contains((*x, *y).into()) =>
829 {
830 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
831 state.move_to(state.offset() + n)
832 } else {
833 ComboboxOutcome::Unchanged
834 }
835 }
836 _ => ComboboxOutcome::Continue,
837 });
838 r
839 }
840 ChoiceSelect::MouseMove => {
841 let mut r = if let Some(selected) = state.choice.core.selected() {
843 let rel_sel = selected.saturating_sub(state.offset());
844 let mut sas = ScrollAreaState::new()
845 .area(state.choice.popup.area)
846 .v_scroll(&mut state.choice.popup_scroll);
847 match sas.handle(event, MouseOnly) {
848 ScrollOutcome::Up(n) => {
849 state.choice.popup_scroll.scroll_up(n);
850 if state.select(state.offset() + rel_sel) {
851 ComboboxOutcome::Value
852 } else {
853 ComboboxOutcome::Unchanged
854 }
855 }
856 ScrollOutcome::Down(n) => {
857 state.choice.popup_scroll.scroll_down(n);
858 if state.select(state.offset() + rel_sel) {
859 ComboboxOutcome::Value
860 } else {
861 ComboboxOutcome::Unchanged
862 }
863 }
864 ScrollOutcome::VPos(n) => {
865 if state.choice.popup_scroll.set_offset(n) {
866 ComboboxOutcome::Value
867 } else {
868 ComboboxOutcome::Unchanged
869 }
870 }
871 _ => ComboboxOutcome::Continue,
872 }
873 } else {
874 ComboboxOutcome::Continue
875 };
876
877 r = r.or_else(|| match event {
878 ct_event!(mouse moved for x,y)
879 if state.choice.popup.area.contains((*x, *y).into()) =>
880 {
881 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
882 state.move_to(state.offset() + n)
883 } else {
884 ComboboxOutcome::Unchanged
885 }
886 }
887 _ => ComboboxOutcome::Continue,
888 });
889 r
890 }
891 ChoiceSelect::MouseClick => {
892 let mut sas = ScrollAreaState::new()
894 .area(state.choice.popup.area)
895 .v_scroll(&mut state.choice.popup_scroll);
896 let mut r = match sas.handle(event, MouseOnly) {
897 ScrollOutcome::Up(n) => {
898 if state.choice.popup_scroll.scroll_up(n) {
899 ComboboxOutcome::Changed
900 } else {
901 ComboboxOutcome::Unchanged
902 }
903 }
904 ScrollOutcome::Down(n) => {
905 if state.choice.popup_scroll.scroll_down(n) {
906 ComboboxOutcome::Changed
907 } else {
908 ComboboxOutcome::Unchanged
909 }
910 }
911 ScrollOutcome::VPos(n) => {
912 if state.choice.popup_scroll.set_offset(n) {
913 ComboboxOutcome::Changed
914 } else {
915 ComboboxOutcome::Unchanged
916 }
917 }
918 _ => ComboboxOutcome::Continue,
919 };
920
921 r = r.or_else(|| match event {
922 ct_event!(mouse down Left for x,y)
923 if state.choice.popup.area.contains((*x, *y).into()) =>
924 {
925 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
926 state.move_to(state.offset() + n)
927 } else {
928 ComboboxOutcome::Unchanged
929 }
930 }
931 ct_event!(mouse drag Left for x,y)
932 if state.choice.popup.area.contains((*x, *y).into()) =>
933 {
934 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
935 state.move_to(state.offset() + n)
936 } else {
937 ComboboxOutcome::Unchanged
938 }
939 }
940 _ => ComboboxOutcome::Continue,
941 });
942 r
943 }
944 }
945}
946
947fn handle_close(state: &mut ComboboxState, event: &crossterm::event::Event) -> ComboboxOutcome {
948 match state.choice.behave_close {
949 ChoiceClose::SingleClick => match event {
950 ct_event!(mouse down Left for x,y)
951 if state.choice.popup.area.contains((*x, *y).into()) =>
952 {
953 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
954 let r = state.move_to(state.offset() + n);
955 let s = if state.set_popup_active(false) {
956 ComboboxOutcome::Changed
957 } else {
958 ComboboxOutcome::Unchanged
959 };
960 max(r, s)
961 } else {
962 ComboboxOutcome::Unchanged
963 }
964 }
965 _ => ComboboxOutcome::Continue,
966 },
967 ChoiceClose::DoubleClick => match event {
968 ct_event!(mouse any for m) if state.mouse.doubleclick(state.choice.popup.area, m) => {
969 if let Some(n) = item_at(&state.choice.item_areas, m.column, m.row) {
970 let r = state.move_to(state.offset() + n);
971 let s = if state.set_popup_active(false) {
972 ComboboxOutcome::Changed
973 } else {
974 ComboboxOutcome::Unchanged
975 };
976 max(r, s)
977 } else {
978 ComboboxOutcome::Unchanged
979 }
980 }
981 _ => ComboboxOutcome::Continue,
982 },
983 }
984}
985
986pub fn handle_events(
990 state: &mut ComboboxState,
991 focus: bool,
992 event: &crossterm::event::Event,
993) -> ComboboxOutcome {
994 state.focus.set(focus);
995 HandleEvent::handle(state, event, Popup)
996}
997
998pub fn handle_mouse_events(
1000 state: &mut ComboboxState,
1001 event: &crossterm::event::Event,
1002) -> ComboboxOutcome {
1003 HandleEvent::handle(state, event, MouseOnly)
1004}