ferroui_controls/flyouts/
menu_flyout.rs1use super::{
2 Flyout, FlyoutBaseImpl, MenuFlyoutPresenter, PopupFlyoutBase, PopupFlyoutBaseImpl, PopupFlyoutBaseImplExt,
3};
4use crate::item_collection::ItemCollection;
5use crate::items_source::{IItemsList, ItemsChangedHandler, ItemsSource};
6use crate::templates::IDataTemplate;
7use crate::{Control, ItemsControl, ItemsSourceView};
8use ferroui_base::controls::Classes;
9use ferroui_base::styling::ControlTheme;
10use ferroui_base::utilities::CancelEventArgs;
11use ferroui_base::{
12 ferro_class, ferro_class_info, ferro_impl_classes, ferro_properties, instantiate, BoxedValue, FerroObjectImpl,
13 FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Nullable, Ref, StyledElement, StyledProperty,
14};
15use std::any::Any;
16use std::cell::OnceCell;
17use std::rc::Rc;
18
19#[repr(C)]
21pub struct MenuFlyout {
22 base: PopupFlyoutBase,
23 items: ItemCollection,
24 presenter_items_source: ItemsSource,
26 classes: OnceCell<Classes>,
27}
28
29ferro_class!(MenuFlyout: PopupFlyoutBase);
30ferro_class_info!(MenuFlyout {
31 new: MenuFlyout::new,
32 markup: {
33 content: Items,
34 properties: [
35 Items: ItemCollection { get: MenuFlyout::items },
36 ],
37 },
38});
39ferro_impl_classes!(MenuFlyout: FlyoutBaseImpl);
40
41impl FerroObjectImpl for MenuFlyout {
42 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
43 Self::parent_on_property_changed(this, change);
44
45 if change.property() == Self::items_source_property().as_property() {
46 this.items.set_items_source(change.get_new_value::<Option<ItemsSource>>());
47 }
48 }
49}
50
51impl PopupFlyoutBaseImpl for MenuFlyout {
52 fn create_presenter(this: &Self) -> Ref<Control> {
53 let presenter = MenuFlyoutPresenter::new();
54 presenter.set_items_source(Some(this.presenter_items_source.clone()));
55 presenter.bind_indexer(
56 &ItemsControl::item_template_property().bind(),
57 &this.indexer(&Self::item_template_property().bind()),
58 );
59 presenter.bind_indexer(
60 &ItemsControl::item_container_theme_property().bind(),
61 &this.indexer(&Self::item_container_theme_property().bind()),
62 );
63 presenter.upcast()
64 }
65
66 fn on_opening(this: &Self, args: &CancelEventArgs) {
67 if let Some(presenter) = this.popup().child() {
68 if let Some(classes) = this.classes.get() {
69 PopupFlyoutBase::set_presenter_classes(Some(&presenter), classes);
70 }
71
72 if let Some(theme) = this.flyout_presenter_theme() {
73 presenter.set_value(StyledElement::theme_property(), Some(theme));
74 }
75 }
76
77 Self::parent_on_opening(this, args);
78 }
79}
80
81struct ItemCollectionList(Rc<ItemsSourceView>);
84
85impl IItemsList for ItemCollectionList {
86 fn count(&self) -> usize {
87 self.0.count()
88 }
89
90 fn get_at(&self, index: usize) -> Option<BoxedValue> {
91 self.0.get_at(index)
92 }
93
94 fn index_of(&self, item: &Option<BoxedValue>) -> i32 {
95 self.0.index_of(item)
96 }
97
98 fn is_notifying(&self) -> bool {
99 true
100 }
101
102 fn add_collection_changed(&self, handler: Rc<ItemsChangedHandler>) -> Option<u64> {
103 Some(self.0.add_collection_changed(handler))
104 }
105
106 fn remove_collection_changed(&self, token: u64) {
107 self.0.remove_collection_changed(token);
108 }
109
110 fn as_any(&self) -> &dyn Any {
111 self
112 }
113}
114
115ferro_properties! {
116 impl MenuFlyout {
117 pub fn items_source_property() -> StyledProperty<Option<ItemsSource>> {
119 FerroProperty::register::<MenuFlyout, _>("ItemsSource", None)
120 }
121
122 pub fn item_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
124 FerroProperty::register::<MenuFlyout, _>("ItemTemplate", None)
125 }
126
127 pub fn item_container_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
129 ItemsControl::item_container_theme_property().add_owner::<MenuFlyout>()
130 }
131
132 pub fn flyout_presenter_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
134 Flyout::flyout_presenter_theme_property().add_owner::<MenuFlyout>()
135 }
136 }
137}
138
139impl MenuFlyout {
140 pub fn construct() -> Self {
142 let items = ItemCollection::new();
143 let presenter_items_source = ItemsSource::new(Rc::new(ItemCollectionList(items.view().clone())));
144 Self { base: PopupFlyoutBase::construct(), items, presenter_items_source, classes: OnceCell::new() }
145 }
146
147 pub fn new() -> Ref<Self> {
149 instantiate(Self::construct())
150 }
151
152 pub fn flyout_presenter_classes(&self) -> Classes {
155 self.classes.get_or_init(Classes::new).clone()
156 }
157
158 pub fn items(&self) -> ItemCollection {
160 self.items.clone()
161 }
162
163 pub fn items_source(&self) -> Option<ItemsSource> {
165 self.get_value(Self::items_source_property())
166 }
167
168 pub fn set_items_source(&self, value: Option<ItemsSource>) {
169 self.set_value(Self::items_source_property(), value)
170 }
171
172 pub fn item_template(&self) -> Option<Rc<dyn IDataTemplate>> {
174 self.get_value(Self::item_template_property())
175 }
176
177 pub fn set_item_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
178 self.set_value(Self::item_template_property(), value)
179 }
180
181 pub fn item_container_theme(&self) -> Option<Ref<ControlTheme>> {
184 self.get_value(Self::item_container_theme_property())
185 }
186
187 pub fn set_item_container_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
188 self.set_value(Self::item_container_theme_property(), value.into().0)
189 }
190
191 pub fn flyout_presenter_theme(&self) -> Option<Ref<ControlTheme>> {
194 self.get_value(Self::flyout_presenter_theme_property())
195 }
196
197 pub fn set_flyout_presenter_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
198 self.set_value(Self::flyout_presenter_theme_property(), value.into().0)
199 }
200}