gpui: Add text alignment (#24090)

Adds a text property for controlling left, center, or right text
alignment.

#8792 should stay open since this doesn't add support for `justify`
(which would require a much bigger change since this can just alter the
origin of each line, but justify requires changing spacing, whereas
justify requires changes to each platform's shaping code).

Release Notes:

- N/A
This commit is contained in:
someone13574 2025-02-02 12:15:12 -05:00 committed by GitHub
parent 4a65315f3b
commit aa42e206b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 112 additions and 6 deletions

View file

@ -1,6 +1,7 @@
use crate::{
black, fill, point, px, size, App, Bounds, Half, Hsla, LineLayout, Pixels, Point, Result,
SharedString, StrikethroughStyle, UnderlineStyle, Window, WrapBoundary, WrappedLineLayout,
SharedString, StrikethroughStyle, TextAlign, UnderlineStyle, Window, WrapBoundary,
WrappedLineLayout,
};
use derive_more::{Deref, DerefMut};
use smallvec::SmallVec;
@ -70,6 +71,8 @@ impl ShapedLine {
origin,
&self.layout,
line_height,
TextAlign::default(),
None,
&self.decoration_runs,
&[],
window,
@ -103,6 +106,7 @@ impl WrappedLine {
&self,
origin: Point<Pixels>,
line_height: Pixels,
align: TextAlign,
window: &mut Window,
cx: &mut App,
) -> Result<()> {
@ -110,6 +114,8 @@ impl WrappedLine {
origin,
&self.layout.unwrapped_layout,
line_height,
align,
self.layout.wrap_width,
&self.decoration_runs,
&self.wrap_boundaries,
window,
@ -120,10 +126,13 @@ impl WrappedLine {
}
}
#[allow(clippy::too_many_arguments)]
fn paint_line(
origin: Point<Pixels>,
layout: &LineLayout,
line_height: Pixels,
align: TextAlign,
align_width: Option<Pixels>,
decoration_runs: &[DecorationRun],
wrap_boundaries: &[WrapBoundary],
window: &mut Window,
@ -147,7 +156,17 @@ fn paint_line(
let mut current_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None;
let mut current_background: Option<(Point<Pixels>, Hsla)> = None;
let text_system = cx.text_system().clone();
let mut glyph_origin = origin;
let mut glyph_origin = point(
aligned_origin_x(
origin,
align_width.unwrap_or(layout.width),
px(0.0),
&align,
layout,
wraps.peek(),
),
origin.y,
);
let mut prev_glyph_position = Point::default();
let mut max_glyph_size = size(px(0.), px(0.));
for (run_ix, run) in layout.runs.iter().enumerate() {
@ -200,7 +219,14 @@ fn paint_line(
strikethrough_origin.y += line_height;
}
glyph_origin.x = origin.x;
glyph_origin.x = aligned_origin_x(
origin,
align_width.unwrap_or(layout.width),
prev_glyph_position.x,
&align,
layout,
wraps.peek(),
);
glyph_origin.y += line_height;
}
prev_glyph_position = glyph.position;
@ -390,3 +416,36 @@ fn paint_line(
Ok(())
})
}
fn aligned_origin_x(
origin: Point<Pixels>,
align_width: Pixels,
last_glyph_x: Pixels,
align: &TextAlign,
layout: &LineLayout,
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
}
} else {
layout.width
};
let line_width = end_of_line - last_glyph_x;
match align {
TextAlign::Left => origin.x,
TextAlign::Center => (2.0 * origin.x + align_width - line_width) / 2.0,
TextAlign::Right => origin.x + align_width - line_width,
}
}