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