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.widget_with_flags(self.focus(), self.area(), 0, self.navigable());
483 builder.widget_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, Popup, ComboboxOutcome> for ComboboxState {
726 fn handle(&mut self, event: &Event, _qualifier: Popup) -> ComboboxOutcome {
727 let r = if self.is_focused() {
728 match event {
729 ct_event!(keycode press Enter) => {
730 self.flip_popup_active();
731 ComboboxOutcome::Changed
732 }
733 ct_event!(keycode press Esc) => {
734 if self.set_popup_active(false) {
735 ComboboxOutcome::Changed
736 } else {
737 ComboboxOutcome::Continue
738 }
739 }
740 ct_event!(keycode press Down) => self.move_down(1),
741 ct_event!(keycode press Up) => self.move_up(1),
742 ct_event!(keycode press PageUp) => self.move_up(self.page_len()),
743 ct_event!(keycode press PageDown) => self.move_down(self.page_len()),
744 ct_event!(keycode press ALT-Home) => self.move_to(0),
745 ct_event!(keycode press ALT-End) => self.move_to(self.len().saturating_sub(1)),
746 Event::Key(_) => match self.text.handle(event, Regular) {
747 TextOutcome::Continue => ComboboxOutcome::Continue,
748 TextOutcome::Unchanged => ComboboxOutcome::Unchanged,
749 TextOutcome::Changed => ComboboxOutcome::Changed,
750 TextOutcome::TextChanged => ComboboxOutcome::TextChanged,
751 },
752 _ => ComboboxOutcome::Continue,
753 }
754 } else {
755 ComboboxOutcome::Continue
756 };
757
758 if !r.is_consumed() {
759 self.handle(event, MouseOnly)
760 } else {
761 r
762 }
763 }
764}
765
766impl HandleEvent<Event, MouseOnly, ComboboxOutcome> for ComboboxState {
767 fn handle(&mut self, event: &Event, _qualifier: MouseOnly) -> ComboboxOutcome {
768 let r0 = handle_mouse(self, event);
769 let r1 = handle_select(self, event);
770 let r2 = handle_close(self, event);
771 let mut r = max(r0, max(r1, r2));
772
773 r = r.or_else(|| match self.text.handle(event, MouseOnly) {
774 TextOutcome::Continue => ComboboxOutcome::Continue,
775 TextOutcome::Unchanged => ComboboxOutcome::Unchanged,
776 TextOutcome::Changed => ComboboxOutcome::Changed,
777 TextOutcome::TextChanged => ComboboxOutcome::TextChanged,
778 });
779 r = r.or_else(|| mouse_trap(event, self.choice.popup.area).into());
780
781 r
782 }
783}
784
785fn handle_mouse(state: &mut ComboboxState, event: &Event) -> ComboboxOutcome {
786 match event {
787 ct_event!(mouse down Left for x,y)
788 if state.choice.button_area.contains((*x, *y).into()) =>
789 {
790 if !state.gained_focus() {
791 state.flip_popup_active();
792 ComboboxOutcome::Changed
793 } else {
794 ComboboxOutcome::Continue
797 }
798 }
799 ct_event!(mouse down Left for x,y)
800 | ct_event!(mouse down Right for x,y)
801 | ct_event!(mouse down Middle for x,y)
802 if !state.choice.item_area.contains((*x, *y).into())
803 && !state.choice.button_area.contains((*x, *y).into()) =>
804 {
805 match state.choice.popup.handle(event, Popup) {
806 PopupOutcome::Hide => {
807 state.set_popup_active(false);
808 ComboboxOutcome::Changed
809 }
810 r => r.into(),
811 }
812 }
813 _ => ComboboxOutcome::Continue,
814 }
815}
816
817fn handle_select(state: &mut ComboboxState, event: &Event) -> ComboboxOutcome {
818 match state.choice.behave_select {
819 ChoiceSelect::MouseScroll => {
820 let mut sas = ScrollAreaState::new()
821 .area(state.choice.popup.area)
822 .v_scroll(&mut state.choice.popup_scroll);
823 let mut r = match sas.handle(event, MouseOnly) {
824 ScrollOutcome::Up(n) => state.move_up(n),
825 ScrollOutcome::Down(n) => state.move_down(n),
826 ScrollOutcome::VPos(n) => state.move_to(n),
827 _ => ComboboxOutcome::Continue,
828 };
829
830 r = r.or_else(|| match event {
831 ct_event!(mouse down Left for x,y)
832 if state.choice.popup.area.contains((*x, *y).into()) =>
833 {
834 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
835 state.move_to(state.offset() + n)
836 } else {
837 ComboboxOutcome::Unchanged
838 }
839 }
840 ct_event!(mouse drag Left for x,y)
841 if state.choice.popup.area.contains((*x, *y).into()) =>
842 {
843 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
844 state.move_to(state.offset() + n)
845 } else {
846 ComboboxOutcome::Unchanged
847 }
848 }
849 _ => ComboboxOutcome::Continue,
850 });
851 r
852 }
853 ChoiceSelect::MouseMove => {
854 let mut r = if let Some(selected) = state.choice.core.selected() {
856 let rel_sel = selected.saturating_sub(state.offset());
857 let mut sas = ScrollAreaState::new()
858 .area(state.choice.popup.area)
859 .v_scroll(&mut state.choice.popup_scroll);
860 match sas.handle(event, MouseOnly) {
861 ScrollOutcome::Up(n) => {
862 state.choice.popup_scroll.scroll_up(n);
863 if state.select(state.offset() + rel_sel) {
864 ComboboxOutcome::Value
865 } else {
866 ComboboxOutcome::Unchanged
867 }
868 }
869 ScrollOutcome::Down(n) => {
870 state.choice.popup_scroll.scroll_down(n);
871 if state.select(state.offset() + rel_sel) {
872 ComboboxOutcome::Value
873 } else {
874 ComboboxOutcome::Unchanged
875 }
876 }
877 ScrollOutcome::VPos(n) => {
878 if state.choice.popup_scroll.set_offset(n) {
879 ComboboxOutcome::Value
880 } else {
881 ComboboxOutcome::Unchanged
882 }
883 }
884 _ => ComboboxOutcome::Continue,
885 }
886 } else {
887 ComboboxOutcome::Continue
888 };
889
890 r = r.or_else(|| match event {
891 ct_event!(mouse moved for x,y)
892 if state.choice.popup.area.contains((*x, *y).into()) =>
893 {
894 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
895 state.move_to(state.offset() + n)
896 } else {
897 ComboboxOutcome::Unchanged
898 }
899 }
900 _ => ComboboxOutcome::Continue,
901 });
902 r
903 }
904 ChoiceSelect::MouseClick => {
905 let mut sas = ScrollAreaState::new()
907 .area(state.choice.popup.area)
908 .v_scroll(&mut state.choice.popup_scroll);
909 let mut r = match sas.handle(event, MouseOnly) {
910 ScrollOutcome::Up(n) => {
911 if state.choice.popup_scroll.scroll_up(n) {
912 ComboboxOutcome::Changed
913 } else {
914 ComboboxOutcome::Unchanged
915 }
916 }
917 ScrollOutcome::Down(n) => {
918 if state.choice.popup_scroll.scroll_down(n) {
919 ComboboxOutcome::Changed
920 } else {
921 ComboboxOutcome::Unchanged
922 }
923 }
924 ScrollOutcome::VPos(n) => {
925 if state.choice.popup_scroll.set_offset(n) {
926 ComboboxOutcome::Changed
927 } else {
928 ComboboxOutcome::Unchanged
929 }
930 }
931 _ => ComboboxOutcome::Continue,
932 };
933
934 r = r.or_else(|| match event {
935 ct_event!(mouse down Left for x,y)
936 if state.choice.popup.area.contains((*x, *y).into()) =>
937 {
938 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
939 state.move_to(state.offset() + n)
940 } else {
941 ComboboxOutcome::Unchanged
942 }
943 }
944 ct_event!(mouse drag Left for x,y)
945 if state.choice.popup.area.contains((*x, *y).into()) =>
946 {
947 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
948 state.move_to(state.offset() + n)
949 } else {
950 ComboboxOutcome::Unchanged
951 }
952 }
953 _ => ComboboxOutcome::Continue,
954 });
955 r
956 }
957 }
958}
959
960fn handle_close(state: &mut ComboboxState, event: &Event) -> ComboboxOutcome {
961 match state.choice.behave_close {
962 ChoiceClose::SingleClick => match event {
963 ct_event!(mouse down Left for x,y)
964 if state.choice.popup.area.contains((*x, *y).into()) =>
965 {
966 if let Some(n) = item_at(&state.choice.item_areas, *x, *y) {
967 let r = state.move_to(state.offset() + n);
968 let s = if state.set_popup_active(false) {
969 ComboboxOutcome::Changed
970 } else {
971 ComboboxOutcome::Unchanged
972 };
973 max(r, s)
974 } else {
975 ComboboxOutcome::Unchanged
976 }
977 }
978 _ => ComboboxOutcome::Continue,
979 },
980 ChoiceClose::DoubleClick => match event {
981 ct_event!(mouse any for m) if state.mouse.doubleclick(state.choice.popup.area, m) => {
982 if let Some(n) = item_at(&state.choice.item_areas, m.column, m.row) {
983 let r = state.move_to(state.offset() + n);
984 let s = if state.set_popup_active(false) {
985 ComboboxOutcome::Changed
986 } else {
987 ComboboxOutcome::Unchanged
988 };
989 max(r, s)
990 } else {
991 ComboboxOutcome::Unchanged
992 }
993 }
994 _ => ComboboxOutcome::Continue,
995 },
996 }
997}
998
999pub fn handle_events(state: &mut ComboboxState, focus: bool, event: &Event) -> ComboboxOutcome {
1003 state.focus.set(focus);
1004 HandleEvent::handle(state, event, Popup)
1005}
1006
1007pub fn handle_mouse_events(state: &mut ComboboxState, event: &Event) -> ComboboxOutcome {
1009 HandleEvent::handle(state, event, MouseOnly)
1010}