zng_wgt_grid/
cell.rs

1use super::*;
2
3/// Grid cell container.
4///
5/// This widget defines properties that position and size widgets in a [`Grid!`].
6///
7/// See the [`Grid::cells`] property for more details.
8///
9/// [`Grid!`]: struct@Grid
10#[widget($crate::Cell)]
11pub struct Cell(zng_wgt_container::Container);
12impl Cell {
13    fn widget_intrinsic(&mut self) {
14        widget_set! {
15            self;
16            access_role = AccessRole::GridCell;
17        }
18    }
19}
20
21/// Represents values set by cell properties in a widget.
22#[derive(Clone, Copy, Debug)]
23#[non_exhaustive]
24pub struct CellInfo {
25    /// The [`column`] value.
26    ///
27    /// [`column`]: fn@column
28    pub column: usize,
29
30    /// The [`column_span`] value.
31    ///
32    /// [`column_span`]: fn@column_span
33    pub column_span: usize,
34
35    /// The [`row`] value.
36    ///
37    /// [`row`]: fn@row
38    pub row: usize,
39
40    /// The [`row_span`] value.
41    ///
42    /// [`row_span`]: fn@row_span
43    pub row_span: usize,
44}
45impl Default for CellInfo {
46    fn default() -> Self {
47        Self {
48            column: 0,
49            column_span: 1,
50            row: 0,
51            row_span: 1,
52        }
53    }
54}
55impl CellInfo {
56    /// Compute or correct the column and row of the cell.
57    ///
58    /// The `logical_index` is the index of the cell widget in the cell node list.
59    pub fn actual(mut self, logical_index: usize, columns_len: usize) -> Self {
60        if self.column == usize::MAX {
61            self.column = logical_index % columns_len;
62        } else {
63            self.column = self.column.min(columns_len - 1);
64        }
65        if self.row == usize::MAX {
66            self.row = logical_index / columns_len
67        }
68        self
69    }
70
71    /// Get the cell info stored in the [`WIDGET`] state.
72    ///
73    /// [`WIDGET`]: zng_wgt::prelude::WIDGET
74    pub fn get() -> Self {
75        WIDGET.get_state(*INFO_ID).unwrap_or_default()
76    }
77
78    /// Get the cell info stored in the `wgt` state.
79    pub fn get_wgt(wgt: &mut UiNode) -> Self {
80        match wgt.as_widget() {
81            Some(mut wgt) => wgt.with_context(WidgetUpdateMode::Ignore, Self::get),
82            None => CellInfo::default(),
83        }
84    }
85}
86
87static_id! {
88    /// Id for widget state set by cell properties.
89    ///
90    /// The parent grid uses this info to position and size the cell widget.
91    pub static ref INFO_ID: StateId<CellInfo>;
92}
93
94/// Cell column index.
95///
96/// If set to [`usize::MAX`] the cell is positioned based on the logical index.
97///
98/// Is `0` by default.
99///
100/// This property sets the [`INFO_ID`].
101///
102/// See also the [`at`] property to bind both indexes at the same time.
103///
104/// [`at`]: fn@at
105#[property(CONTEXT, default(0), widget_impl(Cell))]
106pub fn column(child: impl IntoUiNode, col: impl IntoVar<usize>) -> UiNode {
107    with_widget_state_modify(child, *INFO_ID, col, CellInfo::default, |i, &c| {
108        if i.column != c {
109            i.column = c;
110            WIDGET.layout();
111        }
112    })
113}
114
115/// Cell row index.
116///
117/// If set to [`usize::MAX`] the cell is positioned based on the logical index.
118///
119/// Is `0` by default.
120///
121/// This property sets the [`INFO_ID`].
122///
123/// See also the [`at`] property to bind both indexes at the same time.
124///
125/// [`at`]: fn@at
126#[property(CONTEXT, default(0), widget_impl(Cell))]
127pub fn row(child: impl IntoUiNode, row: impl IntoVar<usize>) -> UiNode {
128    with_widget_state_modify(child, *INFO_ID, row, CellInfo::default, |i, &r| {
129        if i.row != r {
130            i.row = r;
131            WIDGET.layout();
132        }
133    })
134}
135
136/// Cell column and row indexes.
137///
138/// If set to [`AT_AUTO`] the cell is positioned based on the logical index.
139///
140/// Is `(0, 0)` by default.
141///
142/// This property sets the [`INFO_ID`].
143///
144/// See also the [`column`] or [`row`] properties to bind each index individually.
145///
146/// [`column`]: fn@column
147/// [`row`]: fn@row
148#[property(CONTEXT, default((0, 0)), widget_impl(Cell))]
149pub fn at(child: impl IntoUiNode, column_row: impl IntoVar<(usize, usize)>) -> UiNode {
150    with_widget_state_modify(child, *INFO_ID, column_row, CellInfo::default, |i, &(col, row)| {
151        if i.column != col || i.row != row {
152            i.column = col;
153            i.row = row;
154            WIDGET.layout();
155        }
156    })
157}
158
159/// Cell column span.
160///
161/// Number of *cells* this one spans over horizontally, starting from the column index and spanning to the right.
162///
163/// Is `1` by default, the index is clamped between `1..max` where max is the maximum number of valid columns
164/// to the right of the cell column index.
165///
166/// Note that the cell will not influence the column width if it spans over multiple columns.
167///
168/// This property sets the [`INFO_ID`].
169///
170/// See also the [`span`] property to bind both spans at the same time.
171///
172/// [`span`]: fn@span
173#[property(CONTEXT, default(1), widget_impl(Cell))]
174pub fn column_span(child: impl IntoUiNode, span: impl IntoVar<usize>) -> UiNode {
175    with_widget_state_modify(child, *INFO_ID, span, CellInfo::default, |i, &s| {
176        if i.column_span != s {
177            i.column_span = s;
178            WIDGET.layout();
179        }
180    })
181}
182
183/// Cell row span.
184///
185/// Number of *cells* this one spans over vertically, starting from the row index and spanning down.
186///
187/// Is `1` by default, the index is clamped between `1..max` where max is the maximum number of valid rows
188/// down from the cell column index.
189///
190/// Note that the cell will not influence the row height if it spans over multiple rows.
191///
192/// This property sets the [`INFO_ID`].
193///
194/// See also the [`span`] property to bind both spans at the same time.
195///
196/// [`span`]: fn@span
197#[property(CONTEXT, default(1), widget_impl(Cell))]
198pub fn row_span(child: impl IntoUiNode, span: impl IntoVar<usize>) -> UiNode {
199    with_widget_state_modify(child, *INFO_ID, span, CellInfo::default, |i, &s| {
200        if i.row_span != s {
201            i.row_span = s;
202            WIDGET.layout();
203        }
204    })
205}
206
207/// Cell column and row span.
208///
209/// Is `(1, 1)` by default.
210///
211/// This property sets the [`INFO_ID`].
212///
213/// See also the [`column_span`] or [`row_span`] properties to bind each index individually.
214///
215/// [`column_span`]: fn@column_span
216/// [`row_span`]: fn@row_span
217#[property(CONTEXT, default((1, 1)), widget_impl(Cell))]
218pub fn span(child: impl IntoUiNode, span: impl IntoVar<(usize, usize)>) -> UiNode {
219    with_widget_state_modify(child, *INFO_ID, span, CellInfo::default, |i, &(cs, rs)| {
220        if i.column_span != rs || i.row_span != rs {
221            i.column_span = cs;
222            i.row_span = rs;
223            WIDGET.layout();
224        }
225    })
226}
227
228/// Value for [`at`] that causes the cell to be positioned based on the logical index *i*,
229/// for columns *i % columns* and for rows *i / columns*.
230///
231/// [`at`]: fn@at
232pub const AT_AUTO: (usize, usize) = (usize::MAX, usize::MAX);