gpui: Fix text-align with nowrap mode (#24116)

Release Notes:

- N/A


------

- Continue #24090 to fix text align for when used `whitespace_nowrap`.
- Fix wrapped line length calculation.

And add example

```
cargo run -p gpui --example text_layout
```

<img width="760" alt="image"
src="https://github.com/user-attachments/assets/a087c300-0e0e-4a80-98c6-90161a9b0905"
/>

---------

Co-authored-by: Owen Law <owenlaw222@gmail.com>
This commit is contained in:
Jason Lee 2025-02-07 10:51:00 +08:00 committed by GitHub
parent e689c8c01b
commit c5913899d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 83 additions and 16 deletions

View file

@ -0,0 +1,64 @@
use gpui::{
div, prelude::*, px, size, App, Application, Bounds, Context, Window, WindowBounds,
WindowOptions,
};
struct HelloWorld {}
impl Render for HelloWorld {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
div()
.bg(gpui::white())
.flex()
.flex_col()
.gap_3()
.p_4()
.size_full()
.child(div().child("Text left"))
.child(div().text_center().child("Text center"))
.child(div().text_right().child("Text right"))
.child(
div()
.flex()
.gap_2()
.justify_between()
.child(
div()
.w(px(400.))
.border_1()
.border_color(gpui::blue())
.p_1()
.whitespace_nowrap()
.overflow_hidden()
.text_center()
.child("A long non-wrapping text align center"),
)
.child(
div()
.w_32()
.border_1()
.border_color(gpui::blue())
.p_1()
.whitespace_nowrap()
.overflow_hidden()
.text_right()
.child("100%"),
),
)
}
}
fn main() {
Application::new().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx);
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
..Default::default()
},
|_, cx| cx.new(|_| HelloWorld {}),
)
.unwrap();
cx.activate(true);
});
}

View file

@ -1684,7 +1684,7 @@ impl Interactivity {
.ok()
.and_then(|mut text| text.pop())
{
text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, window, cx)
text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, None, window, cx)
.ok();
let text_bounds = crate::Bounds {

View file

@ -392,8 +392,15 @@ impl TextLayout {
let mut line_origin = bounds.origin;
let text_style = window.text_style();
for line in &element_state.lines {
line.paint(line_origin, line_height, text_style.text_align, window, cx)
.log_err();
line.paint(
line_origin,
line_height,
text_style.text_align,
Some(bounds),
window,
cx,
)
.log_err();
line_origin.y += line.size(line_height).height;
}
}

View file

@ -107,15 +107,21 @@ impl WrappedLine {
origin: Point<Pixels>,
line_height: Pixels,
align: TextAlign,
bounds: Option<Bounds<Pixels>>,
window: &mut Window,
cx: &mut App,
) -> Result<()> {
let align_width = match bounds {
Some(bounds) => Some(bounds.size.width),
None => self.layout.wrap_width,
};
paint_line(
origin,
&self.layout.unwrapped_layout,
line_height,
align,
self.layout.wrap_width,
align_width,
&self.decoration_runs,
&self.wrap_boundaries,
window,
@ -222,7 +228,7 @@ fn paint_line(
glyph_origin.x = aligned_origin_x(
origin,
align_width.unwrap_or(layout.width),
prev_glyph_position.x,
glyph.position.x,
&align,
layout,
wraps.peek(),
@ -426,17 +432,7 @@ fn aligned_origin_x(
wrap_boundary: Option<&&WrapBoundary>,
) -> Pixels {
let end_of_line = if let Some(WrapBoundary { run_ix, glyph_ix }) = wrap_boundary {
if layout.runs[*run_ix].glyphs.len() == glyph_ix + 1 {
// Next glyph is in next run
layout
.runs
.get(run_ix + 1)
.and_then(|run| run.glyphs.first())
.map_or(layout.width, |glyph| glyph.position.x)
} else {
// Get next glyph
layout.runs[*run_ix].glyphs[*glyph_ix + 1].position.x
}
layout.runs[*run_ix].glyphs[*glyph_ix].position.x
} else {
layout.width
};