@@ -1,461 +1,25 @@
-#![allow(unused_variables, dead_code)]
-
-use derive_more::{Add, Deref, DerefMut};
-use gpui::{
- color::Color,
- elements::layout_highlighted_chunks,
- fonts::{HighlightStyle, Underline},
- geometry::{
- rect::RectF,
- vector::{vec2f, Vector2F},
- },
- json::{json, ToJson},
- serde_json::Value,
- text_layout::{Line, ShapedBoundary},
- AnyElement, AppContext, Element, Entity, LayoutContext, PaintContext, SceneBuilder,
- SizeConstraint, View, ViewContext, WindowContext,
-};
-use length::{Length, Rems};
-use log::warn;
-use optional_struct::*;
-use smallvec::SmallVec;
-use std::{any::Any, borrow::Cow, f32, ops::Range, sync::Arc};
-use util::ResultExt;
-
-use crate::color::{Hsla, Rgba};
-
-use self::length::rems;
-
-pub struct Frame<V> {
- style: FrameStyle,
- metadata: FrameMetadata,
- children: Vec<AnyElement<V>>,
- id: Option<Cow<'static, str>>,
- before_paint: Option<Box<dyn FnMut(RectF, &mut FrameLayout, &mut PaintContext<V>)>>,
-}
-
-pub fn column<V>() -> Frame<V> {
- Frame::default()
-}
-
-pub fn row<V>() -> Frame<V> {
- todo!()
-}
-
-pub fn stack<V>() -> Frame<V> {
- todo!()
+use playground_macros::tailwind_lengths;
+use taffy::style::{Position, *};
+
+#[derive(Clone, Debug)]
+struct Style {
+ display: Display,
+ position: Position,
+ overflow: Point<Overflow>,
+ inset: Edges<Length>,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum Length {
+ Rems(f32),
+ Pixels(f32),
+ Percent(f32),
+ Auto,
}
-impl<V> Default for Frame<V> {
+impl Default for Length {
fn default() -> Self {
- Self {
- style: Default::default(),
- metadata: Default::default(),
- children: Default::default(),
- id: None,
- before_paint: None,
- }
- }
-}
-
-impl<V: 'static> Element<V> for Frame<V> {
- type LayoutState = FrameLayout;
- type PaintState = ();
-
- fn layout(
- &mut self,
- constraint: SizeConstraint,
- view: &mut V,
- cx: &mut LayoutContext<V>,
- ) -> (Vector2F, Self::LayoutState) {
- let mut pushed_text_style = false;
- if self.style.text.is_some() {
- let mut style = TextStyle::from_legacy(&cx.text_style(), cx);
- self.style.text.clone().apply_to(&mut style);
- if let Some(legacy_style) = style.to_legacy(cx).log_err() {
- cx.push_text_style(legacy_style);
- pushed_text_style = true;
- }
- }
-
- let mut child_node_ids = SmallVec::<[_; 2]>::new();
- for child in &mut self.children {
- child.layout(constraint, view, cx);
- child_node_ids.extend(
- child
- .metadata::<FrameMetadata>()
- .and_then(|meta| meta.layout_node_id),
- );
- }
-
- self.metadata.layout_node_id = cx
- .layout_engine()
- .new_with_children(self.style.layout.clone(), child_node_ids.as_slice())
- .log_err();
-
- if pushed_text_style {
- cx.pop_text_style();
- }
-
- (todo!(), todo!())
- }
-
- fn paint(
- &mut self,
- scene: &mut SceneBuilder,
- bounds: RectF,
- visible_bounds: RectF,
- layout: &mut FrameLayout,
- view: &mut V,
- cx: &mut PaintContext<V>,
- ) -> Self::PaintState {
- if let Some(before_paint) = &mut self.before_paint {
- before_paint(bounds, layout, cx);
- }
-
- // // Paint drop shadow
- // for shadow in &self.style.shadows {
- // scene.push_shadow(scene::Shadow {
- // bounds: margined_bounds + shadow.offset,
- // corner_radius: self.style.corner_radius,
- // sigma: shadow.blur,
- // color: shadow.color,
- // });
- // }
-
- // // Paint cursor style
- // if let Some(hit_bounds) = content_bounds.intersection(visible_bounds) {
- // if let Some(style) = self.style.cursor {
- // scene.push_cursor_region(CursorRegion {
- // bounds: hit_bounds,
- // style,
- // });
- // }
- // }
-
- // // Render the background and/or the border.
- // let Fill::Color(fill_color) = self.style.fill;
- // let is_fill_visible = fill_color.a > 0.;
- // if is_fill_visible || self.style.borders.is_visible() {
- // scene.push_quad(Quad {
- // bounds: margined_bounds,
- // background: is_fill_visible.then_some(fill_color.into()),
- // border: scene::Border {
- // width: self.style.borders.width,
- // color: self.style.borders.color,
- // overlay: false,
- // 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.children.is_empty() {
- // // Account for padding first.
- // let borders = &self.style.borders;
- // let padded_bounds = RectF::from_points(
- // margined_bounds.origin()
- // + vec2f(
- // borders.left_width() + layout.padding.left,
- // borders.top_width() + layout.padding.top,
- // ),
- // margined_bounds.lower_right()
- // - vec2f(
- // layout.padding.right + borders.right_width(),
- // layout.padding.bottom + borders.bottom_width(),
- // ),
- // );
-
- // if let Some(axis) = self.style.axis.to_2d() {
- // // let parent_size = padded_bounds.size();
- // let mut child_origin = padded_bounds.origin();
-
- // for child in &mut self.children {
- // child.paint(scene, child_origin, visible_bounds, view, cx);
-
- // // Advance along the primary axis by the size of this child
- // child_origin.set(axis, child_origin.get(axis) + child.size().get(axis));
- // }
- // } else {
- // unimplemented!();
- // }
- // }
- }
-
- fn rect_for_text_range(
- &self,
- range_utf16: Range<usize>,
- _: RectF,
- _: RectF,
- _: &Self::LayoutState,
- _: &Self::PaintState,
- view: &V,
- cx: &ViewContext<V>,
- ) -> Option<RectF> {
- self.children
- .iter()
- .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
- }
-
- fn debug(
- &self,
- bounds: RectF,
- _: &Self::LayoutState,
- _: &Self::PaintState,
- view: &V,
- cx: &ViewContext<V>,
- ) -> Value {
- json!({
- "type": "Frame",
- "bounds": bounds.to_json(),
- // TODO!
- // "children": self.content.iter().map(|child| child.debug(view, cx)).collect::<Vec<Value>>()
- })
- }
-
- fn metadata(&self) -> Option<&dyn Any> {
- Some(&self.metadata)
- }
-}
-
-impl<V: 'static> Frame<V> {
- pub fn id(mut self, id: impl Into<Cow<'static, str>>) -> Self {
- self.id = Some(id.into());
- self
- }
-
- pub fn child(mut self, child: impl Element<V>) -> Self {
- self.children.push(child.into_any());
- self
- }
-
- pub fn children<I, E>(mut self, children: I) -> Self
- where
- I: IntoIterator<Item = E>,
- E: Element<V>,
- {
- self.children
- .extend(children.into_iter().map(|child| child.into_any()));
- self
- }
-
- pub fn fill(mut self, fill: impl Into<Fill>) -> Self {
- self.style.fill = fill.into();
- self
- }
-
- pub fn text_size(mut self, text_size: Rems) -> Self {
- self.style.text.size = Some(text_size);
- self
- }
-
- pub fn text_color(mut self, color: Hsla) -> Self {
- self.style.text.color = Some(color);
- self
- }
-
- fn before_paint<H>(mut self, handler: H) -> Self
- where
- H: 'static + FnMut(RectF, &mut FrameLayout, &mut PaintContext<V>),
- {
- self.before_paint = Some(Box::new(handler));
- self
- }
-}
-
-pub struct TopBottom {
- top: Length,
- bottom: Length,
-}
-
-impl<T: Into<Length>> From<(T, T)> for TopBottom {
- fn from((top, bottom): (T, T)) -> Self {
- Self {
- top: top.into(),
- bottom: bottom.into(),
- }
- }
-}
-
-impl<T: Copy + Into<Length>> From<T> for TopBottom {
- fn from(both: T) -> Self {
- Self {
- top: both.into(),
- bottom: both.into(),
- }
- }
-}
-
-pub struct LeftRight {
- left: Length,
- right: Length,
-}
-
-impl From<(Length, Length)> for LeftRight {
- fn from((left, right): (Length, Length)) -> Self {
- Self { left, right }
- }
-}
-
-impl From<Length> for LeftRight {
- fn from(both: Length) -> Self {
- Self {
- left: both,
- right: both,
- }
- }
-}
-
-struct Interactive<Style> {
- default: Style,
- hovered: Style,
- active: Style,
- disabled: Style,
-}
-
-#[derive(Default)]
-pub struct FrameMetadata {
- layout_node_id: Option<taffy::tree::NodeId>,
-}
-
-#[derive(Clone, Default)]
-pub struct FrameStyle {
- text: OptionalTextStyle,
- opacity: f32,
- fill: Fill,
- borders: Borders,
- corner_radius: f32,
- shadows: Vec<Shadow>,
- layout: taffy::style::Style,
-}
-
-#[optional_struct]
-struct TextStyle {
- size: Rems,
- font_family: Arc<str>,
- weight: FontWeight,
- style: FontStyle,
- color: Hsla,
- soft_wrap: bool,
- underline: Underline,
-}
-
-impl TextStyle {
- fn from_legacy(text_style: &gpui::fonts::TextStyle, _cx: &WindowContext) -> Self {
- Self {
- size: rems(text_style.font_size / 16.), // TODO: Get this from the context!
- font_family: text_style.font_family_name.clone(),
- weight: text_style.font_properties.weight.into(),
- style: text_style.font_properties.style.into(),
- color: text_style.color.into(),
- soft_wrap: text_style.soft_wrap,
- underline: text_style.underline,
- }
- }
-
- fn to_legacy(&self, cx: &WindowContext) -> anyhow::Result<gpui::fonts::TextStyle> {
- let font_family_id = cx.font_cache().load_family(
- &[self.font_family.as_ref()],
- &gpui::fonts::Features::default(),
- )?;
- let font_properties = gpui::fonts::Properties {
- style: self.style.into(),
- weight: self.weight.into(),
- stretch: Default::default(),
- };
- let font_id = cx
- .font_cache()
- .select_font(font_family_id, &font_properties)?;
-
- Ok(gpui::fonts::TextStyle {
- color: self.color.into(),
- font_family_name: self.font_family.clone(),
- font_family_id,
- font_id,
- font_size: self.size.to_pixels(16.), // TODO: Get this from the context!
- font_properties,
- underline: self.underline,
- soft_wrap: self.soft_wrap,
- })
- }
-}
-
-impl OptionalTextStyle {
- pub fn is_some(&self) -> bool {
- self.size.is_some()
- || self.font_family.is_some()
- || self.weight.is_some()
- || self.style.is_some()
- || self.color.is_some()
- }
-}
-
-// pub color: Color,
-// pub font_family_name: Arc<str>,
-// pub font_family_id: FamilyId,
-// pub font_id: FontId,
-// pub font_size: f32,
-// #[schemars(with = "PropertiesDef")]
-// pub font_properties: Properties,
-// pub underline: Underline,
-// pub soft_wrap: bool,
-
-#[derive(Add, Default, Clone)]
-pub struct Size<T> {
- width: T,
- height: T,
-}
-
-impl<T: Copy> Size<T> {
- fn get(&self, axis: Axis2d) -> T {
- match axis {
- Axis2d::X => self.width,
- Axis2d::Y => self.height,
- }
- }
-}
-
-impl Size<Length> {
- fn fixed_pixels(&self, rem_pixels: f32) -> Size<f32> {
- Size {
- width: self.width.fixed_pixels(rem_pixels),
- height: self.height.fixed_pixels(rem_pixels),
- }
- }
-
- pub fn fixed(&self) -> Size<Rems> {
- 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<Rems> {
- pub fn to_pixels(&self, rem_size: f32) -> Vector2F {
- vec2f(
- self.width.to_pixels(rem_size),
- self.height.to_pixels(rem_size),
- )
- }
-}
-
-impl From<Length> for Size<Length> {
- fn from(value: Length) -> Self {
- Self {
- width: value,
- height: value,
- }
+ Self::Rems(0.)
}
}
@@ -467,935 +31,1206 @@ pub struct Edges<T> {
right: T,
}
-impl<T> Edges<T> {
- 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<T: Clone> Edges<T> {
- pub fn set_x(&mut self, value: T) {
- self.left = value.clone();
- self.right = value
- }
-
- pub fn set_y(&mut self, value: T) {
- self.top = value.clone();
- self.bottom = value
- }
-}
-
-impl Edges<f32> {
- fn size(&self) -> Vector2F {
- vec2f(self.left + self.right, self.top + self.bottom)
- }
-
- fn compute_flex_edges(
- &mut self,
- style_edges: &Edges<Length>,
- axis: Axis2d,
- remaining_flex: &mut f32,
- remaining_length: &mut f32,
- rem_pixels: f32,
- ) {
- *self.start_mut(axis) +=
- style_edges
- .start(axis)
- .flex_pixels(rem_pixels, remaining_flex, remaining_length);
- *self.end_mut(axis) +=
- style_edges
- .end(axis)
- .flex_pixels(rem_pixels, remaining_flex, remaining_length);
- }
-}
-
-impl Edges<Length> {
- fn fixed_pixels(&self, rem_pixels: f32) -> Edges<f32> {
- Edges {
- top: self.top.fixed_pixels(rem_pixels),
- bottom: self.bottom.fixed_pixels(rem_pixels),
- left: self.left.fixed_pixels(rem_pixels),
- right: self.right.fixed_pixels(rem_pixels),
- }
- }
-
- fn flex_pixels(
- &self,
- rem_pixels: f32,
- remaining_flex: &mut f32,
- remaining_length: &mut f32,
- ) -> Edges<f32> {
- Edges {
- top: self
- .top
- .flex_pixels(rem_pixels, remaining_flex, remaining_length),
- bottom: self
- .bottom
- .flex_pixels(rem_pixels, remaining_flex, remaining_length),
- left: self
- .left
- .flex_pixels(rem_pixels, remaining_flex, remaining_length),
- right: self
- .right
- .flex_pixels(rem_pixels, remaining_flex, remaining_length),
- }
- }
-
- // pub fn fixed(&self) -> Size<Rems> {
- // 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<Rems> {
- pub fn to_pixels(&self, rem_size: f32) -> Edges<f32> {
- Edges {
- top: self.top.to_pixels(rem_size),
- bottom: self.bottom.to_pixels(rem_size),
- left: self.left.to_pixels(rem_size),
- right: self.right.to_pixels(rem_size),
- }
- }
-}
-
-impl<L> From<L> for Edges<Length>
-where
- L: Into<Length>,
-{
- fn from(uniform: L) -> Self {
- let uniform = uniform.into();
- Edges {
- top: uniform,
- bottom: uniform,
- left: uniform,
- right: uniform,
- }
- }
-}
-
-impl<Vertical, Horizontal> From<(Vertical, Horizontal)> for Edges<Length>
-where
- Vertical: Into<Length>,
- Horizontal: Into<Length>,
-{
- fn from((vertical, horizontal): (Vertical, Horizontal)) -> Self {
- let vertical = vertical.into();
- let horizontal = horizontal.into();
- Edges {
- top: vertical,
- bottom: vertical,
- left: horizontal,
- right: horizontal,
- }
- }
-}
-
-impl<Top, Bottom, Left, Right> From<(Top, Bottom, Left, Right)> for Edges<Length>
-where
- Top: Into<Length>,
- Bottom: Into<Length>,
- Left: Into<Length>,
- Right: Into<Length>,
-{
- fn from((top, bottom, left, right): (Top, Bottom, Left, Right)) -> Self {
- Edges {
- top: top.into(),
- bottom: bottom.into(),
- left: left.into(),
- right: right.into(),
- }
- }
-}
-
-struct CornerRadii {
- top_left: f32,
- top_right: f32,
- bottom_right: f32,
- bottom_left: f32,
-}
-
-#[derive(Clone)]
-pub enum Fill {
- Color(Rgba),
-}
-
-impl<C: Into<Rgba>> From<C> for Fill {
- fn from(value: C) -> Self {
- Fill::Color(value.into())
- }
-}
-
-impl Default for Fill {
- fn default() -> Self {
- Fill::Color(Rgba::default())
- }
-}
-
-#[derive(Clone, Default)]
-struct Borders {
- color: Color,
- width: f32,
- top: bool,
- bottom: bool,
- left: bool,
- right: bool,
-}
-
-impl Borders {
- fn is_visible(&self) -> bool {
- self.width > 0.
- && !self.color.is_fully_transparent()
- && (self.top || self.bottom || self.left || self.right)
- }
-
- fn top_width(&self) -> f32 {
- if self.top {
- self.width
- } else {
- 0.
- }
- }
-
- fn bottom_width(&self) -> f32 {
- if self.bottom {
- self.width
- } else {
- 0.
- }
- }
-
- fn left_width(&self) -> f32 {
- if self.left {
- self.width
- } else {
- 0.
- }
- }
-
- fn right_width(&self) -> f32 {
- if self.right {
- self.width
- } else {
- 0.
- }
- }
-
- fn edges(&self) -> Edges<f32> {
- let mut edges = Edges::default();
- if self.width > 0. {
- if self.top {
- edges.top = self.width;
- }
- if self.bottom {
- edges.bottom = self.width;
- }
- if self.left {
- edges.left = self.width;
- }
- if self.right {
- edges.right = self.width;
- }
- }
- edges
- }
-
- fn size(&self) -> Vector2F {
- let width =
- if self.left { self.width } else { 0. } + if self.right { self.width } else { 0. };
- let height =
- if self.top { self.width } else { 0. } + if self.bottom { self.width } else { 0. };
-
- vec2f(width, height)
- }
-}
-
-pub mod length {
- use derive_more::{Add, AddAssign, Into};
-
- #[derive(Add, AddAssign, Into, Clone, Copy, Default, Debug, PartialEq)]
- pub struct Rems(f32);
-
- pub fn rems(rems: f32) -> Rems {
- Rems(rems)
- }
-
- impl Rems {
- pub fn to_pixels(&self, rem_pixels: f32) -> f32 {
- self.0 * rem_pixels
- }
- }
-
- #[derive(Clone, Copy, Default, Debug)]
- pub enum Length {
- #[default]
- Hug,
- Fixed(Rems),
- Auto {
- flex: f32,
- min: Rems,
- max: Rems,
- },
- }
-
- impl From<Rems> for Length {
- fn from(value: Rems) -> Self {
- Length::Fixed(value)
- }
- }
-
- pub fn auto() -> Length {
- flex(1.)
- }
-
- pub fn flex(flex: f32) -> Length {
- Length::Auto {
- flex,
- min: Default::default(),
- max: rems(f32::INFINITY),
- }
- }
-
- pub fn constrained(flex: f32, min: Option<Rems>, max: Option<Rems>) -> Length {
- Length::Auto {
- flex,
- min: min.unwrap_or(Default::default()),
- max: max.unwrap_or(rems(f32::INFINITY)),
- }
- }
-
- impl Length {
- pub fn flex_pixels(
- &self,
- rem_pixels: 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_pixels), max.to_pixels(rem_pixels));
- *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<f32> {
- match self {
- Length::Auto { flex, .. } => Some(*flex),
- _ => None,
- }
- }
-
- pub fn fixed(&self) -> Option<Rems> {
- match self {
- Length::Fixed(rems) => Some(*rems),
- _ => None,
- }
- }
- }
+#[derive(Clone, Copy, Debug)]
+pub struct Point<T> {
+ x: T,
+ y: T,
}
-#[derive(Clone, Deref, DerefMut)]
-struct Alignment(Vector2F);
+impl Style {
+ // Display ////////////////////
-impl Default for Alignment {
- fn default() -> Self {
- Self(vec2f(-1., -1.))
- }
-}
-
-#[derive(Clone, Copy, Default)]
-enum Axis3d {
- X,
- #[default]
- Y,
- Z,
-}
-
-impl Axis3d {
- fn to_2d(self) -> Option<Axis2d> {
- match self {
- Axis3d::X => Some(Axis2d::X),
- Axis3d::Y => Some(Axis2d::Y),
- Axis3d::Z => None,
- }
+ fn block(mut self) -> Self {
+ self.display = Display::Block;
+ self
}
-}
-
-#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
-pub enum Axis2d {
- X,
- #[default]
- Y,
-}
-impl Axis2d {
- fn rotate(self) -> Self {
- match self {
- Axis2d::X => Axis2d::Y,
- Axis2d::Y => Axis2d::X,
- }
+ fn flex(mut self) -> Self {
+ self.display = Display::Flex;
+ self
}
-}
-#[derive(Clone, Copy, Default)]
-enum Overflow {
- #[default]
- Visible,
- Hidden,
- Auto,
-}
-
-#[derive(Clone, Copy)]
-enum Gap {
- Fixed(f32),
- Around,
- Between,
- Even,
-}
-
-impl Default for Gap {
- fn default() -> Self {
- Gap::Fixed(0.)
+ fn grid(mut self) -> Self {
+ self.display = Display::Grid;
+ self
}
-}
-#[derive(Clone, Copy, Default)]
-struct Shadow {
- offset: Vector2F,
- blur: f32,
- color: Color,
-}
-
-#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
-enum FontStyle {
- #[default]
- Normal,
- Italic,
- Oblique,
-}
-
-impl From<gpui::fonts::Style> for FontStyle {
- fn from(value: gpui::fonts::Style) -> Self {
- use gpui::fonts::Style;
+ // Overflow ///////////////////
- match value {
- Style::Normal => FontStyle::Normal,
- Style::Italic => FontStyle::Italic,
- Style::Oblique => FontStyle::Oblique,
- }
+ pub fn overflow_visible(mut self) -> Self {
+ self.overflow.x = Overflow::Visible;
+ self.overflow.y = Overflow::Visible;
+ self
}
-}
-impl Into<gpui::fonts::Style> for FontStyle {
- fn into(self) -> gpui::fonts::Style {
- use gpui::fonts::Style;
-
- match self {
- FontStyle::Normal => Style::Normal,
- FontStyle::Italic => Style::Italic,
- FontStyle::Oblique => Style::Oblique,
- }
+ pub fn overflow_hidden(mut self) -> Self {
+ self.overflow.x = Overflow::Hidden;
+ self.overflow.y = Overflow::Hidden;
+ self
}
-}
-
-#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
-enum FontWeight {
- Thin,
- ExtraLight,
- Light,
- #[default]
- Normal,
- Medium,
- Semibold,
- Bold,
- ExtraBold,
- Black,
-}
-impl From<gpui::fonts::Weight> for FontWeight {
- fn from(value: gpui::fonts::Weight) -> Self {
- use gpui::fonts::Weight;
-
- if value == Weight::THIN {
- FontWeight::Thin
- } else if value == Weight::EXTRA_LIGHT {
- FontWeight::ExtraLight
- } else if value == Weight::LIGHT {
- FontWeight::Light
- } else if value == Weight::NORMAL {
- FontWeight::Normal
- } else if value == Weight::MEDIUM {
- FontWeight::Medium
- } else if value == Weight::SEMIBOLD {
- FontWeight::Semibold
- } else if value == Weight::BOLD {
- FontWeight::Bold
- } else if value == Weight::EXTRA_BOLD {
- FontWeight::ExtraBold
- } else if value == Weight::BLACK {
- FontWeight::Black
- } else {
- panic!("unknown font weight: {:?}", value);
- }
+ pub fn overflow_scroll(mut self) -> Self {
+ self.overflow.x = Overflow::Scroll;
+ self.overflow.y = Overflow::Scroll;
+ self
}
-}
-impl Into<gpui::fonts::Weight> for FontWeight {
- fn into(self) -> gpui::fonts::Weight {
- use gpui::fonts::Weight;
-
- match self {
- FontWeight::Thin => Weight::THIN,
- FontWeight::ExtraLight => Weight::EXTRA_LIGHT,
- FontWeight::Light => Weight::LIGHT,
- FontWeight::Normal => Weight::NORMAL,
- FontWeight::Medium => Weight::MEDIUM,
- FontWeight::Semibold => Weight::SEMIBOLD,
- FontWeight::Bold => Weight::BOLD,
- FontWeight::ExtraBold => Weight::EXTRA_BOLD,
- FontWeight::Black => Weight::BLACK,
- }
+ pub fn overflow_x_visible(mut self) -> Self {
+ self.overflow.x = Overflow::Visible;
+ self
}
-}
-#[derive(Default)]
-pub struct Text {
- text: Cow<'static, str>,
- highlights: Option<Box<[(Range<usize>, HighlightStyle)]>>,
- custom_runs: Option<(
- Box<[Range<usize>]>,
- Box<dyn FnMut(usize, RectF, &mut SceneBuilder, &mut AppContext)>,
- )>,
-}
-
-pub fn text<V: 'static>(text: impl Into<Cow<'static, str>>) -> Frame<V> {
- row().child(Text {
- text: text.into(),
- ..Default::default()
- })
-}
-
-#[derive(Default, Debug)]
-pub struct FrameLayout {
- size: Vector2F,
- padding: Edges<f32>,
- borders: Edges<f32>,
- margins: Edges<f32>,
-}
-
-impl<V: 'static> Element<V> for Text {
- type LayoutState = TextLayout;
- type PaintState = ();
-
- fn layout(
- &mut self,
- constraint: SizeConstraint,
- _: &mut V,
- cx: &mut LayoutContext<V>,
- ) -> (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
- .as_ref()
- .map_or(Default::default(), AsRef::as_ref)
- .iter()
- .peekable();
- let chunks = std::iter::from_fn(|| {
- let result;
- if let Some((range, highlight_style)) = highlight_ranges.peek() {
- if offset < range.start {
- result = Some((&self.text[offset..range.start], None));
- offset = range.start;
- } else if range.end <= self.text.len() {
- result = Some((&self.text[range.clone()], Some(*highlight_style)));
- highlight_ranges.next();
- offset = range.end;
- } else {
- warn!(
- "Highlight out of text range. Text len: {}, Highlight range: {}..{}",
- self.text.len(),
- range.start,
- range.end
- );
- result = None;
- }
- } else if offset < self.text.len() {
- result = Some((&self.text[offset..], None));
- offset = self.text.len();
- } else {
- result = None;
- }
- result
- });
-
- let style = cx.text_style();
-
- // Perform shaping on these highlighted chunks
- let shaped_lines = layout_highlighted_chunks(
- chunks,
- &style,
- cx.text_layout_cache(),
- &cx.font_cache,
- usize::MAX,
- self.text.matches('\n').count() + 1,
- );
-
- // If line wrapping is enabled, wrap each of the shaped lines.
- let font_id = style.font_id;
- let mut line_count = 0;
- let mut max_line_width = 0_f32;
- let mut wrap_boundaries = Vec::new();
- let mut wrapper = cx.font_cache.line_wrapper(font_id, style.font_size);
- for (line, shaped_line) in self.text.split('\n').zip(&shaped_lines) {
- if style.soft_wrap && constraint.max.x() > 0. {
- let boundaries = wrapper
- .wrap_shaped_line(line, shaped_line, constraint.max.x())
- .collect::<Vec<_>>();
- line_count += boundaries.len() + 1;
- wrap_boundaries.push(boundaries);
- } else {
- line_count += 1;
- }
- max_line_width = max_line_width.max(shaped_line.width());
- }
-
- let line_height = cx.font_cache.line_height(style.font_size);
- let size = vec2f(max_line_width, (line_height * line_count as f32).ceil());
-
- (
- size,
- TextLayout {
- shaped_lines,
- wrap_boundaries,
- line_height,
- },
- )
+ pub fn overflow_x_hidden(mut self) -> Self {
+ self.overflow.x = Overflow::Hidden;
+ self
}
- fn paint(
- &mut self,
- scene: &mut SceneBuilder,
- bounds: RectF,
- visible_bounds: RectF,
- layout: &mut Self::LayoutState,
- _: &mut V,
- cx: &mut PaintContext<V>,
- ) -> Self::PaintState {
- let mut origin = bounds.origin();
- let empty = Vec::new();
- let mut callback = |_, _, _: &mut SceneBuilder, _: &mut AppContext| {};
-
- let mouse_runs;
- let custom_run_callback;
- if let Some((runs, build_region)) = &mut self.custom_runs {
- mouse_runs = runs.iter();
- custom_run_callback = build_region.as_mut();
- } else {
- mouse_runs = [].iter();
- custom_run_callback = &mut callback;
- }
- let mut custom_runs = mouse_runs.enumerate().peekable();
-
- let mut offset = 0;
- for (ix, line) in layout.shaped_lines.iter().enumerate() {
- let wrap_boundaries = layout.wrap_boundaries.get(ix).unwrap_or(&empty);
- let boundaries = RectF::new(
- origin,
- vec2f(
- bounds.width(),
- (wrap_boundaries.len() + 1) as f32 * layout.line_height,
- ),
- );
-
- let style = cx.text_style();
- if boundaries.intersects(visible_bounds) {
- if style.soft_wrap {
- line.paint_wrapped(
- scene,
- origin,
- visible_bounds,
- layout.line_height,
- wrap_boundaries,
- cx,
- );
- } else {
- line.paint(scene, origin, visible_bounds, layout.line_height, cx);
- }
- }
-
- // Paint any custom runs that intersect this line.
- let end_offset = offset + line.len();
- if let Some((custom_run_ix, custom_run_range)) = custom_runs.peek().cloned() {
- if custom_run_range.start < end_offset {
- let mut current_custom_run = None;
- if custom_run_range.start <= offset {
- current_custom_run = Some((custom_run_ix, custom_run_range.end, origin));
- }
-
- let mut glyph_origin = origin;
- let mut prev_position = 0.;
- let mut wrap_boundaries = wrap_boundaries.iter().copied().peekable();
- for (run_ix, glyph_ix, glyph) in
- line.runs().iter().enumerate().flat_map(|(run_ix, run)| {
- run.glyphs()
- .iter()
- .enumerate()
- .map(move |(ix, glyph)| (run_ix, ix, glyph))
- })
- {
- glyph_origin.set_x(glyph_origin.x() + glyph.position.x() - prev_position);
- prev_position = glyph.position.x();
-
- // If we've reached a soft wrap position, move down one line. If there
- // is a custom run in-progress, paint it.
- if wrap_boundaries
- .peek()
- .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
- {
- if let Some((run_ix, _, run_origin)) = &mut current_custom_run {
- let bounds = RectF::from_points(
- *run_origin,
- glyph_origin + vec2f(0., layout.line_height),
- );
- custom_run_callback(*run_ix, bounds, scene, cx);
- *run_origin =
- vec2f(origin.x(), glyph_origin.y() + layout.line_height);
- }
- wrap_boundaries.next();
- glyph_origin = vec2f(origin.x(), glyph_origin.y() + layout.line_height);
- }
-
- // If we've reached the end of the current custom run, paint it.
- if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
- if offset + glyph.index == run_end_offset {
- current_custom_run.take();
- let bounds = RectF::from_points(
- run_origin,
- glyph_origin + vec2f(0., layout.line_height),
- );
- custom_run_callback(run_ix, bounds, scene, cx);
- custom_runs.next();
- }
-
- if let Some((_, run_range)) = custom_runs.peek() {
- if run_range.start >= end_offset {
- break;
- }
- if run_range.start == offset + glyph.index {
- current_custom_run =
- Some((run_ix, run_range.end, glyph_origin));
- }
- }
- }
-
- // If we've reached the start of a new custom run, start tracking it.
- if let Some((run_ix, run_range)) = custom_runs.peek() {
- if offset + glyph.index == run_range.start {
- current_custom_run = Some((*run_ix, run_range.end, glyph_origin));
- }
- }
- }
-
- // If a custom run extends beyond the end of the line, paint it.
- if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
- let line_end = glyph_origin + vec2f(line.width() - prev_position, 0.);
- let bounds = RectF::from_points(
- run_origin,
- line_end + vec2f(0., layout.line_height),
- );
- custom_run_callback(run_ix, bounds, scene, cx);
- if end_offset == run_end_offset {
- custom_runs.next();
- }
- }
- }
- }
-
- offset = end_offset + 1;
- origin.set_y(boundaries.max_y());
- }
+ pub fn overflow_x_scroll(mut self) -> Self {
+ self.overflow.x = Overflow::Scroll;
+ self
}
- fn rect_for_text_range(
- &self,
- _: Range<usize>,
- _: RectF,
- _: RectF,
- _: &Self::LayoutState,
- _: &Self::PaintState,
- _: &V,
- _: &ViewContext<V>,
- ) -> Option<RectF> {
- None
+ pub fn overflow_y_visible(mut self) -> Self {
+ self.overflow.y = Overflow::Visible;
+ self
}
- fn debug(
- &self,
- bounds: RectF,
- _: &Self::LayoutState,
- _: &Self::PaintState,
- _: &V,
- _: &ViewContext<V>,
- ) -> Value {
- json!({
- "type": "Text",
- "bounds": bounds.to_json(),
- "text": &self.text,
- })
+ pub fn overflow_y_hidden(mut self) -> Self {
+ self.overflow.y = Overflow::Hidden;
+ self
}
-}
-
-pub struct TextLayout {
- shaped_lines: Vec<Line>,
- wrap_boundaries: Vec<Vec<ShapedBoundary>>,
- line_height: f32,
-}
-
-trait Vector2FExt {
- fn infinity() -> Self;
- fn get(self, axis: Axis2d) -> f32;
- fn set(&mut self, axis: Axis2d, value: f32);
- fn increment_x(&mut self, delta: f32) -> f32;
- fn increment_y(&mut self, delta: f32) -> f32;
-}
-impl Vector2FExt for Vector2F {
- fn infinity() -> Self {
- vec2f(f32::INFINITY, f32::INFINITY)
+ pub fn overflow_y_scroll(mut self) -> Self {
+ self.overflow.y = Overflow::Scroll;
+ self
}
- fn get(self, axis: Axis2d) -> f32 {
- match axis {
- Axis2d::X => self.x(),
- Axis2d::Y => self.y(),
- }
- }
+ // Position ///////////////////
- fn set(&mut self, axis: Axis2d, value: f32) {
- match axis {
- Axis2d::X => self.set_x(value),
- Axis2d::Y => self.set_y(value),
- }
+ pub fn relative(mut self) -> Self {
+ self.position = Position::Relative;
+ self
}
- fn increment_x(&mut self, delta: f32) -> f32 {
- self.set_x(self.x() + delta);
- self.x()
- }
+ pub fn absolute(mut self) -> Self {
+ self.position = Position::Absolute;
- fn increment_y(&mut self, delta: f32) -> f32 {
- self.set_y(self.y() + delta);
- self.y()
+ self
}
-}
-
-pub fn view<F, E>(mut render: F) -> ViewFn
-where
- F: 'static + FnMut(&mut ViewContext<ViewFn>) -> E,
- E: Element<ViewFn>,
-{
- ViewFn(Box::new(move |cx| (render)(cx).into_any()))
-}
-
-pub struct ViewFn(Box<dyn FnMut(&mut ViewContext<ViewFn>) -> AnyElement<ViewFn>>);
-
-impl Entity for ViewFn {
- type Event = ();
-}
-impl View for ViewFn {
- fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
- (self.0)(cx)
+ #[tailwind_lengths]
+ pub fn inset(mut self, length: Length) -> Self {
+ self.inset.top = length;
+ self.inset.right = length;
+ self.inset.bottom = length;
+ self.inset.left = length;
+ self
}
}
-#[cfg(test)]
-mod tests {
- use super::{
- length::{auto, rems},
- *,
- };
- use crate::themes::rose_pine;
- use gpui::TestAppContext;
-
- #[gpui::test]
- fn test_frame_layout(cx: &mut TestAppContext) {
- // cx.add_window(|_| {
- // view(|_| {
- // let theme = rose_pine::dawn();
- // column()
- // .width(auto())
- // .height(auto())
- // .justify(1.)
- // .child(
- // row()
- // .width(auto())
- // .height(rems(10.))
- // .justify(1.)
- // .child(
- // row()
- // .width(rems(10.))
- // .height(auto())
- // .fill(theme.surface(1.)),
- // )
- // .before_paint(|bounds, layout, cx| {
- // assert_eq!(bounds.origin(), vec2f(0., 0.));
- // assert_eq!(layout.size.x(), cx.window_size().x());
- // assert_eq!(layout.size.y(), rems(10.).to_pixels(cx.rem_pixels()));
- // }),
- // )
- // .child(row())
- // .before_paint(|bounds, layout, cx| {
- // assert_eq!(layout.size, cx.window_size());
- // })
- // })
- // })
- // .remove(cx);
- }
-}
+// mod traits {
+// pub trait Element<V> {
+// type Layout;
+
+// /// Add the element and its children to the layout tree.
+// fn layout(&mut self, &view)
+
+// // fn layout(&self) ->
+// }
+// }
+
+// #![allow(unused_variables, dead_code)]
+
+// use self::length::rems;
+// use crate::color::Hsla;
+// use derive_more::{Add, Deref, DerefMut};
+// use gpui::{
+// color::Color,
+// fonts::{HighlightStyle, Underline},
+// geometry::{
+// rect::RectF,
+// vector::{vec2f, Vector2F},
+// },
+// text_layout::{Line, ShapedBoundary},
+// AppContext, Entity, LayoutContext, SceneBuilder, View, ViewContext, WindowContext,
+// };
+// use length::{Length, Rems};
+// use optional_struct::*;
+// use std::{any::Any, borrow::Cow, f32, ops::Range, sync::Arc};
+
+// mod traits {
+// use super::*;
+
+// pub trait Element<V> {
+// type Paint;
+
+// fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> (NodeId, Self::Paint);
+// fn paint(
+// &mut self,
+// view: &mut V,
+// layout: &Layout,
+// paint: &mut Self::Paint,
+// cx: &mut PaintContext<V>,
+// );
+
+// /// Convert this element into a dynamically-typed element.
+// fn into_any(self) -> super::AnyElement<V>
+// where
+// Self: Sized,
+// {
+// super::AnyElement {
+// element: Box::new(self),
+// paint: None,
+// }
+// }
+// }
+
+// pub trait AnyElement<V> {
+// fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> (NodeId, Box<dyn Any>);
+// fn paint(
+// &mut self,
+// view: &mut V,
+// layout: &Layout,
+// paint: &mut dyn Any,
+// cx: &mut PaintContext<V>,
+// );
+// }
+
+// impl<E, V> AnyElement<V> for E
+// where
+// E: Element<V>,
+// {
+// fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> (NodeId, Box<dyn Any>) {
+// let (node_id, paint_state) = self.layout(view, cx);
+// (node_id, Box::new(paint_state))
+// }
+
+// fn paint(
+// &mut self,
+// view: &mut V,
+// layout: &Layout,
+// paint: &mut dyn Any,
+// cx: &mut PaintContext<V>,
+// ) {
+// let paint = paint.downcast_mut().unwrap();
+// self.paint(view, layout, paint, cx);
+// }
+// }
+// }
+
+// /// A dynamically-typed element.
+// struct AnyElement<V> {
+// // An element as a trait object.
+// element: Box<dyn traits::AnyElement<V>>,
+// // Data computed during layout that is used during paint.
+// paint: Option<Box<dyn Any>>,
+// }
+
+// impl<V> AnyElement<V> {
+// pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> NodeId {
+// let (id, paint_data) = self.element.layout(view, cx);
+// self.paint = Some(paint_data);
+// id
+// }
+
+// pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+// let layout = cx.layout_engine().layout(node)
+// let paint = self
+// .paint
+// .as_mut()
+// .expect("called paint before calling layout");
+// self.element.paint(view, paint, cx);
+// }
+// }
+
+// // impl<V> traits::AnyElement<V> for AnyElement<V> {
+// // fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> NodeId {
+// // self.element.layout(view, cx)
+// // }
+
+// // fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+// // self.element.paint(view, cx)
+// // }
+// // }
+
+// // impl<E, V> traits::AnyElement<V> for E
+// // where
+// // E: Element<V>,
+// // {
+// // fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> NodeId {
+// // todo!()
+// // }
+
+// // fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+// // todo!()
+// // }
+// // }
+
+// /// A basic stylable element, akin to a `div` in HTML.
+// pub struct Frame<V> {
+// style: FrameStyle,
+// metadata: FrameMetadata,
+// children: Vec<AnyElement<V>>,
+// id: Option<Cow<'static, str>>,
+// before_paint: Option<Box<dyn FnMut(RectF, &mut (), &mut PaintContext<V>)>>,
+// }
+
+// /// The final result of the layout engine for a single node.
+// struct Layout {
+// /// The relative ordering of the node
+// ///
+// /// Nodes with a higher order should be rendered on top of those with a lower order.
+// /// This is effectively a topological sort of each tree.
+// pub order: u32,
+// /// The position and size of the element in the layout.
+// pub bounds: RectF,
+// }
+
+// type NodeId = taffy::prelude::NodeId;
+
+// #[derive(Deref, DerefMut)]
+// pub struct PaintContext<'a, 'b, 'c, V>(gpui::PaintContext<'a, 'b, 'c, V>);
+
+// impl<'a, 'b, 'c, V> PaintContext<'a, 'b, 'c, V> {
+// fn set_stroke_style(&mut self, stroke: Stroke) {}
+// fn stroke_rect(&mut self, rect: RectF, edges: Edges<bool>) {}
+
+// fn set_fill_style(&mut self, fill: Fill) {}
+// fn fill_rect(&mut self, rect: RectF) {}
+// }
+
+// impl<V: 'static> traits::Element<V> for Frame<V> {
+// type Paint = ();
+
+// fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> (NodeId, ()) {
+// let children_ids: Vec<_> = self.children.iter_mut().map(|child| todo!()).collect();
+// let node_id = cx
+// .layout_engine()
+// .new_with_children(self.style.layout.clone(), &children_ids)
+// .unwrap();
+// (node_id, ())
+// }
+
+// fn paint(&mut self, view: &mut V, layout: &Layout, paint: &mut (), cx: &mut PaintContext<V>) {
+// // Fill the rectangle with the background color
+// cx.set_fill_style(self.style.fill);
+// cx.fill_rect(layout.bounds);
+
+// // Paint child elements
+// for child in &mut self.children {
+// child.paint(view, cx);
+// }
+
+// // Applying border style if present
+// if self.style.borders.is_visible() {
+// cx.set_stroke_style(self.style.borders.stroke);
+// cx.stroke_rect(layout.bounds, self.style.borders.edges()); // Draw the border
+// }
+// }
+// }
+
+// impl<V: 'static> Frame<V> {
+// pub fn id(mut self, id: impl Into<Cow<'static, str>>) -> Self {
+// self.id = Some(id.into());
+// self
+// }
+
+// pub fn child(mut self, child: impl traits::Element<V>) -> Self {
+// self.children.push(child.into_any());
+// self
+// }
+
+// pub fn children<I, E>(mut self, children: I) -> Self
+// where
+// I: IntoIterator<Item = E>,
+// E: traits::Element<V>,
+// {
+// self.children
+// .extend(children.into_iter().map(|child| child.into_any()));
+// self
+// }
+
+// pub fn fill(mut self, fill: impl Into<Fill>) -> Self {
+// self.style.fill = fill.into();
+// self
+// }
+
+// pub fn text_size(mut self, text_size: Rems) -> Self {
+// self.style.text.size = Some(text_size);
+// self
+// }
+
+// pub fn text_color(mut self, color: Hsla) -> Self {
+// self.style.text.color = Some(color);
+// self
+// }
+
+// fn before_paint<H>(mut self, handler: H) -> Self
+// where
+// H: 'static + FnMut(RectF, &mut (), &mut PaintContext<V>),
+// {
+// self.before_paint = Some(Box::new(handler));
+// self
+// }
+// }
+
+// pub struct TopBottom {
+// top: Length,
+// bottom: Length,
+// }
+
+// impl<T: Into<Length>> From<(T, T)> for TopBottom {
+// fn from((top, bottom): (T, T)) -> Self {
+// Self {
+// top: top.into(),
+// bottom: bottom.into(),
+// }
+// }
+// }
+
+// impl<T: Copy + Into<Length>> From<T> for TopBottom {
+// fn from(both: T) -> Self {
+// Self {
+// top: both.into(),
+// bottom: both.into(),
+// }
+// }
+// }
+
+// pub struct LeftRight {
+// left: Length,
+// right: Length,
+// }
+
+// impl From<(Length, Length)> for LeftRight {
+// fn from((left, right): (Length, Length)) -> Self {
+// Self { left, right }
+// }
+// }
+
+// impl From<Length> for LeftRight {
+// fn from(both: Length) -> Self {
+// Self {
+// left: both,
+// right: both,
+// }
+// }
+// }
+
+// struct Interactive<Style> {
+// default: Style,
+// hovered: Style,
+// active: Style,
+// disabled: Style,
+// }
+
+// #[derive(Default)]
+// pub struct FrameMetadata {
+// layout_node_id: Option<taffy::tree::NodeId>,
+// }
+
+// #[derive(Clone, Default)]
+// pub struct FrameStyle {
+// text: OptionalTextStyle,
+// opacity: f32,
+// fill: Fill,
+// borders: Borders,
+// corner_radius: f32,
+// shadows: Vec<Shadow>,
+// layout: taffy::style::Style,
+// }
+
+// #[optional_struct]
+// struct TextStyle {
+// size: Rems,
+// font_family: Arc<str>,
+// weight: FontWeight,
+// style: FontStyle,
+// color: Hsla,
+// soft_wrap: bool,
+// underline: Underline,
+// }
+
+// impl TextStyle {
+// fn from_legacy(text_style: &gpui::fonts::TextStyle, _cx: &WindowContext) -> Self {
+// Self {
+// size: rems(text_style.font_size / 16.), // TODO: Get this from the context!
+// font_family: text_style.font_family_name.clone(),
+// weight: text_style.font_properties.weight.into(),
+// style: text_style.font_properties.style.into(),
+// color: text_style.color.into(),
+// soft_wrap: text_style.soft_wrap,
+// underline: text_style.underline,
+// }
+// }
+
+// fn to_legacy(&self, cx: &WindowContext) -> anyhow::Result<gpui::fonts::TextStyle> {
+// let font_family_id = cx.font_cache().load_family(
+// &[self.font_family.as_ref()],
+// &gpui::fonts::Features::default(),
+// )?;
+// let font_properties = gpui::fonts::Properties {
+// style: self.style.into(),
+// weight: self.weight.into(),
+// stretch: Default::default(),
+// };
+// let font_id = cx
+// .font_cache()
+// .select_font(font_family_id, &font_properties)?;
+
+// Ok(gpui::fonts::TextStyle {
+// color: self.color.into(),
+// font_family_name: self.font_family.clone(),
+// font_family_id,
+// font_id,
+// font_size: self.size.to_pixels(16.), // TODO: Get this from the context!
+// font_properties,
+// underline: self.underline,
+// soft_wrap: self.soft_wrap,
+// })
+// }
+// }
+
+// impl OptionalTextStyle {
+// pub fn is_some(&self) -> bool {
+// self.size.is_some()
+// || self.font_family.is_some()
+// || self.weight.is_some()
+// || self.style.is_some()
+// || self.color.is_some()
+// }
+// }
+
+// // pub color: Color,
+// // pub font_family_name: Arc<str>,
+// // pub font_family_id: FamilyId,
+// // pub font_id: FontId,
+// // pub font_size: f32,
+// // #[schemars(with = "PropertiesDef")]
+// // pub font_properties: Properties,
+// // pub underline: Underline,
+// // pub soft_wrap: bool,
+
+// #[derive(Add, Default, Clone)]
+// pub struct Size<T> {
+// width: T,
+// height: T,
+// }
+
+// impl<T: Copy> Size<T> {
+// fn get(&self, axis: Axis2d) -> T {
+// match axis {
+// Axis2d::X => self.width,
+// Axis2d::Y => self.height,
+// }
+// }
+// }
+
+// impl Size<Length> {
+// fn fixed_pixels(&self, rem_pixels: f32) -> Size<f32> {
+// Size {
+// width: self.width.fixed_pixels(rem_pixels),
+// height: self.height.fixed_pixels(rem_pixels),
+// }
+// }
+
+// pub fn fixed(&self) -> Size<Rems> {
+// 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<Rems> {
+// pub fn to_pixels(&self, rem_size: f32) -> Vector2F {
+// vec2f(
+// self.width.to_pixels(rem_size),
+// self.height.to_pixels(rem_size),
+// )
+// }
+// }
+
+// impl From<Length> for Size<Length> {
+// fn from(value: Length) -> Self {
+// Self {
+// width: value,
+// height: value,
+// }
+// }
+// }
+
+// impl<T> Edges<T> {
+// 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<T: Clone> Edges<T> {
+// pub fn set_x(&mut self, value: T) {
+// self.left = value.clone();
+// self.right = value
+// }
+
+// pub fn set_y(&mut self, value: T) {
+// self.top = value.clone();
+// self.bottom = value
+// }
+// }
+
+// impl Edges<f32> {
+// fn size(&self) -> Vector2F {
+// vec2f(self.left + self.right, self.top + self.bottom)
+// }
+
+// fn compute_flex_edges(
+// &mut self,
+// style_edges: &Edges<Length>,
+// axis: Axis2d,
+// remaining_flex: &mut f32,
+// remaining_length: &mut f32,
+// rem_pixels: f32,
+// ) {
+// *self.start_mut(axis) +=
+// style_edges
+// .start(axis)
+// .flex_pixels(rem_pixels, remaining_flex, remaining_length);
+// *self.end_mut(axis) +=
+// style_edges
+// .end(axis)
+// .flex_pixels(rem_pixels, remaining_flex, remaining_length);
+// }
+// }
+
+// impl Edges<Length> {
+// fn fixed_pixels(&self, rem_pixels: f32) -> Edges<f32> {
+// Edges {
+// top: self.top.fixed_pixels(rem_pixels),
+// bottom: self.bottom.fixed_pixels(rem_pixels),
+// left: self.left.fixed_pixels(rem_pixels),
+// right: self.right.fixed_pixels(rem_pixels),
+// }
+// }
+
+// fn flex_pixels(
+// &self,
+// rem_pixels: f32,
+// remaining_flex: &mut f32,
+// remaining_length: &mut f32,
+// ) -> Edges<f32> {
+// Edges {
+// top: self
+// .top
+// .flex_pixels(rem_pixels, remaining_flex, remaining_length),
+// bottom: self
+// .bottom
+// .flex_pixels(rem_pixels, remaining_flex, remaining_length),
+// left: self
+// .left
+// .flex_pixels(rem_pixels, remaining_flex, remaining_length),
+// right: self
+// .right
+// .flex_pixels(rem_pixels, remaining_flex, remaining_length),
+// }
+// }
+
+// // pub fn fixed(&self) -> Size<Rems> {
+// // 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<Rems> {
+// pub fn to_pixels(&self, rem_size: f32) -> Edges<f32> {
+// Edges {
+// top: self.top.to_pixels(rem_size),
+// bottom: self.bottom.to_pixels(rem_size),
+// left: self.left.to_pixels(rem_size),
+// right: self.right.to_pixels(rem_size),
+// }
+// }
+// }
+
+// impl<L> From<L> for Edges<Length>
+// where
+// L: Into<Length>,
+// {
+// fn from(uniform: L) -> Self {
+// let uniform = uniform.into();
+// Edges {
+// top: uniform,
+// bottom: uniform,
+// left: uniform,
+// right: uniform,
+// }
+// }
+// }
+
+// impl<Vertical, Horizontal> From<(Vertical, Horizontal)> for Edges<Length>
+// where
+// Vertical: Into<Length>,
+// Horizontal: Into<Length>,
+// {
+// fn from((vertical, horizontal): (Vertical, Horizontal)) -> Self {
+// let vertical = vertical.into();
+// let horizontal = horizontal.into();
+// Edges {
+// top: vertical,
+// bottom: vertical,
+// left: horizontal,
+// right: horizontal,
+// }
+// }
+// }
+
+// impl<Top, Bottom, Left, Right> From<(Top, Bottom, Left, Right)> for Edges<Length>
+// where
+// Top: Into<Length>,
+// Bottom: Into<Length>,
+// Left: Into<Length>,
+// Right: Into<Length>,
+// {
+// fn from((top, bottom, left, right): (Top, Bottom, Left, Right)) -> Self {
+// Edges {
+// top: top.into(),
+// bottom: bottom.into(),
+// left: left.into(),
+// right: right.into(),
+// }
+// }
+// }
+
+// struct CornerRadii {
+// top_left: f32,
+// top_right: f32,
+// bottom_right: f32,
+// bottom_left: f32,
+// }
+
+// #[derive(Clone)]
+// pub enum Fill {
+// Color(Hsla),
+// }
+
+// #[derive(Copy, Clone, Default)]
+// pub struct Stroke {
+// pub width: f32,
+// pub color: Hsla,
+// }
+
+// impl Stroke {
+// fn is_visible(&self) -> bool {
+// self.width > 0. && self.color.a > 0.
+// }
+// }
+
+// impl<C: Into<Hsla>> From<C> for Fill {
+// fn from(value: C) -> Self {
+// Fill::Color(value.into())
+// }
+// }
+
+// impl Default for Fill {
+// fn default() -> Self {
+// Fill::Color(Hsla::default())
+// }
+// }
+
+// #[derive(Clone, Default)]
+// pub struct Borders {
+// stroke: Stroke,
+// top: bool,
+// bottom: bool,
+// left: bool,
+// right: bool,
+// }
+
+// impl Borders {
+// pub fn is_visible(&self) -> bool {
+// self.stroke.is_visible() && (self.top || self.bottom || self.left || self.right)
+// }
+
+// pub fn edges(&self) -> Edges<bool> {
+// Edges {
+// top: self.top,
+// bottom: self.bottom,
+// left: self.left,
+// right: self.right,
+// }
+// }
+
+// pub fn top_width(&self) -> f32 {
+// if self.top {
+// self.stroke.width
+// } else {
+// 0.
+// }
+// }
+
+// pub fn bottom_width(&self) -> f32 {
+// if self.bottom {
+// self.stroke.width
+// } else {
+// 0.
+// }
+// }
+
+// pub fn left_width(&self) -> f32 {
+// if self.left {
+// self.stroke.width
+// } else {
+// 0.
+// }
+// }
+
+// pub fn right_width(&self) -> f32 {
+// if self.right {
+// self.stroke.width
+// } else {
+// 0.
+// }
+// }
+
+// pub fn edge_widths(&self) -> Edges<f32> {
+// let mut edges = Edges::default();
+// if self.stroke.width > 0. {
+// if self.top {
+// edges.top = self.stroke.width;
+// }
+// if self.bottom {
+// edges.bottom = self.stroke.width;
+// }
+// if self.left {
+// edges.left = self.stroke.width;
+// }
+// if self.right {
+// edges.right = self.stroke.width;
+// }
+// }
+// edges
+// }
+
+// pub fn size(&self) -> Vector2F {
+// let width = if self.left { self.stroke.width } else { 0. }
+// + if self.right { self.stroke.width } else { 0. };
+// let height = if self.top { self.stroke.width } else { 0. }
+// + if self.bottom { self.stroke.width } else { 0. };
+
+// vec2f(width, height)
+// }
+// }
+
+// pub mod length {
+// use derive_more::{Add, AddAssign, Into};
+
+// #[derive(Add, AddAssign, Into, Clone, Copy, Default, Debug, PartialEq)]
+// pub struct Rems(f32);
+
+// pub fn rems(rems: f32) -> Rems {
+// Rems(rems)
+// }
+
+// impl Rems {
+// pub fn to_pixels(&self, rem_pixels: f32) -> f32 {
+// self.0 * rem_pixels
+// }
+// }
+
+// #[derive(Clone, Copy, Default, Debug)]
+// pub enum Length {
+// #[default]
+// Hug,
+// Fixed(Rems),
+// Auto {
+// flex: f32,
+// min: Rems,
+// max: Rems,
+// },
+// }
+
+// impl From<Rems> for Length {
+// fn from(value: Rems) -> Self {
+// Length::Fixed(value)
+// }
+// }
+
+// pub fn auto() -> Length {
+// flex(1.)
+// }
+
+// pub fn flex(flex: f32) -> Length {
+// Length::Auto {
+// flex,
+// min: Default::default(),
+// max: rems(f32::INFINITY),
+// }
+// }
+
+// pub fn constrained(flex: f32, min: Option<Rems>, max: Option<Rems>) -> Length {
+// Length::Auto {
+// flex,
+// min: min.unwrap_or(Default::default()),
+// max: max.unwrap_or(rems(f32::INFINITY)),
+// }
+// }
+
+// impl Length {
+// pub fn flex_pixels(
+// &self,
+// rem_pixels: 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_pixels), max.to_pixels(rem_pixels));
+// *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<f32> {
+// match self {
+// Length::Auto { flex, .. } => Some(*flex),
+// _ => None,
+// }
+// }
+
+// pub fn fixed(&self) -> Option<Rems> {
+// match self {
+// Length::Fixed(rems) => Some(*rems),
+// _ => None,
+// }
+// }
+// }
+// }
+
+// #[derive(Clone, Deref, DerefMut)]
+// struct Alignment(Vector2F);
+
+// impl Default for Alignment {
+// fn default() -> Self {
+// Self(vec2f(-1., -1.))
+// }
+// }
+
+// #[derive(Clone, Copy, Default)]
+// enum Axis3d {
+// X,
+// #[default]
+// Y,
+// Z,
+// }
+
+// impl Axis3d {
+// fn to_2d(self) -> Option<Axis2d> {
+// match self {
+// Axis3d::X => Some(Axis2d::X),
+// Axis3d::Y => Some(Axis2d::Y),
+// Axis3d::Z => None,
+// }
+// }
+// }
+
+// #[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
+// 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]
+// Visible,
+// Hidden,
+// Auto,
+// }
+
+// #[derive(Clone, Copy)]
+// enum Gap {
+// Fixed(f32),
+// Around,
+// Between,
+// Even,
+// }
+
+// impl Default for Gap {
+// fn default() -> Self {
+// Gap::Fixed(0.)
+// }
+// }
+
+// #[derive(Clone, Copy, Default)]
+// struct Shadow {
+// offset: Vector2F,
+// blur: f32,
+// color: Color,
+// }
+
+// #[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
+// enum FontStyle {
+// #[default]
+// Normal,
+// Italic,
+// Oblique,
+// }
+
+// impl From<gpui::fonts::Style> for FontStyle {
+// fn from(value: gpui::fonts::Style) -> Self {
+// use gpui::fonts::Style;
+
+// match value {
+// Style::Normal => FontStyle::Normal,
+// Style::Italic => FontStyle::Italic,
+// Style::Oblique => FontStyle::Oblique,
+// }
+// }
+// }
+
+// impl Into<gpui::fonts::Style> for FontStyle {
+// fn into(self) -> gpui::fonts::Style {
+// use gpui::fonts::Style;
+
+// match self {
+// FontStyle::Normal => Style::Normal,
+// FontStyle::Italic => Style::Italic,
+// FontStyle::Oblique => Style::Oblique,
+// }
+// }
+// }
+
+// #[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
+// enum FontWeight {
+// Thin,
+// ExtraLight,
+// Light,
+// #[default]
+// Normal,
+// Medium,
+// Semibold,
+// Bold,
+// ExtraBold,
+// Black,
+// }
+
+// impl From<gpui::fonts::Weight> for FontWeight {
+// fn from(value: gpui::fonts::Weight) -> Self {
+// use gpui::fonts::Weight;
+
+// if value == Weight::THIN {
+// FontWeight::Thin
+// } else if value == Weight::EXTRA_LIGHT {
+// FontWeight::ExtraLight
+// } else if value == Weight::LIGHT {
+// FontWeight::Light
+// } else if value == Weight::NORMAL {
+// FontWeight::Normal
+// } else if value == Weight::MEDIUM {
+// FontWeight::Medium
+// } else if value == Weight::SEMIBOLD {
+// FontWeight::Semibold
+// } else if value == Weight::BOLD {
+// FontWeight::Bold
+// } else if value == Weight::EXTRA_BOLD {
+// FontWeight::ExtraBold
+// } else if value == Weight::BLACK {
+// FontWeight::Black
+// } else {
+// panic!("unknown font weight: {:?}", value);
+// }
+// }
+// }
+
+// impl Into<gpui::fonts::Weight> for FontWeight {
+// fn into(self) -> gpui::fonts::Weight {
+// use gpui::fonts::Weight;
+
+// match self {
+// FontWeight::Thin => Weight::THIN,
+// FontWeight::ExtraLight => Weight::EXTRA_LIGHT,
+// FontWeight::Light => Weight::LIGHT,
+// FontWeight::Normal => Weight::NORMAL,
+// FontWeight::Medium => Weight::MEDIUM,
+// FontWeight::Semibold => Weight::SEMIBOLD,
+// FontWeight::Bold => Weight::BOLD,
+// FontWeight::ExtraBold => Weight::EXTRA_BOLD,
+// FontWeight::Black => Weight::BLACK,
+// }
+// }
+// }
+
+// #[derive(Default)]
+// pub struct Text {
+// text: Cow<'static, str>,
+// highlights: Option<Box<[(Range<usize>, HighlightStyle)]>>,
+// custom_runs: Option<(
+// Box<[Range<usize>]>,
+// Box<dyn FnMut(usize, RectF, &mut SceneBuilder, &mut AppContext)>,
+// )>,
+// }
+
+// pub struct TextLayout {
+// shaped_lines: Vec<Line>,
+// wrap_boundaries: Vec<Vec<ShapedBoundary>>,
+// line_height: f32,
+// }
+
+// trait Vector2FExt {
+// fn infinity() -> Self;
+// fn get(self, axis: Axis2d) -> f32;
+// fn set(&mut self, axis: Axis2d, value: f32);
+// fn increment_x(&mut self, delta: f32) -> f32;
+// fn increment_y(&mut self, delta: f32) -> f32;
+// }
+
+// impl Vector2FExt for Vector2F {
+// fn infinity() -> Self {
+// vec2f(f32::INFINITY, f32::INFINITY)
+// }
+
+// fn get(self, axis: Axis2d) -> f32 {
+// match axis {
+// Axis2d::X => self.x(),
+// Axis2d::Y => self.y(),
+// }
+// }
+
+// fn set(&mut self, axis: Axis2d, value: f32) {
+// match axis {
+// Axis2d::X => self.set_x(value),
+// Axis2d::Y => self.set_y(value),
+// }
+// }
+
+// fn increment_x(&mut self, delta: f32) -> f32 {
+// self.set_x(self.x() + delta);
+// self.x()
+// }
+
+// fn increment_y(&mut self, delta: f32) -> f32 {
+// self.set_y(self.y() + delta);
+// self.y()
+// }
+// }
+
+// // pub fn view<F, E>(mut render: F) -> ViewFn
+// // where
+// // F: 'static + FnMut(&mut ViewContext<ViewFn>) -> E,
+// // E: traits::Element<ViewFn>,
+// // {
+// // ViewFn(Box::new(move |cx| (render)(cx).into_any()))
+// // }
+
+// // pub struct ViewFn(Box<dyn FnMut(&mut ViewContext<ViewFn>) -> AnyElement<ViewFn>>);
+
+// // impl Entity for ViewFn {
+// // type Event = ();
+// // }
+
+// // impl View for ViewFn {
+// // fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+// // (self.0)(cx)
+// // }
+// // }
+
+// #[cfg(test)]
+// mod tests {
+// use gpui::TestAppContext;
+
+// #[gpui::test]
+// fn test_frame_layout(cx: &mut TestAppContext) {
+// // cx.add_window(|_| {
+// // view(|_| {
+// // let theme = rose_pine::dawn();
+// // column()
+// // .width(auto())
+// // .height(auto())
+// // .justify(1.)
+// // .child(
+// // row()
+// // .width(auto())
+// // .height(rems(10.))
+// // .justify(1.)
+// // .child(
+// // row()
+// // .width(rems(10.))
+// // .height(auto())
+// // .fill(theme.surface(1.)),
+// // )
+// // .before_paint(|bounds, layout, cx| {
+// // assert_eq!(bounds.origin(), vec2f(0., 0.));
+// // assert_eq!(layout.size.x(), cx.window_size().x());
+// // assert_eq!(layout.size.y(), rems(10.).to_pixels(cx.rem_pixels()));
+// // }),
+// // )
+// // .child(row())
+// // .before_paint(|bounds, layout, cx| {
+// // assert_eq!(layout.size, cx.window_size());
+// // })
+// // })
+// // })
+// // .remove(cx);
+// }
+// }