Fix horizontal text alignment

This commit is contained in:
Héctor Ramón Jiménez 2025-03-11 00:43:51 +01:00
parent 35c0e14452
commit 179a34d37b
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
23 changed files with 141 additions and 67 deletions

View file

@ -10,11 +10,14 @@ pub struct Text {
/// The contents of the text
pub content: String,
/// The position of the text relative to the alignment properties.
///
/// By default, this position will be relative to the top-left corner coordinate meaning that
/// if the horizontal and vertical alignments are unchanged, this property will tell where the
/// top-left corner of the text should be placed.
///
/// By changing the horizontal_alignment and vertical_alignment properties, you are are able to
/// change what part of text is placed at this positions.
///
/// For example, when the horizontal_alignment and vertical_alignment are set to Center, the
/// center of the text will be placed at the given position NOT the top-left coordinate.
pub position: Point,

View file

@ -54,7 +54,7 @@ pub enum Text {
/// The font of the text.
font: Font,
/// The horizontal alignment of the text.
horizontal_alignment: alignment::Horizontal,
horizontal_alignment: Option<alignment::Horizontal>,
/// The vertical alignment of the text.
vertical_alignment: alignment::Vertical,
/// The shaping strategy of the text.
@ -84,7 +84,7 @@ impl Text {
Rectangle::new(*position, paragraph.min_bounds)
.intersection(clip_bounds)
.map(|bounds| bounds * *transformation),
Some(paragraph.horizontal_alignment),
paragraph.horizontal_alignment,
Some(paragraph.vertical_alignment),
),
Text::Editor {
@ -108,7 +108,7 @@ impl Text {
..
} => (
bounds.intersection(clip_bounds),
Some(*horizontal_alignment),
*horizontal_alignment,
Some(*vertical_alignment),
),
Text::Raw { raw, .. } => (Some(raw.clip_bounds), None, None),
@ -242,15 +242,19 @@ impl PartialEq for Raw {
}
/// Measures the dimensions of the given [`cosmic_text::Buffer`].
pub fn measure(buffer: &cosmic_text::Buffer) -> Size {
let (width, height) =
buffer
.layout_runs()
.fold((0.0, 0.0), |(width, height), run| {
(run.line_w.max(width), height + run.line_height)
});
pub fn measure(buffer: &cosmic_text::Buffer) -> (Size, bool) {
let (width, height, has_rtl) = buffer.layout_runs().fold(
(0.0, 0.0, false),
|(width, height, has_rtl), run| {
(
run.line_w.max(width),
height + run.line_height,
has_rtl || run.rtl,
)
},
);
Size::new(width, height)
(Size::new(width, height), has_rtl)
}
/// Returns the attributes of the given [`Font`].
@ -309,6 +313,14 @@ fn to_style(style: font::Style) -> cosmic_text::Style {
}
}
fn to_align(alignment: alignment::Horizontal) -> cosmic_text::Align {
match alignment {
alignment::Horizontal::Left => cosmic_text::Align::Left,
alignment::Horizontal::Center => cosmic_text::Align::Center,
alignment::Horizontal::Right => cosmic_text::Align::Right,
}
}
/// Converts some [`Shaping`] strategy to a [`cosmic_text::Shaping`] strategy.
pub fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping {
match shaping {

View file

@ -58,7 +58,16 @@ impl Cache {
text::to_shaping(key.shaping),
);
let bounds = text::measure(&buffer);
let (bounds, has_rtl) = text::measure(&buffer);
if has_rtl {
buffer.set_size(
font_system,
Some(bounds.width),
Some(bounds.height),
);
}
let _ = entry.insert(Entry {
buffer,
min_bounds: bounds,

View file

@ -450,7 +450,10 @@ impl editor::Editor for Editor {
fn min_bounds(&self) -> Size {
let internal = self.internal();
text::measure(buffer_from_editor(&internal.editor))
let (bounds, _has_rtl) =
text::measure(buffer_from_editor(&internal.editor));
bounds
}
fn update(

View file

@ -18,7 +18,7 @@ struct Internal {
font: Font,
shaping: Shaping,
wrapping: Wrapping,
horizontal_alignment: alignment::Horizontal,
horizontal_alignment: Option<alignment::Horizontal>,
vertical_alignment: alignment::Vertical,
bounds: Size,
min_bounds: Size,
@ -89,7 +89,8 @@ impl core::text::Paragraph for Paragraph {
text::to_shaping(text.shaping),
);
let min_bounds = text::measure(&buffer);
let min_bounds =
align(&mut buffer, &mut font_system, text.horizontal_alignment);
Self(Arc::new(Internal {
buffer,
@ -159,7 +160,8 @@ impl core::text::Paragraph for Paragraph {
None,
);
let min_bounds = text::measure(&buffer);
let min_bounds =
align(&mut buffer, &mut font_system, text.horizontal_alignment);
Self(Arc::new(Internal {
buffer,
@ -186,8 +188,10 @@ impl core::text::Paragraph for Paragraph {
Some(new_bounds.height),
);
let (min_bounds, _has_rtl) = text::measure(&paragraph.buffer);
paragraph.bounds = new_bounds;
paragraph.min_bounds = text::measure(&paragraph.buffer);
paragraph.min_bounds = min_bounds;
}
fn compare(&self, text: Text<()>) -> core::text::Difference {
@ -212,7 +216,7 @@ impl core::text::Paragraph for Paragraph {
}
}
fn horizontal_alignment(&self) -> alignment::Horizontal {
fn horizontal_alignment(&self) -> Option<alignment::Horizontal> {
self.internal().horizontal_alignment
}
@ -354,6 +358,42 @@ impl core::text::Paragraph for Paragraph {
}
}
fn align(
buffer: &mut cosmic_text::Buffer,
font_system: &mut text::FontSystem,
align_x: Option<alignment::Horizontal>,
) -> Size {
let (min_bounds, has_rtl) = text::measure(buffer);
let mut needs_relayout = has_rtl;
if let Some(align_x) = align_x {
let has_multiple_lines = buffer.lines.len() > 1
|| buffer.lines.first().is_some_and(|line| {
line.layout_opt().is_some_and(|layout| layout.len() > 1)
});
if has_multiple_lines {
for line in &mut buffer.lines {
let _ = line.set_align(Some(text::to_align(align_x)));
}
needs_relayout = true;
}
}
if needs_relayout {
log::trace!("Relayouting paragraph...");
buffer.set_size(
font_system.raw(),
Some(min_bounds.width),
Some(min_bounds.height),
);
}
min_bounds
}
impl Default for Paragraph {
fn default() -> Self {
Self(Arc::new(Internal::default()))
@ -397,7 +437,7 @@ impl Default for Internal {
font: Font::default(),
shaping: Shaping::default(),
wrapping: Wrapping::default(),
horizontal_alignment: alignment::Horizontal::Left,
horizontal_alignment: None,
vertical_alignment: alignment::Vertical::Top,
bounds: Size::ZERO,
min_bounds: Size::ZERO,
@ -413,7 +453,7 @@ pub struct Weak {
/// The minimum bounds of the [`Paragraph`].
pub min_bounds: Size,
/// The horizontal alignment of the [`Paragraph`].
pub horizontal_alignment: alignment::Horizontal,
pub horizontal_alignment: Option<alignment::Horizontal>,
/// The vertical alignment of the [`Paragraph`].
pub vertical_alignment: alignment::Vertical,
}