diff --git a/examples/basic.rs b/examples/basic.rs index 986cbf9..5f0f4a0 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -13,7 +13,7 @@ use tracing_subscriber::{ use tracing_tree::HierarchicalLayer; use violet::{ assets::{fs::BytesFromFile, AssetKey}, - components::{self, color, filled_rect, flow, font_size, size, text, Edges}, + components::{self, color, filled_rect, flow, font_size, padding, size, text, Edges}, input::{on_focus, on_mouse_input}, layout::{CrossAlign, Direction, Flow}, shapes::FilledRect, @@ -35,7 +35,7 @@ macro_rules! srgba { }}; } -const MARGIN: Edges = Edges::even(0.0); +const MARGIN: Edges = Edges::even(10.0); const EERIE_BLACK: Srgba = srgba!("#222525"); const EERIE_BLACK_300: Srgba = srgba!("#151616"); @@ -406,6 +406,7 @@ impl Widget for MainApp { fn mount(self, scope: &mut Scope) { scope .set(name(), "MainApp".into()) + .set(padding(), Edges::even(10.0)) .set(size(), Unit::rel(vec2(1.0, 1.0))); // scope.attach(Counter); @@ -542,39 +543,42 @@ impl Widget for MainApp { // .with_cross_align(CrossAlign::Stretch) // .with_background_color(Hsla::new(190.0, 0.048, 0.143, 1.0).into_color()); - scope.attach( - List::new(( - LayoutTest { - contain_margins: false, - }, - LayoutTest { - contain_margins: false, - }, - LayoutTest { - contain_margins: true, - }, - LayoutTest { - contain_margins: true, - }, - LayoutTest { - contain_margins: false, - }, - // Text::new("Hello, World!"), - )) - .contain_margins(true) - .with_direction(Direction::Vertical) - .with_padding(Edges::even(0.0)), - // List::new(( - // // list3, - // List::new((list1, list2)) - // .with_cross_align(CrossAlign::Stretch) - // .with_direction(Direction::Vertical) - // .with_background_color(Hsla::new(190.0, 0.048, 0.1, 1.0).into_color()), - // )) - // .with_cross_align(CrossAlign::Stretch) - // .with_direction(Direction::Horizontal) - // .with_background_color(Hsla::new(190.0, 0.048, 0.1, 1.0).into_color()), - ); + scope.attach(LayoutTest { + contain_margins: true, + }); + // scope.attach( + // List::new(( + // LayoutTest { + // contain_margins: false, + // }, + // LayoutTest { + // contain_margins: false, + // }, + // LayoutTest { + // contain_margins: true, + // }, + // LayoutTest { + // contain_margins: true, + // }, + // LayoutTest { + // contain_margins: false, + // }, + // // Text::new("Hello, World!"), + // )) + // .contain_margins(true) + // .with_direction(Direction::Vertical) + // .with_padding(Edges::even(0.0)), + // // List::new(( + // // // list3, + // // List::new((list1, list2)) + // // .with_cross_align(CrossAlign::Stretch) + // // .with_direction(Direction::Vertical) + // // .with_background_color(Hsla::new(190.0, 0.048, 0.1, 1.0).into_color()), + // // )) + // // .with_cross_align(CrossAlign::Stretch) + // // .with_direction(Direction::Horizontal) + // // .with_background_color(Hsla::new(190.0, 0.048, 0.1, 1.0).into_color()), + // ); } } @@ -625,39 +629,41 @@ struct LayoutTest { impl Widget for LayoutTest { fn mount(self, scope: &mut Scope<'_>) { - let row_2 = List::new(( - Rectangle { color: BRONZE } - .with_margin(MARGIN) - .with_size(Unit::px(vec2(100.0, 50.0))), - Rectangle { color: EMERALD } - .with_margin(MARGIN) - .with_size(Unit::px(vec2(20.0, 50.0))), - )) - .contain_margins(self.contain_margins) - .with_background_color(EERIE_BLACK_300) - .with_margin(MARGIN); + // let row_2 = List::new(( + // Rectangle { color: BRONZE } + // .with_margin(MARGIN) + // .with_size(Unit::px(vec2(100.0, 50.0))), + // Rectangle { color: EMERALD } + // .with_margin(MARGIN) + // .with_size(Unit::px(vec2(20.0, 50.0))), + // )) + // .contain_margins(self.contain_margins) + // .with_background_color(EERIE_BLACK_300) + // .with_margin(MARGIN); let row_1 = List::new(( Rectangle { color: CHILI_RED } .with_margin(MARGIN) .with_size(Unit::px(vec2(200.0, 50.0))), - row_2, - StackTest {}, + // row_2, + // StackTest {}, + // Text::new("Hello, World!"), Rectangle { color: TEAL } - .with_margin(MARGIN) + // .with_margin(MARGIN) .with_size(Unit::px(vec2(100.0, 50.0))), - Rectangle { color: TEAL } - .with_margin(MARGIN) - .with_size(Unit::px(vec2(50.0, 50.0))), + // Rectangle { color: TEAL } + // // .with_margin(MARGIN) + // .with_size(Unit::px(vec2(50.0, 50.0))), )) .contain_margins(self.contain_margins) .with_background_color(EERIE_BLACK) .with_margin(MARGIN); - List::new((row_1,)) - .contain_margins(self.contain_margins) - .with_background_color(EERIE_BLACK_300) - .mount(scope); + row_1.mount(scope) + // List::new((row_1,)) + // .contain_margins(self.contain_margins) + // .with_background_color(EERIE_BLACK_300) + // .mount(scope); } } diff --git a/src/app.rs b/src/app.rs index e5a6dc3..be9c28b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -107,7 +107,7 @@ impl App { frame.delta_time = delta_time; - tracing::info!(?dt, fps = 1.0 / delta_time); + // tracing::info!(?dt, fps = 1.0 / delta_time); ex.tick(&mut frame); diff --git a/src/layout/flow.rs b/src/layout/flow.rs index c2a1a9b..99f7ead 100644 --- a/src/layout/flow.rs +++ b/src/layout/flow.rs @@ -7,7 +7,7 @@ use crate::{ layout::query_size, }; -use super::{update_subtree, Block, LayoutLimits, SizeQuery}; +use super::{update_subtree, Block, LayoutLimits, Sizing}; #[derive(Debug, Clone)] struct MarginCursor { @@ -23,6 +23,7 @@ struct MarginCursor { main_margin: (f32, f32), cross_margin: (f32, f32), contain_margins: bool, + total_margin: f32, } impl MarginCursor { @@ -41,6 +42,7 @@ impl MarginCursor { main_margin: (0.0, 0.0), cross_margin: (0.0, 0.0), contain_margins, + total_margin: 0.0, } } @@ -56,10 +58,12 @@ impl MarginCursor { self.main_margin.0 = self.main_margin.0.max(back_margin - self.main_cursor); } + self.total_margin += advance; self.pending_margin = front_margin; self.main_cursor += advance + block.rect.support(-self.axis); + // Cross axis margin calculation let (start_margin, end_margin) = block.margin.in_axis(self.cross_axis); let placement_pos; @@ -92,7 +96,8 @@ impl MarginCursor { self.main_margin.1 = self.main_margin.1.max(self.pending_margin); if self.contain_margins { - self.main_cursor += self.pending_margin + self.main_cursor += self.pending_margin; + self.total_margin += self.pending_margin; } self.pending_margin = 0.0; @@ -158,6 +163,14 @@ impl CrossAlign { } } +pub(crate) struct Row<'a> { + pub(crate) min: Rect, + pub(crate) preferred: Rect, + pub(crate) margin: Edges, + pub(crate) blocks: Vec<(EntityRef<'a>, Sizing)>, + pub(crate) total_margin: f32, +} + #[derive(Default, Debug)] pub struct Flow { pub cross_align: CrossAlign, @@ -179,10 +192,21 @@ impl Flow { let _span = tracing::info_span!("Flow::apply", ?limits, flow=?self).entered(); let (axis, cross_axis) = self.direction.axis(); - let (_, total_preferred_size, _, blocks) = self.query_size(world, entity, content_area); + let row = self.query_size(world, entity, content_area); // Size remaining if everything got at least its preferred size - let total_preferred_size = total_preferred_size.size().dot(axis); + let total_preferred_size = row.preferred.size().dot(axis) - row.total_margin; + + let target_size = total_preferred_size.min(limits.max_size.dot(axis)); + let max_size = limits.max_size.dot(axis) - row.total_margin; + + tracing::info!( + row.total_margin, + total_preferred_size, + target_size, + %limits.max_size, + "query size" + ); let available_size = limits.max_size; @@ -201,20 +225,21 @@ impl Flow { let mut sum = 0.0; - let blocks = blocks + let blocks = row + .blocks .into_iter() - .map(|(entity, block)| { + .map(|(entity, sizing)| { // The size required to go from min to preferred size - let min_size = block.min.size().dot(axis); - let preferred_size = block.preferred.size().dot(axis); + let min_size = sizing.min.size().dot(axis); + let preferred_size = sizing.preferred.size().dot(axis); let to_preferred = preferred_size - min_size; let ratio = to_preferred / total_preferred_size; - tracing::info!("sizing: {}", ratio); + sum += ratio; - let axis_sizing = (min_size - + (limits.max_size.dot(axis) * (to_preferred / total_preferred_size))) - * axis; + + let axis_sizing = (min_size + (max_size * ratio)) * axis; + tracing::info!(%axis_sizing, "sizing: {}", ratio); let child_constraints = if let CrossAlign::Stretch = self.cross_align { let margin = entity.get_copy(margin()).unwrap_or_default(); @@ -222,12 +247,12 @@ impl Flow { let size = inner_rect.size().min(limits.max_size) - margin.size(); LayoutLimits { min_size: size * cross_axis, - max_size: size * cross_axis + axis_sizing, + max_size: axis_sizing + size * cross_axis, } } else { LayoutLimits { min_size: Vec2::ZERO, - max_size: available_size * cross_axis + axis_sizing, + max_size: axis_sizing + available_size * cross_axis, } }; @@ -289,7 +314,7 @@ impl Flow { world: &'a World, entity: &EntityRef, inner_rect: Rect, - ) -> (Rect, Rect, Edges, Vec<(EntityRef<'a>, SizeQuery)>) { + ) -> Row<'a> { let children = entity.get(children()).ok(); let children = children.as_ref().map(|v| v.as_slice()).unwrap_or_default(); @@ -335,11 +360,15 @@ impl Flow { assert_eq!(min_margin, preferred_margin); - ( - min_cursor.finish(), - preferred_cursor.finish(), - min_margin, + let preferred = preferred_cursor.finish(); + let min = min_cursor.finish(); + + Row { + min, + preferred, + margin: preferred_margin, + total_margin: preferred_cursor.total_margin, blocks, - ) + } } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index bbb4ce5..5e73d4b 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -15,13 +15,13 @@ pub use flow::{CrossAlign, Direction, Flow}; pub use stack::Stack; #[derive(Debug, Clone)] -pub struct SizeQuery { +pub struct Sizing { min: Rect, preferred: Rect, margin: Edges, } -pub fn query_size(world: &World, entity: &EntityRef, content_area: Rect) -> SizeQuery { +pub fn query_size(world: &World, entity: &EntityRef, content_area: Rect) -> Sizing { let margin = entity .get(components::margin()) .ok() @@ -41,20 +41,19 @@ pub fn query_size(world: &World, entity: &EntityRef, content_area: Rect) -> Size // For a given layout use the largest size that fits within the constraints and then // potentially shrink it down. - let (min, preferred, inner_margin, _) = - layout.query_size(world, entity, content_area.inset(&padding)); + let row = layout.query_size(world, entity, content_area.inset(&padding)); - SizeQuery { - min: min.pad(&padding), - preferred: preferred.pad(&padding), - margin: margin.max(inner_margin), + Sizing { + min: row.min.pad(&padding), + preferred: row.preferred.pad(&padding), + margin: row.margin.max(row.margin), } } // Stack else if let Ok(children) = entity.get(children()) { let query = Stack {}.query_size(world, &children, content_area.inset(&padding)); - SizeQuery { + Sizing { min: query.min.pad(&padding), preferred: query.preferred.pad(&padding), margin: query.margin.max(query.margin), @@ -67,7 +66,7 @@ pub fn query_size(world: &World, entity: &EntityRef, content_area: Rect) -> Size // Leaf - SizeQuery { + Sizing { min: Rect::from_size_pos(min_size, min_offset), preferred: Rect::from_size_pos(preferred_size, preferred_offset), margin, @@ -124,11 +123,11 @@ pub(crate) fn update_subtree( .unwrap_or_default(); // Flow - if let Ok(layout) = entity.get(flow()) { + if let Ok(flow) = entity.get(flow()) { // For a given layout use the largest size that fits within the constraints and then // potentially shrink it down. - let mut block = layout.apply( + let mut block = flow.apply( world, entity, content_area.inset(&padding), @@ -146,7 +145,15 @@ pub(crate) fn update_subtree( } // Stack else if let Ok(children) = entity.get(children()) { - let block = Stack {}.apply(world, &children, content_area.inset(&padding), limits); + let block = Stack {}.apply( + world, + &children, + content_area.inset(&padding), + LayoutLimits { + min_size: limits.min_size, + max_size: limits.max_size - padding.size(), + }, + ); Block { rect: block.rect.pad(&padding), diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 270be96..1230fbd 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -6,7 +6,7 @@ use crate::{ layout::query_size, }; -use super::{update_subtree, Block, LayoutLimits, SizeQuery}; +use super::{update_subtree, Block, LayoutLimits, Sizing}; // #[derive(Debug)] // struct StackCursor { @@ -128,7 +128,7 @@ impl Stack { world: &World, children: &[Entity], content_area: Rect, - ) -> SizeQuery { + ) -> Sizing { // Reset to local let inner_rect = Rect { min: Vec2::ZERO, @@ -156,7 +156,7 @@ impl Stack { tracing::warn!("margin discrepency: {:?}", min_margin - preferred_margin); } - SizeQuery { + Sizing { min: min_bounds.inner, preferred: preferred_bounds.inner, margin: min_margin.max(preferred_margin), diff --git a/src/unit.rs b/src/unit.rs index e9e1ccc..127f281 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -63,3 +63,17 @@ where } } } + +impl std::ops::Sub for Unit +where + T: ops::Sub + ops::Mul + Copy, +{ + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self { + px: self.px - rhs.px, + rel: self.rel - rhs.rel, + } + } +}