1use crate::i_headered::{register_headered, IHeadered};
2use crate::primitives::HeaderedContentControl;
3use crate::templates::IDataTemplate;
4use crate::utils::debug_display::{append_optional_boxed_value, build_base_debug_display};
5use crate::{AssignedBinding, ContentControl, GridLength, GridUnitType, TableView};
6use ferroui_base::layout::HorizontalAlignment;
7use ferroui_base::styling::ControlTheme;
8use ferroui_base::{
9 ferro_class, ferro_class_info, ferro_impl_classes, ferro_properties, instantiate, BoxedValue, DirectProperty,
10 FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Nullable, Ref, StyledElement, StyledElementImpl,
11 StyledProperty, StyledPropertyMetadata, StyledPropertyOptions, Visual, WeakRef,
12};
13use std::cell::{Cell, RefCell};
14use std::rc::Rc;
15
16#[repr(C)]
18pub struct TableViewColumn {
19 base: StyledElement,
20 table_view: RefCell<Option<WeakRef<TableView>>>,
23 actual_width: Cell<f64>,
24 can_user_effectively_resize: Cell<bool>,
25}
26
27ferro_class!(TableViewColumn: StyledElement);
28ferro_class_info!(TableViewColumn {
29 new: TableViewColumn::new,
30 markup: {
31 property_attributes: [
32 CellTemplate: [InheritDataTypeFromItems("ItemsSource", AncestorType = type(Ref<TableView>))],
33 Binding: [InheritDataTypeFromItems("ItemsSource", AncestorType = type(Ref<TableView>))],
34 ],
35 },
36});
37ferro_impl_classes!(TableViewColumn: StyledElementImpl);
38
39impl FerroObjectImpl for TableViewColumn {
40 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
41 Self::parent_on_property_changed(this, change);
42
43 let property = change.property();
44
45 if property == Self::width_property().as_property() || property == Self::is_visible_property().as_property()
46 {
47 if let Some(table_view) = this.table_view() {
48 table_view.on_columns_size_changed();
49 }
50 }
51
52 if property == Self::can_user_resize_property().as_property() {
53 this.update_can_user_effectively_resize();
54 } else {
55 if Self::is_header_property(property) {
56 if let Some(table_view) = this.table_view() {
57 table_view.refresh_column_headers(&this.to_ref());
58 }
59 }
60 if Self::is_cell_property(property) {
61 if let Some(table_view) = this.table_view() {
62 table_view.refresh_column_cells(&this.to_ref());
63 }
64 }
65 }
66 }
67}
68
69ferro_properties! {
70 impl TableViewColumn {
71 pub fn is_visible_property() -> StyledProperty<bool> {
73 Visual::is_visible_property().add_owner::<TableViewColumn>()
74 }
75
76 pub fn header_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
78 FerroProperty::register::<TableViewColumn, _>("HeaderTheme", None)
79 }
80
81 pub fn header_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
83 HeaderedContentControl::header_template_property().add_owner::<TableViewColumn>()
84 }
85
86 pub fn header_property() -> StyledProperty<Option<BoxedValue>> {
88 HeaderedContentControl::header_property().add_owner::<TableViewColumn>()
89 }
90
91 pub fn width_property() -> StyledProperty<GridLength> {
93 FerroProperty::register::<TableViewColumn, _>("Width", GridLength::new(1.0, GridUnitType::Star))
94 }
95
96 pub fn cell_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
98 FerroProperty::register::<TableViewColumn, _>("CellTheme", None)
99 }
100
101 pub fn cell_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
103 FerroProperty::register::<TableViewColumn, _>("CellTemplate", None)
104 }
105
106 pub fn binding_property() -> StyledProperty<Option<AssignedBinding>> {
108 FerroProperty::register_with::<TableViewColumn, _>(
109 "Binding",
110 StyledPropertyOptions::new(None).assign_binding(true),
111 )
112 }
113
114 pub fn can_user_resize_property() -> StyledProperty<Option<bool>> {
116 FerroProperty::register::<TableViewColumn, _>("CanUserResize", None)
117 }
118
119 pub fn horizontal_content_alignment_property() -> StyledProperty<HorizontalAlignment> {
121 ContentControl::horizontal_content_alignment_property()
122 .add_owner_with::<TableViewColumn>(StyledPropertyMetadata::new(Some(HorizontalAlignment::Left)))
123 }
124
125 pub fn table_view_property() -> DirectProperty<TableViewColumn, Option<Ref<TableView>>> {
130 FerroProperty::register_direct::<TableViewColumn, _>("TableView", |o| o.table_view(), None, None)
131 }
132
133 pub fn actual_width_property() -> DirectProperty<TableViewColumn, f64> {
135 FerroProperty::register_direct::<TableViewColumn, _>("ActualWidth", |o| o.actual_width(), None, 0.0)
136 }
137
138 pub fn can_user_effectively_resize_property() -> DirectProperty<TableViewColumn, bool> {
140 FerroProperty::register_direct::<TableViewColumn, _>(
141 "CanUserEffectivelyResize",
142 |o| o.can_user_effectively_resize(),
143 None,
144 false,
145 )
146 }
147 }
148}
149
150impl TableViewColumn {
151 fn static_constructor() {
152 register_headered::<TableViewColumn>();
153 }
154
155 pub fn construct() -> Self {
157 Self {
158 base: StyledElement::construct(),
159 table_view: RefCell::new(None),
160 actual_width: Cell::new(f64::NAN),
161 can_user_effectively_resize: Cell::new(false),
162 }
163 }
164
165 pub fn new() -> Ref<Self> {
166 instantiate(Self::construct())
167 }
168
169 pub fn is_visible(&self) -> bool {
171 self.get_value(Self::is_visible_property())
172 }
173
174 pub fn set_is_visible(&self, value: bool) {
175 self.set_value(Self::is_visible_property(), value)
176 }
177
178 pub fn header_theme(&self) -> Option<Ref<ControlTheme>> {
181 self.get_value(Self::header_theme_property())
182 }
183
184 pub fn set_header_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
185 self.set_value(Self::header_theme_property(), value.into().0)
186 }
187
188 pub fn header_template(&self) -> Option<Rc<dyn IDataTemplate>> {
190 self.get_value(Self::header_template_property())
191 }
192
193 pub fn set_header_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
194 self.set_value(Self::header_template_property(), value)
195 }
196
197 pub fn header(&self) -> Option<BoxedValue> {
199 self.get_value(Self::header_property())
200 }
201
202 pub fn set_header(&self, value: Option<BoxedValue>) {
203 self.set_value(Self::header_property(), value)
204 }
205
206 pub fn width(&self) -> GridLength {
209 self.get_value(Self::width_property())
210 }
211
212 pub fn set_width(&self, value: GridLength) {
213 self.set_value(Self::width_property(), value)
214 }
215
216 pub fn cell_theme(&self) -> Option<Ref<ControlTheme>> {
219 self.get_value(Self::cell_theme_property())
220 }
221
222 pub fn set_cell_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
223 self.set_value(Self::cell_theme_property(), value.into().0)
224 }
225
226 pub fn cell_template(&self) -> Option<Rc<dyn IDataTemplate>> {
232 self.get_value(Self::cell_template_property())
233 }
234
235 pub fn set_cell_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
236 self.set_value(Self::cell_template_property(), value)
237 }
238
239 pub fn binding(&self) -> Option<AssignedBinding> {
243 self.get_value(Self::binding_property())
244 }
245
246 pub fn set_binding(&self, value: Option<AssignedBinding>) {
247 self.set_value(Self::binding_property(), value)
248 }
249
250 pub fn can_user_resize(&self) -> Option<bool> {
254 self.get_value(Self::can_user_resize_property())
255 }
256
257 pub fn set_can_user_resize(&self, value: Option<bool>) {
258 self.set_value(Self::can_user_resize_property(), value)
259 }
260
261 pub fn horizontal_content_alignment(&self) -> HorizontalAlignment {
263 self.get_value(Self::horizontal_content_alignment_property())
264 }
265
266 pub fn set_horizontal_content_alignment(&self, value: HorizontalAlignment) {
267 self.set_value(Self::horizontal_content_alignment_property(), value)
268 }
269
270 pub fn table_view(&self) -> Option<Ref<TableView>> {
272 self.table_view.borrow().as_ref().and_then(WeakRef::upgrade)
273 }
274
275 pub(crate) fn set_table_view(&self, value: Option<Ref<TableView>>) {
276 let old_value = self.table_view();
277 if old_value == value {
278 return;
279 }
280
281 *self.table_view.borrow_mut() = value.as_ref().map(Ref::downgrade);
282 self.raise_direct_property_changed(Self::table_view_property(), &old_value, &value);
283 self.update_can_user_effectively_resize();
284 }
285
286 pub fn actual_width(&self) -> f64 {
289 self.actual_width.get()
290 }
291
292 pub(crate) fn set_actual_width(&self, value: f64) {
293 if value.is_nan() && self.actual_width.get().is_nan() {
295 return;
296 }
297
298 self.set_and_raise_cell(Self::actual_width_property(), &self.actual_width, value);
299 }
300
301 pub fn can_user_effectively_resize(&self) -> bool {
305 self.can_user_effectively_resize.get()
306 }
307
308 pub(crate) fn set_can_user_effectively_resize(&self, value: bool) {
309 self.set_and_raise_cell(
310 Self::can_user_effectively_resize_property(),
311 &self.can_user_effectively_resize,
312 value,
313 );
314 }
315
316 fn is_cell_property(property: &'static FerroProperty) -> bool {
317 property == Self::cell_theme_property().as_property()
318 || property == Self::cell_template_property().as_property()
319 || property == Self::binding_property().as_property()
320 || property == Self::horizontal_content_alignment_property().as_property()
321 || property == Self::is_visible_property().as_property()
322 }
323
324 fn is_header_property(property: &'static FerroProperty) -> bool {
325 property == Self::header_theme_property().as_property()
326 || property == Self::header_template_property().as_property()
327 || property == Self::header_property().as_property()
328 || property == Self::horizontal_content_alignment_property().as_property()
329 || property == Self::is_visible_property().as_property()
330 }
331
332 pub(crate) fn update_can_user_effectively_resize(&self) {
333 let value = self
334 .can_user_resize()
335 .or_else(|| self.table_view().map(|table_view| table_view.can_user_resize_columns()))
336 .unwrap_or(true);
337 self.set_can_user_effectively_resize(value);
338 }
339
340 pub(crate) fn debug_display(&self) -> String {
342 let mut builder = String::new();
343 self.build_debug_display(&mut builder, true);
344 builder
345 }
346
347 pub(crate) fn build_debug_display(&self, builder: &mut String, include_content: bool) {
350 build_base_debug_display(self, builder);
351
352 if include_content {
353 append_optional_boxed_value(builder, "Header", self.header().as_ref(), include_content);
354 }
355 }
356}
357
358impl IHeadered for TableViewColumn {
359 fn header(&self) -> Option<BoxedValue> {
360 TableViewColumn::header(self)
361 }
362
363 fn set_header(&self, value: Option<BoxedValue>) {
364 TableViewColumn::set_header(self, value)
365 }
366}