From 8865ddb7e20561628a3926e1c985b43a89d5f93e Mon Sep 17 00:00:00 2001 From: Freja Roberts Date: Fri, 15 Mar 2024 00:02:26 +0100 Subject: [PATCH] chore: move query args to struct --- violet-core/src/constraints.rs | 16 ++--- violet-core/src/layout/cache.rs | 6 +- violet-core/src/layout/flow.rs | 96 +++++++++++++++++++---------- violet-core/src/layout/mod.rs | 106 +++++++++++++------------------- violet-core/src/layout/stack.rs | 36 +++++------ violet-wgpu/src/text.rs | 27 ++++---- 6 files changed, 146 insertions(+), 141 deletions(-) diff --git a/violet-core/src/constraints.rs b/violet-core/src/constraints.rs index d48c845..2c7d96c 100644 --- a/violet-core/src/constraints.rs +++ b/violet-core/src/constraints.rs @@ -1,6 +1,6 @@ use glam::{vec2, BVec2, Vec2}; -use crate::layout::{Direction, LayoutLimits, SizeResolver, SizingHints}; +use crate::layout::{Direction, QueryArgs, SizeResolver, SizingHints}; pub struct FixedAreaConstraint { pub area: f32, @@ -24,16 +24,12 @@ impl SizeResolver for FixedAreaConstraint { // } // } - fn query( - &mut self, - _: &flax::EntityRef, - _content_area: Vec2, - limits: LayoutLimits, - squeeze: Direction, - ) -> (Vec2, Vec2, SizingHints) { - let size = (limits.max_size / self.unit_size).floor().max(Vec2::ONE); + fn query(&mut self, _: &flax::EntityRef, args: QueryArgs) -> (Vec2, Vec2, SizingHints) { + let size = (args.limits.max_size / self.unit_size) + .floor() + .max(Vec2::ONE); - let min = match squeeze { + let min = match args.direction { Direction::Horizontal => vec2((self.area / size.y).ceil(), size.y), Direction::Vertical => vec2(size.x, (self.area / size.x).ceil()), }; diff --git a/violet-core/src/layout/cache.rs b/violet-core/src/layout/cache.rs index 9d45c0a..5f6d10d 100644 --- a/violet-core/src/layout/cache.rs +++ b/violet-core/src/layout/cache.rs @@ -68,7 +68,7 @@ impl LayoutCache { pub(crate) fn insert_query_row(&mut self, value: CachedValue) { self.query_row = Some(value); if let Some(f) = self.on_invalidated.as_ref() { - f(LayoutUpdate::SizeQueryUpdate) + // f(LayoutUpdate::SizeQueryUpdate) } } @@ -83,8 +83,8 @@ impl LayoutCache { self.layout.as_ref() } - pub fn query(&self) -> &[Option>; 2] { - &self.query + pub(crate) fn get_query(&self, direction: Direction) -> Option<&CachedValue> { + self.query[direction as usize].as_ref() } } diff --git a/violet-core/src/layout/flow.rs b/violet-core/src/layout/flow.rs index e14e515..9b31204 100644 --- a/violet-core/src/layout/flow.rs +++ b/violet-core/src/layout/flow.rs @@ -14,7 +14,8 @@ use crate::{ }; use super::{ - cache::LayoutCache, resolve_pos, update_subtree, Block, Direction, LayoutLimits, Sizing, + cache::LayoutCache, resolve_pos, update_subtree, Block, Direction, LayoutLimits, QueryArgs, + Sizing, }; #[derive(Debug, Clone)] @@ -193,7 +194,16 @@ impl FlowLayout { // Query the minimum and preferred size of this flow layout, optimizing for minimum size in // the direction of this axis. - let row = self.query_row(world, cache, children, content_area, limits); + let row = self.query_row( + world, + cache, + children, + QueryArgs { + limits, + content_area: content_area.size(), + direction: self.direction, + }, + ); // tracing::info!(?row.margin, "row margins to be contained"); self.distribute_children(world, entity, &row, content_area, limits, preferred_size) @@ -405,9 +415,7 @@ impl FlowLayout { &self, world: &World, row: &Row, - content_area: Rect, - limits: LayoutLimits, - direction: Direction, + args: QueryArgs, preferred_size: Vec2, ) -> Sizing { puffin::profile_function!(); @@ -441,7 +449,7 @@ impl FlowLayout { // Clipped maximum that we remap to let target_inner_size = distribute_size - .min(limits.max_size.dot(axis) - minimum_inner_size) + .min(args.limits.max_size.dot(axis) - minimum_inner_size) .max(0.0); tracing::debug!( @@ -452,12 +460,10 @@ impl FlowLayout { "distribute" ); - let available_size = limits.max_size; + let available_size = args.limits.max_size; - let mut min_cursor = - MarginCursor::new(content_area.min, axis, cross_axis, self.contain_margins); - let mut cursor = - MarginCursor::new(content_area.min, axis, cross_axis, self.contain_margins); + let mut min_cursor = MarginCursor::new(Vec2::ZERO, axis, cross_axis, self.contain_margins); + let mut cursor = MarginCursor::new(Vec2::ZERO, axis, cross_axis, self.contain_margins); let mut sum = 0.0; @@ -537,7 +543,12 @@ impl FlowLayout { // NOTE: optimize for the minimum size in the query direction, not the // direction of the flow - let sizing = query_size(world, &entity, content_area.size(), child_limits, direction); + let sizing = query_size(world, &entity, QueryArgs { + limits: child_limits, + content_area: args.content_area, + // Use the query direction, not the flow direction + direction: args.direction, + }); hints = hints.combine(sizing.hints); @@ -557,8 +568,8 @@ impl FlowLayout { .to_edges(cursor.main_margin, cursor.cross_margin, self.reverse); Sizing { - min: min_rect.max_size(limits.min_size), - preferred: rect.max_size(limits.min_size), + min: min_rect.max_size(args.limits.min_size), + preferred: rect.max_size(args.limits.min_size), margin, hints, } @@ -569,12 +580,11 @@ impl FlowLayout { world: &World, cache: &mut LayoutCache, children: &[Entity], - content_area: Rect, - limits: LayoutLimits, + args: QueryArgs, ) -> Row { puffin::profile_function!(); if let Some(value) = cache.query_row.as_ref() { - if validate_cached_row(value, limits, content_area.size()) { + if validate_cached_row(value, args.limits, args.content_area) { return value.value.clone(); } } @@ -601,7 +611,19 @@ impl FlowLayout { let entity = world.entity(child).expect("Invalid child"); let child_margin = if self.contain_margins { - query_size(world, &entity, content_area.size(), limits, self.direction).margin + query_size( + world, + &entity, + QueryArgs { + limits: LayoutLimits { + min_size: Vec2::ZERO, + max_size: args.limits.max_size, + }, + content_area: args.content_area, + direction: self.direction, + }, + ) + .margin } else { Edges::ZERO }; @@ -609,13 +631,14 @@ impl FlowLayout { let sizing = query_size( world, &entity, - content_area.size(), - LayoutLimits { - min_size: Vec2::ZERO, - // max_size: limits.max_size, - max_size: limits.max_size - child_margin.size(), + QueryArgs { + limits: LayoutLimits { + min_size: Vec2::ZERO, + max_size: args.limits.max_size - child_margin.size(), + }, + content_area: args.content_area, + direction: self.direction, }, - self.direction, ); hints = hints.combine(sizing.hints); @@ -649,7 +672,11 @@ impl FlowLayout { hints, }; - cache.insert_query_row(CachedValue::new(limits, content_area.size(), row.clone())); + cache.insert_query_row(CachedValue::new( + args.limits, + args.content_area, + row.clone(), + )); row } @@ -658,12 +685,10 @@ impl FlowLayout { world: &World, cache: &mut LayoutCache, children: &[Entity], - content_area: Rect, - limits: LayoutLimits, - direction: Direction, + args: QueryArgs, preferred_size: Vec2, ) -> Sizing { - puffin::profile_function!(format!("{direction:?}")); + puffin::profile_function!(format!("{args:?}")); // We want to query the min/preferred size in the direction orthogonal to the flows // layout @@ -695,10 +720,17 @@ impl FlowLayout { // exist a better solution where some widgets may get slightly more space and still // fall within the max height. If anybody comes across a non-iterative solution for // this, be sure to let me know :) - let row = self.query_row(world, cache, children, content_area, limits); + let row = self.query_row( + world, + cache, + children, + QueryArgs { + direction: self.direction, + ..args + }, + ); - let sizing = - self.distribute_query(world, &row, content_area, limits, direction, preferred_size); + let sizing = self.distribute_query(world, &row, args, preferred_size); tracing::debug!(?self.direction, ?sizing, "query"); sizing } diff --git a/violet-core/src/layout/mod.rs b/violet-core/src/layout/mod.rs index d1d567d..8d1b724 100644 --- a/violet-core/src/layout/mod.rs +++ b/violet-core/src/layout/mod.rs @@ -12,7 +12,7 @@ use crate::{ self, anchor, aspect_ratio, children, layout, max_size, min_size, offset, padding, size, size_resolver, }, - layout::cache::{validate_cached_layout, validate_cached_query, CachedValue, LAYOUT_TOLERANCE}, + layout::cache::{validate_cached_layout, validate_cached_query, CachedValue}, Edges, Rect, }; @@ -24,8 +24,8 @@ use self::cache::{layout_cache, LayoutCache}; #[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd, Hash, Ord, Eq)] pub enum Direction { #[default] - Horizontal, - Vertical, + Horizontal = 0, + Vertical = 1, } impl Direction { @@ -98,34 +98,21 @@ impl Layout { world: &World, cache: &mut LayoutCache, children: &[Entity], - inner_rect: Rect, - limits: LayoutLimits, - squeeze: Direction, + args: QueryArgs, preferred_size: Vec2, ) -> Sizing { match self { - Layout::Stack(v) => { - v.query_size(world, children, inner_rect, limits, squeeze, preferred_size) - } - Layout::Flow(v) => v.query_size( - world, - cache, - children, - inner_rect, - limits, - squeeze, - preferred_size, - ), + Layout::Stack(v) => v.query_size(world, children, args, preferred_size), + Layout::Flow(v) => v.query_size(world, cache, children, args, preferred_size), } } } -#[derive(Debug, Clone)] -pub(crate) struct QueryArgs { - limits: LayoutLimits, - content_area: Vec2, - distribute: bool, - direction: Direction, +#[derive(Debug, Clone, Copy)] +pub struct QueryArgs { + pub limits: LayoutLimits, + pub content_area: Vec2, + pub direction: Direction, } #[derive(Debug, Clone, Copy)] @@ -274,18 +261,12 @@ fn validate_block(entity: &EntityRef, block: &Block, limits: LayoutLimits) { } } -pub(crate) fn query_size( - world: &World, - entity: &EntityRef, - content_area: Vec2, - mut limits: LayoutLimits, - direction: Direction, -) -> Sizing { - puffin::profile_function!(format!("{entity}")); +pub(crate) fn query_size(world: &World, entity: &EntityRef, mut args: QueryArgs) -> Sizing { + puffin::profile_function!(format!("{entity} {args:?}")); // assert!(limits.min_size.x <= limits.max_size.x); // assert!(limits.min_size.y <= limits.max_size.y); let _span = - tracing::debug_span!("query_size", name=entity.name().as_deref(), ?limits, %content_area) + tracing::debug_span!("query_size", name=entity.name().as_deref(), ?args.limits, %args.content_area) .entered(); // tracing::info!(name=entity.name().as_deref(), ?limits, %content_area, ?direction, "query_size"); @@ -308,22 +289,24 @@ pub(crate) fn query_size( let fixed_boundary_size = min_size.is_relative() | max_size.map(|v| v.is_relative()).unwrap_or(BVec2::FALSE); - let min_size = min_size.resolve(content_area); - let max_size = max_size.map(|v| v.resolve(content_area)); - limits.min_size = limits.min_size.max(min_size); + let min_size = min_size.resolve(args.content_area); + let max_size = max_size.map(|v| v.resolve(args.content_area)); + args.limits.min_size = args.limits.min_size.max(min_size); - let external_max_size = limits.max_size; + let external_max_size = args.limits.max_size; + + let external_max_size = args.limits.max_size; // Minimum size is *always* respected, even if that entails overflowing - limits.max_size = limits.max_size.max(limits.min_size); + args.limits.max_size = args.limits.max_size.max(args.limits.min_size); if let Some(max_size) = max_size { - limits.max_size = limits.max_size.min(max_size); + args.limits.max_size = args.limits.max_size.min(max_size); } // Check if cache is valid - if let Some(cache) = &cache.query[direction as usize] { - if validate_cached_query(cache, limits, content_area) { + if let Some(cache) = cache.get_query(args.direction) { + if validate_cached_query(cache, args.limits, args.content_area) { return cache.value; } } @@ -335,7 +318,7 @@ pub(crate) fn query_size( let children = children.map(Vec::as_slice).unwrap_or(&[]); - let resolved_size = size.resolve(content_area); + let resolved_size = size.resolve(args.content_area); let hints = SizingHints { relative_size: fixed_boundary_size | size.is_relative(), can_grow: BVec2::new( @@ -349,7 +332,7 @@ pub(crate) fn query_size( // } // Clamp max size here since we ensure it is > min_size - let resolved_size = resolved_size.clamp(limits.min_size, limits.max_size); + let resolved_size = resolved_size.clamp(args.limits.min_size, args.limits.max_size); // Flow let mut sizing = if let Some(layout) = layout { @@ -357,12 +340,14 @@ pub(crate) fn query_size( world, cache, children, - Rect::from_size(content_area).inset(&padding), - LayoutLimits { - min_size: (limits.min_size - padding.size()).max(Vec2::ZERO), - max_size: (limits.max_size - padding.size()).max(Vec2::ZERO), + QueryArgs { + limits: LayoutLimits { + min_size: (args.limits.min_size - padding.size()).max(Vec2::ZERO), + max_size: (args.limits.max_size - padding.size()).max(Vec2::ZERO), + }, + content_area: args.content_area - padding.size(), + ..args }, - direction, resolved_size - padding.size(), ); @@ -374,18 +359,18 @@ pub(crate) fn query_size( } } else if let [child] = children { let child = world.entity(*child).unwrap(); - query_size(world, &child, content_area, limits, direction) + query_size(world, &child, args) } else { let (instrisic_min_size, intrinsic_size, intrinsic_hints) = size_resolver - .map(|v| v.query(entity, content_area, limits, direction)) + .map(|v| v.query(entity, args)) .unwrap_or((Vec2::ZERO, Vec2::ZERO, SizingHints::default())); // If intrinsic_min_size > max_size we overflow, but respect the minimum size nonetheless - limits.min_size = limits.min_size.max(instrisic_min_size); + args.limits.min_size = args.limits.min_size.max(instrisic_min_size); let size = intrinsic_size.max(resolved_size); - let min_size = instrisic_min_size.max(limits.min_size); + let min_size = instrisic_min_size.max(args.limits.min_size); Sizing { min: Rect::from_size(min_size), @@ -402,8 +387,8 @@ pub(crate) fn query_size( .preferred .with_size(constraints.apply(sizing.preferred.size())); - let min_offset = resolve_pos(entity, content_area, sizing.min.size()); - let offset = resolve_pos(entity, content_area, sizing.preferred.size()); + let min_offset = resolve_pos(entity, args.content_area, sizing.min.size()); + let offset = resolve_pos(entity, args.content_area, sizing.preferred.size()); sizing.min = sizing.min.translate(min_offset); sizing.preferred = sizing.preferred.translate(offset); @@ -421,7 +406,10 @@ pub(crate) fn query_size( // validate_sizing(entity, &sizing, limits); - cache.insert_query(direction, CachedValue::new(limits, content_area, sizing)); + cache.insert_query( + args.direction, + CachedValue::new(args.limits, args.content_area, sizing), + ); cache.relative_size = sizing.hints.relative_size; sizing @@ -595,13 +583,7 @@ pub trait SizeResolver: Send + Sync { /// /// Returns a minimum possible size optimized for the given direction, and the preferred /// size - fn query( - &mut self, - entity: &EntityRef, - content_area: Vec2, - limits: LayoutLimits, - direction: Direction, - ) -> (Vec2, Vec2, SizingHints); + fn query(&mut self, entity: &EntityRef, args: QueryArgs) -> (Vec2, Vec2, SizingHints); /// Uses the current constraints to determine the size of the widget fn apply( diff --git a/violet-core/src/layout/stack.rs b/violet-core/src/layout/stack.rs index 6a240c5..3fc8c74 100644 --- a/violet-core/src/layout/stack.rs +++ b/violet-core/src/layout/stack.rs @@ -8,7 +8,9 @@ use crate::{ Edges, Rect, }; -use super::{resolve_pos, update_subtree, Alignment, Block, Direction, LayoutLimits, Sizing}; +use super::{ + resolve_pos, update_subtree, Alignment, Block, Direction, LayoutLimits, QueryArgs, Sizing, +}; #[derive(Debug)] pub struct StackableBounds { @@ -173,13 +175,12 @@ impl StackLayout { &self, world: &World, children: &[Entity], - content_area: Rect, - limits: LayoutLimits, - squeeze: Direction, + args: QueryArgs, preferred_size: Vec2, ) -> Sizing { puffin::profile_function!(); - let min_rect = Rect::from_size_pos(limits.min_size, content_area.min); + let min_rect = Rect::from_size(args.limits.min_size); + let mut min_bounds = StackableBounds::from_rect(min_rect); let mut preferred_bounds = StackableBounds::from_rect(min_rect); @@ -191,32 +192,29 @@ impl StackLayout { let sizing = query_size( world, &entity, - content_area.size(), - LayoutLimits { - min_size: Vec2::ZERO, - max_size: limits.max_size, + QueryArgs { + limits: LayoutLimits { + min_size: Vec2::ZERO, + max_size: args.limits.max_size, + }, + content_area: args.content_area, + direction: args.direction, }, - squeeze, ); hints = hints.combine(sizing.hints); - min_bounds = min_bounds.merge(&StackableBounds::new( - sizing.min.translate(content_area.min), - sizing.margin, - )); + min_bounds = min_bounds.merge(&StackableBounds::new(sizing.min, sizing.margin)); - preferred_bounds = preferred_bounds.merge(&StackableBounds::new( - sizing.preferred.translate(content_area.min), - sizing.margin, - )); + preferred_bounds = + preferred_bounds.merge(&StackableBounds::new(sizing.preferred, sizing.margin)); } let min_margin = min_bounds.margin(); let preferred_margin = preferred_bounds.margin(); Sizing { - min: min_bounds.inner.max_size(limits.min_size), + min: min_bounds.inner.max_size(args.limits.min_size), // .clamp_size(limits.min_size, limits.max_size), preferred: preferred_bounds.inner.max_size(preferred_size), // .clamp_size(limits.min_size, limits.max_size), diff --git a/violet-wgpu/src/text.rs b/violet-wgpu/src/text.rs index 03e2bf8..9cde39a 100644 --- a/violet-wgpu/src/text.rs +++ b/violet-wgpu/src/text.rs @@ -10,7 +10,7 @@ use parking_lot::Mutex; use violet_core::{ components::font_size, - layout::{Direction, LayoutLimits, SizeResolver, SizingHints}, + layout::{Direction, LayoutLimits, QueryArgs, SizeResolver, SizingHints}, text::{LayoutGlyphs, LayoutLineGlyphs, TextSegment}, Rect, }; @@ -54,15 +54,9 @@ pub struct TextSizeResolver { } impl SizeResolver for TextSizeResolver { - fn query( - &mut self, - entity: &flax::EntityRef, - _content_area: Vec2, - limits: LayoutLimits, - direction: Direction, - ) -> (Vec2, Vec2, SizingHints) { + fn query(&mut self, entity: &flax::EntityRef, args: QueryArgs) -> (Vec2, Vec2, SizingHints) { puffin::profile_scope!("TextSizeResolver::query"); - let _span = tracing::debug_span!("TextSizeResolver::query", ?direction).entered(); + let _span = tracing::debug_span!("TextSizeResolver::query", ?args.direction).entered(); let query = (text_buffer_state().as_mut(), font_size()); @@ -78,9 +72,12 @@ impl SizeResolver for TextSizeResolver { state, text_system, font_size, - match direction { - Direction::Horizontal => vec2(1.0, limits.max_size.y.max(line_height)), - Direction::Vertical => vec2(limits.max_size.x, limits.max_size.y.max(line_height)), + match args.direction { + Direction::Horizontal => vec2(1.0, args.limits.max_size.y.max(line_height)), + Direction::Vertical => vec2( + args.limits.max_size.x, + args.limits.max_size.y.max(line_height), + ), }, ); @@ -88,12 +85,12 @@ impl SizeResolver for TextSizeResolver { state, text_system, font_size, - limits.max_size.max(vec2(1.0, line_height)), + args.limits.max_size.max(vec2(1.0, line_height)), ); // + vec2(5.0, 5.0); - if min.dot(direction.to_axis()) > preferred.dot(direction.to_axis()) { - tracing::error!(%entity, text=?state.text(), %min, %preferred, ?direction, %limits.max_size, "Text wrapping failed"); + if min.dot(args.direction.to_axis()) > preferred.dot(args.direction.to_axis()) { + tracing::error!(%entity, text=?state.text(), %min, %preferred, ?args.direction, %args.limits.max_size, "Text wrapping failed"); } ( min,