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);