ferroui_controls/
virtualizing_panel.rs1use crate::generators::ItemContainerGenerator;
2use crate::items_source::ItemsChangedEventArgs;
3use crate::utils::CollectionUtils;
4use crate::{Control, ControlImpl, ItemsControl, ItemsSourceView, Panel, PanelImpl};
5use ferroui_base::input::{INavigableContainer, InputElement, InputElementImpl, NavigationDirection};
6use ferroui_base::interactivity::InteractiveImpl;
7use ferroui_base::layout::LayoutableImpl;
8use ferroui_base::{ferro_class, ferro_impl_classes, FerroObjectImpl, Ref, StyledElementImpl, VisualImpl, WeakRef};
9use std::cell::{Cell, RefCell};
10use std::rc::Rc;
11
12#[repr(C)]
25pub struct VirtualizingPanel {
26 base: Panel,
27 items_control: RefCell<Option<WeakRef<ItemsControl>>>,
28 items_changed_token: Cell<u64>,
29}
30
31ferro_class! {
32 VirtualizingPanel: Panel, virtuals VirtualizingPanelImpl: PanelImpl {
33 fn scroll_into_view(this, index: i32) -> Option<Ref<Control>>;
38 fn container_from_index(this, index: i32) -> Option<Ref<Control>>;
48 fn index_from_container(this, container: &Ref<Control>) -> i32;
54 fn get_realized_containers(this) -> Option<Vec<Ref<Control>>>;
56 fn get_control_in_direction(
62 this,
63 direction: NavigationDirection,
64 from: Option<&Ref<InputElement>>,
65 wrap: bool
66 ) -> Option<Ref<InputElement>>;
67 fn on_items_control_changed(this, old_value: Option<&Ref<ItemsControl>>);
69 fn on_items_changed(this, items: &Rc<ItemsSourceView>, e: &ItemsChangedEventArgs<'_>);
77 }
78}
79
80ferro_impl_classes!(
81 VirtualizingPanel: FerroObjectImpl,
82 StyledElementImpl,
83 VisualImpl,
84 LayoutableImpl,
85 InteractiveImpl,
86 InputElementImpl,
87 ControlImpl
88);
89
90impl PanelImpl for VirtualizingPanel {
91 fn invalidate_measure_on_children_changed(_this: &Self) {
92 }
95}
96
97impl VirtualizingPanelImpl for VirtualizingPanel {
98 fn scroll_into_view(_this: &Self, _index: i32) -> Option<Ref<Control>> {
99 panic!("VirtualizingPanel is abstract.")
100 }
101
102 fn container_from_index(_this: &Self, _index: i32) -> Option<Ref<Control>> {
103 panic!("VirtualizingPanel is abstract.")
104 }
105
106 fn index_from_container(_this: &Self, _container: &Ref<Control>) -> i32 {
107 panic!("VirtualizingPanel is abstract.")
108 }
109
110 fn get_realized_containers(_this: &Self) -> Option<Vec<Ref<Control>>> {
111 panic!("VirtualizingPanel is abstract.")
112 }
113
114 fn get_control_in_direction(
115 _this: &Self,
116 _direction: NavigationDirection,
117 _from: Option<&Ref<InputElement>>,
118 _wrap: bool,
119 ) -> Option<Ref<InputElement>> {
120 panic!("VirtualizingPanel is abstract.")
121 }
122
123 fn on_items_control_changed(_this: &Self, _old_value: Option<&Ref<ItemsControl>>) {}
124
125 fn on_items_changed(_this: &Self, _items: &Rc<ItemsSourceView>, _e: &ItemsChangedEventArgs<'_>) {}
126}
127
128impl INavigableContainer for VirtualizingPanel {
129 fn get_control(
130 &self,
131 direction: NavigationDirection,
132 from: Option<&Ref<InputElement>>,
133 wrap: bool,
134 ) -> Option<Ref<InputElement>> {
135 self.get_control_in_direction(direction, from, wrap)
136 }
137}
138
139impl VirtualizingPanel {
140 pub fn construct() -> Self {
142 Self { base: Panel::construct(), items_control: RefCell::new(None), items_changed_token: Cell::new(0) }
143 }
144
145 pub fn item_container_generator(&self) -> Option<Rc<ItemContainerGenerator>> {
147 self.items_control().map(|items_control| items_control.item_container_generator())
148 }
149
150 pub fn items(&self) -> Rc<ItemsSourceView> {
152 match self.items_control() {
153 Some(items_control) => items_control.items_view().clone(),
154 None => ItemsSourceView::empty(),
155 }
156 }
157
158 pub fn items_control(&self) -> Option<Ref<ItemsControl>> {
160 self.items_control.borrow().as_ref().and_then(WeakRef::upgrade)
161 }
162
163 fn set_items_control(&self, value: Option<Ref<ItemsControl>>) {
164 let old_value = self.items_control();
165 if old_value != value {
166 *self.items_control.borrow_mut() = value.as_ref().map(Ref::downgrade);
167 self.on_items_control_changed(old_value.as_ref());
168 }
169 }
170
171 pub fn add_internal_child(&self, control: &Ref<Control>) {
174 let items_control = self.ensure_items_control();
175 items_control.add_logical_child(control);
176 self.children().add(control);
177 }
178
179 pub fn insert_internal_child(&self, index: usize, control: &Ref<Control>) {
182 let items_control = self.ensure_items_control();
183 items_control.add_logical_child(control);
184 self.children().insert(index, control);
185 }
186
187 pub fn remove_internal_child(&self, child: &Ref<Control>) {
189 let items_control = self.ensure_items_control();
190 items_control.remove_logical_child(child);
191 self.children().remove(child);
192 }
193
194 pub fn remove_internal_child_range(&self, index: usize, count: usize) {
196 let items_control = self.ensure_items_control();
197
198 for i in index..count {
199 let c = self.children().get(i);
200 items_control.remove_logical_child(&c);
201 }
202
203 self.children().remove_range(index, count);
204 }
205
206 pub(crate) fn attach(&self, items_control: &Ref<ItemsControl>) {
207 if self.items_control().is_some() {
208 panic!("The VirtualizingPanel is already attached to an ItemsControl");
209 }
210
211 self.set_items_control(Some(items_control.clone()));
212
213 let weak = self.to_ref().downgrade();
214 let token = items_control.items_view().add_post_collection_changed(Rc::new(
215 move |e: &ItemsChangedEventArgs<'_>| {
216 if let Some(this) = weak.upgrade() {
217 this.on_items_control_items_changed(e);
218 }
219 },
220 ));
221 self.items_changed_token.set(token);
222 }
223
224 pub(crate) fn detach(&self) {
225 let items_control = self.ensure_items_control();
226 items_control.items_view().remove_post_collection_changed(self.items_changed_token.get());
227 self.set_items_control(None);
228 self.children().clear();
229 }
230
231 pub(crate) fn refresh(&self) {
232 self.on_items_control_items_changed(&CollectionUtils::RESET_EVENT_ARGS);
233 }
234
235 fn ensure_items_control(&self) -> Ref<ItemsControl> {
236 match self.items_control() {
237 Some(items_control) => items_control,
238 None => panic!("The VirtualizingPanel does not belong to an ItemsControl."),
239 }
240 }
241
242 fn on_items_control_items_changed(&self, e: &ItemsChangedEventArgs<'_>) {
243 self.on_items_changed(&self.items(), e);
244 }
245}