1use crate::generators::RecycleKey;
2use crate::presenters::table_view_layout_helper::TableViewLayoutHelper;
3use crate::presenters::TableViewColumnHeadersPresenter;
4use crate::primitives::{SelectingItemsControlImpl, TemplatedControlImpl};
5use crate::templates::IDataTemplate;
6use crate::{
7 AssignedBinding, Control, ControlImpl, ItemsControl, ItemsControlImpl, ItemsControlImplExt, ListBox, TableViewColumn, TableViewRow,
8};
9use ferroui_base::collections::{FerroList, NotifyCollectionChangedAction, ResetBehavior};
10use ferroui_base::input::InputElementImpl;
11use ferroui_base::interactivity::InteractiveImpl;
12use ferroui_base::layout::LayoutableImpl;
13use ferroui_base::reactive::{Disposable, IDisposable};
14use ferroui_base::{
15 ferro_class, ferro_class_info, ferro_impl_classes, ferro_properties, instantiate, BoxedValue, DirectProperty,
16 FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Ref, StyledElement,
17 StyledElementImpl, StyledElementImplExt, StyledProperty, VisualImpl, WeakRef,
18};
19use std::cell::{Cell, RefCell};
20use std::rc::Rc;
21
22#[repr(C)]
24pub struct TableView {
25 base: ListBox,
26 columns_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
27 columns: RefCell<Option<FerroList<Ref<TableViewColumn>>>>,
28 headers_presenter: RefCell<Option<WeakRef<TableViewColumnHeadersPresenter>>>,
31}
32
33ferro_class!(TableView: ListBox);
34ferro_class_info!(TableView { new: TableView::new });
35ferro_impl_classes!(
36 TableView: VisualImpl,
37 LayoutableImpl,
38 InteractiveImpl,
39 InputElementImpl,
40 ControlImpl,
41 TemplatedControlImpl,
42 SelectingItemsControlImpl
43);
44
45impl FerroObjectImpl for TableView {
46 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
47 Self::parent_on_property_changed(this, change);
48
49 if change.property() == Self::can_user_resize_columns_property().as_property() {
50 let columns = this.columns.borrow().clone();
51 if let Some(columns) = columns {
52 for column in columns.snapshot().iter() {
53 column.update_can_user_effectively_resize();
54 }
55 }
56 }
57 }
58}
59
60impl StyledElementImpl for TableView {
61 fn on_initialized(this: &Self) {
62 Self::parent_on_initialized(this);
63
64 let columns = this.columns.borrow().clone();
65 if let Some(columns) = columns {
66 this.subscribe_to_columns(&columns);
67 }
68 }
69}
70
71impl ItemsControlImpl for TableView {
72 fn create_container_for_item_override(
73 _this: &Self,
74 _item: &Option<BoxedValue>,
75 _index: i32,
76 _recycle_key: Option<RecycleKey>,
77 ) -> Ref<Control> {
78 TableViewRow::new().upcast()
79 }
80
81 fn prepare_container_for_item_override(
82 this: &Self,
83 container: &Ref<Control>,
84 item: &Option<BoxedValue>,
85 index: i32,
86 ) {
87 Self::parent_prepare_container_for_item_override(this, container, item, index);
88
89 if let Some(row) = container.downcast_ref::<TableViewRow>() {
90 row.set_columns(Some(this.columns()));
91 row.rebuild_cells();
92 }
93 }
94
95 fn needs_container_override(this: &Self, item: &Option<BoxedValue>, _index: i32) -> (bool, Option<RecycleKey>) {
96 this.needs_container::<TableViewRow>(item)
97 }
98
99 fn clear_container_for_item_override(this: &Self, element: &Ref<Control>) {
100 Self::parent_clear_container_for_item_override(this, element);
101
102 if let Some(row) = element.downcast_ref::<TableViewRow>() {
103 row.set_columns(None);
104 row.clear_cells();
105 }
106 }
107}
108
109ferro_properties! {
110 impl TableView {
111 pub fn columns_property() -> DirectProperty<TableView, Option<FerroList<Ref<TableViewColumn>>>> {
113 FerroProperty::register_direct::<TableView, _>(
114 "Columns",
115 |o| Some(o.columns()),
116 Some(|o, v| o.set_columns(v)),
117 None,
118 )
119 }
120
121 pub fn can_user_resize_columns_property() -> StyledProperty<bool> {
123 FerroProperty::register::<TableView, _>("CanUserResizeColumns", true)
124 }
125 }
126}
127
128impl TableView {
129 pub fn construct() -> Self {
131 Self {
132 base: ListBox::construct(),
133 columns_subscription: RefCell::new(None),
134 columns: RefCell::new(None),
135 headers_presenter: RefCell::new(None),
136 }
137 }
138
139 pub fn new() -> Ref<Self> {
140 instantiate(Self::construct())
141 }
142
143 #[deprecated(note = "DisplayMemberBinding has no effect on a TableView. Use TableViewColumn.Binding instead.")]
146 pub fn display_member_binding(&self) -> Option<AssignedBinding> {
147 ItemsControl::display_member_binding(self)
148 }
149
150 #[deprecated(note = "DisplayMemberBinding has no effect on a TableView. Use TableViewColumn.Binding instead.")]
151 pub fn set_display_member_binding(&self, value: Option<AssignedBinding>) {
152 ItemsControl::set_display_member_binding(self, value)
153 }
154
155 #[deprecated(note = "ItemTemplate has no effect on a TableView. Use TableViewColumn.CellTemplate instead.")]
158 pub fn item_template(&self) -> Option<Rc<dyn IDataTemplate>> {
159 ItemsControl::item_template(self)
160 }
161
162 #[deprecated(note = "ItemTemplate has no effect on a TableView. Use TableViewColumn.CellTemplate instead.")]
163 pub fn set_item_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
164 ItemsControl::set_item_template(self, value)
165 }
166
167 pub fn columns(&self) -> FerroList<Ref<TableViewColumn>> {
170 if let Some(columns) = self.columns.borrow().clone() {
171 return columns;
172 }
173
174 let columns = FerroList::new();
175 columns.set_reset_behavior(ResetBehavior::Remove);
176 *self.columns.borrow_mut() = Some(columns.clone());
177 if self.is_initialized() {
178 self.subscribe_to_columns(&columns);
179 }
180
181 columns
182 }
183
184 pub fn set_columns(&self, value: Option<FerroList<Ref<TableViewColumn>>>) {
185 let old_value = self.columns.borrow().clone();
186 if old_value == value {
187 return;
188 }
189
190 self.unsubscribe_from_columns();
191 if let Some(value) = &value {
192 value.set_reset_behavior(ResetBehavior::Remove);
193 }
194 self.set_and_raise(Self::columns_property(), &self.columns, value.clone());
195
196 if self.is_initialized() {
197 if let Some(value) = &value {
198 self.subscribe_to_columns(value);
199 }
200
201 self.rebuild_headers();
202 self.rebuild_cells();
203 }
204 }
205
206 pub fn can_user_resize_columns(&self) -> bool {
209 self.get_value(Self::can_user_resize_columns_property())
210 }
211
212 pub fn set_can_user_resize_columns(&self, value: bool) {
213 self.set_value(Self::can_user_resize_columns_property(), value)
214 }
215
216 pub(crate) fn headers_presenter(&self) -> Option<Ref<TableViewColumnHeadersPresenter>> {
217 self.headers_presenter.borrow().as_ref().and_then(WeakRef::upgrade)
218 }
219
220 pub(crate) fn set_headers_presenter(&self, value: Option<&Ref<TableViewColumnHeadersPresenter>>) {
221 *self.headers_presenter.borrow_mut() = value.map(Ref::downgrade);
222 }
223
224 fn subscribe_to_columns(&self, columns: &FerroList<Ref<TableViewColumn>>) {
225 debug_assert!(self.columns_subscription.borrow().is_none());
226
227 let is_initial_iteration = Rc::new(Cell::new(true));
228
229 let weak = self.to_ref().downgrade();
230 let added = {
231 let weak = weak.clone();
232 let is_initial_iteration = is_initial_iteration.clone();
233 move |column: &Ref<TableViewColumn>| {
234 let Some(this) = weak.upgrade() else { return };
235 this.attach_column(column);
236 if !is_initial_iteration.get() {
237 this.on_columns_changed();
238 }
239 }
240 };
241 let removed = {
242 let weak = weak.clone();
243 move |column: &Ref<TableViewColumn>| {
244 let Some(this) = weak.upgrade() else { return };
245 this.detach_column(column);
246 this.on_columns_changed();
247 }
248 };
249 let items = move || {
250 let this = weak.upgrade()?;
251 let columns = this.columns.borrow().clone();
252 columns.map(|columns| columns.snapshot())
253 };
254
255 let subscription = for_each_item(columns, items, added, removed, || {});
256 *self.columns_subscription.borrow_mut() = Some(subscription);
257
258 is_initial_iteration.set(false);
259 }
260
261 fn attach_column(&self, column: &Ref<TableViewColumn>) {
262 let this = self.to_ref();
263
264 if column.table_view().is_some_and(|table_view| table_view != this) {
265 panic!("The column {} is already attached to a TableView.", column.debug_display());
266 }
267
268 column.set_parent(this.clone().upcast::<StyledElement>());
272 column.set_table_view(Some(this));
273 }
274
275 fn detach_column(&self, column: &Ref<TableViewColumn>) {
276 debug_assert!(column.table_view().is_none_or(|table_view| table_view == self.to_ref()));
277
278 column.set_table_view(None);
279 column.set_parent(None::<Ref<StyledElement>>);
280 column.set_actual_width(f64::NAN);
281 }
282
283 fn unsubscribe_from_columns(&self) {
284 let Some(subscription) = self.columns_subscription.take() else { return };
285
286 subscription.dispose();
287
288 for column in self.columns().snapshot().iter() {
289 self.detach_column(column);
290 }
291 }
292
293 fn on_columns_changed(&self) {
294 TableViewLayoutHelper::reset_actual_widths(&self.columns());
295 self.rebuild_headers();
296 self.rebuild_cells();
297 }
298
299 pub(crate) fn on_columns_size_changed(&self) {
300 TableViewLayoutHelper::reset_actual_widths(&self.columns());
301 self.invalidate_headers_measure();
302 self.invalidate_cells_measure();
303 }
304
305 fn rebuild_headers(&self) {
306 if let Some(headers_presenter) = self.headers_presenter() {
307 headers_presenter.rebuild_headers();
308 }
309 }
310
311 fn rebuild_cells(&self) {
312 let columns = self.columns();
313
314 for row in self.get_realized_containers() {
315 if let Some(table_view_row) = row.downcast_ref::<TableViewRow>() {
316 table_view_row.set_columns(Some(columns.clone()));
317 table_view_row.rebuild_cells();
318 } else {
319 row.invalidate_measure();
320 }
321 }
322 }
323
324 pub(crate) fn invalidate_headers_measure(&self) {
325 if let Some(headers_presenter) = self.headers_presenter() {
326 headers_presenter.invalidate_measure();
327 }
328 }
329
330 pub(crate) fn invalidate_cells_measure(&self) {
331 for row in self.get_realized_containers() {
332 if let Some(table_view_row) = row.downcast_ref::<TableViewRow>() {
333 table_view_row.invalidate_cells_measure();
334 } else {
335 row.invalidate_measure();
336 }
337 }
338 }
339
340 pub(crate) fn refresh_column_headers(&self, column: &Ref<TableViewColumn>) {
341 let Some(column_index) = self.columns().index_of(column) else { return };
342
343 if let Some(headers_presenter) = self.headers_presenter() {
344 headers_presenter.refresh_header(column_index);
345 }
346 }
347
348 pub(crate) fn refresh_column_cells(&self, column: &Ref<TableViewColumn>) {
349 let Some(column_index) = self.columns().index_of(column) else { return };
350
351 for row in self.get_realized_containers() {
352 if let Some(table_view_row) = row.downcast_ref::<TableViewRow>() {
353 table_view_row.refresh_cell(column_index);
354 }
355 }
356 }
357}
358
359fn for_each_item<T: Clone + 'static>(
367 collection: &FerroList<T>,
368 items: impl Fn() -> Option<Rc<Vec<T>>> + 'static,
369 added: impl Fn(&T) + 'static,
370 removed: impl Fn(&T) + 'static,
371 reset: impl Fn() + 'static,
372) -> Rc<dyn IDisposable> {
373 let add = move |items: &[T]| {
374 for item in items {
375 added(item);
376 }
377 };
378 let remove = move |items: &[T]| {
379 for item in items.iter().rev() {
380 removed(item);
381 }
382 };
383
384 add(&collection.snapshot());
385
386 let token = collection.add_collection_changed(Rc::new(move |e| match e.action {
387 NotifyCollectionChangedAction::Add => add(e.new_items),
388 NotifyCollectionChangedAction::Move | NotifyCollectionChangedAction::Replace
389 if e.old_starting_index >= 0 =>
390 {
391 remove(e.old_items);
392 add(e.new_items);
393 }
394 NotifyCollectionChangedAction::Remove => remove(e.old_items),
395 NotifyCollectionChangedAction::Move
396 | NotifyCollectionChangedAction::Replace
397 | NotifyCollectionChangedAction::Reset => {
398 reset();
399 if let Some(items) = items() {
400 add(&items);
401 }
402 }
403 }));
404
405 let collection = collection.clone();
406 Disposable::create(move || {
407 collection.remove_collection_changed(token);
408 })
409}