From 7172ce37b1ce82e6d42c46e84122ec65e24bf7a6 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 18 Nov 2023 20:35:22 -0700 Subject: [PATCH 1/5] Fix text wrapping when a child of a v_stack() --- crates/gpui2/src/elements/text.rs | 9 +++++++-- crates/storybook2/src/stories/text.rs | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/gpui2/src/elements/text.rs b/crates/gpui2/src/elements/text.rs index 6849a8971107f011ee3e5ee06b186ada3da78ed4..aaf214e6eed2e0ade4a472ff5131878ac8234d0f 100644 --- a/crates/gpui2/src/elements/text.rs +++ b/crates/gpui2/src/elements/text.rs @@ -64,13 +64,18 @@ impl Element for Text { let layout_id = cx.request_measured_layout(Default::default(), rem_size, { let element_state = element_state.clone(); - move |known_dimensions, _| { + move |known_dimensions, available_space| { + let wrap_width = known_dimensions.width.or(match available_space.width { + crate::AvailableSpace::Definite(x) => Some(x), + _ => None, + }); + let Some(lines) = text_system .shape_text( &text, font_size, &runs[..], - known_dimensions.width, // Wrap if we know the width. + wrap_width, // Wrap if we know the width. ) .log_err() else { diff --git a/crates/storybook2/src/stories/text.rs b/crates/storybook2/src/stories/text.rs index 6fc76ab9073c770dba90626611c83313c7b6445a..50b379cc13bbf014a532a8714125a01e3b314d38 100644 --- a/crates/storybook2/src/stories/text.rs +++ b/crates/storybook2/src/stories/text.rs @@ -1,4 +1,5 @@ use gpui::{div, white, Div, ParentComponent, Render, Styled, View, VisualContext, WindowContext}; +use ui::v_stack; pub struct TextStory; @@ -12,7 +13,7 @@ impl Render for TextStory { type Element = Div; fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { - div().size_full().bg(white()).child(concat!( + v_stack().w_96().bg(white()).child(concat!( "The quick brown fox jumps over the lazy dog. ", "Meanwhile, the lazy dog decided it was time for a change. ", "He started daily workout routines, ate healthier and became the fastest dog in town.", From 242a0b85b5f88c382e1e571b8cff300cc2cac5d8 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 18 Nov 2023 21:30:33 -0700 Subject: [PATCH 2/5] Update Taffy to latest main --- Cargo.lock | 23 +++++++++++++++++--- crates/gpui2/Cargo.toml | 2 +- crates/gpui2/src/taffy.rs | 46 +++++++++++++++++++-------------------- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1747eae2d25fb958fa096a748793a3b0f24ab02b..e196464aa9f14aacd5b56235e07f35a8b8a893c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3759,7 +3759,7 @@ dependencies = [ "smol", "sqlez", "sum_tree", - "taffy", + "taffy 0.3.11 (git+https://github.com/DioxusLabs/taffy?rev=4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e)", "thiserror", "time", "tiny-skia", @@ -3824,7 +3824,7 @@ dependencies = [ "smol", "sqlez", "sum_tree", - "taffy", + "taffy 0.3.11 (git+https://github.com/DioxusLabs/taffy?rev=1876f72bee5e376023eaa518aa7b8a34c769bd1b)", "thiserror", "time", "tiny-skia", @@ -3859,6 +3859,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c" +[[package]] +name = "grid" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df00eed8d1f0db937f6be10e46e8072b0671accb504cf0f959c5c52c679f5b9" + [[package]] name = "h2" version = "0.3.21" @@ -9053,13 +9059,24 @@ dependencies = [ "winx", ] +[[package]] +name = "taffy" +version = "0.3.11" +source = "git+https://github.com/DioxusLabs/taffy?rev=1876f72bee5e376023eaa518aa7b8a34c769bd1b#1876f72bee5e376023eaa518aa7b8a34c769bd1b" +dependencies = [ + "arrayvec 0.7.4", + "grid 0.11.0", + "num-traits", + "slotmap", +] + [[package]] name = "taffy" version = "0.3.11" source = "git+https://github.com/DioxusLabs/taffy?rev=4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e#4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e" dependencies = [ "arrayvec 0.7.4", - "grid", + "grid 0.10.0", "num-traits", "slotmap", ] diff --git a/crates/gpui2/Cargo.toml b/crates/gpui2/Cargo.toml index 1bec9d43dc34f8ebcd13b65ec962e67b16bf363c..afb5d3ea0ce7080830bbcb1eb86aad62b8f7432c 100644 --- a/crates/gpui2/Cargo.toml +++ b/crates/gpui2/Cargo.toml @@ -47,7 +47,7 @@ serde_derive.workspace = true serde_json.workspace = true smallvec.workspace = true smol.workspace = true -taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e" } +taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "1876f72bee5e376023eaa518aa7b8a34c769bd1b" } thiserror.workspace = true time.workspace = true tiny-skia = "0.5" diff --git a/crates/gpui2/src/taffy.rs b/crates/gpui2/src/taffy.rs index ea87f73872cd445ee37e530d973d5e0e054a76fd..5896011f96d6aaf952208abdab2a2240addb2876 100644 --- a/crates/gpui2/src/taffy.rs +++ b/crates/gpui2/src/taffy.rs @@ -5,12 +5,14 @@ use std::fmt::Debug; use taffy::{ geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize}, style::AvailableSpace as TaffyAvailableSpace, - tree::{Measurable, MeasureFunc, NodeId}, + tree::NodeId, Taffy, }; +type Measureable = dyn Fn(Size>, Size) -> Size + Send + Sync; + pub struct TaffyLayoutEngine { - taffy: Taffy, + taffy: Taffy>, children_to_parents: HashMap, absolute_layout_bounds: HashMap>, computed_layouts: HashSet, @@ -70,9 +72,9 @@ impl TaffyLayoutEngine { ) -> LayoutId { let style = style.to_taffy(rem_size); - let measurable = Box::new(Measureable(measure)) as Box; + let measurable = Box::new(measure); self.taffy - .new_leaf_with_measure(style, MeasureFunc::Boxed(measurable)) + .new_leaf_with_context(style, measurable) .expect(EXPECT_MESSAGE) .into() } @@ -154,7 +156,22 @@ impl TaffyLayoutEngine { // let started_at = std::time::Instant::now(); self.taffy - .compute_layout(id.into(), available_space.into()) + .compute_layout_with_measure( + id.into(), + available_space.into(), + |known_dimensions, available_space, node_id, context| { + let Some(measure) = context else { + return taffy::geometry::Size::default(); + }; + + let known_dimensions = Size { + width: known_dimensions.width.map(Pixels), + height: known_dimensions.height.map(Pixels), + }; + + measure(known_dimensions, available_space.into()).into() + }, + ) .expect(EXPECT_MESSAGE); // println!("compute_layout took {:?}", started_at.elapsed()); } @@ -202,25 +219,6 @@ impl From for NodeId { } } -struct Measureable(F); - -impl taffy::tree::Measurable for Measureable -where - F: Fn(Size>, Size) -> Size + Send + Sync, -{ - fn measure( - &self, - known_dimensions: TaffySize>, - available_space: TaffySize, - ) -> TaffySize { - let known_dimensions: Size> = known_dimensions.into(); - let known_dimensions: Size> = known_dimensions.map(|d| d.map(Into::into)); - let available_space = available_space.into(); - let size = (self.0)(known_dimensions, available_space); - size.into() - } -} - trait ToTaffy { fn to_taffy(&self, rem_size: Pixels) -> Output; } From cf47ed2c5fabb61987c2a848d399b639022549ea Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 18 Nov 2023 21:31:16 -0700 Subject: [PATCH 3/5] Fix wrapping when parent has a max_width (Also avoid re-wrapping text we already wrapped) --- crates/gpui2/src/elements/text.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/gpui2/src/elements/text.rs b/crates/gpui2/src/elements/text.rs index aaf214e6eed2e0ade4a472ff5131878ac8234d0f..49a63e925a20d4dcfb833bf7e11d33d5416d3a75 100644 --- a/crates/gpui2/src/elements/text.rs +++ b/crates/gpui2/src/elements/text.rs @@ -1,5 +1,5 @@ use crate::{ - AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, LayoutId, Pixels, + px, AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, LayoutId, Pixels, SharedString, Size, TextRun, ViewContext, WrappedLine, }; use parking_lot::{Mutex, MutexGuard}; @@ -70,6 +70,14 @@ impl Element for Text { _ => None, }); + if let Some(text_state) = element_state.0.lock().as_ref() { + if text_state.size.is_some() + && (wrap_width.is_none() || wrap_width == text_state.wrap_width) + { + return text_state.size.unwrap(); + } + } + let Some(lines) = text_system .shape_text( &text, @@ -82,6 +90,8 @@ impl Element for Text { element_state.lock().replace(TextStateInner { lines: Default::default(), line_height, + wrap_width, + size: Some(Size::default()), }); return Size::default(); }; @@ -93,9 +103,12 @@ impl Element for Text { size.width = size.width.max(line_size.width); } - element_state - .lock() - .replace(TextStateInner { lines, line_height }); + element_state.lock().replace(TextStateInner { + lines, + line_height, + wrap_width, + size: Some(size), + }); size } @@ -138,6 +151,8 @@ impl TextState { struct TextStateInner { lines: SmallVec<[WrappedLine; 1]>, line_height: Pixels, + wrap_width: Option, + size: Option>, } struct InteractiveText { From 0711121586dba39f30ac76ee45d60ce3c4d2ab6b Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 18 Nov 2023 22:16:28 -0700 Subject: [PATCH 4/5] Add some test cases --- crates/gpui2/src/elements/text.rs | 2 +- crates/gpui2/src/taffy.rs | 2 +- crates/storybook2/src/stories/text.rs | 50 +++++++++++++++++++++++---- crates/storybook3/src/storybook3.rs | 19 ++++++++-- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/crates/gpui2/src/elements/text.rs b/crates/gpui2/src/elements/text.rs index 49a63e925a20d4dcfb833bf7e11d33d5416d3a75..e3c63523e1244b3005fd1803e6af2fef4e3c5576 100644 --- a/crates/gpui2/src/elements/text.rs +++ b/crates/gpui2/src/elements/text.rs @@ -1,5 +1,5 @@ use crate::{ - px, AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, LayoutId, Pixels, + AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, LayoutId, Pixels, SharedString, Size, TextRun, ViewContext, WrappedLine, }; use parking_lot::{Mutex, MutexGuard}; diff --git a/crates/gpui2/src/taffy.rs b/crates/gpui2/src/taffy.rs index 5896011f96d6aaf952208abdab2a2240addb2876..81a057055a1af794d0d19f0921d7807e09417f70 100644 --- a/crates/gpui2/src/taffy.rs +++ b/crates/gpui2/src/taffy.rs @@ -159,7 +159,7 @@ impl TaffyLayoutEngine { .compute_layout_with_measure( id.into(), available_space.into(), - |known_dimensions, available_space, node_id, context| { + |known_dimensions, available_space, _node_id, context| { let Some(measure) = context else { return taffy::geometry::Size::default(); }; diff --git a/crates/storybook2/src/stories/text.rs b/crates/storybook2/src/stories/text.rs index 50b379cc13bbf014a532a8714125a01e3b314d38..512d680d377564b8240d77b9f4b56f5a8c07efe7 100644 --- a/crates/storybook2/src/stories/text.rs +++ b/crates/storybook2/src/stories/text.rs @@ -1,4 +1,6 @@ -use gpui::{div, white, Div, ParentComponent, Render, Styled, View, VisualContext, WindowContext}; +use gpui::{ + blue, div, red, white, Div, ParentComponent, Render, Styled, View, VisualContext, WindowContext, +}; use ui::v_stack; pub struct TextStory; @@ -13,10 +15,46 @@ impl Render for TextStory { type Element = Div; fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { - v_stack().w_96().bg(white()).child(concat!( - "The quick brown fox jumps over the lazy dog. ", - "Meanwhile, the lazy dog decided it was time for a change. ", - "He started daily workout routines, ate healthier and became the fastest dog in town.", - )) + v_stack() + .bg(blue()) + .child( + div() + .flex() + .child(div().max_w_96().bg(white()).child(concat!( + "max-width: 96. The quick brown fox jumps over the lazy dog. ", + "Meanwhile, the lazy dog decided it was time for a change. ", + "He started daily workout routines, ate healthier and became the fastest dog in town.", + ))), + ) + .child(div().h_5()) + .child(div().flex().flex_col().w_96().bg(white()).child(concat!( + "flex-col. width: 96; The quick brown fox jumps over the lazy dog. ", + "Meanwhile, the lazy dog decided it was time for a change. ", + "He started daily workout routines, ate healthier and became the fastest dog in town.", + ))) + .child(div().h_5()) + .child( + div() + .flex() + .child(div().min_w_96().bg(white()).child(concat!( + "min-width: 96. The quick brown fox jumps over the lazy dog. ", + "Meanwhile, the lazy dog decided it was time for a change. ", + "He started daily workout routines, ate healthier and became the fastest dog in town.", +)))) + .child(div().h_5()) + .child(div().flex().w_96().bg(white()).child(div().overflow_hidden().child(concat!( + "flex-row. width 96. overflow-hidden. The quick brown fox jumps over the lazy dog. ", + "Meanwhile, the lazy dog decided it was time for a change. ", + "He started daily workout routines, ate healthier and became the fastest dog in town.", + )))) + // NOTE: When rendering text in a horizonal flex container, + // Taffy will not pass width constraints down from the parent. + // To fix this, render text in a praent with overflow: hidden, which + .child(div().h_5()) + .child(div().flex().w_96().bg(red()).child(concat!( + "flex-row. width 96. The quick brown fox jumps over the lazy dog. ", + "Meanwhile, the lazy dog decided it was time for a change. ", + "He started daily workout routines, ate healthier and became the fastest dog in town.", + ))) } } diff --git a/crates/storybook3/src/storybook3.rs b/crates/storybook3/src/storybook3.rs index 291f8ce2ac451ca7c49e72776fcd4b7411644c6d..38b4250a50bb23917e81ce8f0a82fd8b85a313d7 100644 --- a/crates/storybook3/src/storybook3.rs +++ b/crates/storybook3/src/storybook3.rs @@ -1,14 +1,14 @@ use anyhow::Result; -use gpui::AssetSource; use gpui::{ div, px, size, AnyView, Bounds, Div, Render, ViewContext, VisualContext, WindowBounds, WindowOptions, }; +use gpui::{white, AssetSource}; use settings::{default_settings, Settings, SettingsStore}; use std::borrow::Cow; use std::sync::Arc; use theme::ThemeSettings; -use ui::{prelude::*, ContextMenuStory}; +use ui::{h_stack, prelude::*, ContextMenuStory}; struct Assets; @@ -65,9 +65,22 @@ impl Render for TestView { fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { div() .flex() + .bg(gpui::blue()) .flex_col() .size_full() .font("Helvetica") - .child(self.story.clone()) + .child(div().h_5()) + .child( + div() + .flex() + .w_96() + .bg(white()) + .relative() + .child(div().child(concat!( + "The quick brown fox jumps over the lazy dog. ", + "Meanwhile, the lazy dog decided it was time for a change. ", + "He started daily workout routines, ate healthier and became the fastest dog in town.", + ))), + ) } } From c5738a509cdd714d2cf32810aa1c0ddb7773455e Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 18 Nov 2023 22:20:50 -0700 Subject: [PATCH 5/5] delint --- crates/storybook3/src/storybook3.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/storybook3/src/storybook3.rs b/crates/storybook3/src/storybook3.rs index 38b4250a50bb23917e81ce8f0a82fd8b85a313d7..9885208b4140cb5cee55026a4a5ed19a45dced57 100644 --- a/crates/storybook3/src/storybook3.rs +++ b/crates/storybook3/src/storybook3.rs @@ -8,7 +8,7 @@ use settings::{default_settings, Settings, SettingsStore}; use std::borrow::Cow; use std::sync::Arc; use theme::ThemeSettings; -use ui::{h_stack, prelude::*, ContextMenuStory}; +use ui::{prelude::*, ContextMenuStory}; struct Assets; @@ -56,6 +56,7 @@ fn main() { } struct TestView { + #[allow(unused)] story: AnyView, }