ferroui_controls/primitives/
headered_selecting_items_control.rs1use super::{
2 HeaderedContentControl, HeaderedItemsControl, SelectingItemsControl, SelectingItemsControlImpl, TemplatedControlImpl,
3};
4use crate::metadata::TemplatePartAttribute;
5use crate::presenters::{register_content_presenter_host, ContentPresenter, IContentPresenterHost};
6use crate::templates::IDataTemplate;
7use crate::{Control, ControlImpl, ItemsControl, ItemsControlImpl};
8use ferroui_base::collections::FerroList;
9use ferroui_base::input::InputElementImpl;
10use ferroui_base::interactivity::InteractiveImpl;
11use ferroui_base::layout::LayoutableImpl;
12use ferroui_base::logical_tree::LogicalTreeAttachmentEventArgs;
13use ferroui_base::reactive::IDisposable;
14use ferroui_base::{
15 ferro_class, ferro_impl_classes, ferro_property, instantiate, BoxedValue, FerroObjectImpl,
16 FerroPropertyChangedEventArgs, Ref, StaticType, StyledElement, StyledElementImpl,
17 StyledElementImplExt, StyledProperty, VisualImpl, WeakRef,
18};
19use std::cell::RefCell;
20use std::rc::Rc;
21
22#[repr(C)]
24pub struct HeaderedSelectingItemsControl {
25 base: SelectingItemsControl,
26 items_binding: RefCell<Option<Rc<dyn IDisposable>>>,
27 prepare_item_container_on_attach: RefCell<Option<WeakRef<ItemsControl>>>,
28 header_presenter: RefCell<Option<Ref<ContentPresenter>>>,
29}
30
31ferro_class! {
32 HeaderedSelectingItemsControl: SelectingItemsControl, virtuals HeaderedSelectingItemsControlImpl: SelectingItemsControlImpl {
33 fn register_content_presenter(this, presenter: &ContentPresenter) -> bool;
37 }
38}
39ferroui_base::ferro_class_info!(HeaderedSelectingItemsControl { new: HeaderedSelectingItemsControl::new });
40
41ferro_impl_classes!(
42 HeaderedSelectingItemsControl: VisualImpl,
43 LayoutableImpl,
44 InteractiveImpl,
45 InputElementImpl,
46 ControlImpl,
47 TemplatedControlImpl,
48 ItemsControlImpl,
49 SelectingItemsControlImpl
50);
51
52ferroui_base::ferro_impl_classes!(HeaderedSelectingItemsControl: FerroObjectImpl);
53
54impl StyledElementImpl for HeaderedSelectingItemsControl {
55 fn on_attached_to_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
56 Self::parent_on_attached_to_logical_tree(this, e);
57
58 let pending = this.prepare_item_container_on_attach.take();
59 if let Some(parent) = pending.and_then(|parent| parent.upgrade()) {
60 this.prepare_item_container(&parent);
61 }
62 }
63}
64
65impl HeaderedSelectingItemsControlImpl for HeaderedSelectingItemsControl {
66 fn register_content_presenter(this: &Self, presenter: &ContentPresenter) -> bool {
67 if presenter.name().as_deref() == Some("PART_HeaderPresenter") {
68 *this.header_presenter.borrow_mut() = Some(presenter.to_ref());
69 return true;
70 }
71
72 false
73 }
74}
75
76impl IContentPresenterHost for HeaderedSelectingItemsControl {
77 fn logical_children(&self) -> &FerroList<Ref<StyledElement>> {
78 StyledElement::logical_children(self)
79 }
80
81 fn register_content_presenter(&self, presenter: &ContentPresenter) -> bool {
82 HeaderedSelectingItemsControl::register_content_presenter(self, presenter)
83 }
84}
85
86impl HeaderedSelectingItemsControl {
87 pub const TEMPLATE_PARTS: &'static [TemplatePartAttribute] =
89 &[TemplatePartAttribute::new("PART_HeaderPresenter", <ContentPresenter as StaticType>::TYPE)];
90}
91
92ferroui_base::ferro_properties! { impl HeaderedSelectingItemsControl {
93 ferro_property!(
94 pub fn header_property() -> StyledProperty<Option<BoxedValue>> {
96 HeaderedContentControl::header_property().add_owner::<HeaderedSelectingItemsControl>()
97 }
98 );
99
100 ferro_property!(
101 pub fn header_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
103 HeaderedItemsControl::header_template_property().add_owner::<HeaderedSelectingItemsControl>()
104 }
105 );
106} }
107
108impl HeaderedSelectingItemsControl {
109 fn static_constructor() {
110 register_content_presenter_host::<HeaderedSelectingItemsControl>();
111 Self::header_property().changed().add_class_handler::<HeaderedSelectingItemsControl>(|x, e| x.header_changed(e));
112 }
113
114 pub fn construct() -> Self {
116 Self {
117 base: SelectingItemsControl::construct(),
118 items_binding: RefCell::new(None),
119 prepare_item_container_on_attach: RefCell::new(None),
120 header_presenter: RefCell::new(None),
121 }
122 }
123
124 pub fn new() -> Ref<Self> {
125 instantiate(Self::construct())
126 }
127
128 pub fn header(&self) -> Option<BoxedValue> {
130 self.get_value(Self::header_property())
131 }
132
133 pub fn set_header(&self, value: Option<BoxedValue>) {
134 self.set_value(Self::header_property(), value)
135 }
136
137 pub fn header_template(&self) -> Option<Rc<dyn IDataTemplate>> {
140 self.get_value(Self::header_template_property())
141 }
142
143 pub fn set_header_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
144 self.set_value(Self::header_template_property(), value)
145 }
146
147 pub fn header_presenter(&self) -> Option<Ref<ContentPresenter>> {
149 self.header_presenter.borrow().clone()
150 }
151
152 pub(crate) fn prepare_item_container(&self, parent: &ItemsControl) {
153 if let Some(items_binding) = self.items_binding.take() {
154 items_binding.dispose();
155 }
156
157 let Some(item) = self.header() else {
158 *self.prepare_item_container_on_attach.borrow_mut() = None;
159 return;
160 };
161
162 let mut header_template = self.header_template().or_else(|| parent.item_template());
163
164 if header_template.is_none() {
165 if self.is_attached_to_logical_tree() {
166 header_template = self.find_data_template(Some(&item), None);
167 } else {
168 *self.prepare_item_container_on_attach.borrow_mut() = Some(parent.to_ref().downgrade());
169 }
170 }
171
172 if let Some(tree_template) = header_template.as_ref().and_then(|t| t.as_tree_data_template()) {
173 if tree_template.match_(Some(&item)) {
174 let items_binding =
175 tree_template.bind_children(self, ItemsControl::items_source_property().as_property(), &item);
176 *self.items_binding.borrow_mut() = Some(items_binding);
177 }
178 }
179 }
180
181 fn header_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
182 let (old_value, new_value) = e.get_old_and_new_value::<Option<BoxedValue>>();
183
184 if let Some(old_child) = old_value.as_ref().and_then(Control::logical_from_boxed) {
185 StyledElement::logical_children(self).remove(&old_child);
186 }
187
188 if let Some(new_child) = new_value.as_ref().and_then(Control::logical_from_boxed) {
189 StyledElement::logical_children(self).add(new_child);
190 }
191 }
192}