From 1445e068e0d366c96e85ecb528f9a6c9e697c049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 30 Apr 2025 16:12:14 +0200 Subject: [PATCH] Enforce minimum pane size by counting splits in each axis --- widget/src/pane_grid/axis.rs | 21 ++--- widget/src/pane_grid/node.rs | 151 ++++++++++++++++++++++++++++++----- 2 files changed, 141 insertions(+), 31 deletions(-) diff --git a/widget/src/pane_grid/axis.rs b/widget/src/pane_grid/axis.rs index 9aacd8ef..e5bb7f97 100644 --- a/widget/src/pane_grid/axis.rs +++ b/widget/src/pane_grid/axis.rs @@ -17,17 +17,18 @@ impl Axis { rectangle: &Rectangle, ratio: f32, spacing: f32, - min_size: f32, + min_size_a: f32, + min_size_b: f32, ) -> (Rectangle, Rectangle, f32) { match self { Axis::Horizontal => { let height_top = (rectangle.height * ratio - spacing / 2.0) .round() - .max(min_size) - .min(rectangle.height - min_size); + .max(min_size_a) + .min(rectangle.height - min_size_b - spacing); let height_bottom = - (rectangle.height - height_top - spacing).max(min_size); + (rectangle.height - height_top - spacing).max(min_size_b); let ratio = (height_top + spacing / 2.0) / rectangle.height; @@ -47,11 +48,11 @@ impl Axis { Axis::Vertical => { let width_left = (rectangle.width * ratio - spacing / 2.0) .round() - .max(min_size) - .min(rectangle.width - min_size); + .max(min_size_a) + .min(rectangle.width - min_size_b - spacing); let width_right = - (rectangle.width - width_left - spacing).max(min_size); + (rectangle.width - width_left - spacing).max(min_size_b); let ratio = (width_left + spacing / 2.0) / rectangle.width; @@ -202,7 +203,8 @@ mod tests { width: 10.0, height: overall_height, }; - let (top, bottom, _ratio) = a.split(&r, 0.5, spacing, 0.0); + let (top, bottom, _ratio) = + a.split(&r, 0.5, spacing, 0.0, 0.0); assert_eq!( top, Rectangle { @@ -233,7 +235,8 @@ mod tests { width: overall_width, height: 10.0, }; - let (left, right, _ratio) = a.split(&r, 0.5, spacing, 0.0); + let (left, right, _ratio) = + a.split(&r, 0.5, spacing, 0.0, 0.0); assert_eq!( left, Rectangle { diff --git a/widget/src/pane_grid/node.rs b/widget/src/pane_grid/node.rs index 38440731..83a655c7 100644 --- a/widget/src/pane_grid/node.rs +++ b/widget/src/pane_grid/node.rs @@ -29,6 +29,33 @@ pub enum Node { Pane(Pane), } +#[derive(Debug)] +enum Count { + Split { + horizontal: usize, + vertical: usize, + a: Box, + b: Box, + }, + Pane, +} + +impl Count { + fn horizontal(&self) -> usize { + match self { + Count::Split { horizontal, .. } => *horizontal, + Count::Pane => 0, + } + } + + fn vertical(&self) -> usize { + match self { + Count::Split { vertical, .. } => *vertical, + Count::Pane => 0, + } + } +} + impl Node { /// Returns an iterator over each [`Split`] in this [`Node`]. pub fn splits(&self) -> impl Iterator { @@ -48,6 +75,28 @@ impl Node { }) } + fn count(&self) -> Count { + match self { + Node::Split { a, b, axis, .. } => { + let a = a.count(); + let b = b.count(); + + let (horizontal, vertical) = match axis { + Axis::Horizontal => (1, 0), + Axis::Vertical => (0, 1), + }; + + Count::Split { + horizontal: a.horizontal() + b.horizontal() + horizontal, + vertical: a.vertical() + b.vertical() + vertical, + a: Box::new(a), + b: Box::new(b), + } + } + Node::Pane(_) => Count::Pane, + } + } + /// Returns the rectangular region for each [`Pane`] in the [`Node`] given /// the spacing between panes and the total available space. pub fn pane_regions( @@ -57,6 +106,7 @@ impl Node { bounds: Size, ) -> BTreeMap { let mut regions = BTreeMap::new(); + let count = self.count(); self.compute_regions( spacing, @@ -67,6 +117,7 @@ impl Node { width: bounds.width, height: bounds.height, }, + &count, &mut regions, ); @@ -83,6 +134,7 @@ impl Node { bounds: Size, ) -> BTreeMap { let mut splits = BTreeMap::new(); + let count = self.count(); self.compute_splits( spacing, @@ -93,6 +145,7 @@ impl Node { width: bounds.width, height: bounds.height, }, + &count, &mut splits, ); @@ -198,21 +251,50 @@ impl Node { spacing: f32, min_size: f32, current: &Rectangle, + count: &Count, regions: &mut BTreeMap, ) { - match self { - Node::Split { - axis, ratio, a, b, .. - } => { - let (region_a, region_b, _ratio) = - axis.split(current, *ratio, spacing, min_size); + match (self, count) { + ( + Node::Split { + axis, ratio, a, b, .. + }, + Count::Split { + a: count_a, + b: count_b, + .. + }, + ) => { + let (a_factor, b_factor) = match axis { + Axis::Horizontal => { + (count_a.horizontal(), count_b.horizontal()) + } + Axis::Vertical => (count_a.vertical(), count_b.vertical()), + }; - a.compute_regions(spacing, min_size, ®ion_a, regions); - b.compute_regions(spacing, min_size, ®ion_b, regions); + let (region_a, region_b, _ratio) = axis.split( + current, + *ratio, + spacing, + min_size * (a_factor + 1) as f32 + + spacing * a_factor as f32, + min_size * (b_factor + 1) as f32 + + spacing * b_factor as f32, + ); + + a.compute_regions( + spacing, min_size, ®ion_a, count_a, regions, + ); + b.compute_regions( + spacing, min_size, ®ion_b, count_b, regions, + ); } - Node::Pane(pane) => { + (Node::Pane(pane), Count::Pane) => { let _ = regions.insert(*pane, *current); } + _ => { + unreachable!("Node configuration and count do not match") + } } } @@ -221,25 +303,50 @@ impl Node { spacing: f32, min_size: f32, current: &Rectangle, + count: &Count, splits: &mut BTreeMap, ) { - match self { - Node::Split { - axis, - ratio, - a, - b, - id, - } => { - let (region_a, region_b, ratio) = - axis.split(current, *ratio, spacing, min_size); + match (self, count) { + ( + Node::Split { + axis, + ratio, + a, + b, + id, + }, + Count::Split { + a: count_a, + b: count_b, + .. + }, + ) => { + let (a_factor, b_factor) = match axis { + Axis::Horizontal => { + (count_a.horizontal(), count_b.horizontal()) + } + Axis::Vertical => (count_a.vertical(), count_b.vertical()), + }; + + let (region_a, region_b, ratio) = axis.split( + current, + *ratio, + spacing, + min_size * (a_factor + 1) as f32 + + spacing * a_factor as f32, + min_size * (b_factor + 1) as f32 + + spacing * b_factor as f32, + ); let _ = splits.insert(*id, (*axis, *current, ratio)); - a.compute_splits(spacing, min_size, ®ion_a, splits); - b.compute_splits(spacing, min_size, ®ion_b, splits); + a.compute_splits(spacing, min_size, ®ion_a, count_a, splits); + b.compute_splits(spacing, min_size, ®ion_b, count_b, splits); + } + (Node::Pane(_), Count::Pane) => {} + _ => { + unreachable!("Node configuration and split count do not match") } - Node::Pane(_) => {} } } }