Fix text wrapping when a child of a v_stack() (#3362)
Previously text that was rendered in a flex-column would reserve the correct amount of space during layout, and then paint itself incorrectly. Release Notes: - N/A
This commit is contained in:
Generated
+20
-3
@@ -3811,7 +3811,7 @@ dependencies = [
|
|||||||
"smol",
|
"smol",
|
||||||
"sqlez",
|
"sqlez",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"taffy",
|
"taffy 0.3.11 (git+https://github.com/DioxusLabs/taffy?rev=4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e)",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
@@ -3876,7 +3876,7 @@ dependencies = [
|
|||||||
"smol",
|
"smol",
|
||||||
"sqlez",
|
"sqlez",
|
||||||
"sum_tree",
|
"sum_tree",
|
||||||
"taffy",
|
"taffy 0.3.11 (git+https://github.com/DioxusLabs/taffy?rev=1876f72bee5e376023eaa518aa7b8a34c769bd1b)",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
@@ -3911,6 +3911,12 @@ version = "0.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c"
|
checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "grid"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1df00eed8d1f0db937f6be10e46e8072b0671accb504cf0f959c5c52c679f5b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
@@ -9105,13 +9111,24 @@ dependencies = [
|
|||||||
"winx",
|
"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]]
|
[[package]]
|
||||||
name = "taffy"
|
name = "taffy"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
source = "git+https://github.com/DioxusLabs/taffy?rev=4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e#4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e"
|
source = "git+https://github.com/DioxusLabs/taffy?rev=4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e#4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.7.4",
|
"arrayvec 0.7.4",
|
||||||
"grid",
|
"grid 0.10.0",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ serde_derive.workspace = true
|
|||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
smol.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
|
thiserror.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
tiny-skia = "0.5"
|
tiny-skia = "0.5"
|
||||||
|
|||||||
@@ -64,19 +64,34 @@ impl<V: 'static> Element<V> for Text {
|
|||||||
|
|
||||||
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
|
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
|
||||||
let element_state = element_state.clone();
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
|
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
|
let Some(lines) = text_system
|
||||||
.shape_text(
|
.shape_text(
|
||||||
&text,
|
&text,
|
||||||
font_size,
|
font_size,
|
||||||
&runs[..],
|
&runs[..],
|
||||||
known_dimensions.width, // Wrap if we know the width.
|
wrap_width, // Wrap if we know the width.
|
||||||
)
|
)
|
||||||
.log_err()
|
.log_err()
|
||||||
else {
|
else {
|
||||||
element_state.lock().replace(TextStateInner {
|
element_state.lock().replace(TextStateInner {
|
||||||
lines: Default::default(),
|
lines: Default::default(),
|
||||||
line_height,
|
line_height,
|
||||||
|
wrap_width,
|
||||||
|
size: Some(Size::default()),
|
||||||
});
|
});
|
||||||
return Size::default();
|
return Size::default();
|
||||||
};
|
};
|
||||||
@@ -88,9 +103,12 @@ impl<V: 'static> Element<V> for Text {
|
|||||||
size.width = size.width.max(line_size.width);
|
size.width = size.width.max(line_size.width);
|
||||||
}
|
}
|
||||||
|
|
||||||
element_state
|
element_state.lock().replace(TextStateInner {
|
||||||
.lock()
|
lines,
|
||||||
.replace(TextStateInner { lines, line_height });
|
line_height,
|
||||||
|
wrap_width,
|
||||||
|
size: Some(size),
|
||||||
|
});
|
||||||
|
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
@@ -133,6 +151,8 @@ impl TextState {
|
|||||||
struct TextStateInner {
|
struct TextStateInner {
|
||||||
lines: SmallVec<[WrappedLine; 1]>,
|
lines: SmallVec<[WrappedLine; 1]>,
|
||||||
line_height: Pixels,
|
line_height: Pixels,
|
||||||
|
wrap_width: Option<Pixels>,
|
||||||
|
size: Option<Size<Pixels>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InteractiveText {
|
struct InteractiveText {
|
||||||
|
|||||||
+22
-24
@@ -5,12 +5,14 @@ use std::fmt::Debug;
|
|||||||
use taffy::{
|
use taffy::{
|
||||||
geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
|
geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
|
||||||
style::AvailableSpace as TaffyAvailableSpace,
|
style::AvailableSpace as TaffyAvailableSpace,
|
||||||
tree::{Measurable, MeasureFunc, NodeId},
|
tree::NodeId,
|
||||||
Taffy,
|
Taffy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Measureable = dyn Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels> + Send + Sync;
|
||||||
|
|
||||||
pub struct TaffyLayoutEngine {
|
pub struct TaffyLayoutEngine {
|
||||||
taffy: Taffy,
|
taffy: Taffy<Box<Measureable>>,
|
||||||
children_to_parents: HashMap<LayoutId, LayoutId>,
|
children_to_parents: HashMap<LayoutId, LayoutId>,
|
||||||
absolute_layout_bounds: HashMap<LayoutId, Bounds<Pixels>>,
|
absolute_layout_bounds: HashMap<LayoutId, Bounds<Pixels>>,
|
||||||
computed_layouts: HashSet<LayoutId>,
|
computed_layouts: HashSet<LayoutId>,
|
||||||
@@ -70,9 +72,9 @@ impl TaffyLayoutEngine {
|
|||||||
) -> LayoutId {
|
) -> LayoutId {
|
||||||
let style = style.to_taffy(rem_size);
|
let style = style.to_taffy(rem_size);
|
||||||
|
|
||||||
let measurable = Box::new(Measureable(measure)) as Box<dyn Measurable>;
|
let measurable = Box::new(measure);
|
||||||
self.taffy
|
self.taffy
|
||||||
.new_leaf_with_measure(style, MeasureFunc::Boxed(measurable))
|
.new_leaf_with_context(style, measurable)
|
||||||
.expect(EXPECT_MESSAGE)
|
.expect(EXPECT_MESSAGE)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@@ -154,7 +156,22 @@ impl TaffyLayoutEngine {
|
|||||||
|
|
||||||
// let started_at = std::time::Instant::now();
|
// let started_at = std::time::Instant::now();
|
||||||
self.taffy
|
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);
|
.expect(EXPECT_MESSAGE);
|
||||||
// println!("compute_layout took {:?}", started_at.elapsed());
|
// println!("compute_layout took {:?}", started_at.elapsed());
|
||||||
}
|
}
|
||||||
@@ -202,25 +219,6 @@ impl From<LayoutId> for NodeId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Measureable<F>(F);
|
|
||||||
|
|
||||||
impl<F> taffy::tree::Measurable for Measureable<F>
|
|
||||||
where
|
|
||||||
F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels> + Send + Sync,
|
|
||||||
{
|
|
||||||
fn measure(
|
|
||||||
&self,
|
|
||||||
known_dimensions: TaffySize<Option<f32>>,
|
|
||||||
available_space: TaffySize<TaffyAvailableSpace>,
|
|
||||||
) -> TaffySize<f32> {
|
|
||||||
let known_dimensions: Size<Option<f32>> = known_dimensions.into();
|
|
||||||
let known_dimensions: Size<Option<Pixels>> = 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<Output> {
|
trait ToTaffy<Output> {
|
||||||
fn to_taffy(&self, rem_size: Pixels) -> Output;
|
fn to_taffy(&self, rem_size: Pixels) -> Output;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
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;
|
pub struct TextStory;
|
||||||
|
|
||||||
@@ -12,10 +15,46 @@ impl Render for TextStory {
|
|||||||
type Element = Div<Self>;
|
type Element = Div<Self>;
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||||
div().size_full().bg(white()).child(concat!(
|
v_stack()
|
||||||
"The quick brown fox jumps over the lazy dog. ",
|
.bg(blue())
|
||||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
.child(
|
||||||
"He started daily workout routines, ate healthier and became the fastest dog in town.",
|
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.",
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gpui::AssetSource;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, size, AnyView, Bounds, Div, Render, ViewContext, VisualContext, WindowBounds,
|
div, px, size, AnyView, Bounds, Div, Render, ViewContext, VisualContext, WindowBounds,
|
||||||
WindowOptions,
|
WindowOptions,
|
||||||
};
|
};
|
||||||
|
use gpui::{white, AssetSource};
|
||||||
use settings::{default_settings, Settings, SettingsStore};
|
use settings::{default_settings, Settings, SettingsStore};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -56,6 +56,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct TestView {
|
struct TestView {
|
||||||
|
#[allow(unused)]
|
||||||
story: AnyView,
|
story: AnyView,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +66,22 @@ impl Render for TestView {
|
|||||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
|
.bg(gpui::blue())
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.size_full()
|
.size_full()
|
||||||
.font("Helvetica")
|
.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.",
|
||||||
|
))),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user