ferroui_controls/presenters/
table_view_column_headers_presenter.rs1use super::table_view_layout_helper::TableViewLayoutHelper;
2use crate::{ControlImpl, Panel, PanelImpl, TableView, TableViewColumnHeader};
3use ferroui_base::input::{InputElement, InputElementImpl};
4use ferroui_base::interactivity::InteractiveImpl;
5use ferroui_base::layout::{LayoutHelper, LayoutableImpl};
6use ferroui_base::logical_tree::LogicalTreeAttachmentEventArgs;
7use ferroui_base::reactive::IDisposable;
8use ferroui_base::{
9 ferro_class, ferro_class_info, ferro_impl_classes, instantiate, FerroObjectImpl, Ref, Size, StyledElementImpl,
10 StyledElementImplExt, VisualImpl, WeakRef,
11};
12use std::cell::RefCell;
13use std::rc::Rc;
14
15#[repr(C)]
19pub struct TableViewColumnHeadersPresenter {
20 base: Panel,
21 table_view: RefCell<Option<WeakRef<TableView>>>,
23 scroll_subscription: RefCell<Option<(WeakRef<InputElement>, Rc<dyn IDisposable>)>>,
26}
27
28ferro_class!(TableViewColumnHeadersPresenter: Panel);
29ferro_class_info!(TableViewColumnHeadersPresenter { new: TableViewColumnHeadersPresenter::new });
30ferro_impl_classes!(
31 TableViewColumnHeadersPresenter: FerroObjectImpl,
32 VisualImpl,
33 InteractiveImpl,
34 InputElementImpl,
35 ControlImpl,
36 PanelImpl
37);
38
39impl StyledElementImpl for TableViewColumnHeadersPresenter {
40 fn on_attached_to_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
41 Self::parent_on_attached_to_logical_tree(this, e);
42
43 let mut table_view = None;
44 let mut current = this.parent();
45 while let Some(ancestor) = current {
46 if let Some(found) = ancestor.clone().cast::<TableView>() {
47 table_view = Some(found);
48 break;
49 }
50 current = ancestor.parent();
51 }
52
53 this.set_table_view(table_view.as_ref());
54
55 this.rebuild_headers();
56
57 if let Some(table_view) = table_view {
58 table_view.set_headers_presenter(Some(&this.to_ref()));
59
60 if let Some(scroll) = table_view.scroll() {
61 let weak = this.to_ref().downgrade();
62 let subscription = scroll.property_changed(move |e| {
63 if e.property().name() == "Offset" {
64 if let Some(this) = weak.upgrade() {
65 this.invalidate_arrange();
66 }
67 }
68 });
69 *this.scroll_subscription.borrow_mut() = Some((scroll.downgrade(), subscription));
70 }
71 }
72 }
73
74 fn on_detached_from_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
75 if let Some(table_view) = this.table_view() {
76 table_view.set_headers_presenter(None);
77
78 if let Some(scroll) = table_view.scroll() {
81 let subscribed = this
82 .scroll_subscription
83 .borrow()
84 .as_ref()
85 .is_some_and(|(element, _)| element.upgrade().is_some_and(|element| element == scroll));
86 if subscribed {
87 if let Some((_, subscription)) = this.scroll_subscription.take() {
88 subscription.dispose();
89 }
90 }
91 }
92
93 this.set_table_view(None);
94 this.rebuild_headers();
95 }
96
97 Self::parent_on_detached_from_logical_tree(this, e);
98 }
99}
100
101impl LayoutableImpl for TableViewColumnHeadersPresenter {
102 fn measure_override(this: &Self, available_size: Size) -> Size {
103 let Some(table_view) = this.table_view() else { return Size::default() };
104
105 let columns = table_view.columns();
106
107 if TableViewLayoutHelper::update_actual_widths(
108 &columns,
109 available_size.width,
110 this.use_layout_rounding(),
111 LayoutHelper::get_layout_scale(this),
112 ) {
113 table_view.invalidate_cells_measure();
114 }
115
116 TableViewLayoutHelper::measure_row(&columns, &this.children(), available_size)
117 }
118
119 fn arrange_override(this: &Self, final_size: Size) -> Size {
120 let Some(table_view) = this.table_view() else { return final_size };
121
122 let columns = table_view.columns();
123
124 if TableViewLayoutHelper::update_actual_widths(
125 &columns,
126 final_size.width,
127 this.use_layout_rounding(),
128 LayoutHelper::get_layout_scale(this),
129 ) {
130 table_view.invalidate_cells_measure();
131 }
132
133 let offset = table_view
134 .scroll()
135 .and_then(|scroll| scroll.as_scrollable().map(|scrollable| scrollable.offset().x))
136 .unwrap_or(0.0);
137 TableViewLayoutHelper::arrange_row(&columns, &this.children(), final_size, -offset)
138 }
139}
140
141impl TableViewColumnHeadersPresenter {
142 pub fn construct() -> Self {
144 Self { base: Panel::construct(), table_view: RefCell::new(None), scroll_subscription: RefCell::new(None) }
145 }
146
147 pub fn new() -> Ref<Self> {
148 instantiate(Self::construct())
149 }
150
151 pub(crate) fn table_view(&self) -> Option<Ref<TableView>> {
152 self.table_view.borrow().as_ref().and_then(WeakRef::upgrade)
153 }
154
155 pub(crate) fn set_table_view(&self, value: Option<&Ref<TableView>>) {
156 *self.table_view.borrow_mut() = value.map(Ref::downgrade);
157 }
158
159 pub(crate) fn rebuild_headers(&self) {
160 let children = self.children();
161 children.clear();
162
163 let Some(table_view) = self.table_view() else { return };
164
165 for column in table_view.columns().snapshot().iter() {
166 let header = TableViewColumnHeader::new();
167 header.set_column(Some(column.clone()));
168 children.add(header);
169 }
170
171 self.invalidate_measure();
172 }
173
174 pub(crate) fn refresh_header(&self, column_index: usize) {
175 let child = self.children().get(column_index);
176 match child.downcast_ref::<TableViewColumnHeader>() {
177 Some(header) => header.refresh(),
178 None => panic!(
179 "Unable to cast object of type '{}' to type '{}'.",
180 child.get_type().name(),
181 <TableViewColumnHeader as ferroui_base::StaticType>::TYPE.name()
182 ),
183 }
184 }
185}