From ab8906551d461de239ad383d9501622022c464e6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 26 Jul 2023 22:37:59 -0600 Subject: [PATCH] WIP --- crates/gpui/src/app.rs | 2 +- crates/gpui/src/app/window.rs | 30 +- crates/gpui/src/elements/node.rs | 580 +++++++++++++++++++++---------- 3 files changed, 423 insertions(+), 189 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 867e0162f88942452dc793be42e2d398f52a9881..14419e504f2aca3ab692fd5ff484bde5d371291e 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3339,7 +3339,7 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { self.element_state::(element_id, T::default()) } - pub fn pixels_per_rem(&self) -> f32 { + pub fn rem_pixels(&self) -> f32 { 16. } } diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 902033b8f01e1b9266639b0cc01f34b0bdcf0dd4..5dd3e97e19abf3303854c0b1212c25b432d7697e 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -1,5 +1,5 @@ use crate::{ - elements::AnyRootElement, + elements::{node::Axis2d, AnyRootElement}, geometry::rect::RectF, json::ToJson, keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult}, @@ -1248,16 +1248,38 @@ impl Column for Axis { } pub trait Vector2FExt { + fn infinity() -> Self; fn along(self, axis: Axis) -> f32; + fn get(self, axis: Axis2d) -> f32; + fn set(&mut self, axis: Axis2d, value: f32) -> Self; } impl Vector2FExt for Vector2F { + fn infinity() -> Self { + Self::new(f32::INFINITY, f32::INFINITY) + } + fn along(self, axis: Axis) -> f32 { match axis { Axis::Horizontal => self.x(), Axis::Vertical => self.y(), } } + + fn get(self, axis: Axis2d) -> f32 { + match axis { + Axis2d::X => self.x(), + Axis2d::Y => self.y(), + } + } + + fn set(&mut self, axis: Axis2d, value: f32) -> Self { + match axis { + Axis2d::X => self.set_x(value), + Axis2d::Y => self.set_y(value), + } + *self + } } pub trait RectFExt { @@ -1290,6 +1312,12 @@ impl SizeConstraint { max: size, } } + pub fn loose(max: Vector2F) -> Self { + Self { + min: Vector2F::zero(), + max, + } + } pub fn strict_along(axis: Axis, max: f32) -> Self { match axis { diff --git a/crates/gpui/src/elements/node.rs b/crates/gpui/src/elements/node.rs index c324a4d55b5c178cd181de951b7eb2e56be1058e..00753aa148c2170ba6e3396dcb7d818714cfe20b 100644 --- a/crates/gpui/src/elements/node.rs +++ b/crates/gpui/src/elements/node.rs @@ -11,17 +11,23 @@ use crate::{ serde_json::Value, text_layout::{Line, ShapedBoundary}, AnyElement, AppContext, Element, LayoutContext, PaintContext, Quad, SceneBuilder, - SizeConstraint, View, ViewContext, + SizeConstraint, Vector2FExt, View, ViewContext, }; use derive_more::Add; use length::{Length, Rems}; use log::warn; use optional_struct::*; -use std::{any::Any, borrow::Cow, f32, ops::Range, sync::Arc}; +use std::{ + any::Any, + borrow::Cow, + f32, + ops::{Add, Range}, + sync::Arc, +}; pub struct Node { style: NodeStyle, - content: Vec>, + children: Vec>, } pub fn node(child: impl Element) -> Node { @@ -38,7 +44,7 @@ pub fn row() -> Node { axis: Axis3d::X, ..Default::default() }, - content: Default::default(), + children: Default::default(), } } @@ -48,7 +54,7 @@ pub fn stack() -> Node { axis: Axis3d::Z, ..Default::default() }, - content: Default::default(), + children: Default::default(), } } @@ -56,14 +62,14 @@ impl Default for Node { fn default() -> Self { Self { style: Default::default(), - content: Default::default(), + children: Default::default(), } } } impl Node { pub fn child(mut self, child: impl Element) -> Self { - self.content.push(child.into_any()); + self.children.push(child.into_any()); self } @@ -72,18 +78,18 @@ impl Node { I: IntoIterator, E: Element, { - self.content + self.children .extend(children.into_iter().map(|child| child.into_any())); self } pub fn width(mut self, width: impl Into) -> Self { - self.style.width = width.into(); + self.style.size.width = width.into(); self } pub fn height(mut self, height: impl Into) -> Self { - self.style.height = height.into(); + self.style.size.height = height.into(); self } @@ -104,7 +110,7 @@ impl Node { ) -> Self { let top_bottom = top_bottom.into(); let left_right = left_right.into(); - self.style.margin = Edges { + self.style.margins = Edges { top: top_bottom.top, bottom: top_bottom.bottom, left: left_right.left, @@ -114,115 +120,122 @@ impl Node { } pub fn margin_top(mut self, top: Length) -> Self { - self.style.margin.top = top; + self.style.margins.top = top; self } pub fn margin_bottom(mut self, bottom: Length) -> Self { - self.style.margin.bottom = bottom; + self.style.margins.bottom = bottom; self } pub fn margin_left(mut self, left: impl Into) -> Self { - self.style.margin.left = left.into(); + self.style.margins.left = left.into(); self } pub fn margin_right(mut self, right: impl Into) -> Self { - self.style.margin.right = right.into(); + self.style.margins.right = right.into(); self } - fn layout_2d_children( + fn layout_xy( &mut self, axis: Axis2d, - size: Vector2F, + max_size: Vector2F, + rem_length: f32, + computed_margins: &mut Edges, + computed_padding: &mut Edges, view: &mut V, cx: &mut LayoutContext, ) -> Vector2F { - let mut total_flex: Option = None; - let mut total_size = 0.0; - let mut cross_axis_max: f32 = 0.0; - - // First pass: Layout non-flex children only - for child in &mut self.content { - let child_flex = child.metadata::().and_then(|style| match axis { - Axis2d::X => style.width.flex(), - Axis2d::Y => style.height.flex(), - }); - - if let Some(child_flex) = child_flex { - *total_flex.get_or_insert(0.) += child_flex; + *computed_margins = self.style.margins.fixed_pixels(rem_length); + *computed_padding = self.style.padding.fixed_pixels(rem_length); + + let padded_max = + max_size - computed_margins.size() - self.style.borders.width - computed_padding.size(); + let mut remaining_length = padded_max.get(axis); + + // Pass 1: Total up flex units and layout inflexible children. + // + // Consume the remaining length as we layout inflexible children, so that any + // remaining length can be distributed among flexible children in the next pass. + let mut remaining_flex: f32 = 0.; + let mut cross_axis_max: f32 = 0.; + let cross_axis = axis.rotate(); + + // Fixed children are unconstrained along the primary axis, and constrained to + // the padded max size along the cross axis. + let mut child_constraint = + SizeConstraint::loose(Vector2F::infinity().set(cross_axis, padded_max.get(cross_axis))); + + for child in &mut self.children { + if let Some(child_flex) = child + .metadata::() + .and_then(|style| style.flex(axis)) + { + remaining_flex += child_flex; } else { - match axis { - Axis2d::X => { - let child_constraint = - SizeConstraint::new(Vector2F::zero(), vec2f(f32::INFINITY, size.y())); - let child_size = child.layout(child_constraint, view, cx); - cross_axis_max = cross_axis_max.max(child_size.y()); - total_size += child_size.x(); - } - Axis2d::Y => { - let child_constraint = - SizeConstraint::new(Vector2F::zero(), vec2f(size.x(), f32::INFINITY)); - let child_size = child.layout(child_constraint, view, cx); - cross_axis_max = cross_axis_max.max(child_size.x()); - total_size += child_size.y(); - } - } + let child_size = child.layout(child_constraint, view, cx); + cross_axis_max = cross_axis_max.max(child_size.get(cross_axis)); + remaining_length -= child_size.get(axis); } } - let remaining_space = match axis { - Axis2d::X => size.x() - total_size, - Axis2d::Y => size.y() - total_size, - }; - - // Second pass: Layout flexible children - if let Some(total_flex) = total_flex { - if total_flex > 0. { - let space_per_flex = remaining_space.max(0.) / total_flex; - - for child in &mut self.content { - let child_flex = child.metadata::().and_then(|style| match axis { - Axis2d::X => style.width.flex(), - Axis2d::Y => style.height.flex(), - }); - if let Some(child_flex) = child_flex { - let child_max = space_per_flex * child_flex; - let mut child_constraint = SizeConstraint::new(Vector2F::zero(), size); - match axis { - Axis2d::X => { - child_constraint.min.set_x(0.0); - child_constraint.max.set_x(child_max); - } - Axis2d::Y => { - child_constraint.min.set_y(0.0); - child_constraint.max.set_y(child_max); - } - } + // Pass 2: Allocate the remaining space among flexible lengths along the primary axis. + if remaining_flex > 0. { + // Add flex pixels from margin and padding. + *computed_margins.start_mut(axis) += self.style.margins.start(axis).flex_pixels( + rem_length, + &mut remaining_flex, + &mut remaining_length, + ); + *computed_padding.start_mut(axis) += self.style.padding.start(axis).flex_pixels( + rem_length, + &mut remaining_flex, + &mut remaining_length, + ); - let child_size = child.layout(child_constraint, view, cx); - cross_axis_max = match axis { - Axis2d::X => { - total_size += child_size.x(); - cross_axis_max.max(child_size.y()) - } - Axis2d::Y => { - total_size += child_size.y(); - cross_axis_max.max(child_size.x()) - } - }; - } + // Lay out the flexible children + let mut child_max = padded_max; + for child in &mut self.children { + if let Some(child_flex) = child + .metadata::() + .and_then(|style| style.flex(axis)) + { + child_max.set(axis, child_flex / remaining_flex * remaining_length); + let child_size = child.layout(SizeConstraint::loose(child_max), view, cx); + + remaining_flex -= child_flex; + remaining_length -= child_size.get(axis); + cross_axis_max = child_size.get(cross_axis).max(cross_axis_max); } } + + // Add flex pixels from margin and padding. + *computed_margins.end_mut(axis) += self.style.margins.end(axis).flex_pixels( + rem_length, + &mut remaining_flex, + &mut remaining_length, + ); + *computed_padding.end_mut(axis) += self.style.padding.end(axis).flex_pixels( + rem_length, + &mut remaining_flex, + &mut remaining_length, + ); } - let size = match axis { - Axis2d::X => vec2f(total_size, cross_axis_max), - Axis2d::Y => vec2f(cross_axis_max, total_size), + let width = match self.style.size.width { + Length::Hug => todo!(), + Length::Fixed(_) => todo!(), + Length::Auto { flex, min, max } => todo!(), }; - size + + let length = max_size.get(axis) - remaining_length; + match axis { + Axis2d::X => vec2f(length, cross_axis_max), + Axis2d::Y => vec2f(cross_axis_max, length), + } } fn paint_2d_children( @@ -231,7 +244,7 @@ impl Node { axis: Axis2d, bounds: RectF, visible_bounds: RectF, - size_of_children: &mut Vector2F, + layout: &mut NodeLayout, view: &mut V, cx: &mut ViewContext, ) { @@ -248,13 +261,13 @@ impl Node { align_child( &mut child_origin, parent_size, - *size_of_children, + layout.content_size, self.style.align.0, align_horizontally, align_vertically, ); - for child in &mut self.content { + for child in &mut self.children { // Align each child along the cross axis align_horizontally = !align_horizontally; align_vertically = !align_vertically; @@ -295,16 +308,13 @@ impl Node { // } fn inset_size(&self, rem_size: f32) -> Vector2F { - self.padding_size(rem_size) + self.border_size() + self.margin_size(rem_size) + todo!() + // self.padding_size(rem_size) + self.border_size() + self.margin_size(rem_size) } - fn margin_size(&self, rem_size: f32) -> Vector2F { - // We need to account for auto margins - todo!() - // vec2f( - // (self.style.margin.left + self.style.margin.right).to_pixels(rem_size), - // (self.style.margin.top + self.style.margin.bottom).to_pixels(rem_size), - // ) + // + fn margin_fixed_size(&self, rem_size: f32) -> Vector2F { + self.style.margins.fixed().to_pixels(rem_size) } fn padding_size(&self, rem_size: f32) -> Vector2F { @@ -318,19 +328,19 @@ impl Node { fn border_size(&self) -> Vector2F { let mut x = 0.0; - if self.style.border.left { - x += self.style.border.width; + if self.style.borders.left { + x += self.style.borders.width; } - if self.style.border.right { - x += self.style.border.width; + if self.style.borders.right { + x += self.style.borders.width; } let mut y = 0.0; - if self.style.border.top { - y += self.style.border.width; + if self.style.borders.top { + y += self.style.borders.width; } - if self.style.border.bottom { - y += self.style.border.width; + if self.style.borders.bottom { + y += self.style.borders.width; } vec2f(x, y) @@ -338,7 +348,7 @@ impl Node { } impl Element for Node { - type LayoutState = Vector2F; // Content size + type LayoutState = NodeLayout; type PaintState = (); fn layout( @@ -347,48 +357,23 @@ impl Element for Node { view: &mut V, cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { - let mut size = Vector2F::zero(); - let rem_size = cx.pixels_per_rem(); - let margin_size = self.margin_size(rem_size); - match self.style.width { - Length::Hug => size.set_x(f32::INFINITY), - Length::Fixed(width) => size.set_x(width.to_pixels(rem_size) + margin_size.x()), - Length::Auto { min, max, .. } => size.set_x(constraint.max.x().max(min).min(max)), - } - match self.style.height { - Length::Hug => size.set_y(f32::INFINITY), - Length::Fixed(height) => size.set_y(height.to_pixels(rem_size) + margin_size.y()), - Length::Auto { min, max, .. } => size.set_y(constraint.max.y().max(min).min(max)), - } - - // Impose horizontal constraints - if constraint.min.x().is_finite() { - size.set_x(size.x().max(constraint.min.x())); - } - size.set_x(size.x().min(constraint.max.x())); - - // Impose vertical constraints - if constraint.min.y().is_finite() { - size.set_y(size.y().max(constraint.min.y())); - } - size.set_y(size.y().min(constraint.max.y())); - - let inset_size = self.inset_size(rem_size); - let inner_size = size - inset_size; - let size_of_children = match self.style.axis { - Axis3d::X => self.layout_2d_children(Axis2d::X, inner_size, view, cx), - Axis3d::Y => self.layout_2d_children(Axis2d::Y, inner_size, view, cx), - Axis3d::Z => todo!(), // self.layout_stacked_children(inner_constraint, view, cx), + let mut layout = NodeLayout::default(); + + let size = if let Some(axis) = self.style.axis.to_2d() { + self.layout_xy( + axis, + constraint.max, + cx.rem_pixels(), + &mut layout.margins, + &mut layout.padding, + view, + cx, + ) + } else { + todo!() }; - if matches!(self.style.width, Length::Hug) { - size.set_x(size_of_children.x() + inset_size.x()); - } - if matches!(self.style.height, Length::Hug) { - size.set_y(size_of_children.y() + inset_size.y()); - } - - (size, size_of_children) + (size, layout); } fn paint( @@ -396,15 +381,39 @@ impl Element for Node { scene: &mut SceneBuilder, bounds: RectF, visible_bounds: RectF, - size_of_children: &mut Vector2F, + layout: &mut NodeLayout, view: &mut V, cx: &mut PaintContext, ) -> Self::PaintState { - let rem_size = cx.pixels_per_rem(); - let margin: Edges = todo!(); // &self.style.margin.to_pixels(rem_size); + let rem_pixels = cx.rem_pixels(); + // let margin: Edges = todo!(); // &self.style.margin.to_pixels(rem_size); + // + + let size = bounds.size(); + let mut remaining_flex = layout.flex_size; + let mut fixed_size = layout.fixed_size; + + // let margin_left = self.style.margin.left.to_pixels(rem_pixels, size.x() - fixed_size.x() / layout.); + // fixed_size += + // let mut origin = bounds.origin(); + // origin.set_x( + // origin.x() + // , + // Length::Hug => 0., + // Length::Fixed(rems) => rems.to_pixels(rem_pixels), + // Length::Auto { flex, min, max } => { + // flex * (size.x() - fixed_size.x()) / layout.flex_size.x() + // } + // }, + // ); + + let mut low_right = bounds.lower_right(); + + let mut remaining_fixed = bounds.size() - layout.fixed_size; + let mut remaining_flex = layout.flex_size; // Account for margins - let content_bounds = RectF::from_points( + let margin_bounds = RectF::from_points( bounds.origin() + vec2f(margin.left, margin.top), bounds.lower_right() - vec2f(margin.right, margin.bottom), ); @@ -412,7 +421,7 @@ impl Element for Node { // Paint drop shadow for shadow in &self.style.shadows { scene.push_shadow(scene::Shadow { - bounds: content_bounds + shadow.offset, + bounds: margin_bounds + shadow.offset, corner_radius: self.style.corner_radius, sigma: shadow.blur, color: shadow.color, @@ -432,29 +441,29 @@ impl Element for Node { // Render the background and/or the border (if it not an overlay border). let Fill::Color(fill_color) = self.style.fill; let is_fill_visible = !fill_color.is_fully_transparent(); - if is_fill_visible || self.style.border.is_visible() { + if is_fill_visible || self.style.borders.is_visible() { scene.push_quad(Quad { - bounds: content_bounds, + bounds: margin_bounds, background: is_fill_visible.then_some(fill_color), border: scene::Border { - width: self.style.border.width, - color: self.style.border.color, + width: self.style.borders.width, + color: self.style.borders.color, overlay: false, - top: self.style.border.top, - right: self.style.border.right, - bottom: self.style.border.bottom, - left: self.style.border.left, + top: self.style.borders.top, + right: self.style.borders.right, + bottom: self.style.borders.bottom, + left: self.style.borders.left, }, corner_radius: self.style.corner_radius, }); } - if !self.content.is_empty() { + if !self.children.is_empty() { // Account for padding first. let padding: Edges = todo!(); // &self.style.padding.to_pixels(rem_size); let padded_bounds = RectF::from_points( - content_bounds.origin() + vec2f(padding.left, padding.top), - content_bounds.lower_right() - vec2f(padding.right, padding.top), + margin_bounds.origin() + vec2f(padding.left, padding.top), + margin_bounds.lower_right() - vec2f(padding.right, padding.top), ); match self.style.axis { @@ -463,7 +472,7 @@ impl Element for Node { Axis2d::X, padded_bounds, visible_bounds, - size_of_children, + layout, view, cx, ), @@ -472,7 +481,7 @@ impl Element for Node { Axis2d::Y, padded_bounds, visible_bounds, - size_of_children, + layout, view, cx, ), @@ -491,7 +500,7 @@ impl Element for Node { view: &V, cx: &ViewContext, ) -> Option { - self.content + self.children .iter() .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx)) } @@ -598,18 +607,40 @@ pub struct NodeStyle { gap_x: Gap, gap_y: Gap, - width: Length, - height: Length, - margin: Edges, + size: Size, + margins: Edges, padding: Edges, text: OptionalTextStyle, opacity: f32, fill: Fill, - border: Border, + borders: Border, corner_radius: f32, // corner radius matches swift! shadows: Vec, } +impl NodeStyle { + fn flex(&self, axis: Axis2d) -> Option { + let mut sum = None; + match axis { + Axis2d::X => { + sum = optional_add(sum, self.margins.left.flex()); + sum = optional_add(sum, self.padding.left.flex()); + sum = optional_add(sum, self.size.width.flex()); + sum = optional_add(sum, self.padding.right.flex()); + sum = optional_add(sum, self.margins.right.flex()); + } + Axis2d::Y => { + sum = optional_add(sum, self.margins.top.flex()); + sum = optional_add(sum, self.padding.top.flex()); + sum = optional_add(sum, self.size.height.flex()); + sum = optional_add(sum, self.padding.bottom.flex()); + sum = optional_add(sum, self.margins.bottom.flex()); + } + } + sum + } +} + #[optional_struct] struct TextStyle { size: Rems, @@ -618,12 +649,44 @@ struct TextStyle { style: FontStyle, } -#[derive(Add)] +#[derive(Add, Default, Clone)] struct Size { width: T, height: T, } +impl> Size> { + fn add_assign_optional(&mut self, rhs: Size>) { + self.width = optional_add(self.width, rhs.width); + self.height = optional_add(self.height, rhs.height); + } +} + +impl Size { + pub fn fixed(&self) -> Size { + Size { + width: self.width.fixed().unwrap_or_default(), + height: self.height.fixed().unwrap_or_default(), + } + } + + pub fn flex(&self) -> Vector2F { + vec2f( + self.width.flex().unwrap_or(0.), + self.height.flex().unwrap_or(0.), + ) + } +} + +impl Size { + pub fn to_pixels(&self, rem_size: f32) -> Vector2F { + vec2f( + self.width.to_pixels(rem_size), + self.height.to_pixels(rem_size), + ) + } +} + // Sides? #[derive(Clone, Default)] struct Edges { @@ -633,6 +696,89 @@ struct Edges { right: T, } +impl Edges { + fn start(&self, axis: Axis2d) -> &T { + match axis { + Axis2d::X => &self.left, + Axis2d::Y => &self.top, + } + } + + fn start_mut(&mut self, axis: Axis2d) -> &mut T { + match axis { + Axis2d::X => &mut self.left, + Axis2d::Y => &mut self.top, + } + } + + fn end(&self, axis: Axis2d) -> &T { + match axis { + Axis2d::X => &self.right, + Axis2d::Y => &self.bottom, + } + } + + fn end_mut(&mut self, axis: Axis2d) -> &mut T { + match axis { + Axis2d::X => &mut self.right, + Axis2d::Y => &mut self.bottom, + } + } +} + +impl Edges { + fn size(&self) -> Vector2F { + vec2f(self.left + self.right, self.top + self.bottom) + } +} + +impl Edges { + fn fixed_pixels(&self, rem_length: f32) -> Edges { + Edges { + top: self.top.fixed_pixels(rem_length), + bottom: self.bottom.fixed_pixels(rem_length), + left: self.left.fixed_pixels(rem_length), + right: self.right.fixed_pixels(rem_length), + } + } + + fn flex_pixels( + &self, + rem_length: f32, + remaining_flex: &mut f32, + remaining_length: &mut f32, + ) -> Edges { + Edges { + top: self + .top + .flex_pixels(rem_length, remaining_flex, remaining_length), + bottom: self + .bottom + .flex_pixels(rem_length, remaining_flex, remaining_length), + left: self + .left + .flex_pixels(rem_length, remaining_flex, remaining_length), + right: self + .right + .flex_pixels(rem_length, remaining_flex, remaining_length), + } + } + + // pub fn fixed(&self) -> Size { + // let mut size = Size::default(); + // size.width += self.left.fixed().unwrap_or_default(); + // size.width += self.right.fixed().unwrap_or_default(); + // size + // } + + pub fn flex(&self) -> Vector2F { + vec2f( + self.left.flex().unwrap_or(0.) + self.right.flex().unwrap_or(0.), + self.top.flex().unwrap_or(0.) + self.bottom.flex().unwrap_or(0.), + ) + } +} + impl Edges { pub fn to_pixels(&self, rem_size: f32) -> Edges { Edges { @@ -688,9 +834,9 @@ impl Border { } pub mod length { - use derive_more::{Add, Into}; + use derive_more::{Add, AddAssign, Into}; - #[derive(Add, Into, Clone, Copy, Default, Debug, PartialEq)] + #[derive(Add, AddAssign, Into, Clone, Copy, Default, Debug, PartialEq)] pub struct Rems(f32); pub fn rems(rems: f32) -> Rems { @@ -698,8 +844,8 @@ pub mod length { } impl Rems { - pub fn to_pixels(&self, root_font_size: f32) -> f32 { - self.0 * root_font_size + pub fn to_pixels(&self, rem_length: f32) -> f32 { + self.0 * rem_length } } @@ -710,8 +856,8 @@ pub mod length { Fixed(Rems), Auto { flex: f32, - min: f32, - max: f32, + min: Rems, + max: Rems, }, } @@ -728,26 +874,59 @@ pub mod length { pub fn flex(flex: f32) -> Length { Length::Auto { flex, - min: 0., - max: f32::INFINITY, + min: Default::default(), + max: rems(f32::INFINITY), } } - pub fn constrained(flex: f32, min: Option, max: Option) -> Length { + pub fn constrained(flex: f32, min: Option, max: Option) -> Length { Length::Auto { flex, - min: min.unwrap_or(0.), - max: max.unwrap_or(f32::INFINITY), + min: min.unwrap_or(Default::default()), + max: max.unwrap_or(rems(f32::INFINITY)), } } impl Length { + pub fn flex_pixels( + &self, + rem_length: f32, + remaining_flex: &mut f32, + remaining_length: &mut f32, + ) -> f32 { + match self { + Length::Auto { flex, min, max } => { + let flex_length = *remaining_length / *remaining_flex; + let length = (flex * flex_length) + .clamp(min.to_pixels(rem_length), max.to_pixels(rem_length)); + *remaining_flex -= flex; + *remaining_length -= length; + length + } + _ => 0., + } + } + + pub fn fixed_pixels(&self, rem: f32) -> f32 { + match self { + Length::Fixed(rems) => rems.to_pixels(rem), + _ => 0., + } + } + pub fn flex(&self) -> Option { match self { Length::Auto { flex, .. } => Some(*flex), _ => None, } } + + pub fn fixed(&self) -> Option { + match self { + Length::Fixed(rems) => Some(*rems), + _ => None, + } + } } } @@ -779,12 +958,21 @@ impl Axis3d { } #[derive(Clone, Copy, Default)] -enum Axis2d { +pub enum Axis2d { X, #[default] Y, } +impl Axis2d { + fn rotate(self) -> Self { + match self { + Axis2d::X => Axis2d::Y, + Axis2d::Y => Axis2d::X, + } + } +} + #[derive(Clone, Copy, Default)] enum Overflow { #[default] @@ -853,6 +1041,13 @@ pub fn text(text: impl Into>) -> Node { }) } +#[derive(Default)] +struct NodeLayout { + content_size: Vector2F, + margins: Edges, + padding: Edges, +} + impl Element for Text { type LayoutState = TextLayout; type PaintState = (); @@ -864,7 +1059,6 @@ impl Element for Text { cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { // Convert the string and highlight ranges into an iterator of highlighted chunks. - let mut offset = 0; let mut highlight_ranges = self .highlights @@ -1127,3 +1321,15 @@ pub struct TextLayout { wrap_boundaries: Vec>, line_height: f32, } + +fn optional_add(a: Option, b: Option) -> Option +where + T: Add, +{ + match (a, b) { + (Some(a), Some(b)) => Some(a + b), + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + (None, None) => None, + } +}