From 81726db30fda5c0667f0dcc9112727ce640bca18 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Mon, 9 Sep 2024 18:02:09 -0600 Subject: [PATCH 01/13] wip invert size resolution --- src/layout.rs | 302 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 250 insertions(+), 52 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index 8ee19f0..f4ad3e1 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,3 +1,5 @@ +use std::{f64::consts::PI, ops::RangeInclusive}; + use crate::{anynode::AnyNode, drawable::Drawable, models::*}; /** @@ -123,6 +125,68 @@ impl NodeValue { } } + pub(crate) fn sizes(&mut self) -> SizeConstraints { + match self { + NodeValue::Padding { amounts, element } => { + // element.sizes().accumulate(SizeConstraints { + // width: Constraint::Specific(amounts.trailing + amounts.leading), + // height: Constraint::Specific(amounts.bottom + amounts.top), + // }) + element.sizes().accumulate(SizeConstraints { + width: Constraint::Range { + lower: Some(amounts.trailing + amounts.leading), + upper: None, + }, + height: Constraint::Range { + lower: Some(amounts.bottom + amounts.top), + upper: None, + }, + }) + } + NodeValue::Column { elements, .. } => elements.iter_mut().fold( + SizeConstraints { + width: Constraint::None, + height: Constraint::None, + }, + |current, element| SizeConstraints { + width: current.width.combine(element.sizes().width), + height: current.height.accumulate(element.sizes().height), + }, + ), + NodeValue::Row { elements, .. } => elements.iter_mut().fold( + SizeConstraints { + width: Constraint::None, + height: Constraint::None, + }, + |current, element| SizeConstraints { + width: current.width.accumulate(element.sizes().width), + height: current.height.combine(element.sizes().height), + }, + ), + NodeValue::Stack(elements) => { + let cumulative_size = SizeConstraints { + width: Constraint::None, + height: Constraint::None, + }; + for element in elements { + cumulative_size.combine(element.sizes()); + } + cumulative_size + } + NodeValue::Explicit { options, element } => { + element.sizes().combine(SizeConstraints::from(*options)) + } + NodeValue::Offset { .. } => { + todo!() + } + NodeValue::Scope { .. } => todo!(), + _ => SizeConstraints { + width: Constraint::None, + height: Constraint::None, + }, + } + } + pub(crate) fn layout(&mut self, available_area: Area) { match self { NodeValue::Padding { @@ -239,54 +303,155 @@ enum Orientation { Vertical, } +#[derive(Clone, Copy, Debug)] +struct SizeConstraints { + width: Constraint, + height: Constraint, +} + +#[derive(Clone, Copy, Debug)] +enum Constraint { + None, + // Specific(f32), + Range { + lower: Option, + upper: Option, + }, +} + +impl SizeConstraints { + fn combine(self, other: Self) -> Self { + SizeConstraints { + width: self.width.combine(other.width), + height: self.height.combine(other.height), + } + } + fn accumulate(self, other: Self) -> Self { + SizeConstraints { + width: self.width.accumulate(other.width), + height: self.height.accumulate(other.height), + } + } +} + +impl Constraint { + fn combine(self, other: Self) -> Self { + match (self, other) { + (Constraint::None, Constraint::None) => Constraint::None, + (value, Constraint::None) | (Constraint::None, value) => value, + // (Constraint::Specific(a), Constraint::Specific(b)) => Constraint::Specific(a.max(b)), + // (Constraint::Specific(a), Constraint::Range { .. }) + // | (Constraint::Range { .. }, Constraint::Specific(a)) => Constraint::Specific(a), + ( + Constraint::Range { + lower: a_lower, + upper: a_upper, + }, + Constraint::Range { + lower: b_lower, + upper: b_upper, + }, + ) => { + let lower = if let (Some(a), Some(b)) = (a_lower, b_lower) { + Some(a.min(b)) + } else { + None + }; + let upper = if let (Some(a), Some(b)) = (a_upper, b_upper) { + Some(a.max(b)) + } else { + None + }; + Constraint::Range { lower, upper } + } + } + } + fn accumulate(self, other: Self) -> Self { + match (self, other) { + (Constraint::None, Constraint::None) => Constraint::None, + (value, Constraint::None) | (Constraint::None, value) => value, + // (Constraint::Specific(a), Constraint::Specific(b)) => Constraint::Range { + // lower: Some(a + b), + // upper: None, + // }, + // (Constraint::Specific(a), Constraint::Range { lower, upper }) + // | (Constraint::Range { lower, upper }, Constraint::Specific(a)) => Constraint::Range { + // lower: match lower { + // Some(l) => Some(l + a), + // None => Some(a), + // }, + // upper: match upper { + // Some(u) => Some(u + a), + // None => Some(a), + // }, + // }, + ( + Constraint::Range { + lower: a_lower, + upper: a_upper, + }, + Constraint::Range { + lower: b_lower, + upper: b_upper, + }, + ) => { + let lower = if let (Some(a), Some(b)) = (a_lower, b_lower) { + Some(a + b) + } else { + None + }; + let upper = if let (Some(a), Some(b)) = (a_upper, b_upper) { + Some(a + b) + } else { + None + }; + Constraint::Range { lower, upper } + } + } + } +} + +impl From for SizeConstraints { + fn from(value: Size) -> Self { + SizeConstraints { + width: if value.width.is_some() { + Constraint::Range { + lower: value.width, + upper: value.width, + } + } else if value.width_min.is_some() || value.width_max.is_some() { + Constraint::Range { + lower: value.width_min, + upper: value.width_max, + } + } else { + Constraint::None + }, + height: if value.height.is_some() { + Constraint::Range { + lower: value.height, + upper: value.height, + } + } else if value.height_min.is_some() || value.height_max.is_some() { + Constraint::Range { + lower: value.height_min, + upper: value.height_max, + } + } else { + Constraint::None + }, + } + } +} + fn layout_axis( elements: &mut [NodeValue], spacing: &f32, available_area: Area, orientation: Orientation, ) { - let sizes: Vec> = elements - .iter_mut() - .map(|element| match element { - NodeValue::Explicit { - options, - element: _, - } => match orientation { - Orientation::Horizontal => { - if let Some(width) = options.width { - if options.x_relative { - options.width = None; - return Some(width * available_area.width); - } else { - return Some(width); - } - } - None - } - Orientation::Vertical => { - if let Some(height) = options.height { - if options.y_relative { - options.height = None; - return Some(height * available_area.height); - } else { - return Some(height); - } - } - None - } - }, - _ => None, - }) - .map(|size| { - let Some(size) = size else { return size }; - match orientation { - Orientation::Horizontal => Some(size.min(available_area.width)), - Orientation::Vertical => Some(size.min(available_area.height)), - } - }) - .collect(); - - let element_count = sizes.len(); + let sizes: Vec = elements.iter_mut().map(|element| element.sizes()).collect(); + let element_count = elements.len(); let total_spacing = *spacing * (element_count as i32 - 1).max(0) as f32; let available_size = match orientation { @@ -294,22 +459,55 @@ fn layout_axis( Orientation::Vertical => available_area.height, } - total_spacing; - let explicit_consumed = sizes.iter().filter_map(|&s| s).sum::(); - let remaining = available_size - explicit_consumed; - let unconstrained_sizes = sizes.iter().filter(|&s| s.is_none()).count(); - let default_size = remaining / unconstrained_sizes as f32; + let default_size = available_size / element_count as f32; + + let mut pool = 0.; + let mut final_sizes: Vec> = elements.iter().map(|_| Option::::None).collect(); + + for (i, constraint) in sizes.iter().enumerate() { + let constraint = match orientation { + Orientation::Horizontal => constraint.width, + Orientation::Vertical => constraint.height, + }; + if let Constraint::Range { lower, upper } = constraint { + if let Some(lower) = lower { + if default_size < lower { + pool += default_size - lower; + final_sizes[i] = lower.into(); + continue; + } + } + if let Some(upper) = upper { + if default_size > upper { + pool += default_size - upper; + final_sizes[i] = upper.into(); + continue; + } + } + final_sizes[i] = default_size.into(); + } + } + + let unconstrained_count = final_sizes.iter().filter(|&s| s.is_none()).count() as f32; + + let new_default = if unconstrained_count > 0. { + (default_size) + (pool / unconstrained_count) + } else { + 0. + } + .max(0.); + final_sizes.iter_mut().for_each(|size| { + if size.is_none() { + *size = Some(new_default); + } + }); let mut current_pos = match orientation { Orientation::Horizontal => available_area.x, Orientation::Vertical => available_area.y, }; - for (i, child) in elements.iter_mut().enumerate() { - let child_size = sizes - .get(i) - .unwrap_or(&Some(default_size)) - .unwrap_or(default_size); - + let child_size = final_sizes[i].unwrap(); let area = match orientation { Orientation::Horizontal => Area { x: current_pos, From cf3895ba51e63dab9542bcc906ff799fc60b9d77 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 08:43:35 -0600 Subject: [PATCH 02/13] wip sequence --- src/layout.rs | 43 ++++++++++++++----------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index f4ad3e1..ac4f1bc 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,5 +1,3 @@ -use std::{f64::consts::PI, ops::RangeInclusive}; - use crate::{anynode::AnyNode, drawable::Drawable, models::*}; /** @@ -128,10 +126,6 @@ impl NodeValue { pub(crate) fn sizes(&mut self) -> SizeConstraints { match self { NodeValue::Padding { amounts, element } => { - // element.sizes().accumulate(SizeConstraints { - // width: Constraint::Specific(amounts.trailing + amounts.leading), - // height: Constraint::Specific(amounts.bottom + amounts.top), - // }) element.sizes().accumulate(SizeConstraints { width: Constraint::Range { lower: Some(amounts.trailing + amounts.leading), @@ -298,21 +292,21 @@ impl NodeValue { } } +#[derive(Debug, Clone, Copy)] enum Orientation { Horizontal, Vertical, } -#[derive(Clone, Copy, Debug)] -struct SizeConstraints { +#[derive(Debug, Clone, Copy)] +pub(crate) struct SizeConstraints { width: Constraint, height: Constraint, } -#[derive(Clone, Copy, Debug)] -enum Constraint { +#[derive(Debug, Clone, Copy)] +pub(crate) enum Constraint { None, - // Specific(f32), Range { lower: Option, upper: Option, @@ -339,9 +333,6 @@ impl Constraint { match (self, other) { (Constraint::None, Constraint::None) => Constraint::None, (value, Constraint::None) | (Constraint::None, value) => value, - // (Constraint::Specific(a), Constraint::Specific(b)) => Constraint::Specific(a.max(b)), - // (Constraint::Specific(a), Constraint::Range { .. }) - // | (Constraint::Range { .. }, Constraint::Specific(a)) => Constraint::Specific(a), ( Constraint::Range { lower: a_lower, @@ -370,21 +361,6 @@ impl Constraint { match (self, other) { (Constraint::None, Constraint::None) => Constraint::None, (value, Constraint::None) | (Constraint::None, value) => value, - // (Constraint::Specific(a), Constraint::Specific(b)) => Constraint::Range { - // lower: Some(a + b), - // upper: None, - // }, - // (Constraint::Specific(a), Constraint::Range { lower, upper }) - // | (Constraint::Range { lower, upper }, Constraint::Specific(a)) => Constraint::Range { - // lower: match lower { - // Some(l) => Some(l + a), - // None => Some(a), - // }, - // upper: match upper { - // Some(u) => Some(u + a), - // None => Some(a), - // }, - // }, ( Constraint::Range { lower: a_lower, @@ -463,6 +439,7 @@ fn layout_axis( let mut pool = 0.; let mut final_sizes: Vec> = elements.iter().map(|_| Option::::None).collect(); + let mut room: Vec = elements.iter().map(|_| 0.).collect(); for (i, constraint) in sizes.iter().enumerate() { let constraint = match orientation { @@ -472,9 +449,12 @@ fn layout_axis( if let Constraint::Range { lower, upper } = constraint { if let Some(lower) = lower { if default_size < lower { + dbg!("}}}}}}}"); pool += default_size - lower; final_sizes[i] = lower.into(); continue; + } else { + room[i] = -(default_size - lower); } } if let Some(upper) = upper { @@ -482,12 +462,16 @@ fn layout_axis( pool += default_size - upper; final_sizes[i] = upper.into(); continue; + } else { + room[i] = -(default_size - upper); } } final_sizes[i] = default_size.into(); } } + dbg!(&room); + let unconstrained_count = final_sizes.iter().filter(|&s| s.is_none()).count() as f32; let new_default = if unconstrained_count > 0. { @@ -496,6 +480,7 @@ fn layout_axis( 0. } .max(0.); + final_sizes.iter_mut().for_each(|size| { if size.is_none() { *size = Some(new_default); From 4d7e0c60b2b3b4cd1d362ed6109e58dceba9ead6 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 19:54:22 -0600 Subject: [PATCH 03/13] constraint resolution algorithm --- examples/egui-example/src/main.rs | 12 ++-- src/layout.rs | 94 +++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 26 deletions(-) diff --git a/examples/egui-example/src/main.rs b/examples/egui-example/src/main.rs index d3660d9..3832de2 100644 --- a/examples/egui-example/src/main.rs +++ b/examples/egui-example/src/main.rs @@ -25,14 +25,10 @@ fn my_layout_fn(ui: &mut Ui) -> Node { 10., vec![ draw_a(ui), - row_spaced( - 10., - vec![ - draw_b(ui).min_width(200.), - column_spaced(10., vec![draw_a(ui), draw_b(ui), draw_c(ui)]), - ], - ), - draw_c(ui), + draw_b(ui).min_height(20.), + draw_c(ui).max_height(30.), + draw_a(ui), + draw_b(ui).min_height(20.), ], ) .pad(10.) diff --git a/src/layout.rs b/src/layout.rs index ac4f1bc..14f9575 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,3 +1,6 @@ +use core::f32; +use std::f64::INFINITY; + use crate::{anynode::AnyNode, drawable::Drawable, models::*}; /** @@ -439,7 +442,8 @@ fn layout_axis( let mut pool = 0.; let mut final_sizes: Vec> = elements.iter().map(|_| Option::::None).collect(); - let mut room: Vec = elements.iter().map(|_| 0.).collect(); + let mut positive_room: Vec = elements.iter().map(|_| 0.).collect(); + let mut room_to_shrink: Vec = elements.iter().map(|_| 0.).collect(); for (i, constraint) in sizes.iter().enumerate() { let constraint = match orientation { @@ -449,13 +453,15 @@ fn layout_axis( if let Constraint::Range { lower, upper } = constraint { if let Some(lower) = lower { if default_size < lower { - dbg!("}}}}}}}"); pool += default_size - lower; final_sizes[i] = lower.into(); continue; } else { - room[i] = -(default_size - lower); + room_to_shrink[i] = -(default_size - lower); } + } else { + // Effectively, this means the element can shrink to 0 + room_to_shrink[i] = -default_size; } if let Some(upper) = upper { if default_size > upper { @@ -463,29 +469,81 @@ fn layout_axis( final_sizes[i] = upper.into(); continue; } else { - room[i] = -(default_size - upper); + positive_room[i] = -(default_size - upper); } + } else { + // Effectively, this means the element can expand any amount + positive_room[i] = default_size * 2.; } - final_sizes[i] = default_size.into(); + } else { + room_to_shrink[i] = -default_size; + positive_room[i] = default_size * 2.; } + final_sizes[i] = default_size.into(); } - dbg!(&room); - - let unconstrained_count = final_sizes.iter().filter(|&s| s.is_none()).count() as f32; - - let new_default = if unconstrained_count > 0. { - (default_size) + (pool / unconstrained_count) - } else { - 0. + fn room_available(room: &[f32]) -> bool { + room.iter().filter(|r| r.abs() > 0.).count() as f32 > 0. } - .max(0.); - final_sizes.iter_mut().for_each(|size| { - if size.is_none() { - *size = Some(new_default); + let limit = 0; + let mut i = 0; + loop { + if i > limit { + break; } - }); + i += 1; + let pool_empty = pool.abs() < 0.1; + if !pool_empty && pool.is_sign_positive() && room_available(&positive_room) { + // We need to use more room + let mut enumerated_room: Vec<(usize, f32)> = positive_room + .iter() + .enumerate() + .map(|(i, v)| (i, *v)) + .collect(); + enumerated_room.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap().reverse()); + let distribution_candidates = + positive_room.iter().filter(|r| r.abs() > 0.).count() as f32; + let distribution_amount = + (pool / distribution_candidates).min(enumerated_room.first().unwrap().1); + pool -= distribution_amount * distribution_candidates; + enumerated_room + .iter() + .filter(|r| r.1 != 0.) + .for_each(|&(i, _)| { + positive_room[i] -= distribution_amount; + if let Some(size) = &mut final_sizes[i] { + *size += distribution_amount + } + }); + } else if !pool_empty && pool.is_sign_negative() && room_available(&room_to_shrink) { + // We need to use less room + let mut enumerated_room: Vec<(usize, f32)> = room_to_shrink + .iter() + .enumerate() + .map(|(i, v)| (i, *v)) + .collect(); + enumerated_room.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + let distribution_candidates = room_to_shrink + .iter() + .filter(|r| r.abs() > 0. && r.is_sign_negative()) + .count() as f32; + let distribution_amount = + (pool / distribution_candidates).max(enumerated_room.first().unwrap().1); + pool -= distribution_amount * distribution_candidates; + enumerated_room + .iter() + .filter(|r| r.1 != 0.) + .for_each(|&(i, _)| { + room_to_shrink[i] -= distribution_amount; + if let Some(size) = &mut final_sizes[i] { + *size += distribution_amount + } + }); + } else { + break; + } + } let mut current_pos = match orientation { Orientation::Horizontal => available_area.x, From 448aa5fafacafc2efa4fd4de01d4a94eea6ca9a8 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 21:07:15 -0600 Subject: [PATCH 04/13] fix minimum resolution --- examples/egui-example/src/main.rs | 17 +++++++---------- src/layout.rs | 16 +++++++++------- src/modifiers.rs | 18 ++++++++++++++++-- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/examples/egui-example/src/main.rs b/examples/egui-example/src/main.rs index 3832de2..6a37de2 100644 --- a/examples/egui-example/src/main.rs +++ b/examples/egui-example/src/main.rs @@ -21,16 +21,13 @@ fn main() -> eframe::Result { } fn my_layout_fn(ui: &mut Ui) -> Node { - column_spaced( - 10., - vec![ - draw_a(ui), - draw_b(ui).min_height(20.), - draw_c(ui).max_height(30.), - draw_a(ui), - draw_b(ui).min_height(20.), - ], - ) + row(vec![ + draw_a(ui), + draw_b(ui).width_range(..300.), + draw_c(ui).width_range(100.0..), + draw_a(ui), + draw_b(ui).width_range(200.0..), + ]) .pad(10.) } diff --git a/src/layout.rs b/src/layout.rs index 14f9575..7f65fad 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -451,6 +451,7 @@ fn layout_axis( Orientation::Vertical => constraint.height, }; if let Constraint::Range { lower, upper } = constraint { + // dbg!(&constraint, default_size); if let Some(lower) = lower { if default_size < lower { pool += default_size - lower; @@ -485,8 +486,9 @@ fn layout_axis( fn room_available(room: &[f32]) -> bool { room.iter().filter(|r| r.abs() > 0.).count() as f32 > 0. } + // dbg!(&final_sizes); - let limit = 0; + let limit = 1; let mut i = 0; loop { if i > limit { @@ -522,8 +524,9 @@ fn layout_axis( .iter() .enumerate() .map(|(i, v)| (i, *v)) + .filter(|(_, v)| *v != 0.) .collect(); - enumerated_room.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + enumerated_room.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap().reverse()); let distribution_candidates = room_to_shrink .iter() .filter(|r| r.abs() > 0. && r.is_sign_negative()) @@ -531,15 +534,14 @@ fn layout_axis( let distribution_amount = (pool / distribution_candidates).max(enumerated_room.first().unwrap().1); pool -= distribution_amount * distribution_candidates; - enumerated_room - .iter() - .filter(|r| r.1 != 0.) - .for_each(|&(i, _)| { + enumerated_room.iter().for_each(|&(i, _)| { + if room_to_shrink[i].abs() > 0. && room_to_shrink[i].is_sign_negative() { room_to_shrink[i] -= distribution_amount; if let Some(size) = &mut final_sizes[i] { *size += distribution_amount } - }); + } + }); } else { break; } diff --git a/src/modifiers.rs b/src/modifiers.rs index 65c6f1a..9584cfe 100644 --- a/src/modifiers.rs +++ b/src/modifiers.rs @@ -1,3 +1,5 @@ +use std::ops::RangeBounds; + use crate::{layout::Node, layout::NodeValue, models::*}; impl Node { @@ -186,9 +188,21 @@ impl Node { /// /// When used inside a [crate::nodes::row] this will not impact row layout. /// If you'd like to impact row layout use [Node::width] or [Node::relative_width] - pub fn max_width(self, width: f32) -> Self { + pub fn width_range(self, range: R) -> Self + where + R: RangeBounds, + { self.wrap_or_update_explicit(|options| { - options.width_max = width.into(); + match range.start_bound() { + std::ops::Bound::Included(bound) => options.width_min = Some(*bound), + std::ops::Bound::Excluded(bound) => options.width_min = Some(*bound), + std::ops::Bound::Unbounded => (), + } + match range.end_bound() { + std::ops::Bound::Included(bound) => options.width_max = Some(*bound), + std::ops::Bound::Excluded(bound) => options.width_max = Some(*bound), + std::ops::Bound::Unbounded => (), + } }) } /// Specifies an upper bound on a node's height From faae094d4f3450f5511c57ede88e640f69fdcf00 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 21:29:52 -0600 Subject: [PATCH 05/13] fix maximum resolution --- examples/egui-example/src/main.rs | 4 ++-- src/layout.rs | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/examples/egui-example/src/main.rs b/examples/egui-example/src/main.rs index 6a37de2..050f79e 100644 --- a/examples/egui-example/src/main.rs +++ b/examples/egui-example/src/main.rs @@ -24,9 +24,9 @@ fn my_layout_fn(ui: &mut Ui) -> Node { row(vec![ draw_a(ui), draw_b(ui).width_range(..300.), - draw_c(ui).width_range(100.0..), + draw_c(ui).width_range(..100.0), draw_a(ui), - draw_b(ui).width_range(200.0..), + draw_b(ui).width_range(..200.0), ]) .pad(10.) } diff --git a/src/layout.rs b/src/layout.rs index 7f65fad..68da232 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -486,7 +486,6 @@ fn layout_axis( fn room_available(room: &[f32]) -> bool { room.iter().filter(|r| r.abs() > 0.).count() as f32 > 0. } - // dbg!(&final_sizes); let limit = 1; let mut i = 0; @@ -502,22 +501,24 @@ fn layout_axis( .iter() .enumerate() .map(|(i, v)| (i, *v)) + .filter(|(_, v)| *v != 0.) .collect(); - enumerated_room.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap().reverse()); - let distribution_candidates = - positive_room.iter().filter(|r| r.abs() > 0.).count() as f32; + enumerated_room.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + let distribution_candidates = positive_room + .iter() + .filter(|r| r.abs() > 0. && r.is_sign_positive()) + .count() as f32; let distribution_amount = (pool / distribution_candidates).min(enumerated_room.first().unwrap().1); pool -= distribution_amount * distribution_candidates; - enumerated_room - .iter() - .filter(|r| r.1 != 0.) - .for_each(|&(i, _)| { + enumerated_room.iter().for_each(|&(i, _)| { + if positive_room[i].abs() > 0. && positive_room[i].is_sign_positive() { positive_room[i] -= distribution_amount; if let Some(size) = &mut final_sizes[i] { *size += distribution_amount } - }); + } + }); } else if !pool_empty && pool.is_sign_negative() && room_available(&room_to_shrink) { // We need to use less room let mut enumerated_room: Vec<(usize, f32)> = room_to_shrink From e42d4299dde49f694a4c2339cc7c4f36fa3a4033 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 21:42:33 -0600 Subject: [PATCH 06/13] use range for size constraints --- src/layout.rs | 2 +- src/modifiers.rs | 44 ++++++++++++++++---------------------------- 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index 68da232..2b322c0 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -487,7 +487,7 @@ fn layout_axis( room.iter().filter(|r| r.abs() > 0.).count() as f32 > 0. } - let limit = 1; + let limit = 4; let mut i = 0; loop { if i > limit { diff --git a/src/modifiers.rs b/src/modifiers.rs index 9584cfe..83e0424 100644 --- a/src/modifiers.rs +++ b/src/modifiers.rs @@ -166,28 +166,25 @@ impl Node { options.y_relative = true; }) } - /// Specifies a lower bound on a node's width - /// - /// When used inside a [crate::nodes::row] this will not impact row layout. - /// If you'd like to impact row layout use [Node::width] or [Node::relative_width] - pub fn min_width(self, width: f32) -> Self { - self.wrap_or_update_explicit(|options| { - options.width_min = width.into(); - }) - } - /// Specifies a lower bound on a node's height - /// - /// When used inside a [crate::nodes::column] this will not impact column layout. - /// If you'd like to impact column layout use [Node::height] or [Node::relative_height] - pub fn min_height(self, height: f32) -> Self { + /// Specifies bounds on a node's height + pub fn height_range(self, range: R) -> Self + where + R: RangeBounds, + { self.wrap_or_update_explicit(|options| { - options.height_min = height.into(); + match range.start_bound() { + std::ops::Bound::Included(bound) => options.height_min = Some(*bound), + std::ops::Bound::Excluded(bound) => options.height_min = Some(*bound), + std::ops::Bound::Unbounded => (), + } + match range.end_bound() { + std::ops::Bound::Included(bound) => options.height_max = Some(*bound), + std::ops::Bound::Excluded(bound) => options.height_max = Some(*bound), + std::ops::Bound::Unbounded => (), + } }) } - /// Specifies an upper bound on a node's width - /// - /// When used inside a [crate::nodes::row] this will not impact row layout. - /// If you'd like to impact row layout use [Node::width] or [Node::relative_width] + /// Specifies bounds on a node's width pub fn width_range(self, range: R) -> Self where R: RangeBounds, @@ -205,15 +202,6 @@ impl Node { } }) } - /// Specifies an upper bound on a node's height - /// - /// When used inside a [crate::nodes::column] this will not impact column layout. - /// If you'd like to impact column layout use [Node::height] or [Node::relative_height] - pub fn max_height(self, height: f32) -> Self { - self.wrap_or_update_explicit(|options| { - options.height_max = height.into(); - }) - } /// Specifies an alignment along the x axis. /// /// This will only have an effect if the node is constrained to be smaller than the area that is available, From c215f987ede564201a35e3104c1f97e171819cca Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 21:49:42 -0600 Subject: [PATCH 07/13] remove relative methods, maybe another time --- examples/egui-example/src/main.rs | 9 +----- src/layout.rs | 50 +++++++++++++++---------------- src/modifiers.rs | 14 --------- 3 files changed, 25 insertions(+), 48 deletions(-) diff --git a/examples/egui-example/src/main.rs b/examples/egui-example/src/main.rs index 050f79e..3eec347 100644 --- a/examples/egui-example/src/main.rs +++ b/examples/egui-example/src/main.rs @@ -21,14 +21,7 @@ fn main() -> eframe::Result { } fn my_layout_fn(ui: &mut Ui) -> Node { - row(vec![ - draw_a(ui), - draw_b(ui).width_range(..300.), - draw_c(ui).width_range(..100.0), - draw_a(ui), - draw_b(ui).width_range(..200.0), - ]) - .pad(10.) + row(vec![draw_a(ui), draw_b(ui), draw_c(ui)]).pad(10.) } fn draw_a(ui: &mut Ui) -> Node { diff --git a/src/layout.rs b/src/layout.rs index 2b322c0..56b6888 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,5 +1,4 @@ use core::f32; -use std::f64::INFINITY; use crate::{anynode::AnyNode, drawable::Drawable, models::*}; @@ -451,7 +450,6 @@ fn layout_axis( Orientation::Vertical => constraint.height, }; if let Constraint::Range { lower, upper } = constraint { - // dbg!(&constraint, default_size); if let Some(lower) = lower { if default_size < lower { pool += default_size - lower; @@ -612,7 +610,7 @@ mod tests { draw(|a, _| { assert_eq!(a, Area::new(0., 0., 100., 10.)); }) - .rel_height(0.1), + .height(10.), draw(|a, _| { assert_eq!(a, Area::new(0., 10., 100., 90.)); }), @@ -642,7 +640,7 @@ mod tests { draw(|a, _| { assert_eq!(a, Area::new(0., 90., 100., 10.)); }) - .rel_height(0.1), + .height(10.), ]) }) .draw(Area::new(0., 0., 100., 100.), &mut ()); @@ -681,19 +679,19 @@ mod tests { draw(|a, _| { assert_eq!(a, Area::new(0., 0., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2) + .width(10.) + .height(20.) .y_align(YAlign::Top), draw(|a, _| { assert_eq!(a, Area::new(10., 40., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2), + .width(10.) + .height(20.), draw(|a, _| { assert_eq!(a, Area::new(20., 80., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2) + .width(10.) + .height(20.) .y_align(YAlign::Bottom), draw(|a, _| { assert_eq!(a, Area::new(30., 0., 70., 100.)); @@ -737,19 +735,19 @@ mod tests { draw(|a, _| { assert_eq!(a, Area::new(70., 0., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2) + .width(10.) + .height(20.) .y_align(YAlign::Top), draw(|a, _| { assert_eq!(a, Area::new(80., 40., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2), + .width(10.) + .height(20.), draw(|a, _| { assert_eq!(a, Area::new(90., 80., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2) + .width(10.) + .height(20.) .y_align(YAlign::Bottom), ]) }) @@ -777,32 +775,32 @@ mod tests { draw(|a, _| { assert_eq!(a, Area::new(0., 0., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2) + .width(10.) + .height(20.) .align(Align::TopLeading), draw(|a, _| { assert_eq!(a, Area::new(45., 0., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2) + .width(10.) + .height(20.) .align(Align::TopCenter), draw(|a, _| { assert_eq!(a, Area::new(90., 0., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2) + .width(10.) + .height(20.) .align(Align::TopTrailing), draw(|a, _| { assert_eq!(a, Area::new(90., 40., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2) + .width(10.) + .height(20.) .align(Align::CenterTrailing), draw(|a, _| { assert_eq!(a, Area::new(90., 80., 10., 20.)); }) - .rel_width(0.1) - .rel_height(0.2) + .width(10.) + .height(20.) .align(Align::BottomTrailing), draw(|a, _| { assert_eq!(a, Area::new(45., 80., 10., 20.)); diff --git a/src/modifiers.rs b/src/modifiers.rs index 83e0424..080a565 100644 --- a/src/modifiers.rs +++ b/src/modifiers.rs @@ -152,20 +152,6 @@ impl Node { options.y_relative = false; }) } - /// Specifies a width for a node relative to the available width - pub fn rel_width(self, ratio: f32) -> Self { - self.wrap_or_update_explicit(|options| { - options.width = ratio.into(); - options.x_relative = true; - }) - } - /// Specifies a height for a node relative to the available height - pub fn rel_height(self, ratio: f32) -> Self { - self.wrap_or_update_explicit(|options| { - options.height = ratio.into(); - options.y_relative = true; - }) - } /// Specifies bounds on a node's height pub fn height_range(self, range: R) -> Self where From f7410ef059317ad6a8ead247df9cecadc8883b63 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 22:20:16 -0600 Subject: [PATCH 08/13] fix bad state model & redundant logic --- examples/egui-example/src/main.rs | 14 +- src/layout.rs | 282 +++++++++++++++++++----------- 2 files changed, 197 insertions(+), 99 deletions(-) diff --git a/examples/egui-example/src/main.rs b/examples/egui-example/src/main.rs index 3eec347..a387188 100644 --- a/examples/egui-example/src/main.rs +++ b/examples/egui-example/src/main.rs @@ -21,7 +21,19 @@ fn main() -> eframe::Result { } fn my_layout_fn(ui: &mut Ui) -> Node { - row(vec![draw_a(ui), draw_b(ui), draw_c(ui)]).pad(10.) + column(vec![ + row(vec![ + // draw_a(ui).height(20.), + draw_b(ui).height(10.), + draw_c(ui).height(30.), + ]), + row(vec![ + // draw_a(ui).height(20.), + draw_b(ui), + draw_c(ui).height(30.), + ]) + .pad(0.), + ]) } fn draw_a(ui: &mut Ui) -> Node { diff --git a/src/layout.rs b/src/layout.rs index 56b6888..0d9ee8c 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -129,11 +129,11 @@ impl NodeValue { match self { NodeValue::Padding { amounts, element } => { element.sizes().accumulate(SizeConstraints { - width: Constraint::Range { + width: Constraint { lower: Some(amounts.trailing + amounts.leading), upper: None, }, - height: Constraint::Range { + height: Constraint { lower: Some(amounts.bottom + amounts.top), upper: None, }, @@ -141,8 +141,8 @@ impl NodeValue { } NodeValue::Column { elements, .. } => elements.iter_mut().fold( SizeConstraints { - width: Constraint::None, - height: Constraint::None, + width: Constraint::none(), + height: Constraint::none(), }, |current, element| SizeConstraints { width: current.width.combine(element.sizes().width), @@ -151,8 +151,8 @@ impl NodeValue { ), NodeValue::Row { elements, .. } => elements.iter_mut().fold( SizeConstraints { - width: Constraint::None, - height: Constraint::None, + width: Constraint::none(), + height: Constraint::none(), }, |current, element| SizeConstraints { width: current.width.accumulate(element.sizes().width), @@ -161,8 +161,8 @@ impl NodeValue { ), NodeValue::Stack(elements) => { let cumulative_size = SizeConstraints { - width: Constraint::None, - height: Constraint::None, + width: Constraint::none(), + height: Constraint::none(), }; for element in elements { cumulative_size.combine(element.sizes()); @@ -177,8 +177,8 @@ impl NodeValue { } NodeValue::Scope { .. } => todo!(), _ => SizeConstraints { - width: Constraint::None, - height: Constraint::None, + width: Constraint::none(), + height: Constraint::none(), }, } } @@ -300,19 +300,25 @@ enum Orientation { Vertical, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub(crate) struct SizeConstraints { width: Constraint, height: Constraint, } -#[derive(Debug, Clone, Copy)] -pub(crate) enum Constraint { - None, - Range { - lower: Option, - upper: Option, - }, +#[derive(Debug, Clone, Copy, PartialEq)] +pub(crate) struct Constraint { + lower: Option, + upper: Option, +} + +impl Constraint { + fn none() -> Self { + Self { + lower: None, + upper: None, + } + } } impl SizeConstraints { @@ -332,60 +338,32 @@ impl SizeConstraints { impl Constraint { fn combine(self, other: Self) -> Self { - match (self, other) { - (Constraint::None, Constraint::None) => Constraint::None, - (value, Constraint::None) | (Constraint::None, value) => value, - ( - Constraint::Range { - lower: a_lower, - upper: a_upper, - }, - Constraint::Range { - lower: b_lower, - upper: b_upper, - }, - ) => { - let lower = if let (Some(a), Some(b)) = (a_lower, b_lower) { - Some(a.min(b)) - } else { - None - }; - let upper = if let (Some(a), Some(b)) = (a_upper, b_upper) { - Some(a.max(b)) - } else { - None - }; - Constraint::Range { lower, upper } - } - } + // This always takes the bigger bound + let lower = match (self.lower, other.lower) { + (None, None) => None, + (None, Some(a)) | (Some(a), None) => Some(a), + (Some(bound_a), Some(bound_b)) => Some(bound_a.max(bound_b)), + }; + // In terms of upper constraints - no constraint is biggest + let upper = match (self.upper, other.upper) { + (None, None) => None, + (None, Some(_)) | (Some(_), None) => None, + (Some(bound_a), Some(bound_b)) => Some(bound_a.max(bound_b)), + }; + Constraint { lower, upper } } fn accumulate(self, other: Self) -> Self { - match (self, other) { - (Constraint::None, Constraint::None) => Constraint::None, - (value, Constraint::None) | (Constraint::None, value) => value, - ( - Constraint::Range { - lower: a_lower, - upper: a_upper, - }, - Constraint::Range { - lower: b_lower, - upper: b_upper, - }, - ) => { - let lower = if let (Some(a), Some(b)) = (a_lower, b_lower) { - Some(a + b) - } else { - None - }; - let upper = if let (Some(a), Some(b)) = (a_upper, b_upper) { - Some(a + b) - } else { - None - }; - Constraint::Range { lower, upper } - } - } + let lower = if let (Some(a), Some(b)) = (self.lower, other.lower) { + Some(a + b) + } else { + None + }; + let upper = if let (Some(a), Some(b)) = (self.upper, other.upper) { + Some(a + b) + } else { + None + }; + Constraint { lower, upper } } } @@ -393,30 +371,36 @@ impl From for SizeConstraints { fn from(value: Size) -> Self { SizeConstraints { width: if value.width.is_some() { - Constraint::Range { + Constraint { lower: value.width, upper: value.width, } } else if value.width_min.is_some() || value.width_max.is_some() { - Constraint::Range { + Constraint { lower: value.width_min, upper: value.width_max, } } else { - Constraint::None + Constraint { + lower: None, + upper: None, + } }, height: if value.height.is_some() { - Constraint::Range { + Constraint { lower: value.height, upper: value.height, } } else if value.height_min.is_some() || value.height_max.is_some() { - Constraint::Range { + Constraint { lower: value.height_min, upper: value.height_max, } } else { - Constraint::None + Constraint { + lower: None, + upper: None, + } }, } } @@ -449,35 +433,32 @@ fn layout_axis( Orientation::Horizontal => constraint.width, Orientation::Vertical => constraint.height, }; - if let Constraint::Range { lower, upper } = constraint { - if let Some(lower) = lower { - if default_size < lower { - pool += default_size - lower; - final_sizes[i] = lower.into(); - continue; - } else { - room_to_shrink[i] = -(default_size - lower); - } + let Constraint { lower, upper } = constraint; + if let Some(lower) = lower { + if default_size < lower { + pool += default_size - lower; + final_sizes[i] = lower.into(); + continue; } else { - // Effectively, this means the element can shrink to 0 - room_to_shrink[i] = -default_size; + room_to_shrink[i] = -(default_size - lower); } - if let Some(upper) = upper { - if default_size > upper { - pool += default_size - upper; - final_sizes[i] = upper.into(); - continue; - } else { - positive_room[i] = -(default_size - upper); - } + } else { + // Effectively, this means the element can shrink to 0 + room_to_shrink[i] = -default_size; + } + if let Some(upper) = upper { + if default_size > upper { + pool += default_size - upper; + final_sizes[i] = upper.into(); + continue; } else { - // Effectively, this means the element can expand any amount - positive_room[i] = default_size * 2.; + positive_room[i] = -(default_size - upper); } } else { - room_to_shrink[i] = -default_size; + // Effectively, this means the element can expand any amount positive_room[i] = default_size * 2.; } + final_sizes[i] = default_size.into(); } @@ -881,4 +862,109 @@ mod tests { }) .draw(Area::new(0., 0., 100., 100.), &mut ()); } + #[test] + fn test_row_with_constrained_item() { + Layout::new(|()| { + row(vec![ + draw(|a, _| { + assert_eq!(a, Area::new(0., 0., 30., 100.)); + }) + .width(30.), + draw(|a, _| { + assert_eq!(a, Area::new(30., 0., 70., 100.)); + }), + ]) + }) + .draw(Area::new(0., 0., 100., 100.), &mut ()); + } + + #[test] + fn test_nested_row_with_constrained_item() { + Layout::new(|()| { + row(vec![ + row(vec![ + draw(|a, _| { + assert_eq!(a, Area::new(0., 0., 20., 100.)); + }) + .width(20.), + draw(|a, _| { + assert_eq!(a, Area::new(20., 0., 30., 100.)); + }), + ]) + .width(50.), + draw(|a, _| { + assert_eq!(a, Area::new(50., 0., 50., 100.)); + }), + ]) + }) + .draw(Area::new(0., 0., 100., 100.), &mut ()); + } + + #[test] + fn test_stack_with_constrained_item() { + Layout::new(|()| { + stack(vec![ + draw(|a, _| { + assert_eq!(a, Area::new(0., 0., 100., 100.)); + }), + draw(|a, _| { + assert_eq!(a, Area::new(25., 25., 50., 50.)); + }) + .width(50.) + .height(50.), + ]) + }) + .draw(Area::new(0., 0., 100., 100.), &mut ()); + } + + #[test] + fn test_row_with_multiple_constrained_items() { + Layout::new(|()| { + row(vec![ + draw(|a, _| { + assert_eq!(a, Area::new(0., 0., 20., 100.)); + }) + .width(20.), + draw(|a, _| { + assert_eq!(a, Area::new(20., 25., 30., 50.)); + }) + .width(30.) + .height(50.), + draw(|a, _| { + assert_eq!(a, Area::new(50., 0., 25., 100.)); + }), + draw(|a, _| { + assert_eq!(a, Area::new(75., 0., 25., 100.)); + }), + ]) + }) + .draw(Area::new(0., 0., 100., 100.), &mut ()); + } + + #[test] + fn test_idk() { + assert_eq!( + row::<()>(vec![space(), space().height(30.)]).inner.sizes(), + SizeConstraints { + width: Constraint { + lower: None, + upper: None + }, + height: Constraint { + lower: Some(30.), + upper: None + } + } + ); + // dbg!(row::<()>(vec![space(), space().height(30.)]) + // .pad(0.) + // .inner + // .sizes()); + // dbg!(column::<()>(vec![ + // row(vec![space(), space().height(30.)]).pad(0.), + // row(vec![space(), space().height(30.)]), + // ]) + // .inner + // .sizes()); + } } From 1295e72872985f0d8920489bdb816a4f6a4c186b Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 22:25:19 -0600 Subject: [PATCH 09/13] fix bound accumulation --- src/layout.rs | 68 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index 0d9ee8c..966c946 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -353,15 +353,15 @@ impl Constraint { Constraint { lower, upper } } fn accumulate(self, other: Self) -> Self { - let lower = if let (Some(a), Some(b)) = (self.lower, other.lower) { - Some(a + b) - } else { - None + let lower = match (self.lower, other.lower) { + (None, None) => None, + (None, Some(a)) | (Some(a), None) => Some(a), + (Some(bound_a), Some(bound_b)) => Some(bound_a.max(bound_b)), }; - let upper = if let (Some(a), Some(b)) = (self.upper, other.upper) { - Some(a + b) - } else { - None + let upper = match (self.upper, other.upper) { + (None, None) => None, + (None, Some(bound)) | (Some(bound), None) => Some(bound), + (Some(bound_a), Some(bound_b)) => Some(bound_a + bound_b), }; Constraint { lower, upper } } @@ -942,29 +942,55 @@ mod tests { } #[test] - fn test_idk() { + fn test_constraint_combination() { assert_eq!( row::<()>(vec![space(), space().height(30.)]).inner.sizes(), + SizeConstraints { + width: Constraint::none(), + height: Constraint { + lower: Some(30.), + upper: None + } + } + ); + assert_eq!( + row::<()>(vec![space().height(40.), space().height(30.)]) + .inner + .sizes(), + SizeConstraints { + width: Constraint::none(), + height: Constraint { + lower: Some(40.), + upper: None + } + } + ); + assert_eq!( + row::<()>(vec![space().height(40.), space().height_range(30.0..35.)]) + .inner + .sizes(), + SizeConstraints { + width: Constraint::none(), + height: Constraint { + lower: Some(40.), + upper: None + } + } + ); + assert_eq!( + column::<()>(vec![space().width(20.).height(50.), space().width(10.)]) + .inner + .sizes(), SizeConstraints { width: Constraint { - lower: None, + lower: Some(20.), upper: None }, height: Constraint { - lower: Some(30.), + lower: Some(50.), upper: None } } ); - // dbg!(row::<()>(vec![space(), space().height(30.)]) - // .pad(0.) - // .inner - // .sizes()); - // dbg!(column::<()>(vec![ - // row(vec![space(), space().height(30.)]).pad(0.), - // row(vec![space(), space().height(30.)]), - // ]) - // .inner - // .sizes()); } } From 2b8bb4254c36e6dd986e2e6635d71b2b3a7ed464 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 22:40:25 -0600 Subject: [PATCH 10/13] fix stack bound accumulation --- src/layout.rs | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index 966c946..58f9d4a 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -160,12 +160,12 @@ impl NodeValue { }, ), NodeValue::Stack(elements) => { - let cumulative_size = SizeConstraints { + let mut cumulative_size = SizeConstraints { width: Constraint::none(), height: Constraint::none(), }; for element in elements { - cumulative_size.combine(element.sizes()); + cumulative_size = cumulative_size.combine(element.sizes()); } cumulative_size } @@ -966,28 +966,52 @@ mod tests { } ); assert_eq!( - row::<()>(vec![space().height(40.), space().height_range(30.0..35.)]) + column::<()>(vec![space(), space().width(10.)]) + .inner + .sizes(), + SizeConstraints { + width: Constraint { + lower: Some(10.), + upper: None + }, + height: Constraint::none() + } + ); + assert_eq!( + column::<()>(vec![space().width(20.), space().width(10.)]) + .inner + .sizes(), + SizeConstraints { + width: Constraint { + lower: Some(20.), + upper: None + }, + height: Constraint::none() + } + ); + assert_eq!( + stack::<()>(vec![space(), space().height(10.)]) .inner .sizes(), SizeConstraints { width: Constraint::none(), height: Constraint { - lower: Some(40.), + lower: Some(10.), upper: None } } ); assert_eq!( - column::<()>(vec![space().width(20.).height(50.), space().width(10.)]) + stack::<()>(vec![space().height(20.), space().width(10.)]) .inner .sizes(), SizeConstraints { width: Constraint { - lower: Some(20.), + lower: Some(10.), upper: None }, height: Constraint { - lower: Some(50.), + lower: Some(20.), upper: None } } From 053a91d17671e0d40e9105aac2ac0972db812d5e Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 23:00:00 -0600 Subject: [PATCH 11/13] fix explicit in explicit bounds --- src/layout.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/layout.rs b/src/layout.rs index 58f9d4a..388a7bf 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -170,7 +170,7 @@ impl NodeValue { cumulative_size } NodeValue::Explicit { options, element } => { - element.sizes().combine(SizeConstraints::from(*options)) + element.sizes().wrap(SizeConstraints::from(*options)) } NodeValue::Offset { .. } => { todo!() @@ -328,6 +328,12 @@ impl SizeConstraints { height: self.height.combine(other.height), } } + fn wrap(self, other: Self) -> Self { + SizeConstraints { + width: self.width.wrap(other.width), + height: self.height.wrap(other.height), + } + } fn accumulate(self, other: Self) -> Self { SizeConstraints { width: self.width.accumulate(other.width), @@ -352,6 +358,19 @@ impl Constraint { }; Constraint { lower, upper } } + fn wrap(self, other: Self) -> Self { + let lower = match (self.lower, other.lower) { + (None, None) => None, + (None, Some(a)) | (Some(a), None) => Some(a), + (Some(bound_a), Some(bound_b)) => Some(bound_a.max(bound_b)), + }; + let upper = match (self.upper, other.upper) { + (None, None) => None, + (None, Some(a)) | (Some(a), None) => Some(a), + (Some(bound_a), Some(bound_b)) => Some(bound_a.max(bound_b)), + }; + Constraint { lower, upper } + } fn accumulate(self, other: Self) -> Self { let lower = match (self.lower, other.lower) { (None, None) => None, From bbd9f244950be4ea5519074fdbe113968cf42895 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 23:05:05 -0600 Subject: [PATCH 12/13] implement anynode sizes --- src/anynode.rs | 8 +++++++- src/layout.rs | 12 +++++------- src/nodes.rs | 6 ++++++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/anynode.rs b/src/anynode.rs index 47e8ed5..857ca45 100644 --- a/src/anynode.rs +++ b/src/anynode.rs @@ -1,4 +1,4 @@ -use crate::models::Area; +use crate::{layout::SizeConstraints, models::Area}; use std::{any::Any, fmt, rc::Rc}; type AnyDrawFn = Rc; @@ -6,6 +6,7 @@ pub(crate) struct AnyNode { pub(crate) inner: Box, pub(crate) clone: fn(&Box) -> Box, pub(crate) layout: fn(&mut dyn Any, Area), + pub(crate) sizes: fn(&dyn Any) -> SizeConstraints, pub(crate) draw: AnyDrawFn, } @@ -17,6 +18,10 @@ impl AnyNode { pub(crate) fn layout(&mut self, available_area: Area) { (self.layout)(&mut *self.inner, available_area) } + + pub(crate) fn sizes(&self) -> SizeConstraints { + (self.sizes)(&*self.inner) + } } impl Clone for AnyNode { @@ -25,6 +30,7 @@ impl Clone for AnyNode { inner: (self.clone)(&self.inner), clone: self.clone, layout: self.layout, + sizes: self.sizes, draw: self.draw.clone(), } } diff --git a/src/layout.rs b/src/layout.rs index 388a7bf..71b77eb 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -125,7 +125,7 @@ impl NodeValue { } } - pub(crate) fn sizes(&mut self) -> SizeConstraints { + pub(crate) fn sizes(&self) -> SizeConstraints { match self { NodeValue::Padding { amounts, element } => { element.sizes().accumulate(SizeConstraints { @@ -139,7 +139,7 @@ impl NodeValue { }, }) } - NodeValue::Column { elements, .. } => elements.iter_mut().fold( + NodeValue::Column { elements, .. } => elements.iter().fold( SizeConstraints { width: Constraint::none(), height: Constraint::none(), @@ -149,7 +149,7 @@ impl NodeValue { height: current.height.accumulate(element.sizes().height), }, ), - NodeValue::Row { elements, .. } => elements.iter_mut().fold( + NodeValue::Row { elements, .. } => elements.iter().fold( SizeConstraints { width: Constraint::none(), height: Constraint::none(), @@ -172,10 +172,8 @@ impl NodeValue { NodeValue::Explicit { options, element } => { element.sizes().wrap(SizeConstraints::from(*options)) } - NodeValue::Offset { .. } => { - todo!() - } - NodeValue::Scope { .. } => todo!(), + NodeValue::Offset { element, .. } => element.sizes(), + NodeValue::Scope { scoped, .. } => scoped.sizes(), _ => SizeConstraints { width: Constraint::none(), height: Constraint::none(), diff --git a/src/nodes.rs b/src/nodes.rs index 56ddf24..07d7781 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -140,6 +140,12 @@ pub fn scope(scope: impl Fn(&mut U) -> &mut V + 'static, node: No .inner .draw(scope(state)) }), + sizes: |any| { + any.downcast_ref::>() + .expect("Invalid downcast") + .inner + .sizes() + }, }, }, }, From 1548b9caaa6cd36fac2ab3f666e77b624caccad1 Mon Sep 17 00:00:00 2001 From: ejjonny Date: Tue, 10 Sep 2024 23:06:42 -0600 Subject: [PATCH 13/13] revert demo changes --- examples/egui-example/src/main.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/examples/egui-example/src/main.rs b/examples/egui-example/src/main.rs index a387188..d9ab399 100644 --- a/examples/egui-example/src/main.rs +++ b/examples/egui-example/src/main.rs @@ -21,19 +21,21 @@ fn main() -> eframe::Result { } fn my_layout_fn(ui: &mut Ui) -> Node { - column(vec![ - row(vec![ - // draw_a(ui).height(20.), - draw_b(ui).height(10.), - draw_c(ui).height(30.), - ]), - row(vec![ - // draw_a(ui).height(20.), - draw_b(ui), - draw_c(ui).height(30.), - ]) - .pad(0.), - ]) + column_spaced( + 10., + vec![ + draw_a(ui), + row_spaced( + 10., + vec![ + draw_b(ui).width_range(200.0..), + column_spaced(10., vec![draw_a(ui), draw_b(ui), draw_c(ui)]), + ], + ), + draw_c(ui), + ], + ) + .pad(10.) } fn draw_a(ui: &mut Ui) -> Node {