1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12use std::{fmt, mem};
13
14use zng_layout::unit::{GridSpacing, PxGridSpacing};
15use zng_wgt::prelude::*;
16use zng_wgt_access::{AccessRole, access_role};
17use zng_wgt_size_offset::*;
18
19#[widget($crate::Grid)]
21pub struct Grid(WidgetBase);
22impl Grid {
23 fn widget_intrinsic(&mut self) {
24 self.widget_builder().push_build_action(|w| {
25 let child = node(
26 w.capture_ui_node_or_nil(property_id!(Self::cells)),
27 w.capture_ui_node_or_nil(property_id!(Self::columns)),
28 w.capture_ui_node_or_nil(property_id!(Self::rows)),
29 w.capture_var_or_else(property_id!(Self::auto_grow_fn), WidgetFn::nil),
30 w.capture_var_or_else(property_id!(Self::auto_grow_mode), AutoGrowMode::rows),
31 w.capture_var_or_default(property_id!(Self::spacing)),
32 );
33 w.set_child(child);
34 });
35
36 widget_set! {
37 self;
38
39 access_role = AccessRole::Grid;
40 }
41 }
42}
43
44#[property(CHILD, widget_impl(Grid))]
54pub fn cells(wgt: &mut WidgetBuilding, cells: impl IntoUiNode) {
55 let _ = cells;
56 wgt.expect_property_capture();
57}
58
59#[property(CHILD, widget_impl(Grid))]
109pub fn columns(wgt: &mut WidgetBuilding, columns: impl IntoUiNode) {
110 let _ = columns;
111 wgt.expect_property_capture();
112}
113
114#[property(CHILD, widget_impl(Grid))]
151pub fn rows(wgt: &mut WidgetBuilding, rows: impl IntoUiNode) {
152 let _ = rows;
153 wgt.expect_property_capture();
154}
155
156#[property(CONTEXT, default(WidgetFn::nil()), widget_impl(Grid))]
164pub fn auto_grow_fn(wgt: &mut WidgetBuilding, auto_grow: impl IntoVar<WidgetFn<AutoGrowFnArgs>>) {
165 let _ = auto_grow;
166 wgt.expect_property_capture();
167}
168
169#[property(CONTEXT, default(AutoGrowMode::rows()), widget_impl(Grid))]
175pub fn auto_grow_mode(wgt: &mut WidgetBuilding, mode: impl IntoVar<AutoGrowMode>) {
176 let _ = mode;
177 wgt.expect_property_capture();
178}
179
180#[property(LAYOUT, default(GridSpacing::default()), widget_impl(Grid))]
182pub fn spacing(wgt: &mut WidgetBuilding, spacing: impl IntoVar<GridSpacing>) {
183 let _ = spacing;
184 wgt.expect_property_capture();
185}
186
187pub fn node(
192 cells: impl IntoUiNode,
193 columns: impl IntoUiNode,
194 rows: impl IntoUiNode,
195 auto_grow_fn: impl IntoVar<WidgetFn<AutoGrowFnArgs>>,
196 auto_grow_mode: impl IntoVar<AutoGrowMode>,
197 spacing: impl IntoVar<GridSpacing>,
198) -> UiNode {
199 let auto_columns = ui_vec![];
200 let auto_rows = ui_vec![];
201 let children = ui_vec![
202 ChainList(ui_vec![columns.into_node().into_list(), auto_columns]),
203 ChainList(ui_vec![rows.into_node().into_list(), auto_rows]),
204 PanelList::new(cells),
205 ];
206 let spacing = spacing.into_var();
207 let auto_grow_fn = auto_grow_fn.into_var();
208 let auto_grow_mode = auto_grow_mode.into_var();
209
210 let mut grid = GridLayout::default();
211 let mut is_measured = false;
212 let mut last_layout = LayoutMetrics::new(1.fct(), PxSize::zero(), Px(0));
213
214 match_node(children, move |c, op| match op {
215 UiNodeOp::Init => {
216 WIDGET.sub_var(&auto_grow_fn).sub_var(&auto_grow_mode).sub_var_layout(&spacing);
217 c.init();
218 grid.update_entries(c.node(), auto_grow_mode.get(), &auto_grow_fn);
219 }
220 UiNodeOp::Deinit => {
221 c.deinit();
222 GridChildrenMut(c.node()).auto_columns().clear();
223 GridChildrenMut(c.node()).auto_rows().clear();
224 is_measured = false;
225 }
226 UiNodeOp::Update { updates } => {
227 let mut any = false;
228 c.update_list(updates, &mut any);
229
230 if auto_grow_fn.is_new() || auto_grow_mode.is_new() {
231 for mut auto in GridChildrenMut(c.node()).auto_columns().drain(..) {
232 auto.deinit();
233 }
234 for mut auto in GridChildrenMut(c.node()).auto_rows().drain(..) {
235 auto.deinit();
236 }
237 any = true;
238 }
239 if any {
240 grid.update_entries(c.node(), auto_grow_mode.get(), &auto_grow_fn);
241 WIDGET.layout();
242 }
243 }
244 UiNodeOp::Measure { wm, desired_size } => {
245 c.delegated();
246
247 let constraints = LAYOUT.constraints().inner();
248
249 *desired_size = if let Some(size) = constraints.fill_or_exact() {
250 size
251 } else {
252 is_measured = true;
253 let s = grid.grid_layout(wm, c.node(), &spacing).1;
254 constraints.clamp_size(s)
255 };
256 }
257 UiNodeOp::Layout { wl, final_size } => {
258 c.delegated();
259 is_measured = false;
260 last_layout = LAYOUT.metrics();
261
262 let (spacing, grid_size) = grid.grid_layout(&mut wl.to_measure(None), c.node(), &spacing);
263 let constraints = last_layout.constraints();
264
265 if grid.is_collapse() {
266 wl.collapse_descendants();
267 *final_size = constraints.fill_or_exact().unwrap_or_default();
268 return;
269 }
270
271 let mut children = GridChildrenMut(c.node());
272 let mut children = children.children().iter_mut();
273 let columns = children.next().unwrap();
274 let rows = children.next().unwrap();
275 let cells = children.next().unwrap();
276 let cells: &mut PanelList = cells.downcast_mut().unwrap();
277
278 let grid = &grid;
279
280 let _ = columns.layout_list(
282 wl,
283 |ci, col, wl| {
284 let info = grid.columns[ci];
285 LAYOUT.with_constraints(constraints.with_exact(info.width, grid_size.height), || col.layout(wl))
286 },
287 |_, _| PxSize::zero(),
288 );
289 let _ = rows.layout_list(
291 wl,
292 |ri, row, wl| {
293 let info = grid.rows[ri];
294 LAYOUT.with_constraints(constraints.with_exact(grid_size.width, info.height), || row.layout(wl))
295 },
296 |_, _| PxSize::zero(),
297 );
298 let cells_offset = columns.children_len() + rows.children_len();
300
301 cells.layout_list(
302 wl,
303 |i, cell, o, wl| {
304 let cell_info = cell::CellInfo::get_wgt(cell).actual(i, grid.columns.len());
305
306 if cell_info.column >= grid.columns.len() || cell_info.row >= grid.rows.len() {
307 wl.collapse_child(cells_offset + i);
308 return PxSize::zero(); }
310
311 let cell_offset = PxVector::new(grid.columns[cell_info.column].x, grid.rows[cell_info.row].y);
312 let mut cell_size = PxSize::zero();
313
314 for col in cell_info.column..(cell_info.column + cell_info.column_span).min(grid.columns.len()) {
315 if grid.columns[col].width != Px(0) {
316 cell_size.width += grid.columns[col].width + spacing.column;
317 }
318 }
319 cell_size.width -= spacing.column;
320
321 for row in cell_info.row..(cell_info.row + cell_info.row_span).min(grid.rows.len()) {
322 if grid.rows[row].height != Px(0) {
323 cell_size.height += grid.rows[row].height + spacing.row;
324 }
325 }
326 cell_size.height -= spacing.row;
327
328 if cell_size.is_empty() {
329 wl.collapse_child(cells_offset + i);
330 return PxSize::zero(); }
332
333 let (_, define_ref_frame) =
334 LAYOUT.with_constraints(constraints.with_exact_size(cell_size), || wl.with_child(|wl| cell.layout(wl)));
335 o.child_offset = cell_offset;
336 o.define_reference_frame = define_ref_frame;
337
338 cell_size
339 },
340 |_, _| PxSize::zero(),
341 );
342 cells.commit_data().request_render();
343
344 *final_size = constraints.inner().fill_size_or(grid_size);
345 }
346 UiNodeOp::Render { frame } => {
347 c.delegated();
348
349 if mem::take(&mut is_measured) {
350 LAYOUT.with_context(last_layout.clone(), || {
351 let _ = grid.grid_layout(&mut WidgetMeasure::new_reuse(None), c.node(), &spacing);
352 });
353 }
354
355 let grid = &grid;
356
357 if grid.is_collapse() {
358 return;
359 }
360
361 let mut children = GridChildrenMut(c.node());
362 let mut children = children.children().iter_mut();
363 let columns = children.next().unwrap();
364 let rows = children.next().unwrap();
365 let cells: &mut PanelList = children.next().unwrap().downcast_mut().unwrap();
366 let offset_key = cells.offset_key();
367
368 columns.for_each_child(|i, child| {
369 let offset = PxVector::new(grid.columns[i].x, Px(0));
370 frame.push_reference_frame(
371 (offset_key, i as u32).into(),
372 FrameValue::Value(offset.into()),
373 true,
374 true,
375 |frame| {
376 child.render(frame);
377 },
378 );
379 });
380 let i_extra = columns.children_len();
381 rows.for_each_child(|i, child| {
382 let offset = PxVector::new(Px(0), grid.rows[i].y);
383 frame.push_reference_frame(
384 (offset_key, (i + i_extra) as u32).into(),
385 FrameValue::Value(offset.into()),
386 true,
387 true,
388 |frame| {
389 child.render(frame);
390 },
391 );
392 });
393 let i_extra = i_extra + rows.children_len();
394 cells.for_each_z_sorted(|i, child, data| {
395 if data.define_reference_frame {
396 frame.push_reference_frame(
397 (offset_key, (i + i_extra) as u32).into(),
398 FrameValue::Value(data.child_offset.into()),
399 true,
400 true,
401 |frame| {
402 child.render(frame);
403 },
404 );
405 } else {
406 frame.push_child(data.child_offset, |frame| child.render(frame));
407 }
408 });
409 }
410 UiNodeOp::RenderUpdate { update } => {
411 c.delegated();
412
413 if mem::take(&mut is_measured) {
414 LAYOUT.with_context(last_layout.clone(), || {
415 let _ = grid.grid_layout(&mut WidgetMeasure::new_reuse(None), c.node(), &spacing);
416 });
417 }
418
419 let grid = &grid;
420
421 if grid.is_collapse() {
422 return;
423 }
424
425 let mut children = GridChildrenMut(c.node());
426 let mut children = children.children().iter_mut();
427 let columns = children.next().unwrap();
428 let rows = children.next().unwrap();
429 let cells: &mut PanelList = children.next().unwrap().downcast_mut().unwrap();
430
431 columns.for_each_child(|i, child| {
432 let offset = PxVector::new(grid.columns[i].x, Px(0));
433 update.with_transform_value(&offset.into(), |update| {
434 child.render_update(update);
435 });
436 });
437 rows.for_each_child(|i, child| {
438 let offset = PxVector::new(Px(0), grid.rows[i].y);
439 update.with_transform_value(&offset.into(), |update| {
440 child.render_update(update);
441 });
442 });
443 cells.for_each_child(|_, child, data| {
444 if data.define_reference_frame {
445 update.with_transform_value(&data.child_offset.into(), |update| {
446 child.render_update(update);
447 });
448 } else {
449 update.with_child(data.child_offset, |update| {
450 child.render_update(update);
451 })
452 }
453 })
454 }
455 _ => {}
456 })
457}
458
459#[derive(Clone, Debug)]
463#[non_exhaustive]
464pub struct AutoGrowFnArgs {
465 pub mode: AutoGrowMode,
467 pub index: usize,
469}
470impl AutoGrowFnArgs {
471 pub fn new(mode: AutoGrowMode, index: usize) -> Self {
473 Self { mode, index }
474 }
475}
476
477#[derive(Clone, Copy, PartialEq, Eq)]
481pub enum AutoGrowMode {
482 Columns(u32),
484 Rows(u32),
486}
487impl AutoGrowMode {
488 pub const fn disabled() -> Self {
490 Self::Rows(0)
491 }
492
493 pub const fn columns() -> Self {
495 Self::Columns(u32::MAX)
496 }
497
498 pub const fn rows() -> Self {
500 Self::Rows(u32::MAX)
501 }
502
503 pub fn with_limit(self, limit: u32) -> Self {
505 match self {
506 AutoGrowMode::Columns(_) => AutoGrowMode::Columns(limit),
507 AutoGrowMode::Rows(_) => AutoGrowMode::Rows(limit),
508 }
509 }
510}
511impl fmt::Debug for AutoGrowMode {
512 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513 if f.alternate() {
514 write!(f, "AutoGrowMode::")?;
515 }
516 match self {
517 AutoGrowMode::Rows(0) => write!(f, "disabled()"),
518 AutoGrowMode::Columns(u32::MAX) => write!(f, "Columns(MAX)"),
519 AutoGrowMode::Rows(u32::MAX) => write!(f, "Rows(MAX)"),
520 AutoGrowMode::Columns(l) => write!(f, "Columns({l})"),
521 AutoGrowMode::Rows(l) => write!(f, "Rows({l})"),
522 }
523 }
524}
525
526#[doc(inline)]
527pub use column::Column;
528
529pub mod column {
531 use super::*;
532
533 #[widget($crate::Column { ($width:expr) => { width = $width; }; })]
545 pub struct Column(WidgetBase);
546 impl Column {
547 widget_impl! {
548 pub max_width(max: impl IntoVar<Length>);
550
551 pub min_width(min: impl IntoVar<Length>);
553
554 pub width(width: impl IntoVar<Length>);
556 }
557
558 fn widget_intrinsic(&mut self) {
559 widget_set! {
560 self;
561 access_role = AccessRole::Column;
562 }
563 }
564 }
565
566 static_id! {
567 pub(super) static ref INDEX_ID: StateId<(usize, usize)>;
569 }
570
571 #[property(CONTEXT, widget_impl(Column))]
577 pub fn is_even(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
578 widget_state_is_state(child, |w| w.get(*INDEX_ID).copied().unwrap_or((0, 0)).0 % 2 == 0, |_| false, state)
579 }
580
581 #[property(CONTEXT, widget_impl(Column))]
587 pub fn is_odd(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
588 widget_state_is_state(child, |w| w.get(*INDEX_ID).copied().unwrap_or((0, 0)).0 % 2 != 0, |_| false, state)
589 }
590
591 #[property(CONTEXT, widget_impl(Column))]
593 pub fn is_first(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
594 widget_state_is_state(
595 child,
596 |w| {
597 let (i, l) = w.get(*INDEX_ID).copied().unwrap_or((0, 0));
598 i == 0 && l > 0
599 },
600 |_| false,
601 state,
602 )
603 }
604
605 #[property(CONTEXT, widget_impl(Column))]
607 pub fn is_last(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
608 widget_state_is_state(
609 child,
610 |w| {
611 let (i, l) = w.get(*INDEX_ID).copied().unwrap_or((0, 0));
612 i < l && i == l - 1
613 },
614 |_| false,
615 state,
616 )
617 }
618
619 #[property(CONTEXT, widget_impl(Column))]
623 pub fn get_index(child: impl IntoUiNode, state: impl IntoVar<usize>) -> UiNode {
624 widget_state_get_state(
625 child,
626 |w, &i| {
627 let a = w.get(*INDEX_ID).copied().unwrap_or((0, 0)).0;
628 if a != i { Some(a) } else { None }
629 },
630 |_, &i| if i != 0 { Some(0) } else { None },
631 state,
632 )
633 }
634
635 #[property(CONTEXT, widget_impl(Column))]
637 pub fn get_index_len(child: impl IntoUiNode, state: impl IntoVar<(usize, usize)>) -> UiNode {
638 widget_state_get_state(
639 child,
640 |w, &i| {
641 let a = w.get(*INDEX_ID).copied().unwrap_or((0, 0));
642 if a != i { Some(a) } else { None }
643 },
644 |_, &i| if i != (0, 0) { Some((0, 0)) } else { None },
645 state,
646 )
647 }
648
649 #[property(CONTEXT, widget_impl(Column))]
651 pub fn get_rev_index(child: impl IntoUiNode, state: impl IntoVar<usize>) -> UiNode {
652 widget_state_get_state(
653 child,
654 |w, &i| {
655 let a = w.get(*INDEX_ID).copied().unwrap_or((0, 0));
656 let a = a.1 - a.0;
657 if a != i { Some(a) } else { None }
658 },
659 |_, &i| if i != 0 { Some(0) } else { None },
660 state,
661 )
662 }
663}
664
665#[doc(inline)]
666pub use row::Row;
667
668pub mod row {
670 use super::*;
671
672 #[widget($crate::Row { ($height:expr) => { height = $height; }; })]
684 pub struct Row(WidgetBase);
685 impl Row {
686 widget_impl! {
687 pub max_height(max: impl IntoVar<Length>);
689
690 pub min_height(max: impl IntoVar<Length>);
692
693 pub height(max: impl IntoVar<Length>);
695 }
696
697 fn widget_intrinsic(&mut self) {
698 widget_set! {
699 self;
700 access_role = AccessRole::Row;
701 }
702 }
703 }
704
705 static_id! {
706 pub(super) static ref INDEX_ID: StateId<(usize, usize)>;
708 }
709
710 #[property(CONTEXT, widget_impl(Row))]
716 pub fn is_even(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
717 widget_state_is_state(child, |w| w.get(*INDEX_ID).copied().unwrap_or((0, 0)).0 % 2 == 0, |_| false, state)
718 }
719
720 #[property(CONTEXT, widget_impl(Row))]
726 pub fn is_odd(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
727 widget_state_is_state(child, |w| w.get(*INDEX_ID).copied().unwrap_or((0, 0)).0 % 2 != 0, |_| false, state)
728 }
729
730 #[property(CONTEXT, widget_impl(Row))]
732 pub fn is_first(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
733 widget_state_is_state(
734 child,
735 |w| {
736 let (i, l) = w.get(*INDEX_ID).copied().unwrap_or((0, 0));
737 i == 0 && l > 0
738 },
739 |_| false,
740 state,
741 )
742 }
743
744 #[property(CONTEXT, widget_impl(Row))]
746 pub fn is_last(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
747 widget_state_is_state(
748 child,
749 |w| {
750 let (i, l) = w.get(*INDEX_ID).copied().unwrap_or((0, 0));
751 i < l && i == l - 1
752 },
753 |_| false,
754 state,
755 )
756 }
757
758 #[property(CONTEXT, widget_impl(Row))]
762 pub fn get_index(child: impl IntoUiNode, state: impl IntoVar<usize>) -> UiNode {
763 widget_state_get_state(
764 child,
765 |w, &i| {
766 let a = w.get(*INDEX_ID).copied().unwrap_or((0, 0)).0;
767 if a != i { Some(a) } else { None }
768 },
769 |_, &i| if i != 0 { Some(0) } else { None },
770 state,
771 )
772 }
773
774 #[property(CONTEXT, widget_impl(Row))]
776 pub fn get_index_len(child: impl IntoUiNode, state: impl IntoVar<(usize, usize)>) -> UiNode {
777 widget_state_get_state(
778 child,
779 |w, &i| {
780 let a = w.get(*INDEX_ID).copied().unwrap_or((0, 0));
781 if a != i { Some(a) } else { None }
782 },
783 |_, &i| if i != (0, 0) { Some((0, 0)) } else { None },
784 state,
785 )
786 }
787
788 #[property(CONTEXT, widget_impl(Row))]
790 pub fn get_rev_index(child: impl IntoUiNode, state: impl IntoVar<usize>) -> UiNode {
791 widget_state_get_state(
792 child,
793 |w, &i| {
794 let a = w.get(*INDEX_ID).copied().unwrap_or((0, 0));
795 let a = a.1 - a.0;
796 if a != i { Some(a) } else { None }
797 },
798 |_, &i| if i != 0 { Some(0) } else { None },
799 state,
800 )
801 }
802}
803
804#[doc(inline)]
805pub use cell::Cell;
806
807pub mod cell {
809 use super::*;
810
811 #[widget($crate::Cell)]
819 pub struct Cell(zng_wgt_container::Container);
820 impl Cell {
821 fn widget_intrinsic(&mut self) {
822 widget_set! {
823 self;
824 access_role = AccessRole::GridCell;
825 }
826 }
827 }
828
829 #[derive(Clone, Copy, Debug)]
831 #[non_exhaustive]
832 pub struct CellInfo {
833 pub column: usize,
837
838 pub column_span: usize,
842
843 pub row: usize,
847
848 pub row_span: usize,
852 }
853 impl Default for CellInfo {
854 fn default() -> Self {
855 Self {
856 column: 0,
857 column_span: 1,
858 row: 0,
859 row_span: 1,
860 }
861 }
862 }
863 impl CellInfo {
864 pub fn actual(mut self, logical_index: usize, columns_len: usize) -> Self {
868 if self.column == usize::MAX {
869 self.column = logical_index % columns_len;
870 } else {
871 self.column = self.column.min(columns_len - 1);
872 }
873 if self.row == usize::MAX {
874 self.row = logical_index / columns_len
875 }
876 self
877 }
878
879 pub fn get() -> Self {
883 WIDGET.get_state(*INFO_ID).unwrap_or_default()
884 }
885
886 pub fn get_wgt(wgt: &mut UiNode) -> Self {
888 match wgt.as_widget() {
889 Some(mut wgt) => wgt.with_context(WidgetUpdateMode::Ignore, Self::get),
890 None => CellInfo::default(),
891 }
892 }
893 }
894
895 static_id! {
896 pub static ref INFO_ID: StateId<CellInfo>;
900 }
901
902 #[property(CONTEXT, default(0), widget_impl(Cell))]
914 pub fn column(child: impl IntoUiNode, col: impl IntoVar<usize>) -> UiNode {
915 with_widget_state_modify(child, *INFO_ID, col, CellInfo::default, |i, &c| {
916 if i.column != c {
917 i.column = c;
918 WIDGET.layout();
919 }
920 })
921 }
922
923 #[property(CONTEXT, default(0), widget_impl(Cell))]
935 pub fn row(child: impl IntoUiNode, row: impl IntoVar<usize>) -> UiNode {
936 with_widget_state_modify(child, *INFO_ID, row, CellInfo::default, |i, &r| {
937 if i.row != r {
938 i.row = r;
939 WIDGET.layout();
940 }
941 })
942 }
943
944 #[property(CONTEXT, default((0, 0)), widget_impl(Cell))]
957 pub fn at(child: impl IntoUiNode, column_row: impl IntoVar<(usize, usize)>) -> UiNode {
958 with_widget_state_modify(child, *INFO_ID, column_row, CellInfo::default, |i, &(col, row)| {
959 if i.column != col || i.row != row {
960 i.column = col;
961 i.row = row;
962 WIDGET.layout();
963 }
964 })
965 }
966
967 #[property(CONTEXT, default(1), widget_impl(Cell))]
982 pub fn column_span(child: impl IntoUiNode, span: impl IntoVar<usize>) -> UiNode {
983 with_widget_state_modify(child, *INFO_ID, span, CellInfo::default, |i, &s| {
984 if i.column_span != s {
985 i.column_span = s;
986 WIDGET.layout();
987 }
988 })
989 }
990
991 #[property(CONTEXT, default(1), widget_impl(Cell))]
1006 pub fn row_span(child: impl IntoUiNode, span: impl IntoVar<usize>) -> UiNode {
1007 with_widget_state_modify(child, *INFO_ID, span, CellInfo::default, |i, &s| {
1008 if i.row_span != s {
1009 i.row_span = s;
1010 WIDGET.layout();
1011 }
1012 })
1013 }
1014
1015 #[property(CONTEXT, default((1, 1)), widget_impl(Cell))]
1026 pub fn span(child: impl IntoUiNode, span: impl IntoVar<(usize, usize)>) -> UiNode {
1027 with_widget_state_modify(child, *INFO_ID, span, CellInfo::default, |i, &(cs, rs)| {
1028 if i.column_span != rs || i.row_span != rs {
1029 i.column_span = cs;
1030 i.row_span = rs;
1031 WIDGET.layout();
1032 }
1033 })
1034 }
1035
1036 pub const AT_AUTO: (usize, usize) = (usize::MAX, usize::MAX);
1041}
1042
1043#[derive(Clone, Copy)]
1044struct ColRowMeta(f32);
1045impl ColRowMeta {
1046 fn is_default(self) -> bool {
1048 self.0.is_sign_negative() && self.0.is_infinite()
1049 }
1050
1051 fn is_leftover(self) -> Option<Factor> {
1053 if self.0 >= 0.0 { Some(Factor(self.0)) } else { None }
1054 }
1055
1056 fn is_exact(self) -> bool {
1058 self.0.is_nan()
1059 }
1060
1061 fn exact() -> Self {
1062 Self(f32::NAN)
1063 }
1064
1065 fn leftover(f: Factor) -> Self {
1066 Self(f.0.max(0.0))
1067 }
1068}
1069impl Default for ColRowMeta {
1070 fn default() -> Self {
1071 Self(f32::NEG_INFINITY)
1072 }
1073}
1074impl fmt::Debug for ColRowMeta {
1075 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1076 if self.is_default() {
1077 write!(f, "default")
1078 } else if self.is_exact() {
1079 write!(f, "exact")
1080 } else if let Some(l) = self.is_leftover() {
1081 write!(f, "leftover({l})")
1082 } else {
1083 write!(f, "ColRowMeta({})", self.0)
1084 }
1085 }
1086}
1087
1088#[derive(Clone, Copy, Debug)]
1089struct ColumnLayout {
1090 meta: ColRowMeta,
1091 was_leftover: bool,
1092 x: Px,
1093 width: Px,
1094 min_width: Px,
1095 max_width: Px,
1096}
1097impl Default for ColumnLayout {
1098 fn default() -> Self {
1099 Self {
1100 meta: ColRowMeta::default(),
1101 was_leftover: false,
1102 x: Px::MIN,
1103 width: Px::MIN,
1104 min_width: Px::MIN,
1105 max_width: Px::MAX,
1106 }
1107 }
1108}
1109#[derive(Clone, Copy, Debug)]
1110struct RowLayout {
1111 meta: ColRowMeta,
1112 was_leftover: bool,
1113 y: Px,
1114 height: Px,
1115 min_height: Px,
1116 max_height: Px,
1117}
1118impl Default for RowLayout {
1119 fn default() -> Self {
1120 Self {
1121 meta: ColRowMeta::default(),
1122 was_leftover: false,
1123 y: Px::MIN,
1124 height: Px::MIN,
1125 min_height: Px::MIN,
1126 max_height: Px::MAX,
1127 }
1128 }
1129}
1130
1131#[derive(Default)]
1132struct GridLayout {
1133 columns: Vec<ColumnLayout>,
1134 rows: Vec<RowLayout>,
1135}
1136impl GridLayout {
1137 fn is_collapse(&self) -> bool {
1138 self.columns.is_empty() || self.rows.is_empty()
1139 }
1140
1141 fn collapse(&mut self) {
1142 self.columns.clear();
1143 self.rows.clear();
1144 }
1145
1146 fn update_entries(&mut self, children: &mut GridChildren, auto_mode: AutoGrowMode, auto_grow_fn: &Var<WidgetFn<AutoGrowFnArgs>>) {
1148 let mut children = GridChildrenMut(children);
1149
1150 let mut max_custom = 0;
1152 let mut max_auto_placed_i = 0;
1153 children.cells().for_each_child(|i, c, _| {
1154 let info = cell::CellInfo::get_wgt(c);
1155
1156 let n = match auto_mode {
1157 AutoGrowMode::Rows(_) => info.row,
1158 AutoGrowMode::Columns(_) => info.column,
1159 };
1160 if n == usize::MAX {
1161 max_auto_placed_i = i;
1162 } else {
1163 max_custom = max_custom.max(n);
1164 }
1165 });
1166
1167 let mut imaginary_cols = 0;
1168 let mut imaginary_rows = 0;
1169
1170 match auto_mode {
1171 AutoGrowMode::Rows(max) => {
1172 let columns_len = children.all_columns().children_len();
1173 if columns_len == 0 {
1174 tracing::warn!(
1175 "grid {} has no columns and auto_grow_mode={:?}, no cell will be visible",
1176 WIDGET.id(),
1177 auto_mode,
1178 );
1179 self.collapse();
1180 return;
1181 }
1182
1183 let max_auto_placed = max_auto_placed_i / columns_len;
1184 let max_needed_len = max_auto_placed.max(max_custom).min(max as usize) + 1;
1185
1186 let rows_len = children.all_rows().children_len();
1187
1188 if rows_len < max_needed_len {
1189 let auto = children.auto_rows();
1190 let mut index = rows_len;
1191
1192 let view = auto_grow_fn.get();
1193 if view.is_nil() {
1194 imaginary_rows = max_needed_len - rows_len;
1195 } else {
1196 while index < max_needed_len {
1197 let mut row = view(AutoGrowFnArgs { mode: auto_mode, index });
1198 row.init();
1199 auto.push(row);
1200 index += 1;
1201 }
1202 }
1203 } else if rows_len > max_needed_len {
1204 let remove = rows_len - max_needed_len;
1205 let auto = children.auto_rows();
1206 let s = auto.len().saturating_sub(remove);
1207 for mut auto in auto.drain(s..) {
1208 auto.deinit();
1209 }
1210 }
1211 }
1212 AutoGrowMode::Columns(max) => {
1213 let rows_len = children.all_rows().children_len();
1214 if rows_len == 0 {
1215 tracing::warn!(
1216 "grid {} has no rows and auto_grow_mode={:?}, no cell will be visible",
1217 WIDGET.id(),
1218 auto_mode,
1219 );
1220 self.collapse();
1221 return;
1222 }
1223
1224 let max_auto_placed = max_auto_placed_i / rows_len;
1225 let max_needed_len = max_auto_placed.max(max_custom).min(max as usize) + 1;
1226
1227 let cols_len = children.all_columns().children_len();
1228
1229 if cols_len < max_needed_len {
1230 let auto = children.auto_columns();
1231 let mut index = cols_len;
1232
1233 let view = auto_grow_fn.get();
1234 if view.is_nil() {
1235 imaginary_cols = max_needed_len - cols_len;
1236 } else {
1237 while index < max_needed_len {
1238 let mut column = view(AutoGrowFnArgs { mode: auto_mode, index });
1239 column.init();
1240 auto.push(column);
1241 index += 1;
1242 }
1243 }
1244 } else if cols_len > max_needed_len {
1245 let remove = cols_len - max_needed_len;
1246 let auto = children.auto_columns();
1247 let s = auto.len().saturating_sub(remove);
1248 for mut auto in auto.drain(s..) {
1249 auto.deinit();
1250 }
1251 }
1252 }
1253 }
1254
1255 let columns_len = children.all_columns().children_len() + imaginary_cols;
1257 children.all_columns_node().for_each_child(|i, c| {
1258 if let Some(mut wgt) = c.as_widget() {
1259 wgt.with_context(WidgetUpdateMode::Bubble, || {
1260 let prev = WIDGET.set_state(*column::INDEX_ID, (i, columns_len));
1261 if prev != Some((i, columns_len)) {
1262 WIDGET.update();
1263 }
1264 });
1265 }
1266 });
1267 let rows_len = children.all_rows().children_len() + imaginary_rows;
1268 children.all_rows_node().for_each_child(|i, r| {
1269 if let Some(mut wgt) = r.as_widget() {
1270 wgt.with_context(WidgetUpdateMode::Bubble, || {
1271 let prev = WIDGET.set_state(*row::INDEX_ID, (i, rows_len));
1272 if prev != Some((i, rows_len)) {
1273 WIDGET.update();
1274 }
1275 });
1276 }
1277 });
1278
1279 self.columns.resize(columns_len, ColumnLayout::default());
1280 self.rows.resize(rows_len, RowLayout::default());
1281 }
1282
1283 #[must_use]
1284 fn grid_layout(&mut self, wm: &mut WidgetMeasure, children: &mut GridChildren, spacing: &Var<GridSpacing>) -> (PxGridSpacing, PxSize) {
1285 if self.is_collapse() {
1286 return (PxGridSpacing::zero(), PxSize::zero());
1287 }
1288
1289 let spacing = spacing.layout();
1290 let constraints = LAYOUT.constraints();
1291
1292 let fill_x = constraints.x.fill_or_exact();
1293 let fill_y = constraints.y.fill_or_exact();
1294
1295 let mut children = GridChildrenMut(children);
1296 let mut children = children.children().iter_mut();
1297 let columns = children.next().unwrap();
1298 let rows = children.next().unwrap();
1299 let cells = children.next().unwrap();
1300
1301 let mut has_default = false;
1304 let mut has_leftover_cols = false;
1305 let mut has_leftover_rows = false;
1306 const MAX_PROBE: i32 = Px::MAX.0 - 1000;
1307
1308 columns.for_each_child(|ci, col| {
1309 let col_kind = WIDGET_SIZE.get_wgt(col).width;
1310
1311 let col_info = &mut self.columns[ci];
1312
1313 col_info.x = Px::MIN;
1314 col_info.width = Px::MIN;
1315 col_info.min_width = Px::MIN;
1316 col_info.max_width = Px::MAX;
1317
1318 match col_kind {
1319 WidgetLength::Default => {
1320 col_info.meta = ColRowMeta::default();
1321 has_default = true;
1322 }
1323 WidgetLength::Leftover(f) => {
1324 col_info.meta = ColRowMeta::leftover(f);
1325 col_info.was_leftover = true;
1326 has_leftover_cols = true;
1327 }
1328 WidgetLength::Exact => {
1329 col_info.width = LAYOUT.with_constraints(Align::TOP_LEFT.child_constraints(constraints), || col.measure(wm).width);
1330 col_info.meta = ColRowMeta::exact();
1331 }
1332 }
1333 if matches!(col_kind, WidgetLength::Default | WidgetLength::Leftover(_)) {
1334 col_info.min_width = LAYOUT.with_constraints(PxConstraints2d::new_unbounded(), || col.measure(wm)).width;
1335 col_info.max_width = LAYOUT
1336 .with_constraints(
1337 PxConstraints2d::new_fill(Px(MAX_PROBE), Px(MAX_PROBE)).with_inner(true, true),
1338 || col.measure(wm),
1339 )
1340 .width;
1341 if col_info.max_width == MAX_PROBE {
1342 col_info.max_width = Px::MAX;
1343 }
1344 }
1345 });
1346 rows.for_each_child(|ri, row| {
1347 let row_kind = WIDGET_SIZE.get_wgt(row).height;
1348
1349 let row_info = &mut self.rows[ri];
1350
1351 row_info.y = Px::MIN;
1352 row_info.height = Px::MIN;
1353
1354 match row_kind {
1355 WidgetLength::Default => {
1356 row_info.meta = ColRowMeta::default();
1357 has_default = true;
1358 }
1359 WidgetLength::Leftover(f) => {
1360 row_info.meta = ColRowMeta::leftover(f);
1361 row_info.was_leftover = true;
1362 has_leftover_rows = true;
1363 }
1364 WidgetLength::Exact => {
1365 row_info.height = LAYOUT.with_constraints(Align::TOP_LEFT.child_constraints(constraints), || row.measure(wm).height);
1366 row_info.meta = ColRowMeta::exact();
1367 }
1368 }
1369 if matches!(row_kind, WidgetLength::Default | WidgetLength::Leftover(_)) {
1370 row_info.min_height = LAYOUT.with_constraints(PxConstraints2d::new_unbounded(), || row.measure(wm)).height;
1371 row_info.max_height = LAYOUT
1372 .with_constraints(
1373 PxConstraints2d::new_fill(Px(MAX_PROBE), Px(MAX_PROBE)).with_inner(true, true),
1374 || row.measure(wm),
1375 )
1376 .width;
1377 if row_info.max_height == MAX_PROBE {
1378 row_info.max_height = Px::MAX;
1379 }
1380 }
1381 });
1382
1383 for col in &mut self.columns[columns.children_len()..] {
1385 col.meta = ColRowMeta::default();
1386 col.x = Px::MIN;
1387 col.width = Px::MIN;
1388 col.min_width = Px::MIN;
1389 col.max_width = Px::MAX;
1390 has_default = true;
1391 }
1392 for row in &mut self.rows[rows.children_len()..] {
1393 row.meta = ColRowMeta::default();
1394 row.y = Px::MIN;
1395 row.height = Px::MIN;
1396 row.min_height = Px::MIN;
1397 row.max_height = Px::MAX;
1398 has_default = true;
1399 }
1400
1401 let mut has_leftover_x_default = false;
1406 let columns_len = self.columns.len();
1407 if has_default || (fill_x.is_none() && has_leftover_cols) || (fill_y.is_none() && has_leftover_rows) {
1408 cells.for_each_child(|i, cell| {
1409 let cell_info = cell::CellInfo::get_wgt(cell);
1410 if cell_info.column_span > 1 || cell_info.row_span > 1 {
1411 return; }
1413 let cell_info = cell_info.actual(i, columns_len);
1414
1415 let col = &mut self.columns[cell_info.column];
1416 let row = &mut self.rows[cell_info.row];
1417
1418 let col_is_default = col.meta.is_default() || (fill_x.is_none() && col.meta.is_leftover().is_some());
1419 let col_is_exact = !col_is_default && col.meta.is_exact();
1420 let col_is_leftover = !col_is_default && col.meta.is_leftover().is_some();
1421
1422 let row_is_default = row.meta.is_default() || (fill_y.is_none() && row.meta.is_leftover().is_some());
1423 let row_is_exact = !row_is_default && row.meta.is_exact();
1424 let row_is_leftover = !row_is_default && row.meta.is_leftover().is_some();
1425
1426 if col_is_default {
1427 if row_is_default {
1428 let size = LAYOUT.with_constraints(
1430 PxConstraints2d::new_range(col.min_width, col.max_width, row.min_height, row.max_height),
1431 || cell.measure(wm),
1432 );
1433
1434 col.width = col.width.max(size.width.clamp(col.min_width, col.max_width));
1435 row.height = row.height.max(size.height);
1436 } else if row_is_exact {
1437 let size = LAYOUT.with_constraints(
1439 PxConstraints2d::new_range(col.min_width, col.max_width, row.height, row.height),
1440 || cell.measure(wm),
1441 );
1442
1443 col.width = col.width.max(size.width.clamp(col.min_width, col.max_width));
1444 } else {
1445 debug_assert!(row_is_leftover);
1446 let size = LAYOUT.with_constraints(
1448 PxConstraints2d::new_range(col.min_width, col.max_width, row.min_height, row.max_height),
1449 || cell.measure(wm),
1450 );
1451
1452 col.width = col.width.max(size.width.clamp(col.min_width, col.max_width));
1453
1454 has_leftover_x_default = true;
1455 }
1456 } else if col_is_exact {
1457 if row_is_default {
1458 let size = LAYOUT.with_constraints(
1460 PxConstraints2d::new_range(col.width, col.width, row.min_height, row.max_height),
1461 || cell.measure(wm),
1462 );
1463
1464 row.height = row.height.max(size.height.clamp(row.min_height, row.max_height));
1465 }
1466 } else if row_is_default {
1467 debug_assert!(col_is_leftover);
1468 let size = LAYOUT.with_constraints(
1470 PxConstraints2d::new_range(col.min_width, col.max_width, row.min_height, row.max_height),
1471 || cell.measure(wm),
1472 );
1473
1474 row.height = row.height.max(size.height.clamp(row.min_height, row.max_height));
1475
1476 has_leftover_x_default = true;
1477 }
1478 });
1479 }
1480
1481 if has_leftover_cols {
1483 let mut no_fill_1_lft = Px(0);
1484 let mut used_width = Px(0);
1485 let mut total_factor = Factor(0.0);
1486 let mut leftover_count = 0;
1487 let mut max_factor = 0.0_f32;
1488
1489 for col in &mut self.columns {
1490 if let Some(f) = col.meta.is_leftover() {
1491 if fill_x.is_none() {
1492 no_fill_1_lft = no_fill_1_lft.max(col.width);
1493 col.width = Px::MIN;
1494 }
1495 max_factor = max_factor.max(f.0);
1496 total_factor += f;
1497 leftover_count += 1;
1498 } else if col.width > Px(0) {
1499 used_width += col.width;
1500 }
1501 }
1502
1503 if total_factor.0.is_infinite() {
1505 total_factor = Factor(0.0);
1506
1507 if max_factor.is_infinite() {
1508 for col in &mut self.columns {
1510 if let Some(f) = col.meta.is_leftover() {
1511 if f.0.is_infinite() {
1512 col.meta = ColRowMeta::leftover(Factor(1.0));
1513 total_factor.0 += 1.0;
1514 } else {
1515 col.meta = ColRowMeta::leftover(Factor(0.0));
1516 }
1517 }
1518 }
1519 } else {
1520 let scale = f32::MAX / max_factor / leftover_count as f32;
1522 for col in &mut self.columns {
1523 if let Some(f) = col.meta.is_leftover() {
1524 let f = Factor(f.0 * scale);
1525 col.meta = ColRowMeta::leftover(f);
1526 total_factor += f;
1527 }
1528 }
1529 }
1530 }
1531
1532 if total_factor < Factor(1.0) {
1534 total_factor = Factor(1.0);
1535 }
1536
1537 let mut leftover_width = if let Some(w) = fill_x {
1538 let vis_columns = self.columns.iter().filter(|c| c.width != Px(0)).count() as i32;
1539 w - used_width - spacing.column * Px(vis_columns - 1).max(Px(0))
1540 } else {
1541 let mut unbounded_width = used_width;
1543 for col in &self.columns {
1544 if let Some(f) = col.meta.is_leftover() {
1545 unbounded_width += no_fill_1_lft * f;
1546 }
1547 }
1548 let bounded_width = constraints.x.clamp(unbounded_width);
1549 bounded_width - used_width
1550 };
1551 leftover_width = leftover_width.max(Px(0));
1552
1553 let view_columns_len = columns.children_len();
1554
1555 let mut settled_all = false;
1557 while !settled_all && leftover_width > Px(0) {
1558 settled_all = true;
1559
1560 for col in self.columns[..view_columns_len].iter_mut() {
1561 let lft = if let Some(lft) = col.meta.is_leftover() {
1562 lft
1563 } else {
1564 continue;
1565 };
1566
1567 let width = lft.0 * leftover_width.0 as f32 / total_factor.0;
1568 let width = Px(width as i32);
1569 col.width = width.clamp(col.min_width, col.max_width);
1570
1571 if col.width != width {
1572 settled_all = false;
1575
1576 col.meta = ColRowMeta::exact();
1577
1578 if col.width != Px(0) {
1579 leftover_width -= col.width + spacing.column;
1580 total_factor -= lft;
1581 if total_factor < Factor(1.0) {
1582 total_factor = Factor(1.0);
1583 }
1584 }
1585 }
1586 }
1587 }
1588
1589 leftover_width = leftover_width.max(Px(0));
1590
1591 for col in &mut self.columns {
1593 let lft = if let Some(lft) = col.meta.is_leftover() {
1594 lft
1595 } else {
1596 continue;
1597 };
1598
1599 let width = lft.0 * leftover_width.0 as f32 / total_factor.0;
1600 col.width = Px(width as i32).clamp(col.min_width, col.max_width);
1601 col.meta = ColRowMeta::exact();
1602 }
1603 }
1604 if has_leftover_rows {
1606 let mut no_fill_1_lft = Px(0);
1607 let mut used_height = Px(0);
1608 let mut total_factor = Factor(0.0);
1609 let mut leftover_count = 0;
1610 let mut max_factor = 0.0_f32;
1611
1612 for row in &mut self.rows {
1613 if let Some(f) = row.meta.is_leftover() {
1614 if fill_y.is_none() {
1615 no_fill_1_lft = no_fill_1_lft.max(row.height);
1616 row.height = Px::MIN;
1617 }
1618 max_factor = max_factor.max(f.0);
1619 total_factor += f;
1620 leftover_count += 1;
1621 } else if row.height > Px(0) {
1622 used_height += row.height;
1623 }
1624 }
1625
1626 if total_factor.0.is_infinite() {
1628 total_factor = Factor(0.0);
1629
1630 if max_factor.is_infinite() {
1631 for row in &mut self.rows {
1633 if let Some(f) = row.meta.is_leftover() {
1634 if f.0.is_infinite() {
1635 row.meta = ColRowMeta::leftover(Factor(1.0));
1636 total_factor.0 += 1.0;
1637 } else {
1638 row.meta = ColRowMeta::leftover(Factor(0.0));
1639 }
1640 }
1641 }
1642 } else {
1643 let scale = f32::MAX / max_factor / leftover_count as f32;
1645 for row in &mut self.rows {
1646 if let Some(f) = row.meta.is_leftover() {
1647 let f = Factor(f.0 * scale);
1648 row.meta = ColRowMeta::leftover(f);
1649 total_factor += f;
1650 }
1651 }
1652 }
1653 }
1654
1655 if total_factor < Factor(1.0) {
1657 total_factor = Factor(1.0);
1658 }
1659
1660 let mut leftover_height = if let Some(h) = fill_y {
1661 let vis_rows = self.rows.iter().filter(|c| c.height != Px(0)).count() as i32;
1662 h - used_height - spacing.row * Px(vis_rows - 1).max(Px(0))
1663 } else {
1664 let mut unbounded_height = used_height;
1666 for row in &self.rows {
1667 if let Some(f) = row.meta.is_leftover() {
1668 unbounded_height += no_fill_1_lft * f;
1669 }
1670 }
1671 let bounded_height = constraints.x.clamp(unbounded_height);
1672 bounded_height - used_height
1673 };
1674 leftover_height = leftover_height.max(Px(0));
1675
1676 let view_rows_len = rows.children_len();
1677
1678 let mut settled_all = false;
1680 while !settled_all && leftover_height > Px(0) {
1681 settled_all = true;
1682
1683 for row in self.rows[..view_rows_len].iter_mut() {
1684 let lft = if let Some(lft) = row.meta.is_leftover() {
1685 lft
1686 } else {
1687 continue;
1688 };
1689
1690 let height = lft.0 * leftover_height.0 as f32 / total_factor.0;
1691 let height = Px(height as i32);
1692 row.height = height.clamp(row.min_height, row.max_height);
1693
1694 if row.height != height {
1695 settled_all = false;
1698
1699 row.meta = ColRowMeta::exact();
1700
1701 if row.height != Px(0) {
1702 leftover_height -= row.height + spacing.row;
1703 total_factor -= lft;
1704 if total_factor < Factor(1.0) {
1705 total_factor = Factor(1.0);
1706 }
1707 }
1708 }
1709 }
1710 }
1711
1712 leftover_height = leftover_height.max(Px(0));
1713
1714 for row in &mut self.rows {
1716 let lft = if let Some(lft) = row.meta.is_leftover() {
1717 lft
1718 } else {
1719 continue;
1720 };
1721
1722 let height = lft.0 * leftover_height.0 as f32 / total_factor.0;
1723 row.height = Px(height as i32).clamp(row.min_height, row.max_height);
1724 row.meta = ColRowMeta::exact();
1725 }
1726 }
1727
1728 if has_leftover_x_default {
1729 let c = LAYOUT.constraints();
1732
1733 cells.for_each_child(|i, cell| {
1734 let cell_info = cell::CellInfo::get_wgt(cell);
1735 if cell_info.column_span > 1 || cell_info.row_span > 1 {
1736 return; }
1738
1739 let cell_info = cell_info.actual(i, columns_len);
1740
1741 let col = &mut self.columns[cell_info.column];
1742 let row = &mut self.rows[cell_info.row];
1743
1744 let col_is_default = col.meta.is_default() || (fill_x.is_none() && col.was_leftover);
1745 let col_is_leftover = col.was_leftover;
1746
1747 let row_is_default = row.meta.is_default() || (fill_y.is_none() && row.was_leftover);
1748 let row_is_leftover = row.was_leftover;
1749
1750 if col_is_default {
1751 if row_is_leftover {
1752 let size = LAYOUT.with_constraints(c.with_fill(false, false).with_exact_y(row.height), || cell.measure(wm));
1755
1756 col.width = col.width.max(size.width);
1757 }
1758 } else if row_is_default && col_is_leftover {
1759 let size = LAYOUT.with_constraints(c.with_fill(false, false).with_exact_x(col.width), || cell.measure(wm));
1762
1763 row.height = row.height.max(size.height);
1764 }
1765 });
1766 }
1767
1768 let mut x = Px(0);
1770 let mut s = Px(0);
1771 for col in &mut self.columns {
1772 if col.width <= Px(0) {
1773 continue;
1775 }
1776 x += s;
1777 col.x = x;
1778 s = spacing.column;
1779 x += col.width;
1780 }
1781 let mut y = Px(0);
1782 let mut s = Px(0);
1783 for row in &mut self.rows {
1784 if row.height <= Px(0) {
1785 continue;
1787 }
1788 y += s;
1789 row.y = y;
1790 s = spacing.row;
1791 y += row.height;
1792 }
1793
1794 let max_width = constraints.x.max().unwrap_or(Px::MAX);
1795 if max_width > Px(0) && x > max_width {
1796 let max_height = constraints.y.max().unwrap_or(Px::MAX);
1799 if y < max_height && self.columns.iter().any(|c| c.meta.is_default()) && self.rows.iter().all(|r| r.meta.is_default()) {
1800 cells.for_each_child(|i, cell| {
1806 let cell_info = cell::CellInfo::get_wgt(cell);
1807 if cell_info.column_span > 1 || cell_info.row_span > 1 {
1808 return; }
1810
1811 let cell_info = cell_info.actual(i, columns_len);
1812 let col = &mut self.columns[cell_info.column];
1813
1814 if col.meta.is_default() {
1815 let row = &mut self.rows[cell_info.row];
1816 debug_assert!(row.meta.is_default());
1817
1818 let min_w_size = LAYOUT.with_constraints(
1820 PxConstraints2d::new_range(col.min_width, col.min_width.max(Px(1)), row.min_height, row.max_height)
1821 .with_inner(true, true),
1822 || cell.measure(wm),
1823 );
1824
1825 col.min_width = col.min_width.max(min_w_size.width);
1826 }
1827 });
1828
1829 let mut min_width = Px(0);
1834 let mut s = Px(0);
1835 for col in &self.columns {
1836 if col.width <= Px(0) {
1837 continue;
1838 }
1839 min_width += s;
1840 s = spacing.column;
1841 min_width += if col.meta.is_default() { col.min_width } else { col.width };
1842 }
1843 let min_width = min_width;
1844
1845 let total_give: Px = self
1847 .columns
1848 .iter()
1849 .filter(|c| c.meta.is_default())
1850 .map(|c| (c.width - c.min_width).max(Px(0)))
1851 .sum();
1852
1853 let available_width = max_width - min_width;
1855
1856 if available_width > Px(0) && total_give > Px(0) {
1857 let available_width = available_width.0 as f32;
1860 let total_give = total_give.0 as f32;
1861 for col in &mut self.columns {
1862 if col.meta.is_default() {
1863 let give = (col.width - col.min_width).max(Px(0)).0 as f32;
1864 let share = available_width * (give / total_give);
1865 col.width = col.min_width + Px(share as i32);
1866 }
1867 }
1868 } else {
1869 for col in &mut self.columns {
1872 if col.meta.is_default() {
1873 col.width = col.min_width;
1874 }
1875 }
1876 }
1877
1878 for row in &mut self.rows {
1880 row.height = row.min_height;
1881 }
1882 cells.for_each_child(|i, cell| {
1883 let cell_info = cell::CellInfo::get_wgt(cell);
1884 if cell_info.column_span > 1 || cell_info.row_span > 1 {
1885 return; }
1887
1888 let cell_info = cell_info.actual(i, columns_len);
1889 let col = &mut self.columns[cell_info.column];
1890
1891 if col.meta.is_default() {
1892 let row = &mut self.rows[cell_info.row];
1893 let height = LAYOUT
1894 .with_constraints(
1895 PxConstraints2d::new_range(col.width, col.width, row.min_height, row.max_height),
1896 || cell.measure(wm),
1897 )
1898 .height;
1899
1900 row.height = row.height.max(height.clamp(row.min_height, row.max_height));
1901 }
1902 });
1903
1904 x = Px(0);
1906 let mut s = Px(0);
1907 for col in &mut self.columns {
1908 if col.width <= Px(0) {
1909 continue;
1910 }
1911 x += s;
1912 col.x = x;
1913 s = spacing.column;
1914 x += col.width;
1915 }
1916 y = Px(0);
1917 let mut s = Px(0);
1918 for row in &mut self.rows {
1919 if row.height <= Px(0) {
1920 continue;
1921 }
1922 y += s;
1923 row.y = y;
1924 s = spacing.row;
1925 y += row.height;
1926 }
1927 }
1928 }
1929
1930 (spacing, PxSize::new(x.max(Px(0)), y.max(Px(0))))
1931 }
1932}
1933
1934type GridChildren = UiNode;
1936struct GridChildrenMut<'a>(&'a mut GridChildren);
1937impl<'a> GridChildrenMut<'a> {
1938 fn children(&mut self) -> &mut UiVec {
1939 self.0.downcast_mut().unwrap()
1940 }
1941
1942 fn all_columns_node(&mut self) -> &mut UiNode {
1943 &mut self.children()[0]
1944 }
1945 fn all_columns(&mut self) -> &mut ChainList {
1946 self.all_columns_node().downcast_mut().unwrap()
1947 }
1948 fn auto_columns(&mut self) -> &mut UiVec {
1949 self.all_columns().0[1].downcast_mut().unwrap()
1950 }
1951
1952 fn all_rows_node(&mut self) -> &mut UiNode {
1953 &mut self.children()[1]
1954 }
1955 fn all_rows(&mut self) -> &mut ChainList {
1956 self.all_rows_node().downcast_mut().unwrap()
1957 }
1958 fn auto_rows(&mut self) -> &mut UiVec {
1959 self.all_rows().0[1].downcast_mut().unwrap()
1960 }
1961
1962 fn cells(&mut self) -> &mut PanelList {
1963 self.children()[2].downcast_mut().unwrap()
1964 }
1965}