layout: adaptive grid and vertical flex; enforce the mechanical style rules
grid_min_cell( width ) adds the adaptive mode WrapGrid was missing: instead of a fixed column count, the count is derived at layout time from the available width so every cell is at least `width` wide (any Length, so a fluid threshold works; never fewer than one column), re-derived on every resize. Cells then share the width equally, and WrapGrid::max_columns( n ) caps the derived count so cells grow instead of multiplying on very wide surfaces — the cap only applies to the adaptive mode, grid( n ) keeps the fixed behaviour untouched. Four new layout tests cover width derivation, spacing accounting, the one-column floor and the cap; the row-wrap assertions check X positions rather than Y because zero-height spacer children produce zero-height rows. flex( child ) now distributes leftover height inside a Column, mirroring its leftover-width behaviour in a Row: flex children join the weight pool alongside weight-only spacers and y-scrolls, draw their child inside the allocated share at full inner width, and contribute zero to the column's natural height exactly like a row counts flex width. This closes the documented "vertical flex is not yet implemented" gap; the Flex rustdoc and the widgets.md entry now describe both axes. Add scripts/style-check.sh and a make stylecheck target, wired into CI: mechanical verification of the grep-checkable subset of code_style_guide.md — tab indentation and spaces inside attribute brackets — with comment lines skipped so prose may cite raw attribute syntax. To turn the check on green, the 82 unspaced attribute sites across 24 files (#[test], #[derive(...)], #[cfg(...)], #[allow(...)]) were normalized to the spaced form. CONTRIBUTING and the style guide reference the new target; brace placement and paren spacing remain review concerns.
This commit is contained in:
@@ -160,11 +160,14 @@ impl<Msg: Clone> Column<Msg>
|
||||
|
||||
fn content_h( &self, inner_w: f32, canvas: &Canvas ) -> f32
|
||||
{
|
||||
// Spacers contribute 0 to natural height; spacing still applies between all children.
|
||||
// Spacers and flex children contribute 0 to natural height (their
|
||||
// real height is leftover distribution, mirroring how a row counts
|
||||
// flex width); spacing still applies between all children.
|
||||
self.children.iter()
|
||||
.map( |c| match c
|
||||
{
|
||||
Element::Spacer( s ) => s.resolved_height( canvas ).unwrap_or( 0.0 ),
|
||||
Element::Flex( _ ) => 0.0,
|
||||
other => other.preferred_size( inner_w, canvas ).1,
|
||||
} )
|
||||
.sum::<f32>()
|
||||
@@ -229,6 +232,7 @@ impl<Msg: Clone> Column<Msg>
|
||||
{
|
||||
Element::Spacer( s ) if s.resolved_height( canvas ).is_none() => s.weight,
|
||||
Element::Scroll( s ) if s.axis.allows_y() => 1,
|
||||
Element::Flex( f ) => f.weight,
|
||||
_ => 0,
|
||||
} )
|
||||
.sum();
|
||||
@@ -237,6 +241,9 @@ impl<Msg: Clone> Column<Msg>
|
||||
.map( |c|
|
||||
{
|
||||
if matches!( c, Element::Scroll( s ) if s.axis.allows_y() )
|
||||
{
|
||||
0.0
|
||||
} else if matches!( c, Element::Flex( _ ) )
|
||||
{
|
||||
0.0
|
||||
} else if let Element::Spacer( s ) = c {
|
||||
@@ -251,7 +258,8 @@ impl<Msg: Clone> Column<Msg>
|
||||
let avail_h = rect.height - pad * 2.0;
|
||||
let avail_spare = ( avail_h - fixed_h ).max( 0.0 );
|
||||
|
||||
// `center_y` only applies when there are no spacers.
|
||||
// `center_y` only applies when there are no flexible children
|
||||
// (weight-only spacers, y-scrolls, flex wrappers).
|
||||
let start_y = if total_weight == 0 && self.center_y
|
||||
{
|
||||
rect.y + pad + avail_spare / 2.0
|
||||
@@ -290,6 +298,16 @@ impl<Msg: Clone> Column<Msg>
|
||||
};
|
||||
( inner_w, h )
|
||||
},
|
||||
Element::Flex( f ) =>
|
||||
{
|
||||
let h = if total_weight > 0
|
||||
{
|
||||
avail_spare * f.weight as f32 / total_weight as f32
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
( inner_w, h )
|
||||
},
|
||||
other => other.preferred_size( inner_w, canvas ),
|
||||
};
|
||||
let x = if self.align_center_x && !matches!( child, Element::Spacer( _ ) )
|
||||
@@ -438,4 +456,52 @@ mod tests
|
||||
let col = column::<()>().padding( 0.0 ).max_width( Length::vmin( 20.0 ) );
|
||||
assert_eq!( col.inner_w( 200.0, &canvas ), 120.0 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn flex_child_takes_leftover_height()
|
||||
{
|
||||
// A 30 px fixed spacer and one flex child in a 100 px rect:
|
||||
// the flex gets the remaining 70 px at full inner width.
|
||||
let canvas = make_canvas();
|
||||
let col = column::<()>()
|
||||
.padding( 0.0 )
|
||||
.spacing( 0.0 )
|
||||
.push( crate::spacer().height( 30.0 ) )
|
||||
.push( crate::flex( crate::spacer() ) );
|
||||
let rect = crate::types::Rect { x: 0.0, y: 0.0, width: 200.0, height: 100.0 };
|
||||
let rects = col.layout( rect, &canvas );
|
||||
assert_eq!( rects.len(), 2 );
|
||||
assert!( ( rects[1].0.height - 70.0 ).abs() < 0.01 );
|
||||
assert!( ( rects[1].0.width - 200.0 ).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn flex_children_split_leftover_by_weight()
|
||||
{
|
||||
// Two flex children weighted 1:3 share 100 px as 25 / 75.
|
||||
let canvas = make_canvas();
|
||||
let col = column::<()>()
|
||||
.padding( 0.0 )
|
||||
.spacing( 0.0 )
|
||||
.push( crate::flex( crate::spacer() ) )
|
||||
.push( crate::flex( crate::spacer() ).weight( 3 ) );
|
||||
let rect = crate::types::Rect { x: 0.0, y: 0.0, width: 200.0, height: 100.0 };
|
||||
let rects = col.layout( rect, &canvas );
|
||||
assert!( ( rects[0].0.height - 25.0 ).abs() < 0.01 );
|
||||
assert!( ( rects[1].0.height - 75.0 ).abs() < 0.01 );
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn flex_contributes_zero_to_natural_height()
|
||||
{
|
||||
// Natural height counts only the fixed spacer, like row width math.
|
||||
let canvas = make_canvas();
|
||||
let col = column::<()>()
|
||||
.padding( 0.0 )
|
||||
.spacing( 0.0 )
|
||||
.push( crate::spacer().height( 30.0 ) )
|
||||
.push( crate::flex( crate::spacer() ) );
|
||||
let ( _, h ) = col.preferred_size( 200.0, &canvas );
|
||||
assert_eq!( h, 30.0 );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user