1use super::{AutoCompleteFilterMode, PopulatedEventArgs, PopulatingEventArgs};
13use crate::primitives::{
14 Popup, SelectingItemsControl, TemplateAppliedEventArgs, TemplatedControl, TemplatedControlImpl,
15 TemplatedControlImplExt,
16};
17use crate::templates::{FuncDataTemplate, IDataTemplate};
18use crate::utils::{BindingEvaluator, ISelectionAdapter, SelectingItemsControlSelectionAdapter};
19use crate::{
20 AssignedBinding, ContentControl, ControlImpl, ItemsChangedEventArgs, ItemsChangedHandler, ItemsSource,
21 SelectionChangedEventArgs, TextBlock, TextBox, TextChangedEventArgs,
22};
23use ferroui_base::animation::TimeSpan;
24use ferroui_base::collections::{FerroList, NotifyCollectionChangedAction};
25use ferroui_base::controls::NameScopeRef;
26use ferroui_base::data::core::ValueTypes;
27use ferroui_base::data::BindingMode;
28use ferroui_base::input::navigation::XYFocusHelpers;
29use ferroui_base::input::{
30 FocusChangedEventArgs, InputElement, InputElementImpl, InputElementImplExt, Key, KeyEventArgs,
31};
32use ferroui_base::interactivity::{
33 Interactive, InteractiveImpl, RoutedEvent, RoutedEventHandlerToken, RoutingStrategies,
34};
35use ferroui_base::layout::LayoutableImpl;
36use ferroui_base::media::IBrush;
37use ferroui_base::reactive::{Disposable, IDisposable, ObservableExt};
38use ferroui_base::threading::{
39 CancellationToken, CancellationTokenSource, Dispatcher, DispatcherPriority, DispatcherTimer,
40 FerroSynchronizationContext, OperationCanceledError,
41};
42use ferroui_base::utilities::{CancelEventArgs, HandlerList, StringComparison};
43use ferroui_base::{
44 ferro_class, ferro_class_info, ferro_impl_classes, ferro_properties, ferro_routed_event, instantiate, BoxedValue,
45 DirectProperty, FerroObject, FerroObjectExtensions, FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs,
46 Ref, StyledElementImpl, StyledProperty, StyledPropertyMetadata, StyledPropertyOptions, VisualImpl,
47};
48use std::cell::{Cell, RefCell};
49use std::future::Future;
50use std::pin::Pin;
51use std::rc::Rc;
52
53pub struct AutoCompleteFilterPredicate<T: 'static>(Rc<dyn Fn(Option<&str>, &T) -> bool>);
65
66impl<T: 'static> AutoCompleteFilterPredicate<T> {
67 pub fn new(filter: impl Fn(Option<&str>, &T) -> bool + 'static) -> Self {
69 Self(Rc::new(filter))
70 }
71
72 pub fn invoke(&self, search: Option<&str>, item: &T) -> bool {
74 (self.0)(search, item)
75 }
76}
77
78impl<T: 'static> Clone for AutoCompleteFilterPredicate<T> {
79 fn clone(&self) -> Self {
80 Self(self.0.clone())
81 }
82}
83
84impl<T: 'static> PartialEq for AutoCompleteFilterPredicate<T> {
85 fn eq(&self, other: &Self) -> bool {
86 Rc::ptr_eq(&self.0, &other.0)
87 }
88}
89
90pub struct AutoCompleteSelector<T: 'static>(Rc<dyn Fn(Option<&str>, &T) -> String>);
102
103impl<T: 'static> AutoCompleteSelector<T> {
104 pub fn new(selector: impl Fn(Option<&str>, &T) -> String + 'static) -> Self {
106 Self(Rc::new(selector))
107 }
108
109 pub fn invoke(&self, search: Option<&str>, item: &T) -> String {
111 (self.0)(search, item)
112 }
113}
114
115impl<T: 'static> Clone for AutoCompleteSelector<T> {
116 fn clone(&self) -> Self {
117 Self(self.0.clone())
118 }
119}
120
121impl<T: 'static> PartialEq for AutoCompleteSelector<T> {
122 fn eq(&self, other: &Self) -> bool {
123 Rc::ptr_eq(&self.0, &other.0)
124 }
125}
126
127pub type AutoCompletePopulation = Pin<Box<dyn Future<Output = Result<Vec<Option<BoxedValue>>, OperationCanceledError>>>>;
130
131#[derive(Clone)]
138pub struct AutoCompleteAsyncPopulator(Rc<dyn Fn(Option<String>, CancellationToken) -> AutoCompletePopulation>);
139
140impl AutoCompleteAsyncPopulator {
141 pub fn new(populator: impl Fn(Option<String>, CancellationToken) -> AutoCompletePopulation + 'static) -> Self {
143 Self(Rc::new(populator))
144 }
145
146 pub fn invoke(&self, search_text: Option<String>, cancellation_token: CancellationToken) -> AutoCompletePopulation {
148 (self.0)(search_text, cancellation_token)
149 }
150}
151
152impl PartialEq for AutoCompleteAsyncPopulator {
153 fn eq(&self, other: &Self) -> bool {
154 Rc::ptr_eq(&self.0, &other.0)
155 }
156}
157
158const ELEMENT_SELECTION_ADAPTER: &str = "PART_SelectionAdapter";
160
161const ELEMENT_SELECTOR: &str = "PART_SelectingItemsControl";
163
164const ELEMENT_POPUP: &str = "PART_Popup";
166
167const ELEMENT_TEXT_BOX: &str = "PART_TextBox";
169
170const PC_DROPDOWN_OPEN: &str = ":dropdownopen";
171
172struct Reset<'a>(&'a Cell<bool>);
174
175impl Drop for Reset<'_> {
176 fn drop(&mut self) {
177 self.0.set(false);
178 }
179}
180
181fn utf16_length(text: Option<&str>) -> i32 {
184 text.map_or(0, |text| text.encode_utf16().count() as i32)
185}
186
187fn utf16_prefix(text: &str, length: i32) -> String {
189 let units: Vec<u16> = text.encode_utf16().take(length.max(0) as usize).collect();
190 String::from_utf16_lossy(&units)
191}
192
193#[repr(C)]
197pub struct AutoCompleteBox {
198 base: TemplatedControl,
199
200 items: RefCell<Option<Rc<Vec<Option<BoxedValue>>>>>,
202
203 view: Rc<FerroList<Option<BoxedValue>>>,
207
208 view_source: ItemsSource,
210
211 ignore_text_property_change: Cell<i32>,
221
222 ignore_text_box_text_change: Cell<i32>,
229
230 ignore_property_change: Cell<bool>,
233
234 ignore_text_selection_change: Cell<bool>,
236
237 skip_selected_item_text_update: Cell<bool>,
240
241 text_selection_start: Cell<i32>,
243
244 user_called_populate: Cell<bool>,
247
248 popup_has_opened: Cell<bool>,
250
251 delay_timer: RefCell<Option<(Rc<DispatcherTimer>, Rc<dyn IDisposable>)>>,
254
255 allow_write: Cell<bool>,
259
260 cancel_requested: Cell<bool>,
262
263 filter_in_action: Cell<bool>,
265
266 text_box: RefCell<Option<Ref<TextBox>>>,
268 text_box_subscriptions: RefCell<Option<Rc<dyn IDisposable>>>,
269
270 adapter: RefCell<Option<Rc<dyn ISelectionAdapter>>>,
272 adapter_subscriptions: RefCell<Vec<Rc<dyn IDisposable>>>,
273
274 value_member_binding_evaluator: RefCell<Option<Ref<BindingEvaluator>>>,
276
277 collection_change_subscription: RefCell<Option<(ItemsSource, u64)>>,
280
281 population_cancellation_token_source: RefCell<Option<CancellationTokenSource>>,
282
283 item_template_is_from_value_member_binding: Cell<bool>,
284 setting_item_template_from_value_member_binding: Cell<bool>,
285
286 search_text: RefCell<Option<String>>,
287
288 drop_down_popup: RefCell<Option<(Ref<Popup>, Rc<dyn IDisposable>)>>,
291
292 populating: HandlerList<dyn Fn(&PopulatingEventArgs)>,
293 populated: HandlerList<dyn Fn(&PopulatedEventArgs)>,
294 drop_down_opening: HandlerList<dyn Fn(&CancelEventArgs)>,
295 drop_down_opened: HandlerList<dyn Fn()>,
296 drop_down_closing: HandlerList<dyn Fn(&CancelEventArgs)>,
297 drop_down_closed: HandlerList<dyn Fn()>,
298}
299
300ferro_class! {
301 AutoCompleteBox: TemplatedControl, virtuals AutoCompleteBoxImpl: TemplatedControlImpl {
302 fn get_selection_adapter_part(this, name_scope: &NameScopeRef) -> Option<Rc<dyn ISelectionAdapter>>;
305 fn on_populating(this, e: &PopulatingEventArgs);
307 fn on_populated(this, e: &PopulatedEventArgs);
309 fn on_selection_changed(this, e: &SelectionChangedEventArgs);
311 fn on_drop_down_opening(this, e: &CancelEventArgs);
313 fn on_drop_down_opened(this);
315 fn on_drop_down_closing(this, e: &CancelEventArgs);
317 fn on_drop_down_closed(this);
319 fn on_text_changed(this, e: &TextChangedEventArgs);
321 fn format_value(this, value: &Option<BoxedValue>) -> Option<String>;
326 }
327}
328
329ferro_class_info!(AutoCompleteBox {
330 new: AutoCompleteBox::new,
331 markup: {
332 property_attributes: [ValueMemberBinding: [InheritDataTypeFromItems("ItemsSource")]],
333 attributes: [
334 TemplatePart("PART_Popup", type(Ref<Popup>)),
335 TemplatePart("PART_SelectingItemsControl", type(Ref<SelectingItemsControl>)),
336 TemplatePart("PART_SelectionAdapter", type(Rc<dyn ISelectionAdapter>)),
337 TemplatePart("PART_TextBox", type(Ref<TextBox>)),
338 PseudoClasses(":dropdownopen"),
339 ],
340 },
341});
342
343ferro_impl_classes!(AutoCompleteBox: StyledElementImpl, VisualImpl, LayoutableImpl, InteractiveImpl);
344
345impl ControlImpl for AutoCompleteBox {
346 fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
347 crate::automation::peers::AutoCompleteBoxAutomationPeer::new(this).upcast()
348 }
349}
350
351impl FerroObjectImpl for AutoCompleteBox {
352 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
353 Self::parent_on_property_changed(this, change);
354
355 if change.property() == Self::value_member_binding_property().as_property() {
356 this.on_value_member_binding_changed(change.get_new_value::<Option<AssignedBinding>>());
357 }
358 }
359}
360
361impl InputElementImpl for AutoCompleteBox {
362 fn on_key_down(this: &Self, e: &KeyEventArgs) {
364 Self::parent_on_key_down(this, e);
365
366 if e.handled() || !this.is_enabled() {
367 return;
368 }
369
370 if this.is_drop_down_open() {
375 if let Some(adapter) = this.selection_adapter() {
376 adapter.handle_key_down(e);
377 if e.handled() {
378 return;
379 }
380 }
381
382 if e.key == Key::Escape {
383 this.on_adapter_selection_canceled();
384 e.set_handled(true);
385 }
386 } else {
387 if e.key == Key::Down && !XYFocusHelpers::is_allowed_xy_navigation_mode(this, Some(e.key_device_type)) {
390 this.set_current_value(Self::is_drop_down_open_property(), true);
391 e.set_handled(true);
392 }
393 }
394
395 match e.key {
397 Key::F4 => {
398 this.set_current_value(Self::is_drop_down_open_property(), !this.is_drop_down_open());
399 e.set_handled(true);
400 }
401 Key::Enter => {
402 if this.is_drop_down_open() {
403 this.on_adapter_selection_complete();
404 e.set_handled(true);
405 }
406 }
407 _ => {}
408 }
409 }
410
411 fn on_got_focus(this: &Self, e: &FocusChangedEventArgs) {
412 if let Some(text_box) = this.text_box() {
413 if this.text_box_selection_length() <= 0 {
414 text_box.focus();
415 text_box.select_all();
416 }
417 }
418
419 Self::parent_on_got_focus(this, e);
420 }
421
422 fn on_lost_focus(this: &Self, e: &FocusChangedEventArgs) {
423 this.set_current_value(Self::is_drop_down_open_property(), false);
424
425 this.user_called_populate.set(false);
426
427 let text_box = this.text_box();
428 let text_box_context_menu_is_open = text_box.as_ref().is_some_and(|text_box| {
429 text_box.context_flyout().is_some_and(|flyout| flyout.is_open())
430 || text_box.context_menu().is_some_and(|menu| menu.is_open())
431 });
432 let context_menu_is_open = this.context_flyout().is_some_and(|flyout| flyout.is_open())
433 || this.context_menu().is_some_and(|menu| menu.is_open());
434
435 if !text_box_context_menu_is_open && !context_menu_is_open && this.clear_selection_on_lost_focus() {
436 this.clear_text_box_selection();
437 }
438
439 Self::parent_on_lost_focus(this, e);
440 }
441}
442
443impl TemplatedControlImpl for AutoCompleteBox {
444 fn on_apply_template(this: &Self, e: &TemplateAppliedEventArgs) {
447 if let Some((_, closed)) = this.drop_down_popup.take() {
448 closed.dispose();
449 }
450
451 let popup = e.name_scope().find_as::<Popup>(ELEMENT_POPUP);
454 if let Some(popup) = &popup {
455 let weak = this.to_ref().downgrade();
456 let closed = popup.closed(move || {
457 if let Some(this) = weak.upgrade() {
458 this.drop_down_popup_closed();
459 }
460 });
461 *this.drop_down_popup.borrow_mut() = Some((popup.clone(), closed));
462 }
463
464 this.set_selection_adapter(this.get_selection_adapter_part(e.name_scope()));
465 this.set_text_box(e.name_scope().find_as::<TextBox>(ELEMENT_TEXT_BOX));
466
467 if this.is_drop_down_open() && popup.is_some_and(|popup| !popup.is_open()) {
470 this.opening_drop_down(false);
471 }
472
473 Self::parent_on_apply_template(this, e);
474 }
475}
476
477impl AutoCompleteBoxImpl for AutoCompleteBox {
478 fn get_selection_adapter_part(_this: &Self, name_scope: &NameScopeRef) -> Option<Rc<dyn ISelectionAdapter>> {
479 let mut adapter: Option<Rc<dyn ISelectionAdapter>> = None;
480 if let Some(selector) = name_scope.find_as::<SelectingItemsControl>(ELEMENT_SELECTOR) {
481 adapter = name_scope.find(ELEMENT_SELECTOR).and_then(|object| <dyn ISelectionAdapter>::from_object(&object));
483 if adapter.is_none() {
484 adapter = Some(SelectingItemsControlSelectionAdapter::with_selector(selector));
486 }
487 }
488 if adapter.is_none() {
489 adapter = name_scope.find(ELEMENT_SELECTION_ADAPTER).map(|object| {
490 <dyn ISelectionAdapter>::from_object(&object).unwrap_or_else(|| {
491 panic!(
492 "Expected control '{ELEMENT_SELECTION_ADAPTER}' to be 'ISelectionAdapter' but it was '{}'.",
493 object.get_type().name()
494 )
495 })
496 });
497 }
498 adapter
499 }
500
501 fn on_populating(this: &Self, e: &PopulatingEventArgs) {
502 for (_, handler) in this.populating.snapshot().iter() {
503 handler(e);
504 }
505 }
506
507 fn on_populated(this: &Self, e: &PopulatedEventArgs) {
508 for (_, handler) in this.populated.snapshot().iter() {
509 handler(e);
510 }
511 }
512
513 fn on_selection_changed(this: &Self, e: &SelectionChangedEventArgs) {
514 this.raise_event(e);
515 }
516
517 fn on_drop_down_opening(this: &Self, e: &CancelEventArgs) {
518 for (_, handler) in this.drop_down_opening.snapshot().iter() {
519 handler(e);
520 }
521 }
522
523 fn on_drop_down_opened(this: &Self) {
524 for (_, handler) in this.drop_down_opened.snapshot().iter() {
525 handler();
526 }
527 }
528
529 fn on_drop_down_closing(this: &Self, e: &CancelEventArgs) {
530 for (_, handler) in this.drop_down_closing.snapshot().iter() {
531 handler(e);
532 }
533 }
534
535 fn on_drop_down_closed(this: &Self) {
536 for (_, handler) in this.drop_down_closed.snapshot().iter() {
537 handler();
538 }
539 }
540
541 fn on_text_changed(this: &Self, e: &TextChangedEventArgs) {
542 this.raise_event(e);
543 }
544
545 fn format_value(this: &Self, value: &Option<BoxedValue>) -> Option<String> {
546 if let Some(value_member_binding) = this.value_member_binding() {
547 let evaluator = this.value_member_binding_evaluator.borrow().clone();
548 let evaluator = evaluator.unwrap_or_else(|| {
549 let evaluator = BindingEvaluator::new();
550 *this.value_member_binding_evaluator.borrow_mut() = Some(evaluator.clone());
551 evaluator
552 });
553 evaluator.update_binding(&value_member_binding);
554 let result = evaluator.evaluate_string(value).unwrap_or_default();
555 evaluator.clear_data_context();
556 return Some(result);
557 }
558
559 Some(ValueTypes::to_display_string(value.as_ref()))
560 }
561}
562
563ferro_properties! {
564 impl AutoCompleteBox {
565 pub fn caret_index_property() -> StyledProperty<i32> {
567 TextBox::caret_index_property().add_owner_with::<AutoCompleteBox>(
568 StyledPropertyMetadata::new(Some(0)).with_default_binding_mode(BindingMode::TwoWay),
569 )
570 }
571
572 pub fn placeholder_text_property() -> StyledProperty<Option<String>> {
574 TextBox::placeholder_text_property().add_owner::<AutoCompleteBox>()
575 }
576
577 pub fn placeholder_foreground_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
579 TextBox::placeholder_foreground_property().add_owner::<AutoCompleteBox>()
580 }
581
582 pub fn minimum_prefix_length_property() -> StyledProperty<i32> {
584 FerroProperty::register_with::<AutoCompleteBox, _>(
585 "MinimumPrefixLength",
586 StyledPropertyOptions::new(1).validate(AutoCompleteBox::is_valid_minimum_prefix_length),
587 )
588 }
589
590 pub fn minimum_populate_delay_property() -> StyledProperty<TimeSpan> {
592 FerroProperty::register_with::<AutoCompleteBox, _>(
593 "MinimumPopulateDelay",
594 StyledPropertyOptions::new(TimeSpan::ZERO).validate(AutoCompleteBox::is_valid_minimum_populate_delay),
595 )
596 }
597
598 pub fn max_drop_down_height_property() -> StyledProperty<f64> {
600 FerroProperty::register_with::<AutoCompleteBox, _>(
601 "MaxDropDownHeight",
602 StyledPropertyOptions::new(f64::INFINITY).validate(AutoCompleteBox::is_valid_max_drop_down_height),
603 )
604 }
605
606 pub fn is_text_completion_enabled_property() -> StyledProperty<bool> {
608 FerroProperty::register::<AutoCompleteBox, _>("IsTextCompletionEnabled", false)
609 }
610
611 pub fn item_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
613 FerroProperty::register::<AutoCompleteBox, _>("ItemTemplate", None)
614 }
615
616 pub fn clear_selection_on_lost_focus_property() -> StyledProperty<bool> {
618 TextBox::clear_selection_on_lost_focus_property().add_owner::<AutoCompleteBox>()
619 }
620
621 pub fn is_drop_down_open_property() -> StyledProperty<bool> {
623 FerroProperty::register::<AutoCompleteBox, _>("IsDropDownOpen", false)
624 }
625
626 pub fn selected_item_property() -> StyledProperty<Option<BoxedValue>> {
628 FerroProperty::register_with::<AutoCompleteBox, _>(
629 "SelectedItem",
630 StyledPropertyOptions::new(None).default_binding_mode(BindingMode::TwoWay).enable_data_validation(true),
631 )
632 }
633
634 pub fn text_property() -> StyledProperty<Option<String>> {
636 TextBlock::text_property().add_owner_with::<AutoCompleteBox>(
637 StyledPropertyMetadata::new(Some(Some(String::new())))
638 .with_default_binding_mode(BindingMode::TwoWay)
639 .with_enable_data_validation(true),
640 )
641 }
642
643 pub fn search_text_property() -> DirectProperty<AutoCompleteBox, Option<String>> {
645 FerroProperty::register_direct::<AutoCompleteBox, _>(
646 "SearchText",
647 |o| o.search_text(),
648 None,
649 Some(String::new()),
650 )
651 }
652
653 pub fn filter_mode_property() -> StyledProperty<AutoCompleteFilterMode> {
655 FerroProperty::register_with::<AutoCompleteBox, _>(
656 "FilterMode",
657 StyledPropertyOptions::new(AutoCompleteFilterMode::StartsWith)
658 .validate(AutoCompleteBox::is_valid_filter_mode),
659 )
660 }
661
662 pub fn item_filter_property() -> StyledProperty<Option<AutoCompleteFilterPredicate<Option<BoxedValue>>>> {
664 FerroProperty::register::<AutoCompleteBox, _>("ItemFilter", None)
665 }
666
667 pub fn text_filter_property() -> StyledProperty<Option<AutoCompleteFilterPredicate<Option<String>>>> {
669 FerroProperty::register::<AutoCompleteBox, _>(
670 "TextFilter",
671 AutoCompleteSearch::get_filter(AutoCompleteFilterMode::StartsWith),
672 )
673 }
674
675 pub fn item_selector_property() -> StyledProperty<Option<AutoCompleteSelector<BoxedValue>>> {
677 FerroProperty::register::<AutoCompleteBox, _>("ItemSelector", None)
678 }
679
680 pub fn text_selector_property() -> StyledProperty<Option<AutoCompleteSelector<Option<String>>>> {
682 FerroProperty::register::<AutoCompleteBox, _>("TextSelector", None)
683 }
684
685 pub fn items_source_property() -> StyledProperty<Option<ItemsSource>> {
687 FerroProperty::register::<AutoCompleteBox, _>("ItemsSource", None)
688 }
689
690 pub fn async_populator_property() -> StyledProperty<Option<AutoCompleteAsyncPopulator>> {
692 FerroProperty::register::<AutoCompleteBox, _>("AsyncPopulator", None)
693 }
694
695 pub fn max_length_property() -> StyledProperty<i32> {
697 TextBox::max_length_property().add_owner::<AutoCompleteBox>()
698 }
699
700 pub fn inner_left_content_property() -> StyledProperty<Option<BoxedValue>> {
702 TextBox::inner_left_content_property().add_owner::<AutoCompleteBox>()
703 }
704
705 pub fn inner_right_content_property() -> StyledProperty<Option<BoxedValue>> {
707 TextBox::inner_right_content_property().add_owner::<AutoCompleteBox>()
708 }
709
710 pub fn value_member_binding_property() -> StyledProperty<Option<AssignedBinding>> {
713 FerroProperty::register_with::<AutoCompleteBox, _>(
714 "ValueMemberBinding",
715 StyledPropertyOptions::new(None).assign_binding(true),
716 )
717 }
718 }
719}
720
721impl AutoCompleteBox {
722 #[deprecated(note = "Use placeholder_text_property instead.")]
724 pub fn watermark_property() -> &'static StyledProperty<Option<String>> {
725 Self::placeholder_text_property()
726 }
727
728 #[deprecated(note = "Use placeholder_foreground_property instead.")]
730 pub fn watermark_foreground_property() -> &'static StyledProperty<Option<Rc<dyn IBrush>>> {
731 Self::placeholder_foreground_property()
732 }
733
734 ferro_routed_event!(
735 pub fn selection_changed_event() -> RoutedEvent<SelectionChangedEventArgs> {
737 RoutedEvent::register::<AutoCompleteBox, _>("SelectionChanged", RoutingStrategies::BUBBLE)
738 }
739 );
740
741 ferro_routed_event!(
742 pub fn text_changed_event() -> RoutedEvent<TextChangedEventArgs> {
744 RoutedEvent::register::<AutoCompleteBox, _>("TextChanged", RoutingStrategies::BUBBLE)
745 }
746 );
747
748 fn is_valid_minimum_prefix_length(value: &i32) -> bool {
749 *value >= -1
750 }
751
752 fn is_valid_minimum_populate_delay(value: &TimeSpan) -> bool {
753 value.total_milliseconds() >= 0.0
754 }
755
756 fn is_valid_max_drop_down_height(value: &f64) -> bool {
757 *value >= 0.0
758 }
759
760 fn is_valid_filter_mode(mode: &AutoCompleteFilterMode) -> bool {
761 match mode {
762 AutoCompleteFilterMode::None
763 | AutoCompleteFilterMode::StartsWith
764 | AutoCompleteFilterMode::StartsWithCaseSensitive
765 | AutoCompleteFilterMode::StartsWithOrdinal
766 | AutoCompleteFilterMode::StartsWithOrdinalCaseSensitive
767 | AutoCompleteFilterMode::Contains
768 | AutoCompleteFilterMode::ContainsCaseSensitive
769 | AutoCompleteFilterMode::ContainsOrdinal
770 | AutoCompleteFilterMode::ContainsOrdinalCaseSensitive
771 | AutoCompleteFilterMode::Equals
772 | AutoCompleteFilterMode::EqualsCaseSensitive
773 | AutoCompleteFilterMode::EqualsOrdinal
774 | AutoCompleteFilterMode::EqualsOrdinalCaseSensitive
775 | AutoCompleteFilterMode::Custom => true,
776 }
777 }
778
779 fn on_control_is_enabled_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
781 let is_enabled = e.get_new_value::<bool>();
782 if !is_enabled {
783 self.set_current_value(Self::is_drop_down_open_property(), false);
784 }
785 }
786
787 fn on_minimum_populate_delay_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
791 let new_value = e.get_new_value::<TimeSpan>();
792
793 let existing = self.delay_timer.borrow().as_ref().map(|(timer, _)| timer.clone());
795 if let Some(timer) = existing {
796 timer.stop();
797
798 if new_value == TimeSpan::ZERO {
799 if let Some((_, tick)) = self.delay_timer.take() {
800 tick.dispose();
801 }
802 }
803 }
804
805 if new_value > TimeSpan::ZERO {
806 let timer = self.delay_timer.borrow().as_ref().map(|(timer, _)| timer.clone());
808 let timer = timer.unwrap_or_else(|| {
809 let timer = DispatcherTimer::new();
810 let weak = self.to_ref().downgrade();
811 let tick = timer.tick(move |_| {
812 if let Some(this) = weak.upgrade() {
813 this.populate_drop_down();
814 }
815 });
816 *self.delay_timer.borrow_mut() = Some((timer.clone(), tick));
817 timer
818 });
819
820 timer.set_interval(new_value.to_duration().unwrap_or_default());
822 }
823 }
824
825 fn on_is_drop_down_open_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
827 if self.ignore_property_change.get() {
829 self.ignore_property_change.set(false);
830 return;
831 }
832
833 let (old_value, new_value) = e.get_old_and_new_value::<bool>();
834
835 if new_value {
836 self.text_updated(self.text(), true);
837 } else {
838 self.closing_drop_down(old_value);
839 }
840
841 self.update_pseudo_classes();
842 }
843
844 fn on_selected_item_property_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
845 if self.ignore_property_change.get() {
846 self.ignore_property_change.set(false);
847 return;
848 }
849
850 let (old_value, new_value) = e.get_old_and_new_value::<Option<BoxedValue>>();
851
852 if self.skip_selected_item_text_update.get() {
854 self.skip_selected_item_text_update.set(false);
855 } else {
856 self.on_selected_item_changed(&new_value);
857 }
858
859 let mut removed = Vec::new();
861 if old_value.is_some() {
862 removed.push(old_value);
863 }
864
865 let mut added = Vec::new();
866 if new_value.is_some() {
867 added.push(new_value);
868 }
869
870 self.on_selection_changed(&SelectionChangedEventArgs::new(
871 Some(Self::selection_changed_event()),
872 removed,
873 added,
874 ));
875 }
876
877 fn on_text_property_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
879 if self.ignore_text_property_change.get() > 0 {
880 self.ignore_text_property_change.set(self.ignore_text_property_change.get() - 1);
881 return;
882 }
883
884 self.text_updated(e.get_new_value::<Option<String>>(), false);
885 }
886
887 fn on_search_text_property_changed(&self, _e: &FerroPropertyChangedEventArgs<'_>) {
888 if self.ignore_property_change.get() {
889 self.ignore_property_change.set(false);
890 return;
891 }
892
893 if !self.allow_write.get() {
895 panic!("Cannot set read-only property SearchText.");
900 }
901 }
902
903 fn on_filter_mode_property_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
905 let mode = e.get_new_value::<AutoCompleteFilterMode>();
906
907 self.set_current_value(Self::text_filter_property(), AutoCompleteSearch::get_filter(mode));
909 }
910
911 fn on_item_filter_property_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
913 let value = e.get_new_value::<Option<AutoCompleteFilterPredicate<Option<BoxedValue>>>>();
914
915 if value.is_none() {
917 self.set_current_value(Self::filter_mode_property(), AutoCompleteFilterMode::None);
918 } else {
919 self.set_current_value(Self::filter_mode_property(), AutoCompleteFilterMode::Custom);
920 self.set_current_value(Self::text_filter_property(), None);
921 }
922 }
923
924 fn on_items_source_property_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
926 self.on_items_source_changed(e.get_new_value::<Option<ItemsSource>>());
927 }
928
929 fn on_item_template_property_changed(&self, _e: &FerroPropertyChangedEventArgs<'_>) {
930 if !self.setting_item_template_from_value_member_binding.get() {
931 self.item_template_is_from_value_member_binding.set(false);
932 }
933 }
934
935 fn on_value_member_binding_changed(&self, value: Option<AssignedBinding>) {
936 if self.item_template_is_from_value_member_binding.get() {
937 let template: Rc<dyn IDataTemplate> = FuncDataTemplate::new(
938 |data| data.is_some(),
939 move |_, _| {
940 let control = ContentControl::new();
941 if let Some(value) = &value {
942 control.bind_binding(ContentControl::content_property().as_property(), &**value);
943 }
944 Some(control.upcast())
945 },
946 false,
947 );
948
949 self.setting_item_template_from_value_member_binding.set(true);
950 self.set_current_value(Self::item_template_property(), Some(template));
951 self.setting_item_template_from_value_member_binding.set(false);
952 }
953 }
954
955 fn static_constructor() {
956 InputElement::focusable_property().override_default_value::<AutoCompleteBox>(true);
957 InputElement::is_tab_stop_property().override_default_value::<AutoCompleteBox>(false);
958
959 Self::minimum_populate_delay_property()
960 .changed()
961 .add_class_handler::<AutoCompleteBox>(|x, e| x.on_minimum_populate_delay_changed(e));
962 Self::is_drop_down_open_property()
963 .changed()
964 .add_class_handler::<AutoCompleteBox>(|x, e| x.on_is_drop_down_open_changed(e));
965 Self::selected_item_property()
966 .changed()
967 .add_class_handler::<AutoCompleteBox>(|x, e| x.on_selected_item_property_changed(e));
968 Self::text_property().changed().add_class_handler::<AutoCompleteBox>(|x, e| x.on_text_property_changed(e));
969 Self::search_text_property()
970 .changed()
971 .add_class_handler::<AutoCompleteBox>(|x, e| x.on_search_text_property_changed(e));
972 Self::filter_mode_property()
973 .changed()
974 .add_class_handler::<AutoCompleteBox>(|x, e| x.on_filter_mode_property_changed(e));
975 Self::item_filter_property()
976 .changed()
977 .add_class_handler::<AutoCompleteBox>(|x, e| x.on_item_filter_property_changed(e));
978 Self::items_source_property()
979 .changed()
980 .add_class_handler::<AutoCompleteBox>(|x, e| x.on_items_source_property_changed(e));
981 Self::item_template_property()
982 .changed()
983 .add_class_handler::<AutoCompleteBox>(|x, e| x.on_item_template_property_changed(e));
984 InputElement::is_enabled_property()
985 .changed()
986 .add_class_handler::<AutoCompleteBox>(|x, e| x.on_control_is_enabled_changed(e));
987 }
988
989 pub fn construct() -> Self {
991 let view = Rc::new(FerroList::new());
994 let view_source = ItemsSource::from(view.clone());
995
996 Self {
997 base: TemplatedControl::construct(),
998 items: RefCell::new(None),
999 view,
1000 view_source,
1001 ignore_text_property_change: Cell::new(0),
1002 ignore_text_box_text_change: Cell::new(0),
1003 ignore_property_change: Cell::new(false),
1004 ignore_text_selection_change: Cell::new(false),
1005 skip_selected_item_text_update: Cell::new(false),
1006 text_selection_start: Cell::new(0),
1007 user_called_populate: Cell::new(false),
1008 popup_has_opened: Cell::new(false),
1009 delay_timer: RefCell::new(None),
1010 allow_write: Cell::new(false),
1011 cancel_requested: Cell::new(false),
1012 filter_in_action: Cell::new(false),
1013 text_box: RefCell::new(None),
1014 text_box_subscriptions: RefCell::new(None),
1015 adapter: RefCell::new(None),
1016 adapter_subscriptions: RefCell::new(Vec::new()),
1017 value_member_binding_evaluator: RefCell::new(None),
1018 collection_change_subscription: RefCell::new(None),
1019 population_cancellation_token_source: RefCell::new(None),
1020 item_template_is_from_value_member_binding: Cell::new(true),
1021 setting_item_template_from_value_member_binding: Cell::new(false),
1022 search_text: RefCell::new(Some(String::new())),
1023 drop_down_popup: RefCell::new(None),
1024 populating: HandlerList::new(),
1025 populated: HandlerList::new(),
1026 drop_down_opening: HandlerList::new(),
1027 drop_down_opened: HandlerList::new(),
1028 drop_down_closing: HandlerList::new(),
1029 drop_down_closed: HandlerList::new(),
1030 }
1031 }
1032
1033 pub fn new() -> Ref<Self> {
1035 instantiate(Self::construct())
1036 }
1037
1038 pub fn caret_index(&self) -> i32 {
1042 self.get_value(Self::caret_index_property())
1043 }
1044
1045 pub fn set_caret_index(&self, value: i32) {
1046 self.set_value(Self::caret_index_property(), value)
1047 }
1048
1049 pub fn minimum_prefix_length(&self) -> i32 {
1058 self.get_value(Self::minimum_prefix_length_property())
1059 }
1060
1061 pub fn set_minimum_prefix_length(&self, value: i32) {
1062 self.set_value(Self::minimum_prefix_length_property(), value)
1063 }
1064
1065 pub fn is_text_completion_enabled(&self) -> bool {
1069 self.get_value(Self::is_text_completion_enabled_property())
1070 }
1071
1072 pub fn set_is_text_completion_enabled(&self, value: bool) {
1073 self.set_value(Self::is_text_completion_enabled_property(), value)
1074 }
1075
1076 pub fn item_template(&self) -> Option<Rc<dyn IDataTemplate>> {
1079 self.get_value(Self::item_template_property())
1080 }
1081
1082 pub fn set_item_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
1083 self.set_value(Self::item_template_property(), value)
1084 }
1085
1086 pub fn minimum_populate_delay(&self) -> TimeSpan {
1090 self.get_value(Self::minimum_populate_delay_property())
1091 }
1092
1093 pub fn set_minimum_populate_delay(&self, value: TimeSpan) {
1094 self.set_value(Self::minimum_populate_delay_property(), value)
1095 }
1096
1097 pub fn max_drop_down_height(&self) -> f64 {
1100 self.get_value(Self::max_drop_down_height_property())
1101 }
1102
1103 pub fn set_max_drop_down_height(&self, value: f64) {
1104 self.set_value(Self::max_drop_down_height_property(), value)
1105 }
1106
1107 pub fn is_drop_down_open(&self) -> bool {
1110 self.get_value(Self::is_drop_down_open_property())
1111 }
1112
1113 pub fn set_is_drop_down_open(&self, value: bool) {
1114 self.set_value(Self::is_drop_down_open_property(), value)
1115 }
1116
1117 pub fn clear_selection_on_lost_focus(&self) -> bool {
1120 self.get_value(Self::clear_selection_on_lost_focus_property())
1121 }
1122
1123 pub fn set_clear_selection_on_lost_focus(&self, value: bool) {
1124 self.set_value(Self::clear_selection_on_lost_focus_property(), value)
1125 }
1126
1127 pub fn value_member_binding(&self) -> Option<AssignedBinding> {
1131 self.get_value(Self::value_member_binding_property())
1132 }
1133
1134 pub fn set_value_member_binding(&self, value: Option<AssignedBinding>) {
1135 self.set_value(Self::value_member_binding_property(), value)
1136 }
1137
1138 pub fn selected_item(&self) -> Option<BoxedValue> {
1146 self.get_value(Self::selected_item_property())
1147 }
1148
1149 pub fn set_selected_item(&self, value: Option<BoxedValue>) {
1150 self.set_value(Self::selected_item_property(), value)
1151 }
1152
1153 pub fn text(&self) -> Option<String> {
1156 self.get_value(Self::text_property())
1157 }
1158
1159 pub fn set_text(&self, value: Option<&str>) {
1160 self.set_value(Self::text_property(), value.map(str::to_owned))
1161 }
1162
1163 pub fn search_text(&self) -> Option<String> {
1170 self.search_text.borrow().clone()
1171 }
1172
1173 fn set_search_text(&self, value: Option<String>) {
1174 self.allow_write.set(true);
1175 let _reset = Reset(&self.allow_write);
1176 self.set_and_raise(Self::search_text_property(), &self.search_text, value);
1177 }
1178
1179 pub fn filter_mode(&self) -> AutoCompleteFilterMode {
1188 self.get_value(Self::filter_mode_property())
1189 }
1190
1191 pub fn set_filter_mode(&self, value: AutoCompleteFilterMode) {
1192 self.set_value(Self::filter_mode_property(), value)
1193 }
1194
1195 pub fn placeholder_text(&self) -> Option<String> {
1198 self.get_value(Self::placeholder_text_property())
1199 }
1200
1201 pub fn set_placeholder_text(&self, value: Option<&str>) {
1202 self.set_value(Self::placeholder_text_property(), value.map(str::to_owned))
1203 }
1204
1205 #[deprecated(note = "Use placeholder_text instead.")]
1208 pub fn watermark(&self) -> Option<String> {
1209 self.placeholder_text()
1210 }
1211
1212 #[deprecated(note = "Use set_placeholder_text instead.")]
1213 pub fn set_watermark(&self, value: Option<&str>) {
1214 self.set_placeholder_text(value)
1215 }
1216
1217 pub fn placeholder_foreground(&self) -> Option<Rc<dyn IBrush>> {
1220 self.get_value(Self::placeholder_foreground_property())
1221 }
1222
1223 pub fn set_placeholder_foreground(&self, value: Option<Rc<dyn IBrush>>) {
1224 self.set_value(Self::placeholder_foreground_property(), value)
1225 }
1226
1227 #[deprecated(note = "Use placeholder_foreground instead.")]
1230 pub fn watermark_foreground(&self) -> Option<Rc<dyn IBrush>> {
1231 self.placeholder_foreground()
1232 }
1233
1234 #[deprecated(note = "Use set_placeholder_foreground instead.")]
1235 pub fn set_watermark_foreground(&self, value: Option<Rc<dyn IBrush>>) {
1236 self.set_placeholder_foreground(value)
1237 }
1238
1239 pub fn item_filter(&self) -> Option<AutoCompleteFilterPredicate<Option<BoxedValue>>> {
1246 self.get_value(Self::item_filter_property())
1247 }
1248
1249 pub fn set_item_filter(&self, value: Option<AutoCompleteFilterPredicate<Option<BoxedValue>>>) {
1250 self.set_value(Self::item_filter_property(), value)
1251 }
1252
1253 pub fn text_filter(&self) -> Option<AutoCompleteFilterPredicate<Option<String>>> {
1260 self.get_value(Self::text_filter_property())
1261 }
1262
1263 pub fn set_text_filter(&self, value: Option<AutoCompleteFilterPredicate<Option<String>>>) {
1264 self.set_value(Self::text_filter_property(), value)
1265 }
1266
1267 pub fn item_selector(&self) -> Option<AutoCompleteSelector<BoxedValue>> {
1270 self.get_value(Self::item_selector_property())
1271 }
1272
1273 pub fn set_item_selector(&self, value: Option<AutoCompleteSelector<BoxedValue>>) {
1274 self.set_value(Self::item_selector_property(), value)
1275 }
1276
1277 pub fn text_selector(&self) -> Option<AutoCompleteSelector<Option<String>>> {
1281 self.get_value(Self::text_selector_property())
1282 }
1283
1284 pub fn set_text_selector(&self, value: Option<AutoCompleteSelector<Option<String>>>) {
1285 self.set_value(Self::text_selector_property(), value)
1286 }
1287
1288 pub fn async_populator(&self) -> Option<AutoCompleteAsyncPopulator> {
1290 self.get_value(Self::async_populator_property())
1291 }
1292
1293 pub fn set_async_populator(&self, value: Option<AutoCompleteAsyncPopulator>) {
1294 self.set_value(Self::async_populator_property(), value)
1295 }
1296
1297 pub fn items_source(&self) -> Option<ItemsSource> {
1300 self.get_value(Self::items_source_property())
1301 }
1302
1303 pub fn set_items_source(&self, value: Option<ItemsSource>) {
1304 self.set_value(Self::items_source_property(), value)
1305 }
1306
1307 pub fn max_length(&self) -> i32 {
1311 self.get_value(Self::max_length_property())
1312 }
1313
1314 pub fn set_max_length(&self, value: i32) {
1315 self.set_value(Self::max_length_property(), value)
1316 }
1317
1318 pub fn inner_left_content(&self) -> Option<BoxedValue> {
1321 self.get_value(Self::inner_left_content_property())
1322 }
1323
1324 pub fn set_inner_left_content(&self, value: Option<BoxedValue>) {
1325 self.set_value(Self::inner_left_content_property(), value)
1326 }
1327
1328 pub fn inner_right_content(&self) -> Option<BoxedValue> {
1331 self.get_value(Self::inner_right_content_property())
1332 }
1333
1334 pub fn set_inner_right_content(&self, value: Option<BoxedValue>) {
1335 self.set_value(Self::inner_right_content_property(), value)
1336 }
1337
1338 fn drop_down_popup(&self) -> Option<Ref<Popup>> {
1342 self.drop_down_popup.borrow().as_ref().map(|(popup, _)| popup.clone())
1343 }
1344
1345 fn text_box(&self) -> Option<Ref<TextBox>> {
1347 self.text_box.borrow().clone()
1348 }
1349
1350 fn set_text_box(&self, value: Option<Ref<TextBox>>) {
1351 if let Some(subscriptions) = self.text_box_subscriptions.take() {
1352 subscriptions.dispose();
1353 }
1354 *self.text_box.borrow_mut() = value.clone();
1355
1356 if let Some(text_box) = value {
1358 let weak = self.to_ref().downgrade();
1359 let skipped = Cell::new(false);
1361 let object: &FerroObject = &text_box;
1362 let subscription =
1363 FerroObjectExtensions::get_observable(object, TextBox::text_property()).subscribe_fn(move |_| {
1364 if !skipped.replace(true) {
1365 return;
1366 }
1367 if let Some(this) = weak.upgrade() {
1368 this.on_text_box_text_changed();
1369 }
1370 });
1371 *self.text_box_subscriptions.borrow_mut() = Some(subscription);
1372
1373 if let Some(text) = self.text() {
1374 self.update_text_value(Some(text), None);
1375 }
1376 }
1377 }
1378
1379 fn text_box_selection_start(&self) -> i32 {
1380 match self.text_box() {
1381 Some(text_box) => text_box.selection_start().min(text_box.selection_end()),
1382 None => 0,
1383 }
1384 }
1385
1386 fn text_box_selection_length(&self) -> i32 {
1387 match self.text_box() {
1388 Some(text_box) => (text_box.selection_end() - text_box.selection_start()).abs(),
1389 None => 0,
1390 }
1391 }
1392
1393 pub fn selection_adapter(&self) -> Option<Rc<dyn ISelectionAdapter>> {
1400 self.adapter.borrow().clone()
1401 }
1402
1403 pub fn set_selection_adapter(&self, value: Option<Rc<dyn ISelectionAdapter>>) {
1406 let old = self.adapter.borrow().clone();
1407 if let Some(old) = old {
1408 for subscription in self.adapter_subscriptions.take() {
1409 subscription.dispose();
1410 }
1411 old.set_items_source(None);
1412 }
1413
1414 *self.adapter.borrow_mut() = value.clone();
1415
1416 if let Some(adapter) = value {
1417 let weak = self.to_ref().downgrade();
1418 let selection_changed = adapter.selection_changed(Rc::new({
1419 let weak = weak.clone();
1420 move |_| {
1421 if let Some(this) = weak.upgrade() {
1422 this.on_adapter_selection_changed();
1423 }
1424 }
1425 }));
1426 let commit = adapter.commit(Rc::new({
1427 let weak = weak.clone();
1428 move |_| {
1429 if let Some(this) = weak.upgrade() {
1430 this.on_adapter_selection_complete();
1431 }
1432 }
1433 }));
1434 let cancel = adapter.cancel(Rc::new({
1435 let weak = weak.clone();
1436 move |_| {
1437 if let Some(this) = weak.upgrade() {
1438 this.on_adapter_selection_canceled();
1439 }
1440 }
1441 }));
1442 let cancel_complete = adapter.cancel(Rc::new(move |_| {
1443 if let Some(this) = weak.upgrade() {
1444 this.on_adapter_selection_complete();
1445 }
1446 }));
1447 *self.adapter_subscriptions.borrow_mut() = vec![selection_changed, commit, cancel, cancel_complete];
1448 adapter.set_items_source(Some(self.view_source.clone()));
1449 }
1450 }
1451
1452 #[deprecated(note = "Use is_keyboard_focus_within instead.")]
1455 pub fn has_focus(&self) -> bool {
1456 self.is_keyboard_focus_within()
1457 }
1458
1459 fn subscription<F: ?Sized + 'static>(
1463 &self,
1464 list: fn(&Self) -> &HandlerList<F>,
1465 handler: Rc<F>,
1466 ) -> Rc<dyn IDisposable> {
1467 let token = list(self).add(handler);
1468 let weak = self.to_ref().downgrade();
1469 Disposable::create(move || {
1470 if let Some(this) = weak.upgrade() {
1471 list(&this).remove(token);
1472 }
1473 })
1474 }
1475
1476 pub fn text_changed(
1479 &self,
1480 handler: impl Fn(&Interactive, &TextChangedEventArgs) + 'static,
1481 ) -> RoutedEventHandlerToken {
1482 self.add_handler(Self::text_changed_event(), handler)
1483 }
1484
1485 pub fn populating(&self, handler: impl Fn(&PopulatingEventArgs) + 'static) -> Rc<dyn IDisposable> {
1494 self.subscription::<dyn Fn(&PopulatingEventArgs)>(|this| &this.populating, Rc::new(handler))
1495 }
1496
1497 pub fn populated(&self, handler: impl Fn(&PopulatedEventArgs) + 'static) -> Rc<dyn IDisposable> {
1500 self.subscription::<dyn Fn(&PopulatedEventArgs)>(|this| &this.populated, Rc::new(handler))
1501 }
1502
1503 pub fn drop_down_opening(&self, handler: impl Fn(&CancelEventArgs) + 'static) -> Rc<dyn IDisposable> {
1506 self.subscription::<dyn Fn(&CancelEventArgs)>(|this| &this.drop_down_opening, Rc::new(handler))
1507 }
1508
1509 pub fn drop_down_opened(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
1512 self.subscription::<dyn Fn()>(|this| &this.drop_down_opened, Rc::new(handler))
1513 }
1514
1515 pub fn drop_down_closing(&self, handler: impl Fn(&CancelEventArgs) + 'static) -> Rc<dyn IDisposable> {
1518 self.subscription::<dyn Fn(&CancelEventArgs)>(|this| &this.drop_down_closing, Rc::new(handler))
1519 }
1520
1521 pub fn drop_down_closed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
1524 self.subscription::<dyn Fn()>(|this| &this.drop_down_closed, Rc::new(handler))
1525 }
1526
1527 pub fn selection_changed(
1530 &self,
1531 handler: impl Fn(&Interactive, &SelectionChangedEventArgs) + 'static,
1532 ) -> RoutedEventHandlerToken {
1533 self.add_handler(Self::selection_changed_event(), handler)
1534 }
1535
1536 fn closing_drop_down(&self, old_value: bool) {
1540 let args = CancelEventArgs::new();
1541 self.on_drop_down_closing(&args);
1542
1543 if args.cancel() {
1544 self.ignore_property_change.set(true);
1545 self.set_current_value(Self::is_drop_down_open_property(), old_value);
1546 } else {
1547 self.close_drop_down();
1548 }
1549
1550 self.update_pseudo_classes();
1551 }
1552
1553 fn opening_drop_down(&self, old_value: bool) {
1556 let args = CancelEventArgs::new();
1557
1558 self.on_drop_down_opening(&args);
1560
1561 if args.cancel() {
1562 self.ignore_property_change.set(true);
1563 self.set_current_value(Self::is_drop_down_open_property(), old_value);
1564 } else {
1565 self.open_drop_down();
1566 }
1567
1568 self.update_pseudo_classes();
1569 }
1570
1571 fn drop_down_popup_closed(&self) {
1573 if self.is_drop_down_open() {
1575 self.set_current_value(Self::is_drop_down_open_property(), false);
1576 }
1577
1578 if self.popup_has_opened.get() {
1580 self.on_drop_down_closed();
1581 }
1582 }
1583
1584 fn populate_drop_down(&self) {
1586 let timer = self.delay_timer.borrow().as_ref().map(|(timer, _)| timer.clone());
1587 if let Some(timer) = timer {
1588 timer.stop();
1589 }
1590
1591 self.set_search_text(self.text());
1593
1594 if self.try_populate_async(self.search_text()) {
1595 return;
1596 }
1597
1598 let populating = PopulatingEventArgs::new(self.search_text());
1603 self.on_populating(&populating);
1604 if !populating.cancel() {
1605 self.populate_complete();
1606 }
1607 }
1608
1609 fn try_populate_async(&self, search_text: Option<String>) -> bool {
1610 if let Some(source) = self.population_cancellation_token_source.take() {
1611 source.cancel();
1612 }
1613
1614 let Some(populator) = self.async_populator() else {
1615 return false;
1616 };
1617
1618 let source = CancellationTokenSource::new();
1619 let token = source.token();
1620 *self.population_cancellation_token_source.borrow_mut() = Some(source);
1621
1622 let context = FerroSynchronizationContext::current().unwrap_or_else(|| {
1625 FerroSynchronizationContext::with_dispatcher(&Dispatcher::current_dispatcher(), DispatcherPriority::NORMAL)
1626 });
1627 drop(context.to_task_scheduler().start_local(Self::populate_async(self.to_ref(), populator, search_text, token)));
1628
1629 true
1630 }
1631
1632 async fn populate_async(
1633 this: Ref<Self>,
1634 populator: AutoCompleteAsyncPopulator,
1635 search_text: Option<String>,
1636 cancellation_token: CancellationToken,
1637 ) {
1638 if let Ok(result_list) = populator.invoke(search_text, cancellation_token.clone()).await {
1640 if !cancellation_token.is_cancellation_requested() {
1641 let target = this.clone();
1642 let token = cancellation_token.clone();
1643 let operation = Dispatcher::ui_thread().invoke_async_local(move || {
1644 if !token.is_cancellation_requested() {
1645 target.set_current_value(
1646 Self::items_source_property(),
1647 Some(ItemsSource::from_items(result_list)),
1648 );
1649 target.populate_complete();
1650 }
1651 });
1652 let _ = operation.await;
1655 }
1656 }
1657
1658 this.population_cancellation_token_source.take();
1659 }
1660
1661 fn open_drop_down(&self) {
1664 if let Some(popup) = self.drop_down_popup() {
1665 popup.set_is_open(true);
1666 }
1667 self.popup_has_opened.set(true);
1668 self.on_drop_down_opened();
1669 }
1670
1671 fn close_drop_down(&self) {
1674 if self.popup_has_opened.get() {
1675 if let Some(adapter) = self.selection_adapter() {
1676 adapter.set_selected_item(None);
1677 }
1678 if let Some(popup) = self.drop_down_popup() {
1679 popup.set_is_open(false);
1680 }
1681 self.on_drop_down_closed();
1682 }
1683 }
1684
1685 fn on_text_box_text_changed(&self) {
1691 if self.ignore_text_box_text_change.get() > 0 {
1692 self.ignore_text_box_text_change.set(self.ignore_text_box_text_change.get() - 1);
1693 return;
1694 }
1695
1696 let this = self.to_ref();
1699 Dispatcher::ui_thread().post_local(
1700 move || {
1701 if let Some(text_box) = this.text_box() {
1704 this.text_updated(text_box.text(), true);
1705 }
1706 },
1707 DispatcherPriority::DEFAULT,
1708 );
1709 }
1710
1711 fn update_text_value(&self, value: Option<String>, user_initiated: Option<bool>) {
1720 let mut call_text_changed = false;
1721
1722 if user_initiated.unwrap_or(true) && self.text() != value {
1724 self.ignore_text_property_change.set(self.ignore_text_property_change.get() + 1);
1725 self.set_current_value(Self::text_property(), value.clone());
1726 call_text_changed = true;
1727 }
1728
1729 if user_initiated != Some(true) {
1731 if let Some(text_box) = self.text_box() {
1732 if text_box.text() != value {
1733 self.ignore_text_box_text_change.set(self.ignore_text_box_text_change.get() + 1);
1734 text_box.set_text(Some(value.as_deref().unwrap_or("")));
1735
1736 if !call_text_changed {
1738 let text = self.text();
1739 if text == value || text.is_none() {
1740 call_text_changed = true;
1741 }
1742 }
1743 }
1744 }
1745 }
1746
1747 if call_text_changed {
1748 self.on_text_changed(&TextChangedEventArgs::with_event(Self::text_changed_event()));
1749 }
1750 }
1751
1752 fn text_updated(&self, new_text: Option<String>, user_initiated: bool) {
1759 let new_text = new_text.unwrap_or_default();
1760
1761 if self.is_text_completion_enabled() {
1765 if let Some(text_box) = self.text_box() {
1766 if self.text_box_selection_length() > 0
1767 && self.text_box_selection_start() != utf16_length(text_box.text().as_deref())
1768 {
1769 return;
1770 }
1771 }
1772 }
1773
1774 let minimum_prefix_length = self.minimum_prefix_length();
1778 let minimum_length_reached =
1779 utf16_length(Some(&new_text)) >= minimum_prefix_length && minimum_prefix_length >= 0;
1780
1781 self.user_called_populate.set(minimum_length_reached && user_initiated);
1782
1783 self.update_text_value(Some(new_text), Some(user_initiated));
1785
1786 if minimum_length_reached {
1787 self.ignore_text_selection_change.set(true);
1788
1789 let timer = self.delay_timer.borrow().as_ref().map(|(timer, _)| timer.clone());
1790 match timer {
1791 Some(timer) => timer.start(),
1792 None => self.populate_drop_down(),
1793 }
1794 } else {
1795 self.set_search_text(Some(String::new()));
1796 if self.selected_item().is_some() {
1797 self.skip_selected_item_text_update.set(true);
1798 }
1799
1800 self.set_current_value(Self::selected_item_property(), None);
1801
1802 if self.is_drop_down_open() {
1803 self.set_current_value(Self::is_drop_down_open_property(), false);
1804 }
1805 }
1806 }
1807
1808 fn clear_view(&self) {
1813 self.view.clear();
1814 }
1815
1816 fn refresh_view(&self) {
1819 if self.filter_in_action.get() {
1821 self.cancel_requested.set(true);
1822 }
1823
1824 self.filter_in_action.set(true);
1826
1827 let _filter_in_action = Reset(&self.filter_in_action);
1830 let _cancel_requested = Reset(&self.cancel_requested);
1831
1832 let items = self.items.borrow().clone();
1833 let Some(items) = items else {
1834 self.clear_view();
1835 return;
1836 };
1837
1838 let text = self.text().unwrap_or_default();
1840
1841 let text_filter = self.text_filter();
1843 let item_filter = self.item_filter();
1844 let object_filtering = self.filter_mode() == AutoCompleteFilterMode::Custom && text_filter.is_none();
1845 let mut new_view_items = Vec::new();
1846
1847 if object_filtering && item_filter.is_none() {
1850 panic!("ItemFilter property can not be null when FilterMode has value AutoCompleteFilterMode.Custom");
1851 }
1852
1853 for item in items.iter() {
1854 if self.cancel_requested.get() {
1856 return;
1857 }
1858
1859 let in_results = if let Some(text_filter) = &text_filter {
1860 text_filter.invoke(Some(&text), &self.format_value(item))
1861 } else if let (true, Some(item_filter)) = (object_filtering, &item_filter) {
1862 item_filter.invoke(Some(&text), item)
1863 } else {
1864 true
1865 };
1866
1867 if in_results {
1868 new_view_items.push(item.clone());
1869 }
1870 }
1871
1872 self.view.clear();
1873 self.view.add_range(new_view_items);
1874 }
1875
1876 fn on_items_source_changed(&self, new_value: Option<ItemsSource>) {
1880 if let Some((source, token)) = self.collection_change_subscription.take() {
1883 source.list().remove_collection_changed(token);
1884 }
1885
1886 if let Some(source) = &new_value {
1889 let weak = self.to_ref().downgrade();
1890 let handler: Rc<ItemsChangedHandler> = Rc::new(move |e: &ItemsChangedEventArgs<'_>| {
1891 if let Some(this) = weak.upgrade() {
1892 this.items_collection_changed(e);
1893 }
1894 });
1895 if let Some(token) = source.list().add_collection_changed(handler) {
1896 *self.collection_change_subscription.borrow_mut() = Some((source.clone(), token));
1897 }
1898 }
1899
1900 *self.items.borrow_mut() = new_value.map(|source| Rc::new(source.to_vec()));
1902
1903 self.clear_view();
1905 self.set_view_on_adapter();
1906 if self.is_drop_down_open() {
1907 self.refresh_view();
1908 }
1909 }
1910
1911 fn set_view_on_adapter(&self) {
1913 if let Some(adapter) = self.selection_adapter() {
1914 if adapter.items_source().as_ref() != Some(&self.view_source) {
1915 adapter.set_items_source(Some(self.view_source.clone()));
1916 }
1917 }
1918 }
1919
1920 fn items_collection_changed(&self, e: &ItemsChangedEventArgs<'_>) {
1923 {
1925 let mut cache = self.items.borrow_mut();
1926 let items = cache.as_mut().map(Rc::make_mut);
1927 if let Some(items) = items {
1928 if e.action == NotifyCollectionChangedAction::Remove {
1929 for _ in 0..e.old_items.len() {
1930 items.remove(e.old_starting_index as usize);
1931 }
1932 }
1933 if e.action == NotifyCollectionChangedAction::Add && items.len() as i32 >= e.new_starting_index {
1934 for index in 0..e.new_items.len() {
1935 items.insert(e.new_starting_index as usize + index, e.new_items.get(index));
1936 }
1937 }
1938 if e.action == NotifyCollectionChangedAction::Replace {
1939 for index in 0..e.new_items.len() {
1940 items[e.new_starting_index as usize] = e.new_items.get(index);
1941 }
1942 }
1943 }
1944 }
1945
1946 if e.action == NotifyCollectionChangedAction::Remove || e.action == NotifyCollectionChangedAction::Replace {
1948 for index in 0..e.old_items.len() {
1949 self.view.remove(&e.old_items.get(index));
1950 }
1951 }
1952
1953 if e.action == NotifyCollectionChangedAction::Reset {
1954 self.clear_view();
1956 if let Some(source) = self.items_source() {
1957 *self.items.borrow_mut() = Some(Rc::new(source.to_vec()));
1958 }
1959 }
1960
1961 self.refresh_view();
1963 }
1964
1965 pub fn populate_complete(&self) {
1978 self.refresh_view();
1980
1981 let populated = PopulatedEventArgs::new(self.view_source.clone());
1983 self.on_populated(&populated);
1984
1985 self.set_view_on_adapter();
1986
1987 let is_drop_down_open = self.user_called_populate.get() && self.view.count() > 0;
1988 if is_drop_down_open != self.is_drop_down_open() {
1989 self.ignore_property_change.set(true);
1990 self.set_current_value(Self::is_drop_down_open_property(), is_drop_down_open);
1991 }
1992 if self.is_drop_down_open() {
1993 self.opening_drop_down(false);
1994 } else {
1995 self.closing_drop_down(true);
1996 }
1997
1998 self.update_text_completion(self.user_called_populate.get());
1999 }
2000
2001 fn update_text_completion(&self, user_initiated: bool) {
2008 let mut new_selected_item: Option<BoxedValue> = None;
2010 let text = self.text();
2011
2012 if self.view.count() > 0 {
2017 let text_box = if self.is_text_completion_enabled() && user_initiated { self.text_box() } else { None };
2018 if let Some(text_box) = text_box {
2019 let current_length = utf16_length(text_box.text().as_deref());
2020 let selection_start = self.text_box_selection_start();
2021 if text.as_deref().is_some_and(|text| selection_start == utf16_length(Some(text)))
2022 && selection_start > self.text_selection_start.get()
2023 {
2024 let filter_mode = self.filter_mode();
2031 let top = if filter_mode == AutoCompleteFilterMode::StartsWith
2032 || filter_mode == AutoCompleteFilterMode::StartsWithCaseSensitive
2033 {
2034 self.view.get(0)
2035 } else {
2036 self.try_get_match(
2037 text.as_deref(),
2038 AutoCompleteSearch::get_filter(AutoCompleteFilterMode::StartsWith),
2039 )
2040 };
2041
2042 if top.is_some() {
2044 let top_string = self.format_value(&top);
2045 new_selected_item = top;
2046
2047 let text = self.text();
2050 let top_length = utf16_length(top_string.as_deref());
2051 let min_length = top_length.min(utf16_length(text.as_deref()));
2052 let text_start = text.as_deref().map(|text| utf16_prefix(text, min_length));
2053 let top_start = top_string.as_deref().map(|top| utf16_prefix(top, min_length));
2054 if AutoCompleteSearch::equals(text_start.as_deref(), &top_start) {
2055 self.update_text_value(top_string, None);
2057
2058 text_box.set_selection_start(current_length);
2060 text_box.set_selection_end(top_length);
2061 }
2062 }
2063 }
2064 } else {
2065 new_selected_item = self.try_get_match(
2073 text.as_deref(),
2074 AutoCompleteSearch::get_filter(AutoCompleteFilterMode::EqualsCaseSensitive),
2075 );
2076 }
2077 }
2078
2079 if !crate::reference_equals(&self.selected_item(), &new_selected_item) {
2081 self.skip_selected_item_text_update.set(true);
2082 }
2083 self.set_current_value(Self::selected_item_property(), new_selected_item);
2084
2085 if self.ignore_text_selection_change.get() {
2087 self.ignore_text_selection_change.set(false);
2088 if self.text_box().is_some() {
2089 self.text_selection_start.set(self.text_box_selection_start());
2090 }
2091 }
2092 }
2093
2094 fn try_get_match(
2100 &self,
2101 search_text: Option<&str>,
2102 predicate: Option<AutoCompleteFilterPredicate<Option<String>>>,
2103 ) -> Option<BoxedValue> {
2104 let predicate = predicate?;
2105
2106 let view = self.view.snapshot();
2107 for item in view.iter() {
2108 if predicate.invoke(search_text, &self.format_value(item)) {
2109 return item.clone();
2110 }
2111 }
2112
2113 None
2114 }
2115
2116 fn update_pseudo_classes(&self) {
2117 self.pseudo_classes().set(PC_DROPDOWN_OPEN, self.is_drop_down_open());
2118 }
2119
2120 fn clear_text_box_selection(&self) {
2121 if let Some(text_box) = self.text_box() {
2122 let length = utf16_length(text_box.text().as_deref());
2123 text_box.set_selection_start(length);
2124 text_box.set_selection_end(length);
2125 }
2126 }
2127
2128 fn on_selected_item_changed(&self, new_item: &Option<BoxedValue>) {
2131 let text = match new_item {
2132 None => self.search_text(),
2133 Some(item) => {
2134 if let Some(text_selector) = self.text_selector() {
2135 Some(text_selector.invoke(self.search_text().as_deref(), &self.format_value(new_item)))
2136 } else if let Some(item_selector) = self.item_selector() {
2137 Some(item_selector.invoke(self.search_text().as_deref(), item))
2138 } else {
2139 self.format_value(new_item)
2140 }
2141 }
2142 };
2143
2144 self.update_text_value(text, None);
2146
2147 self.clear_text_box_selection();
2149 }
2150
2151 fn on_adapter_selection_changed(&self) {
2153 if let Some(adapter) = self.selection_adapter() {
2154 self.set_current_value(Self::selected_item_property(), adapter.selected_item());
2155 }
2156 }
2157
2158 fn on_adapter_selection_complete(&self) {
2160 self.set_current_value(Self::is_drop_down_open_property(), false);
2161
2162 self.clear_text_box_selection();
2164
2165 if let Some(text_box) = self.text_box() {
2166 text_box.focus();
2167 }
2168 }
2169
2170 fn on_adapter_selection_canceled(&self) {
2172 self.update_text_value(self.search_text(), None);
2173
2174 self.update_text_completion(false);
2176 }
2177}
2178
2179struct AutoCompleteSearch;
2182
2183impl AutoCompleteSearch {
2184 fn get_filter(filter_mode: AutoCompleteFilterMode) -> Option<AutoCompleteFilterPredicate<Option<String>>> {
2191 type Filter = fn(Option<&str>, &Option<String>) -> bool;
2192
2193 ferroui_base::ferro_thread_local! {
2194 static FILTERS: [AutoCompleteFilterPredicate<Option<String>>; 12] = {
2195 let filters: [Filter; 12] = [
2196 AutoCompleteSearch::starts_with,
2197 AutoCompleteSearch::starts_with_case_sensitive,
2198 AutoCompleteSearch::starts_with_ordinal,
2199 AutoCompleteSearch::starts_with_ordinal_case_sensitive,
2200 AutoCompleteSearch::contains,
2201 AutoCompleteSearch::contains_case_sensitive,
2202 AutoCompleteSearch::contains_ordinal,
2203 AutoCompleteSearch::contains_ordinal_case_sensitive,
2204 AutoCompleteSearch::equals,
2205 AutoCompleteSearch::equals_case_sensitive,
2206 AutoCompleteSearch::equals_ordinal,
2207 AutoCompleteSearch::equals_ordinal_case_sensitive,
2208 ];
2209 filters.map(AutoCompleteFilterPredicate::new)
2210 };
2211 }
2212
2213 let index = match filter_mode {
2214 AutoCompleteFilterMode::StartsWith => 0,
2215 AutoCompleteFilterMode::StartsWithCaseSensitive => 1,
2216 AutoCompleteFilterMode::StartsWithOrdinal => 2,
2217 AutoCompleteFilterMode::StartsWithOrdinalCaseSensitive => 3,
2218 AutoCompleteFilterMode::Contains => 4,
2219 AutoCompleteFilterMode::ContainsCaseSensitive => 5,
2220 AutoCompleteFilterMode::ContainsOrdinal => 6,
2221 AutoCompleteFilterMode::ContainsOrdinalCaseSensitive => 7,
2222 AutoCompleteFilterMode::Equals => 8,
2223 AutoCompleteFilterMode::EqualsCaseSensitive => 9,
2224 AutoCompleteFilterMode::EqualsOrdinal => 10,
2225 AutoCompleteFilterMode::EqualsOrdinalCaseSensitive => 11,
2226 AutoCompleteFilterMode::None | AutoCompleteFilterMode::Custom => return None,
2227 };
2228
2229 Some(FILTERS.with(|filters| filters[index].clone()))
2230 }
2231
2232 fn contains_with(s: &Option<String>, value: Option<&str>, comparison: StringComparison) -> bool {
2237 match (s, value) {
2238 (Some(s), Some(value)) => comparison.contains(s, value),
2239 _ => false,
2240 }
2241 }
2242
2243 fn starts_with_with(text: Option<&str>, value: &Option<String>, comparison: StringComparison) -> bool {
2244 match (value, text) {
2245 (Some(value), Some(text)) => comparison.starts_with(value, text),
2246 _ => false,
2247 }
2248 }
2249
2250 fn starts_with(text: Option<&str>, value: &Option<String>) -> bool {
2252 Self::starts_with_with(text, value, StringComparison::CurrentCultureIgnoreCase)
2253 }
2254
2255 fn starts_with_case_sensitive(text: Option<&str>, value: &Option<String>) -> bool {
2257 Self::starts_with_with(text, value, StringComparison::CurrentCulture)
2258 }
2259
2260 fn starts_with_ordinal(text: Option<&str>, value: &Option<String>) -> bool {
2262 Self::starts_with_with(text, value, StringComparison::OrdinalIgnoreCase)
2263 }
2264
2265 fn starts_with_ordinal_case_sensitive(text: Option<&str>, value: &Option<String>) -> bool {
2267 Self::starts_with_with(text, value, StringComparison::Ordinal)
2268 }
2269
2270 fn contains(text: Option<&str>, value: &Option<String>) -> bool {
2273 Self::contains_with(value, text, StringComparison::CurrentCultureIgnoreCase)
2274 }
2275
2276 fn contains_case_sensitive(text: Option<&str>, value: &Option<String>) -> bool {
2278 Self::contains_with(value, text, StringComparison::CurrentCulture)
2279 }
2280
2281 fn contains_ordinal(text: Option<&str>, value: &Option<String>) -> bool {
2283 Self::contains_with(value, text, StringComparison::OrdinalIgnoreCase)
2284 }
2285
2286 fn contains_ordinal_case_sensitive(text: Option<&str>, value: &Option<String>) -> bool {
2288 Self::contains_with(value, text, StringComparison::Ordinal)
2289 }
2290
2291 fn equals(text: Option<&str>, value: &Option<String>) -> bool {
2293 StringComparison::CurrentCultureIgnoreCase.equals(value.as_deref(), text)
2294 }
2295
2296 fn equals_case_sensitive(text: Option<&str>, value: &Option<String>) -> bool {
2298 StringComparison::CurrentCulture.equals(value.as_deref(), text)
2299 }
2300
2301 fn equals_ordinal(text: Option<&str>, value: &Option<String>) -> bool {
2303 StringComparison::OrdinalIgnoreCase.equals(value.as_deref(), text)
2304 }
2305
2306 fn equals_ordinal_case_sensitive(text: Option<&str>, value: &Option<String>) -> bool {
2308 StringComparison::Ordinal.equals(value.as_deref(), text)
2309 }
2310}