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, Navigation};
12use rat_popup::Placement;
13use rat_popup::event::PopupOutcome;
14use rat_reloc::RelocatableState;
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_core::buffer::Buffer;
21use ratatui_core::layout::{Alignment, Rect};
22use ratatui_core::style::Style;
23use ratatui_core::text::Line;
24use ratatui_core::widgets::StatefulWidget;
25use ratatui_crossterm::crossterm::event::Event;
26use ratatui_widgets::block::Block;
27use std::cmp::max;
28
29#[derive(Debug, Clone)]
30pub struct Combobox<'a> {
31 choice: Choice<'a, String>,
32 text: TextInput<'a>,
33}
34
35#[derive(Debug)]
36pub struct ComboboxWidget<'a> {
37 choice: ChoiceWidget<'a, String>,
38 text: TextInput<'a>,
39}
40
41#[derive(Debug)]
42pub struct ComboboxPopup<'a> {
43 choice: ChoicePopup<'a, String>,
44}
45
46#[derive(Debug, Clone)]
47pub struct ComboboxStyle {
48 pub choice: ChoiceStyle,
49 pub text: TextStyle,
50
51 pub non_exhaustive: NonExhaustive,
52}
53
54#[derive(Debug)]
55pub struct ComboboxState {
56 pub area: Rect,
59 pub inner: Rect,
61 pub choice: ChoiceState<String>,
63 pub text: TextInputState,
65
66 pub focus: FocusFlag,
69 pub mouse: MouseFlags,
71
72 pub non_exhaustive: NonExhaustive,
73}
74
75pub(crate) mod event {
76 use rat_event::{ConsumedEvent, Outcome};
77 use rat_popup::event::PopupOutcome;
78
79 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
81 pub enum ComboboxOutcome {
82 Continue,
84 Unchanged,
86 Changed,
88 Value,
90 TextChanged,
92 }
93
94 impl ConsumedEvent for ComboboxOutcome {
95 fn is_consumed(&self) -> bool {
96 *self != ComboboxOutcome::Continue
97 }
98 }
99
100 impl From<Outcome> for ComboboxOutcome {
101 fn from(value: Outcome) -> Self {
102 match value {
103 Outcome::Continue => ComboboxOutcome::Continue,
104 Outcome::Unchanged => ComboboxOutcome::Unchanged,
105 Outcome::Changed => ComboboxOutcome::Changed,
106 }
107 }
108 }
109
110 impl From<ComboboxOutcome> for Outcome {
111 fn from(value: ComboboxOutcome) -> Self {
112 match value {
113 ComboboxOutcome::Continue => Outcome::Continue,
114 ComboboxOutcome::Unchanged => Outcome::Unchanged,
115 ComboboxOutcome::Changed => Outcome::Changed,
116 ComboboxOutcome::Value => Outcome::Changed,
117 ComboboxOutcome::TextChanged => Outcome::Changed,
118 }
119 }
120 }
121
122 impl From<PopupOutcome> for ComboboxOutcome {
123 fn from(value: PopupOutcome) -> Self {
124 match value {
125 PopupOutcome::Continue => ComboboxOutcome::Continue,
126 PopupOutcome::Unchanged => ComboboxOutcome::Unchanged,
127 PopupOutcome::Changed => ComboboxOutcome::Changed,
128 PopupOutcome::Hide => ComboboxOutcome::Changed,
129 }
130 }
131 }
132}
133
134impl Default for ComboboxStyle {
135 fn default() -> Self {
136 Self {
137 choice: Default::default(),
138 text: Default::default(),
139 non_exhaustive: NonExhaustive,
140 }
141 }
142}
143
144impl Default for Combobox<'_> {
145 fn default() -> Self {
146 Self {
147 choice: Choice::default().skip_item_render(true),
148 text: Default::default(),
149 }
150 }
151}
152
153impl<'a> Combobox<'a> {
154 pub fn new() -> Self {
155 Self::default()
156 }
157
158 #[inline]
160 pub fn items<V: Into<Line<'a>>>(
161 mut self,
162 items: impl IntoIterator<Item = (String, V)>,
163 ) -> Self {
164 self.choice = self.choice.items(items);
165 self
166 }
167
168 pub fn item(mut self, value: impl Into<String>, item: impl Into<Line<'a>>) -> Self {
170 self.choice = self.choice.item(value.into(), item);
171 self
172 }
173
174 pub fn default_value(mut self, default: String) -> Self {
176 self.choice = self.choice.default_value(default);
177 self
178 }
179
180 pub fn styles(mut self, styles: ComboboxStyle) -> Self {
182 self.choice = self.choice.styles(styles.choice);
183 self.text = self.text.styles(styles.text);
184 self
185 }
186
187 pub fn style(mut self, style: Style) -> Self {
189 self.choice = self.choice.style(style);
190 self.text = self.text.style(style);
191 self
192 }
193
194 pub fn button_style(mut self, style: Style) -> Self {
196 self.choice = self.choice.button_style(style);
197 self
198 }
199
200 pub fn select_style(mut self, style: Style) -> Self {
202 self.choice = self.choice.select_style(style);
203 self
204 }
205
206 pub fn focus_style(mut self, style: Style) -> Self {
208 self.choice = self.choice.focus_style(style);
209 self
210 }
211
212 pub fn text_style(mut self, style: TextStyle) -> Self {
214 self.text = self.text.styles(style);
215 self
216 }
217
218 pub fn block(mut self, block: Block<'a>) -> Self {
220 self.choice = self.choice.block(block);
221 self
222 }
223
224 pub fn popup_alignment(mut self, alignment: Alignment) -> Self {
229 self.choice = self.choice.popup_alignment(alignment);
230 self
231 }
232
233 pub fn popup_placement(mut self, placement: Placement) -> Self {
238 self.choice = self.choice.popup_placement(placement);
239 self
240 }
241
242 pub fn popup_boundary(mut self, boundary: Rect) -> Self {
244 self.choice = self.choice.popup_boundary(boundary);
245 self
246 }
247
248 pub fn popup_len(mut self, len: u16) -> Self {
253 self.choice = self.choice.popup_len(len);
254 self
255 }
256
257 pub fn popup_style(mut self, style: Style) -> Self {
259 self.choice = self.choice.popup_style(style);
260 self
261 }
262
263 pub fn popup_block(mut self, block: Block<'a>) -> Self {
265 self.choice = self.choice.popup_block(block);
266 self
267 }
268
269 pub fn popup_scroll(mut self, scroll: Scroll<'a>) -> Self {
271 self.choice = self.choice.popup_scroll(scroll);
272 self
273 }
274
275 pub fn popup_offset(mut self, offset: (i16, i16)) -> Self {
282 self.choice = self.choice.popup_offset(offset);
283 self
284 }
285
286 pub fn popup_x_offset(mut self, offset: i16) -> Self {
289 self.choice = self.choice.popup_x_offset(offset);
290 self
291 }
292
293 pub fn popup_y_offset(mut self, offset: i16) -> Self {
296 self.choice = self.choice.popup_y_offset(offset);
297 self
298 }
299
300 pub fn behave_focus(mut self, focus: ChoiceFocus) -> Self {
302 self.choice = self.choice.behave_focus(focus);
303 self
304 }
305
306 pub fn behave_select(mut self, select: ChoiceSelect) -> Self {
308 self.choice = self.choice.behave_select(select);
309 self
310 }
311
312 pub fn behave_close(mut self, close: ChoiceClose) -> Self {
314 self.choice = self.choice.behave_close(close);
315 self
316 }
317
318 pub fn width(&self) -> u16 {
320 self.choice.width()
321 }
322
323 pub fn height(&self) -> u16 {
325 self.choice.height()
326 }
327
328 pub fn into_widgets(self) -> (ComboboxWidget<'a>, ComboboxPopup<'a>) {
332 let (choice, choice_popup) = self.choice.into_widgets();
333 (
334 ComboboxWidget {
335 choice,
336 text: self.text,
337 },
338 ComboboxPopup {
339 choice: choice_popup,
340 },
341 )
342 }
343}
344
345impl<'a> ComboboxWidget<'a> {
346 pub fn width(&self) -> u16 {
348 self.choice.width()
349 }
350
351 pub fn height(&self) -> u16 {
353 self.choice.height()
354 }
355}
356
357impl<'a> StatefulWidget for &ComboboxWidget<'a> {
358 type State = ComboboxState;
359
360 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
361 render_combobox(self, area, buf, state);
362 }
363}
364
365impl StatefulWidget for ComboboxWidget<'_> {
366 type State = ComboboxState;
367
368 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
369 render_combobox(&self, area, buf, state);
370 }
371}
372
373fn render_combobox(
374 widget: &ComboboxWidget<'_>,
375 area: Rect,
376 buf: &mut Buffer,
377 state: &mut ComboboxState,
378) {
379 state.area = area;
380 (&widget.choice).render(area, buf, &mut state.choice);
381 state.inner = state.choice.inner;
382 (&widget.text).render(state.choice.item_area, buf, &mut state.text);
383}
384
385impl ComboboxPopup<'_> {
386 pub fn layout(&self, area: Rect, buf: &mut Buffer, state: &mut ComboboxState) -> Rect {
389 self.choice.layout(area, buf, &mut state.choice)
390 }
391}
392
393impl StatefulWidget for &ComboboxPopup<'_> {
394 type State = ComboboxState;
395
396 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
397 render_popup(self, area, buf, state);
398 }
399}
400
401impl StatefulWidget for ComboboxPopup<'_> {
402 type State = ComboboxState;
403
404 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
405 render_popup(&self, area, buf, state);
406 }
407}
408
409fn render_popup(
410 widget: &ComboboxPopup<'_>,
411 _area: Rect,
412 buf: &mut Buffer,
413 state: &mut ComboboxState,
414) {
415 (&widget.choice).render(Rect::default(), buf, &mut state.choice);
416}
417
418impl Clone for ComboboxState {
419 fn clone(&self) -> Self {
420 let mut text = self.text.clone();
421 let mut choice = self.choice.clone();
422 let focus = focus_cb(self.focus.new_instance(), text.focus, choice.focus);
423 text.focus = focus.clone();
424 choice.focus = focus.clone();
425
426 Self {
427 area: self.area,
428 inner: self.inner,
429 choice,
430 text,
431 focus,
432 mouse: Default::default(),
433 non_exhaustive: NonExhaustive,
434 }
435 }
436}
437
438impl Default for ComboboxState {
439 fn default() -> Self {
440 let mut text = TextInputState::default();
441 let mut choice = ChoiceState::default();
442 let focus = focus_cb(FocusFlag::default(), text.focus, choice.focus);
443 text.focus = focus.clone();
444 choice.focus = focus.clone();
445
446 Self {
447 area: Default::default(),
448 inner: Default::default(),
449 choice,
450 text,
451 focus,
452 mouse: Default::default(),
453 non_exhaustive: NonExhaustive,
454 }
455 }
456}
457
458fn focus_cb(flag: FocusFlag, choice: FocusFlag, text: FocusFlag) -> FocusFlag {
459 let choice_clone = choice.clone();
460 let text_clone = text.clone();
461 flag.on_lost(move || {
462 choice_clone.call_on_lost();
463 text_clone.call_on_lost();
464 });
465 let choice_clone = choice.clone();
466 let text_clone = text.clone();
467 flag.on_gained(move || {
468 choice_clone.call_on_gained();
469 text_clone.call_on_gained();
470 });
471 flag
472}
473
474impl HasScreenCursor for ComboboxState {
475 fn screen_cursor(&self) -> Option<(u16, u16)> {
476 self.text.screen_cursor()
477 }
478}
479
480impl HasFocus for ComboboxState {
481 fn build(&self, builder: &mut FocusBuilder) {
482 builder.leaf_with_flags(self.focus(), self.area(), 0, self.navigable());
483 builder.leaf_with_flags(self.focus(), self.choice.popup.area, 1, Navigation::Mouse);
484 }
485
486 fn focus(&self) -> FocusFlag {
487 self.focus.clone()
488 }
489
490 fn area(&self) -> Rect {
491 self.area
492 }
493}
494
495impl RelocatableState for ComboboxState {
496 fn relocate(&mut self, _shift: (i16, i16), _clip: Rect) {
497 }
499
500 fn relocate_popup(&mut self, shift: (i16, i16), clip: Rect) {
501 self.area.relocate(shift, clip);
502 self.inner.relocate(shift, clip);
503 self.choice.relocate(shift, clip);
504 self.text.relocate(shift, clip);
505 self.choice.relocate_popup(shift, clip);
506 }
507}
508
509impl ComboboxState {
510 pub fn new() -> Self {
511 Self::default()
512 }
513
514 pub fn named(name: &str) -> Self {
515 let mut text = TextInputState::default();
516 let mut choice = ChoiceState::default();
517 let focus = focus_cb(FocusFlag::new().with_name(name), text.focus, choice.focus);
518 text.focus = focus.clone();
519 choice.focus = focus.clone();
520
521 Self {
522 area: Default::default(),
523 inner: Default::default(),
524 choice,
525 text,
526 focus,
527 mouse: Default::default(),
528 non_exhaustive: NonExhaustive,
529 }
530 }
531
532 pub fn is_popup_active(&self) -> bool {
534 self.choice.is_popup_active()
535 }
536
537 pub fn flip_popup_active(&mut self) {
539 self.choice.flip_popup_active();
540 }
541
542 pub fn set_popup_active(&mut self, active: bool) -> bool {
544 self.choice.set_popup_active(active)
545 }
546
547 pub fn set_default_value(&mut self, default_value: Option<String>) {
556 self.choice.set_default_value(default_value);
557 }
558
559 pub fn default_value(&self) -> &Option<String> {
561 self.choice.default_value()
562 }
563
564 pub fn set_value(&mut self, value: impl Into<String>) -> bool {
575 let value = value.into();
576 self.text.set_value(value.clone());
577 self.choice.set_value(value)
578 }
579
580 pub fn value(&self) -> String {
582 self.text.value()
583 }
584
585 pub fn clear(&mut self) -> bool {
587 self.text.clear() || self.choice.clear()
588 }
589
590 pub fn select(&mut self, select: usize) -> bool {
602 if self.choice.select(select) {
603 self.text.set_value(self.choice.value());
604 true
605 } else {
606 false
607 }
608 }
609
610 pub fn selected(&self) -> Option<usize> {
615 self.choice.selected()
616 }
617
618 pub fn is_empty(&self) -> bool {
620 self.choice.is_empty()
621 }
622
623 pub fn len(&self) -> usize {
625 self.choice.len()
626 }
627
628 pub fn clear_offset(&mut self) {
630 self.choice.set_offset(0);
631 }
632
633 pub fn set_offset(&mut self, offset: usize) -> bool {
635 self.choice.set_offset(offset)
636 }
637
638 pub fn offset(&self) -> usize {
640 self.choice.offset()
641 }
642
643 pub fn max_offset(&self) -> usize {
645 self.choice.max_offset()
646 }
647
648 pub fn page_len(&self) -> usize {
650 self.choice.page_len()
651 }
652
653 pub fn scroll_by(&self) -> usize {
655 self.choice.scroll_by()
656 }
657
658 pub fn scroll_to_selected(&mut self) -> bool {
660 self.choice.scroll_to_selected()
661 }
662}
663
664impl ComboboxState {
665 pub fn select_by_char(&mut self, c: char) -> bool {
667 if self.choice.select_by_char(c) {
668 self.text.set_value(self.choice.value());
669 true
670 } else {
671 false
672 }
673 }
674
675 pub fn reverse_select_by_char(&mut self, c: char) -> bool {
677 if self.choice.reverse_select_by_char(c) {
678 self.text.set_value(self.choice.value());
679 true
680 } else {
681 false
682 }
683 }
684
685 pub fn move_to(&mut self, n: usize) -> ComboboxOutcome {
687 match self.choice.move_to(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_down(&mut self, n: usize) -> ComboboxOutcome {
700 match self.choice.move_down(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 pub fn move_up(&mut self, n: usize) -> ComboboxOutcome {
713 match self.choice.move_up(n) {
714 ChoiceOutcome::Continue => ComboboxOutcome::Continue,
715 ChoiceOutcome::Unchanged => ComboboxOutcome::Unchanged,
716 ChoiceOutcome::Changed => ComboboxOutcome::Changed,
717 ChoiceOutcome::Value => {
718 self.text.set_value(self.choice.value());
719 ComboboxOutcome::Value
720 }
721 }
722 }
723}
724
725impl HandleEvent<Event, Regular, ComboboxOutcome> for ComboboxState {
726 fn handle(&mut self, event: &Event, _qualifier: Regular) -> ComboboxOutcome {
727 self.handle(event, Popup)
728 }
729}
730
731impl HandleEvent<Event, Popup, ComboboxOutcome> for ComboboxState {
732 fn handle(&mut self, event: &Event, _qualifier: Popup) -> ComboboxOutcome {
733 let r = if self.is_focused() {
734 match event {
735 ct_event!(keycode press Enter) => {
736 self.flip_popup_active();
737 ComboboxOutcome::Changed
738 }
739 ct_event!(keycode press Esc) => {
740 if self.set_popup_active(false) {
741 ComboboxOutcome::Changed
742 } else {
743 ComboboxOutcome::Continue
744 }
745 }
746 ct_event!(keycode press Down) => self.move_down(1),
747 ct_event!(keycode press Up) => self.move_up(1),
748 ct_event!(keycode press PageUp) => self.move_up(self.page_len()),
749 ct_event!(keycode press PageDown) => self.move_down(self.page_len()),
750 ct_event!(keycode press ALT-Home) => self.move_to(0),
751 ct_event!(keycode press ALT-End) => self.move_to(self.len().saturating_sub(1)),
752 Event::Key(_) => match self.text.handle(event, Regular) {
753 TextOutcome::Continue => ComboboxOutcome::Continue,
754 TextOutcome::Unchanged => ComboboxOutcome::Unchanged,
755 TextOutcome::Changed => ComboboxOutcome::Changed,
756 TextOutcome::TextChanged => ComboboxOutcome::TextChanged,
757 },
758 _ => ComboboxOutcome::Continue,
759 }
760 } else {
761 ComboboxOutcome::Continue
762 };
763
764 if !r.is_consumed() {
765 self.handle(event, MouseOnly)
766 } else {
767 r
768 }
769 }
770}
771
772impl HandleEvent<Event, MouseOnly, ComboboxOutcome> for ComboboxState {
773 fn handle(&mut self, event: &Event, _qualifier: MouseOnly) -> ComboboxOutcome {
774 let r0 = handle_mouse(self, event);
775 let r1 = handle_select(self, event);
776 let r2 = handle_close(self, event);
777 let mut r = max(r0, max(r1, r2));
778
779 r = r.or_else(|| match self.text.handle(event, MouseOnly) {
780 TextOutcome::Continue => ComboboxOutcome::Continue,
781 TextOutcome::Unchanged => ComboboxOutcome::Unchanged,
782 TextOutcome::Changed => ComboboxOutcome::Changed,
783 TextOutcome::TextChanged => ComboboxOutcome::TextChanged,
784 });
785 r = r.or_else(|| mouse_trap(event, self.choice.popup.area).into());
786
787 r
788 }
789}
790
791fn handle_mouse(state: &mut ComboboxState, event: &Event) -> ComboboxOutcome {
792 match event {
793 ct_event!(mouse down Left for x,y)
794 | ct_event!(mouse down Right for x,y)
795 | ct_event!(mouse down Middle for x,y)
796 if !state.choice.item_area.contains((*x, *y).into())
797 && !state.choice.button_area.contains((*x, *y).into()) =>
798 {
799 match state.choice.popup.handle(event, Popup) {
800 PopupOutcome::Hide => {
801 state.set_popup_active(false);
802 return ComboboxOutcome::Changed;
803 }
804 _ => {}
805 }
806 }
807 _ => {}
808 }
809
810 if !state.has_mouse_focus() {
811 return ComboboxOutcome::Continue;
812 }
813
814 match event {
815 ct_event!(mouse down Left for x,y)
816 if state.choice.button_area.contains((*x, *y).into()) =>
817 {
818 if !state.gained_focus() {
819 state.flip_popup_active();
820 ComboboxOutcome::Changed
821 } else {
822 ComboboxOutcome::Continue
825 }
826 }
827 _ => ComboboxOutcome::Continue,
828 }
829}
830
831fn handle_select(state: &mut ComboboxState, event: &Event) -> ComboboxOutcome {
832 if !state.has_mouse_focus() {
833 return ComboboxOutcome::Continue;
834 }
835
836 match state.choice.behave_select {
837 ChoiceSelect::MouseScroll => {
838 let mut sas = ScrollAreaState::new()
839 .area(state.choice.popup.area)
840 .v_scroll(&mut state.choice.popup_scroll);
841 let mut r = match sas.handle(event, MouseOnly) {
842 ScrollOutcome::Up(n) => state.move_up(n),
843 ScrollOutcome::Down(n) => state.move_down(n),
844 ScrollOutcome::VPos(n) => state.move_to(n),
845 _ => ComboboxOutcome::Continue,
846 };
847
848 r = r.or_else(|| match event {
849 ct_event!(mouse down Left for x,y)
850 if state.choice.popup.area.contains((*x, *y).into()) =>
851 {
852 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
853 state.move_to(state.offset() + n)
854 } else {
855 ComboboxOutcome::Unchanged
856 }
857 }
858 ct_event!(mouse drag Left for x,y)
859 if state.choice.popup.area.contains((*x, *y).into()) =>
860 {
861 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
862 state.move_to(state.offset() + n)
863 } else {
864 ComboboxOutcome::Unchanged
865 }
866 }
867 _ => ComboboxOutcome::Continue,
868 });
869 r
870 }
871 ChoiceSelect::MouseMove => {
872 let mut r = if let Some(selected) = state.choice.core.selected() {
874 let rel_sel = selected.saturating_sub(state.offset());
875 let mut sas = ScrollAreaState::new()
876 .area(state.choice.popup.area)
877 .v_scroll(&mut state.choice.popup_scroll);
878 match sas.handle(event, MouseOnly) {
879 ScrollOutcome::Up(n) => {
880 state.choice.popup_scroll.scroll_up(n);
881 if state.select(state.offset() + rel_sel) {
882 ComboboxOutcome::Value
883 } else {
884 ComboboxOutcome::Unchanged
885 }
886 }
887 ScrollOutcome::Down(n) => {
888 state.choice.popup_scroll.scroll_down(n);
889 if state.select(state.offset() + rel_sel) {
890 ComboboxOutcome::Value
891 } else {
892 ComboboxOutcome::Unchanged
893 }
894 }
895 ScrollOutcome::VPos(n) => {
896 if state.choice.popup_scroll.set_offset(n) {
897 ComboboxOutcome::Value
898 } else {
899 ComboboxOutcome::Unchanged
900 }
901 }
902 _ => ComboboxOutcome::Continue,
903 }
904 } else {
905 ComboboxOutcome::Continue
906 };
907
908 r = r.or_else(|| match event {
909 ct_event!(mouse moved for x,y)
910 if state.choice.popup.area.contains((*x, *y).into()) =>
911 {
912 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
913 state.move_to(state.offset() + n)
914 } else {
915 ComboboxOutcome::Unchanged
916 }
917 }
918 _ => ComboboxOutcome::Continue,
919 });
920 r
921 }
922 ChoiceSelect::MouseClick => {
923 let mut sas = ScrollAreaState::new()
925 .area(state.choice.popup.area)
926 .v_scroll(&mut state.choice.popup_scroll);
927 let mut r = match sas.handle(event, MouseOnly) {
928 ScrollOutcome::Up(n) => {
929 if state.choice.popup_scroll.scroll_up(n) {
930 ComboboxOutcome::Changed
931 } else {
932 ComboboxOutcome::Unchanged
933 }
934 }
935 ScrollOutcome::Down(n) => {
936 if state.choice.popup_scroll.scroll_down(n) {
937 ComboboxOutcome::Changed
938 } else {
939 ComboboxOutcome::Unchanged
940 }
941 }
942 ScrollOutcome::VPos(n) => {
943 if state.choice.popup_scroll.set_offset(n) {
944 ComboboxOutcome::Changed
945 } else {
946 ComboboxOutcome::Unchanged
947 }
948 }
949 _ => ComboboxOutcome::Continue,
950 };
951
952 r = r.or_else(|| match event {
953 ct_event!(mouse down Left for x,y)
954 if state.choice.popup.area.contains((*x, *y).into()) =>
955 {
956 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
957 state.move_to(state.offset() + n)
958 } else {
959 ComboboxOutcome::Unchanged
960 }
961 }
962 ct_event!(mouse drag Left for x,y)
963 if state.choice.popup.area.contains((*x, *y).into()) =>
964 {
965 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
966 state.move_to(state.offset() + n)
967 } else {
968 ComboboxOutcome::Unchanged
969 }
970 }
971 _ => ComboboxOutcome::Continue,
972 });
973 r
974 }
975 }
976}
977
978fn handle_close(state: &mut ComboboxState, event: &Event) -> ComboboxOutcome {
979 if !state.has_mouse_focus() {
980 return ComboboxOutcome::Continue;
981 }
982
983 match state.choice.behave_close {
984 ChoiceClose::SingleClick => match event {
985 ct_event!(mouse down Left for x,y)
986 if state.choice.popup.area.contains((*x, *y).into()) =>
987 {
988 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
989 let r = state.move_to(state.offset() + n);
990 let s = if state.set_popup_active(false) {
991 ComboboxOutcome::Changed
992 } else {
993 ComboboxOutcome::Unchanged
994 };
995 max(r, s)
996 } else {
997 ComboboxOutcome::Unchanged
998 }
999 }
1000 _ => ComboboxOutcome::Continue,
1001 },
1002 ChoiceClose::DoubleClick => match event {
1003 ct_event!(mouse any for m) if state.mouse.doubleclick(state.choice.popup.area, m) => {
1004 if let Some(n) = item_at(&state.choice.item_areas, m.column, m.row) {
1005 let r = state.move_to(state.offset() + n);
1006 let s = if state.set_popup_active(false) {
1007 ComboboxOutcome::Changed
1008 } else {
1009 ComboboxOutcome::Unchanged
1010 };
1011 max(r, s)
1012 } else {
1013 ComboboxOutcome::Unchanged
1014 }
1015 }
1016 _ => ComboboxOutcome::Continue,
1017 },
1018 }
1019}
1020
1021pub fn handle_events(state: &mut ComboboxState, focus: bool, event: &Event) -> ComboboxOutcome {
1025 state.focus.set(focus);
1026 HandleEvent::handle(state, event, Popup)
1027}
1028
1029pub fn handle_mouse_events(state: &mut ComboboxState, event: &Event) -> ComboboxOutcome {
1031 HandleEvent::handle(state, event, MouseOnly)
1032}