1use crate::assigned_binding::AssignedBinding;
2use crate::generators::{ItemContainerGenerator, RecycleKey};
3use crate::items_source::{ItemsChangedEventArgs, ItemsSource, ItemsView};
4use crate::metadata::PseudoClassesAttribute;
5use crate::navigable_containers::{as_navigable_container, register_navigable_container};
6use crate::presenters::{ContentPresenter, ItemsPresenter};
7use crate::primitives::{
8 HeaderedContentControl, HeaderedItemsControl, HeaderedSelectingItemsControl, TemplateAppliedEventArgs, TemplatedControl, TemplatedControlImpl,
9 TemplatedControlImplExt,
10};
11use crate::templates::{FuncDataTemplate, FuncTemplate, IDataTemplate, ITemplateOf};
12use crate::{
13 Canvas, ContainerClearingEventArgs, ContainerIndexChangedEventArgs, ContainerPreparedEventArgs, ContentControl,
14 Control, ControlImpl, ItemCollection, ItemsSourceView, Panel, StackPanel, TextBlock, VirtualizingPanel, WrapPanel,
15};
16use ferroui_base::collections::NotifyCollectionChangedAction;
17use ferroui_base::input::{
18 FocusChangedEventArgs, FocusManager, INavigableContainer, InputElement, InputElementImpl, InputElementImplExt,
19 KeyEventArgs,
20 KeyModifiers, KeyboardNavigation, NavigationDirection, NavigationMethod,
21};
22use ferroui_base::interactivity::InteractiveImpl;
23use ferroui_base::layout::LayoutableImpl;
24use ferroui_base::logical_tree::{ChildIndexChangedEventArgs, IChildIndexProvider};
25use ferroui_base::reactive::{Disposable, IDisposable};
26use ferroui_base::styling::ControlTheme;
27use ferroui_base::utilities::HandlerList;
28use ferroui_base::{
29 ferro_class, ferro_impl_classes, ferro_property, instantiate, AnyValue, AttachedProperty, BoxedValue,
30 DirectProperty, FerroObject, FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs,
31 ObjectType, PropertyValue, Ref, StyledElement, StyledElementImpl, StyledProperty, Upcast, Visual, VisualImpl,
32 WeakRef,
33};
34use std::cell::{Cell, OnceCell, RefCell};
35use std::rc::Rc;
36
37type ChildIndexChangedHandlers = HandlerList<dyn Fn(&ChildIndexChangedEventArgs)>;
38
39#[repr(C)]
41pub struct ItemsControl {
42 base: TemplatedControl,
43 items: ItemCollection,
44 item_count: Cell<i32>,
45 item_container_generator: OnceCell<Rc<ItemContainerGenerator>>,
46 child_index_changed: Rc<ChildIndexChangedHandlers>,
47 child_index_provider: OnceCell<Rc<ItemsControlChildIndexProvider>>,
48 display_member_item_template: RefCell<Option<Rc<dyn IDataTemplate>>>,
49 items_presenter: RefCell<Option<Ref<ItemsPresenter>>>,
50 presenter: RefCell<Option<Ref<ItemsPresenter>>>,
51 wrap_focus: Cell<bool>,
52 preparing_container: HandlerList<dyn Fn(&ContainerPreparedEventArgs)>,
53 container_prepared: HandlerList<dyn Fn(&ContainerPreparedEventArgs)>,
54 container_index_changed: HandlerList<dyn Fn(&ContainerIndexChangedEventArgs)>,
55 container_clearing: HandlerList<dyn Fn(&ContainerClearingEventArgs)>,
56}
57
58ferro_class! {
59 ItemsControl: TemplatedControl, virtuals ItemsControlImpl: TemplatedControlImpl {
60 fn create_container_for_item_override(
62 this,
63 item: &Option<BoxedValue>,
64 index: i32,
65 recycle_key: Option<RecycleKey>
66 ) -> Ref<Control>;
67 fn prepare_container_for_item_override(this, container: &Ref<Control>, item: &Option<BoxedValue>, index: i32);
69 fn container_for_item_prepared_override(this, container: &Ref<Control>, item: &Option<BoxedValue>, index: i32);
77 fn container_index_changed_override(this, container: &Ref<Control>, old_index: i32, new_index: i32);
80 fn clear_container_for_item_override(this, container: &Ref<Control>);
83 fn needs_container_override(this, item: &Option<BoxedValue>, index: i32) -> (bool, Option<RecycleKey>);
92 fn on_items_view_collection_changed(this, e: &ItemsChangedEventArgs<'_>);
95 }
96}
97ferroui_base::ferro_class_info!(ItemsControl { new: ItemsControl::new });
98
99ferro_impl_classes!(ItemsControl: VisualImpl, LayoutableImpl, InteractiveImpl);
100
101impl ControlImpl for ItemsControl {
102 fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
103 crate::automation::peers::ItemsControlAutomationPeer::new(this).upcast()
104 }
105}
106
107impl FerroObjectImpl for ItemsControl {
108 fn constructed(this: &Self) {
109 Self::parent_constructed(this);
110
111 this.update_pseudo_classes();
112
113 let weak = this.to_ref().downgrade();
114 this.items.add_collection_changed(Rc::new(move |e: &ItemsChangedEventArgs<'_>| {
115 if let Some(this) = weak.upgrade() {
116 this.on_items_view_collection_changed(e);
117 }
118 }));
119 }
120
121 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
122 Self::parent_on_property_changed(this, change);
123
124 if change.property() == Self::item_container_theme_property().as_property()
125 && this.item_container_generator.get().is_some()
126 {
127 this.refresh_containers();
128 } else if change.property() == Self::items_source_property().as_property() {
129 this.items.set_items_source(change.get_new_value::<Option<ItemsSource>>());
130 } else if change.property() == Self::item_template_property().as_property() {
131 if change.get_new_value::<Option<Rc<dyn IDataTemplate>>>().is_some()
132 && this.display_member_binding().is_some()
133 {
134 panic!("Cannot set both DisplayMemberBinding and ItemTemplate.");
135 }
136 this.refresh_containers();
137 } else if change.property() == Self::display_member_binding_property().as_property() {
138 if change.get_new_value::<Option<AssignedBinding>>().is_some() && this.item_template().is_some() {
139 panic!("Cannot set both DisplayMemberBinding and ItemTemplate.");
140 }
141 *this.display_member_item_template.borrow_mut() = None;
142 this.refresh_containers();
143 }
144 }
145}
146
147impl StyledElementImpl for ItemsControl {
148 fn child_index_provider(this: &Self) -> Option<Rc<dyn IChildIndexProvider>> {
149 let provider = this
150 .child_index_provider
151 .get_or_init(|| Rc::new(ItemsControlChildIndexProvider { owner: this.to_ref().downgrade() }))
152 .clone();
153 Some(provider)
154 }
155}
156
157impl InputElementImpl for ItemsControl {
158 fn on_got_focus(this: &Self, e: &FocusChangedEventArgs) {
159 Self::parent_on_got_focus(this, e);
160
161 if !e.is_source(this) {
166 if let Some(ie) = e.source().and_then(|source| source.cast::<InputElement>()) {
167 KeyboardNavigation::set_tab_once_active_element(this, Some(ie));
168 }
169 }
170 }
171
172 fn on_key_down(this: &Self, e: &KeyEventArgs) {
174 if !e.handled() {
175 let focus = FocusManager::get_focus_manager(this);
176 let direction = e.key.to_navigation_direction(KeyModifiers::NONE);
177 let panel = this.presenter().and_then(|presenter| presenter.panel());
178 let container = panel.as_ref().and_then(|panel| as_navigable_container(panel));
179 let focused = focus.as_ref().and_then(|focus| focus.get_focused_element());
180
181 let (Some(container), Some(focused), Some(direction), Some(panel)) =
182 (container, focused, direction, panel.as_ref())
183 else {
184 return;
185 };
186
187 if direction.is_tab() {
188 return;
189 }
190
191 let panel_visual: Ref<Visual> = panel.clone().upcast();
192 let mut current: Option<Ref<Visual>> = Some(focused.upcast());
193
194 while let Some(visual) = current {
195 let parent = visual.visual_parent();
196
197 if parent.as_ref() == Some(&panel_visual) {
198 if let Some(input_element) = visual.cast::<InputElement>() {
199 let next = Self::get_next_control(container, direction, Some(&input_element), this.wrap_focus());
200
201 if let Some(next) = next {
202 next.focus_with(NavigationMethod::Directional, e.key_modifiers);
203 e.set_handled(true);
204 }
205
206 break;
207 }
208 }
209
210 current = parent;
211 }
212 }
213
214 Self::parent_on_key_down(this, e);
215 }
216}
217
218impl TemplatedControlImpl for ItemsControl {
219 fn on_apply_template(this: &Self, e: &TemplateAppliedEventArgs) {
220 Self::parent_on_apply_template(this, e);
221 *this.items_presenter.borrow_mut() = e.name_scope().find_as::<ItemsPresenter>("PART_ItemsPresenter");
222 }
223}
224
225impl ItemsControlImpl for ItemsControl {
226 fn create_container_for_item_override(
227 _this: &Self,
228 _item: &Option<BoxedValue>,
229 _index: i32,
230 _recycle_key: Option<RecycleKey>,
231 ) -> Ref<Control> {
232 ContentPresenter::new().upcast()
233 }
234
235 fn prepare_container_for_item_override(
236 this: &Self,
237 container: &Ref<Control>,
238 item: &Option<BoxedValue>,
239 _index: i32,
240 ) {
241 let item_control = item.as_ref().and_then(Control::from_boxed);
242
243 if item_control.as_ref() == Some(container) {
244 return;
245 }
246
247 let item_template = this.get_effective_item_template();
248
249 if let Some(hcc) = container.downcast_ref::<HeaderedContentControl>() {
250 Self::set_if_unset(hcc, ContentControl::content_property(), item.clone());
251
252 let headered = item_control.as_ref().and_then(|c| crate::i_headered::as_headered(c));
254 if let Some(headered) = headered {
255 Self::set_if_unset(hcc, HeaderedContentControl::header_property(), headered.header());
256 } else if !Self::is_visual(item) {
257 Self::set_if_unset(hcc, HeaderedContentControl::header_property(), item.clone());
258 }
259
260 if let Some(item_template) = &item_template {
261 Self::set_if_unset(
262 hcc,
263 HeaderedContentControl::header_template_property(),
264 Some(item_template.clone()),
265 );
266 }
267 } else if let Some(cc) = container.downcast_ref::<ContentControl>() {
268 Self::set_if_unset(cc, ContentControl::content_property(), item.clone());
269 if let Some(item_template) = &item_template {
270 Self::set_if_unset(cc, ContentControl::content_template_property(), Some(item_template.clone()));
271 }
272 } else if let Some(p) = container.downcast_ref::<ContentPresenter>() {
273 Self::set_if_unset(p, ContentPresenter::content_property(), item.clone());
274 if let Some(item_template) = &item_template {
275 Self::set_if_unset(p, ContentPresenter::content_template_property(), Some(item_template.clone()));
276 }
277 } else if let Some(ic) = container.downcast_ref::<ItemsControl>() {
278 if let Some(item_template) = &item_template {
279 Self::set_if_unset(ic, Self::item_template_property(), Some(item_template.clone()));
280 }
281 if let Some(ict) = this.item_container_theme() {
282 Self::set_if_unset(ic, Self::item_container_theme_property(), Some(ict));
283 }
284 }
285
286 if let Some(hic) = container.downcast_ref::<HeaderedItemsControl>() {
289 Self::set_if_unset(hic, HeaderedItemsControl::header_property(), item.clone());
290 Self::set_if_unset(hic, HeaderedItemsControl::header_template_property(), item_template.clone());
291 hic.prepare_item_container(this);
292 } else if let Some(hsic) = container.downcast_ref::<HeaderedSelectingItemsControl>() {
293 Self::set_if_unset(hsic, HeaderedSelectingItemsControl::header_property(), item.clone());
294 Self::set_if_unset(hsic, HeaderedSelectingItemsControl::header_template_property(), item_template.clone());
295 hsic.prepare_item_container(this);
296 }
297 }
298
299 fn container_for_item_prepared_override(
300 _this: &Self,
301 _container: &Ref<Control>,
302 _item: &Option<BoxedValue>,
303 _index: i32,
304 ) {
305 }
306
307 fn container_index_changed_override(_this: &Self, _container: &Ref<Control>, _old_index: i32, _new_index: i32) {}
308
309 fn clear_container_for_item_override(_this: &Self, container: &Ref<Control>) {
310 if let Some(hcc) = container.downcast_ref::<HeaderedContentControl>() {
311 hcc.clear_value(ContentControl::content_property());
312 hcc.clear_value(HeaderedContentControl::header_property());
313 hcc.clear_value(HeaderedContentControl::header_template_property());
314 } else if let Some(cc) = container.downcast_ref::<ContentControl>() {
315 cc.clear_value(ContentControl::content_property());
316 cc.clear_value(ContentControl::content_template_property());
317 } else if let Some(p) = container.downcast_ref::<ContentPresenter>() {
318 p.clear_value(ContentPresenter::content_property());
319 p.clear_value(ContentPresenter::content_template_property());
320 } else if let Some(ic) = container.downcast_ref::<ItemsControl>() {
321 ic.clear_value(Self::item_template_property());
322 ic.clear_value(Self::item_container_theme_property());
323 }
324
325 if let Some(hic) = container.downcast_ref::<HeaderedItemsControl>() {
326 hic.clear_value(HeaderedItemsControl::header_property());
327 hic.clear_value(HeaderedItemsControl::header_template_property());
328 } else if let Some(hsic) = container.downcast_ref::<HeaderedSelectingItemsControl>() {
329 hsic.clear_value(HeaderedSelectingItemsControl::header_property());
330 hsic.clear_value(HeaderedSelectingItemsControl::header_template_property());
331 }
332
333 }
337
338 fn needs_container_override(this: &Self, item: &Option<BoxedValue>, _index: i32) -> (bool, Option<RecycleKey>) {
339 this.needs_container::<Control>(item)
340 }
341
342 fn on_items_view_collection_changed(this: &Self, e: &ItemsChangedEventArgs<'_>) {
343 if !this.items.is_read_only() {
344 match e.action {
345 NotifyCollectionChangedAction::Add => this.add_control_items_to_logical_children(e.new_items),
346 NotifyCollectionChangedAction::Remove => this.remove_control_items_from_logical_children(e.old_items),
347 _ => {}
348 }
349 }
350
351 this.set_item_count(this.items_view().count() as i32);
352 }
353}
354
355impl ItemsControl {
356 pub const PSEUDO_CLASSES: PseudoClassesAttribute = PseudoClassesAttribute::new(&[":empty", ":singleitem"]);
358}
359
360ferroui_base::ferro_properties! { impl ItemsControl {
361 ferro_property!(
362 pub fn item_container_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
364 FerroProperty::register::<ItemsControl, _>("ItemContainerTheme", None)
365 }
366 );
367
368 ferro_property!(
369 pub fn item_count_property() -> DirectProperty<ItemsControl, i32> {
371 FerroProperty::register_direct::<ItemsControl, _>("ItemCount", |o| o.item_count(), None, 0)
372 }
373 );
374
375 ferro_property!(
376 pub fn items_panel_property() -> StyledProperty<Rc<dyn ITemplateOf<Option<Ref<Panel>>>>> {
378 let default_panel: Rc<dyn ITemplateOf<Option<Ref<Panel>>>> =
380 FuncTemplate::new(|| Some(StackPanel::new().upcast::<Panel>()));
381 FerroProperty::register::<ItemsControl, _>("ItemsPanel", default_panel)
382 }
383 );
384
385 ferro_property!(
386 pub fn items_source_property() -> StyledProperty<Option<ItemsSource>> {
388 FerroProperty::register::<ItemsControl, _>("ItemsSource", None)
389 }
390 );
391
392 ferro_property!(
393 pub fn item_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
395 FerroProperty::register::<ItemsControl, _>("ItemTemplate", None)
396 }
397 );
398
399 ferro_property!(
400 pub fn display_member_binding_property() -> StyledProperty<Option<AssignedBinding>> {
402 FerroProperty::register::<ItemsControl, _>("DisplayMemberBinding", None)
403 }
404 );
405
406 ferro_property!(
407 fn applied_item_container_theme_property() -> AttachedProperty<Option<Ref<ControlTheme>>> {
408 FerroProperty::register_attached::<ItemsControl, Control, _>("AppliedItemContainerTheme", None)
409 }
410 );
411} }
412
413impl ItemsControl {
414 fn static_constructor() {
415 register_navigable_container::<StackPanel>();
416 register_navigable_container::<WrapPanel>();
417 register_navigable_container::<Canvas>();
418 register_navigable_container::<VirtualizingPanel>();
419 }
420
421 pub fn construct() -> Self {
423 Self {
424 base: TemplatedControl::construct(),
425 items: ItemCollection::new(),
426 item_count: Cell::new(0),
427 item_container_generator: OnceCell::new(),
428 child_index_changed: Rc::new(HandlerList::new()),
429 child_index_provider: OnceCell::new(),
430 display_member_item_template: RefCell::new(None),
431 items_presenter: RefCell::new(None),
432 presenter: RefCell::new(None),
433 wrap_focus: Cell::new(false),
434 preparing_container: HandlerList::new(),
435 container_prepared: HandlerList::new(),
436 container_index_changed: HandlerList::new(),
437 container_clearing: HandlerList::new(),
438 }
439 }
440
441 pub fn new() -> Ref<Self> {
442 instantiate(Self::construct())
443 }
444
445 pub fn display_member_binding(&self) -> Option<AssignedBinding> {
448 self.get_value(Self::display_member_binding_property())
449 }
450
451 pub fn set_display_member_binding(&self, value: Option<AssignedBinding>) {
452 self.set_value(Self::display_member_binding_property(), value)
453 }
454
455 pub fn item_container_generator(&self) -> Rc<ItemContainerGenerator> {
457 self.item_container_generator.get_or_init(|| Rc::new(ItemContainerGenerator::new(self))).clone()
458 }
459
460 pub fn items(&self) -> ItemCollection {
471 self.items.clone()
472 }
473
474 pub fn item_container_theme(&self) -> Option<Ref<ControlTheme>> {
477 self.get_value(Self::item_container_theme_property())
478 }
479
480 pub fn set_item_container_theme(&self, value: Option<Ref<ControlTheme>>) {
481 self.set_value(Self::item_container_theme_property(), value)
482 }
483
484 pub fn item_count(&self) -> i32 {
486 self.item_count.get()
487 }
488
489 fn set_item_count(&self, value: i32) {
490 if self.set_and_raise_cell(Self::item_count_property(), &self.item_count, value) {
491 self.update_pseudo_classes();
492 self.raise_child_index_changed(&ChildIndexChangedEventArgs::total_count_changed());
493 }
494 }
495
496 pub fn items_panel(&self) -> Rc<dyn ITemplateOf<Option<Ref<Panel>>>> {
498 self.get_value(Self::items_panel_property())
499 }
500
501 pub fn set_items_panel(&self, value: Rc<dyn ITemplateOf<Option<Ref<Panel>>>>) {
502 self.set_value(Self::items_panel_property(), value)
503 }
504
505 pub fn items_source(&self) -> Option<ItemsSource> {
520 self.get_value(Self::items_source_property())
521 }
522
523 pub fn set_items_source(&self, value: Option<ItemsSource>) {
524 self.set_value(Self::items_source_property(), value)
525 }
526
527 pub fn item_template(&self) -> Option<Rc<dyn IDataTemplate>> {
530 self.get_value(Self::item_template_property())
531 }
532
533 pub fn set_item_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
534 self.set_value(Self::item_template_property(), value)
535 }
536
537 pub fn presenter(&self) -> Option<Ref<ItemsPresenter>> {
539 self.presenter.borrow().clone()
540 }
541
542 pub fn items_panel_root(&self) -> Option<Ref<Panel>> {
544 self.presenter().and_then(|presenter| presenter.panel())
545 }
546
547 pub fn items_view(&self) -> &Rc<ItemsSourceView> {
549 self.items.view()
550 }
551
552 pub fn wrap_focus(&self) -> bool {
555 self.wrap_focus.get()
556 }
557
558 pub fn set_wrap_focus(&self, value: bool) {
559 self.wrap_focus.set(value)
560 }
561
562 pub fn preparing_container(&self, handler: impl Fn(&ContainerPreparedEventArgs) + 'static) -> Rc<dyn IDisposable> {
567 let token = self.preparing_container.add(Rc::new(handler));
568 let weak = self.to_ref().downgrade();
569 Disposable::create(move || {
570 if let Some(this) = weak.upgrade() {
571 this.preparing_container.remove(token);
572 }
573 })
574 }
575
576 pub fn container_prepared(&self, handler: impl Fn(&ContainerPreparedEventArgs) + 'static) -> Rc<dyn IDisposable> {
581 let token = self.container_prepared.add(Rc::new(handler));
582 let weak = self.to_ref().downgrade();
583 Disposable::create(move || {
584 if let Some(this) = weak.upgrade() {
585 this.container_prepared.remove(token);
586 }
587 })
588 }
589
590 pub fn container_index_changed(
598 &self,
599 handler: impl Fn(&ContainerIndexChangedEventArgs) + 'static,
600 ) -> Rc<dyn IDisposable> {
601 let token = self.container_index_changed.add(Rc::new(handler));
602 let weak = self.to_ref().downgrade();
603 Disposable::create(move || {
604 if let Some(this) = weak.upgrade() {
605 this.container_index_changed.remove(token);
606 }
607 })
608 }
609
610 pub fn container_clearing(&self, handler: impl Fn(&ContainerClearingEventArgs) + 'static) -> Rc<dyn IDisposable> {
616 let token = self.container_clearing.add(Rc::new(handler));
617 let weak = self.to_ref().downgrade();
618 Disposable::create(move || {
619 if let Some(this) = weak.upgrade() {
620 this.container_clearing.remove(token);
621 }
622 })
623 }
624
625 pub const DEFAULT_RECYCLE_KEY: RecycleKey = RecycleKey::Default;
628
629 pub fn container_from_index(&self, index: i32) -> Option<Ref<Control>> {
634 self.presenter().and_then(|presenter| presenter.container_from_index(index))
635 }
636
637 pub fn container_from_item(&self, item: &Option<BoxedValue>) -> Option<Ref<Control>> {
643 let index = self.items.index_of(item);
644 if index >= 0 {
645 self.container_from_index(index)
646 } else {
647 None
648 }
649 }
650
651 pub fn index_from_container(&self, container: &Ref<Control>) -> i32 {
657 self.presenter().map_or(-1, |presenter| presenter.index_from_container(container))
658 }
659
660 pub fn item_from_container(&self, container: &Ref<Control>) -> Option<BoxedValue> {
666 let index = self.index_from_container(container);
667 if index >= 0 && (index as usize) < self.items.count() {
668 self.items.get_at(index as usize)
669 } else {
670 None
671 }
672 }
673
674 pub fn get_realized_containers(&self) -> Vec<Ref<Control>> {
676 self.presenter().and_then(|presenter| presenter.get_realized_containers()).unwrap_or_default()
677 }
678
679 pub fn scroll_into_view(&self, index: i32) {
681 if let Some(presenter) = self.presenter() {
682 presenter.scroll_into_view(index);
683 }
684 }
685
686 pub fn scroll_into_view_item(&self, item: &Option<BoxedValue>) {
688 self.scroll_into_view(self.items_view().index_of(item))
689 }
690
691 pub fn items_control_from_item_container(container: &Ref<Control>) -> Option<Ref<ItemsControl>> {
697 let mut c = container.parent().and_then(|parent| parent.cast::<Control>());
698
699 while let Some(control) = c {
700 if let Some(items_control) = control.cast::<ItemsControl>() {
701 return if items_control.index_from_container(container) >= 0 { Some(items_control) } else { None };
702 }
703
704 c = control.parent().and_then(|parent| parent.cast::<Control>());
705 }
706
707 None
708 }
709
710 pub fn needs_container<T: ObjectType + Upcast<Control>>(
715 &self,
716 item: &Option<BoxedValue>,
717 ) -> (bool, Option<RecycleKey>) {
718 let is_t = item.as_ref().and_then(Control::from_boxed).is_some_and(|control| control.is::<T>());
719
720 if is_t {
721 (false, None)
722 } else {
723 (true, Some(Self::DEFAULT_RECYCLE_KEY))
724 }
725 }
726
727 pub fn refresh_containers(&self) {
731 if let Some(presenter) = self.presenter() {
732 presenter.refresh();
733 }
734 }
735
736 pub(crate) fn add_logical_child(&self, c: &Ref<Control>) {
737 let child: Ref<StyledElement> = c.clone().upcast();
738 if !self.logical_children().contains(&child) {
739 self.logical_children().add(child);
740 }
741 }
742
743 pub(crate) fn remove_logical_child(&self, c: &Ref<Control>) {
744 let child: Ref<StyledElement> = c.clone().upcast();
745 self.logical_children().remove(&child);
746 }
747
748 pub(crate) fn register_items_presenter(&self, presenter: &ItemsPresenter) {
755 *self.presenter.borrow_mut() = Some(presenter.to_ref());
756 self.raise_child_index_changed(&ChildIndexChangedEventArgs::child_indexes_reset());
757 }
758
759 pub(crate) fn prepare_item_container(&self, container: &Ref<Control>, item: &Option<BoxedValue>, index: i32) {
760 if !self.preparing_container.is_empty() {
761 let e = ContainerPreparedEventArgs::new(container.clone(), index);
762 for (_, handler) in self.preparing_container.snapshot().iter() {
763 handler(&e);
764 }
765 }
766
767 if !container.is_set(StyledElement::theme_property().as_property())
771 || container.get_value(Self::applied_item_container_theme_property()) == container.theme()
772 {
773 let item_container_theme = self.item_container_theme();
774 let matches = item_container_theme
775 .as_ref()
776 .and_then(|theme| theme.target_type())
777 .is_some_and(|target_type| target_type.is_assignable_from(container.style_key()));
778
779 if matches {
780 container.set_current_value(StyledElement::theme_property(), item_container_theme.clone());
784 container.set_value(Self::applied_item_container_theme_property(), item_container_theme);
785 } else {
786 container.clear_value(StyledElement::theme_property());
789 container.clear_value(Self::applied_item_container_theme_property());
790 }
791 }
792
793 if item.as_ref().and_then(Control::from_boxed).is_none() {
794 container.set_data_context(item.clone());
795 }
796
797 self.prepare_container_for_item_override(container, item, index);
798 }
799
800 pub(crate) fn item_container_prepared(&self, container: &Ref<Control>, item: &Option<BoxedValue>, index: i32) {
801 self.container_for_item_prepared_override(container, item, index);
802 if !self.child_index_changed.is_empty() {
803 self.raise_child_index_changed(&ChildIndexChangedEventArgs::new(container.clone().upcast(), index));
804 }
805 if !self.container_prepared.is_empty() {
806 let e = ContainerPreparedEventArgs::new(container.clone(), index);
807 for (_, handler) in self.container_prepared.snapshot().iter() {
808 handler(&e);
809 }
810 }
811 }
812
813 pub(crate) fn item_container_index_changed(&self, container: &Ref<Control>, old_index: i32, new_index: i32) {
814 self.container_index_changed_override(container, old_index, new_index);
815 if !self.child_index_changed.is_empty() {
816 self.raise_child_index_changed(&ChildIndexChangedEventArgs::new(container.clone().upcast(), new_index));
817 }
818 if !self.container_index_changed.is_empty() {
819 let e = ContainerIndexChangedEventArgs::new(container.clone(), old_index, new_index);
820 for (_, handler) in self.container_index_changed.snapshot().iter() {
821 handler(&e);
822 }
823 }
824 }
825
826 pub(crate) fn clear_item_container(&self, container: &Ref<Control>) {
827 self.clear_container_for_item_override(container);
828 if !self.container_clearing.is_empty() {
829 let e = ContainerClearingEventArgs::new(container.clone());
830 for (_, handler) in self.container_clearing.snapshot().iter() {
831 handler(&e);
832 }
833 }
834 }
835
836 fn add_control_items_to_logical_children(&self, items: ItemsView<'_>) {
837 let mut to_add: Option<Vec<Ref<StyledElement>>> = None;
838
839 for i in items {
840 if let Some(control) = i.as_ref().and_then(Control::from_boxed) {
841 let control: Ref<StyledElement> = control.upcast();
842 if !self.logical_children().contains(&control) {
843 to_add.get_or_insert_with(Vec::new).push(control);
844 }
845 }
846 }
847
848 if let Some(to_add) = to_add {
849 self.logical_children().add_range(to_add);
850 }
851 }
852
853 pub(crate) fn set_if_unset<T: PropertyValue>(target: &FerroObject, property: &'static StyledProperty<T>, value: T) {
854 if !target.is_set(property.as_property()) {
855 target.set_current_value(property, value);
856 }
857 }
858
859 fn remove_control_items_from_logical_children(&self, items: ItemsView<'_>) {
860 let mut to_remove: Option<Vec<Ref<StyledElement>>> = None;
861
862 for i in items {
863 if let Some(control) = i.as_ref().and_then(Control::from_boxed) {
864 to_remove.get_or_insert_with(Vec::new).push(control.upcast());
865 }
866 }
867
868 if let Some(to_remove) = to_remove {
869 self.logical_children().remove_all(to_remove);
870 }
871 }
872
873 pub(crate) fn get_effective_item_template(&self) -> Option<Rc<dyn IDataTemplate>> {
876 if let Some(item_template) = self.item_template() {
877 return Some(item_template);
878 }
879
880 if self.display_member_item_template.borrow().is_none() {
881 if let Some(binding) = self.display_member_binding() {
882 let template: Rc<dyn IDataTemplate> = FuncDataTemplate::new(
883 |_| true,
884 move |_, _| {
885 let text_block = TextBlock::new();
886 text_block.bind_binding(TextBlock::text_property().as_property(), &*binding);
887 Some(text_block.upcast())
888 },
889 false,
890 );
891 *self.display_member_item_template.borrow_mut() = Some(template);
892 }
893 }
894
895 self.display_member_item_template.borrow().clone()
896 }
897
898 fn update_pseudo_classes(&self) {
899 self.pseudo_classes().set(":empty", self.item_count() == 0);
900 self.pseudo_classes().set(":singleitem", self.item_count() == 1);
901 }
902
903 fn is_visual(item: &Option<BoxedValue>) -> bool {
906 match item {
907 Some(item) => {
908 let value: &dyn AnyValue = &**item;
909 value.is::<Ref<Control>>()
910 || value.is::<Ref<Visual>>()
911 || ferroui_base::data::core::ValueTypes::as_object(value).is_some_and(|o| o.is::<Visual>())
912 }
913 None => false,
914 }
915 }
916
917 pub fn get_next_control(
920 container: &dyn INavigableContainer,
921 mut direction: NavigationDirection,
922 from: Option<&Ref<InputElement>>,
923 wrap: bool,
924 ) -> Option<Ref<InputElement>> {
925 let mut from: Option<Ref<InputElement>> = from.cloned();
926 let mut current = from.clone();
927
928 loop {
929 let result = container.get_control(direction, current.as_ref(), wrap)?;
930
931 if result.focusable() && result.is_effectively_enabled() && result.is_effectively_visible() {
932 return Some(result);
933 }
934
935 current = Some(result.clone());
936
937 if current == from {
938 return None;
939 }
940
941 match direction {
942 NavigationDirection::First => {
945 direction = NavigationDirection::Down;
946 from = Some(result);
947 }
948 NavigationDirection::Last => {
951 direction = NavigationDirection::Up;
952 from = Some(result);
953 }
954 _ => {}
955 }
956 }
957 }
958
959 fn raise_child_index_changed(&self, e: &ChildIndexChangedEventArgs) {
960 if !self.child_index_changed.is_empty() {
961 for (_, handler) in self.child_index_changed.snapshot().iter() {
962 handler(e);
963 }
964 }
965 }
966}
967
968struct ItemsControlChildIndexProvider {
970 owner: WeakRef<ItemsControl>,
971}
972
973impl IChildIndexProvider for ItemsControlChildIndexProvider {
974 fn get_child_index(&self, child: &StyledElement) -> i32 {
975 let Some(owner) = self.owner.upgrade() else { return -1 };
976 match child.downcast_ref::<Control>() {
977 Some(container) => owner.index_from_container(&container.to_ref()),
978 None => -1,
979 }
980 }
981
982 fn try_get_total_count(&self) -> Option<i32> {
983 let owner = self.owner.upgrade()?;
984 Some(owner.items_view().count() as i32)
985 }
986
987 fn child_index_changed(&self, handler: Rc<dyn Fn(&ChildIndexChangedEventArgs)>) -> Rc<dyn IDisposable> {
988 let Some(owner) = self.owner.upgrade() else { return Disposable::empty() };
989 let token = owner.child_index_changed.add(handler);
990 let handlers = Rc::downgrade(&owner.child_index_changed);
991 Disposable::create(move || {
992 if let Some(handlers) = handlers.upgrade() {
993 handlers.remove(token);
994 }
995 })
996 }
997}