Skip to main content

ferroui_controls/
table_view_cell.rs

1use crate::primitives::TemplatedControlImpl;
2use crate::{ContentControl, ContentControlImpl, ControlImpl, TableViewColumn};
3use ferroui_base::data::CompiledBinding;
4use ferroui_base::input::InputElementImpl;
5use ferroui_base::interactivity::InteractiveImpl;
6use ferroui_base::layout::LayoutableImpl;
7use ferroui_base::{
8    ferro_class, ferro_class_info, ferro_impl_classes, ferro_properties, instantiate, DirectProperty, FerroObjectImpl,
9    FerroProperty, Ref, StyledElement, StyledElementImpl, Visual, VisualImpl,
10};
11use std::cell::RefCell;
12use std::rc::Rc;
13
14ferroui_base::ferro_thread_local! {
15    /// The binding of the content of a cell to the item of its row (the
16    /// data context of the cell).
17    static ROW_BINDING: Rc<CompiledBinding> = CompiledBinding::empty();
18}
19
20/// Represents a single cell in a [`TableViewRow`](crate::TableViewRow).
21#[repr(C)]
22pub struct TableViewCell {
23    base: ContentControl,
24    column: RefCell<Option<Ref<TableViewColumn>>>,
25}
26
27ferro_class!(TableViewCell: ContentControl);
28ferro_class_info!(TableViewCell { new: TableViewCell::new });
29ferro_impl_classes!(
30    TableViewCell: FerroObjectImpl,
31    StyledElementImpl,
32    VisualImpl,
33    LayoutableImpl,
34    InteractiveImpl,
35    InputElementImpl,
36    ControlImpl,
37    TemplatedControlImpl,
38    ContentControlImpl
39);
40
41ferro_properties! {
42    impl TableViewCell {
43        /// Defines the `Column` property.
44        pub fn column_property() -> DirectProperty<TableViewCell, Option<Ref<TableViewColumn>>> {
45            FerroProperty::register_direct::<TableViewCell, _>("Column", |o| o.column(), None, None)
46        }
47    }
48}
49
50impl TableViewCell {
51    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
52    pub fn construct() -> Self {
53        Self { base: ContentControl::construct(), column: RefCell::new(None) }
54    }
55
56    pub fn new() -> Ref<Self> {
57        instantiate(Self::construct())
58    }
59
60    /// Gets the column associated with this cell.
61    pub fn column(&self) -> Option<Ref<TableViewColumn>> {
62        self.column.borrow().clone()
63    }
64
65    pub(crate) fn set_column(&self, value: Option<Ref<TableViewColumn>>) {
66        let old_value = self.column();
67        if !self.set_and_raise(Self::column_property(), &self.column, value.clone()) {
68            return;
69        }
70
71        if old_value.is_some() {
72            self.clear_properties();
73        }
74
75        if let Some(value) = value {
76            self.set_properties(&value);
77        }
78    }
79
80    fn clear_properties(&self) {
81        self.clear_value(Visual::is_visible_property());
82        self.clear_value(StyledElement::theme_property());
83        self.clear_value(ContentControl::horizontal_content_alignment_property());
84        self.clear_value(ContentControl::content_template_property());
85        self.clear_value(ContentControl::content_property());
86    }
87
88    fn set_properties(&self, column: &TableViewColumn) {
89        // We don't bind the various properties here.
90        // First, it's pretty rare for a column's properties to change after
91        // the initial setup.
92        // Second, we have additional logic depending on whether a cell
93        // template is specified.
94        // Instead, values are updated manually via `refresh`.
95
96        self.set_value(Visual::is_visible_property(), column.is_visible());
97        self.set_value(StyledElement::theme_property(), column.cell_theme());
98        self.set_value(ContentControl::horizontal_content_alignment_property(), column.horizontal_content_alignment());
99
100        let content_property = ContentControl::content_property().as_property();
101        let row_binding = ROW_BINDING.with(Rc::clone);
102
103        if let Some(cell_template) = column.cell_template() {
104            self.set_value(ContentControl::content_template_property(), Some(cell_template));
105            self.bind_binding(content_property, &row_binding);
106        } else {
107            self.set_value(ContentControl::content_template_property(), None);
108            match column.binding() {
109                Some(binding) => self.bind_binding(content_property, &*binding),
110                None => self.bind_binding(content_property, &row_binding),
111            };
112        }
113    }
114
115    pub(crate) fn refresh(&self) {
116        if let Some(column) = self.column() {
117            self.set_properties(&column);
118        }
119    }
120}