diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 5732c41b..7fc28bc0 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -87,8 +87,8 @@ impl text::Paragraph for () { text::Difference::None } - fn horizontal_alignment(&self) -> alignment::Horizontal { - alignment::Horizontal::Left + fn horizontal_alignment(&self) -> Option { + None } fn vertical_alignment(&self) -> alignment::Vertical { diff --git a/core/src/text.rs b/core/src/text.rs index a7e1f281..5cffe2b2 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -34,7 +34,7 @@ pub struct Text { pub font: Font, /// The horizontal alignment of the [`Text`]. - pub horizontal_alignment: alignment::Horizontal, + pub horizontal_alignment: Option, /// The vertical alignment of the [`Text`]. pub vertical_alignment: alignment::Vertical, diff --git a/core/src/text/paragraph.rs b/core/src/text/paragraph.rs index 700c2c75..99d8da20 100644 --- a/core/src/text/paragraph.rs +++ b/core/src/text/paragraph.rs @@ -24,7 +24,7 @@ pub trait Paragraph: Sized + Default { fn compare(&self, text: Text<(), Self::Font>) -> Difference; /// Returns the horizontal alignment of the [`Paragraph`]. - fn horizontal_alignment(&self) -> alignment::Horizontal; + fn horizontal_alignment(&self) -> Option; /// Returns the vertical alignment of the [`Paragraph`]. fn vertical_alignment(&self) -> alignment::Vertical; @@ -108,7 +108,7 @@ impl Plain

{ } /// Returns the horizontal alignment of the [`Paragraph`]. - pub fn horizontal_alignment(&self) -> alignment::Horizontal { + pub fn horizontal_alignment(&self) -> Option { self.raw.horizontal_alignment() } diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 9a00fcdb..fe9a8a81 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -67,7 +67,7 @@ where line_height: LineHeight, width: Length, height: Length, - horizontal_alignment: alignment::Horizontal, + horizontal_alignment: Option, vertical_alignment: alignment::Vertical, font: Option, shaping: Shaping, @@ -89,7 +89,7 @@ where font: None, width: Length::Shrink, height: Length::Shrink, - horizontal_alignment: alignment::Horizontal::Left, + horizontal_alignment: None, vertical_alignment: alignment::Vertical::Top, shaping: Shaping::default(), wrapping: Wrapping::default(), @@ -140,7 +140,7 @@ where mut self, alignment: impl Into, ) -> Self { - self.horizontal_alignment = alignment.into(); + self.horizontal_alignment = Some(alignment.into()); self } @@ -290,7 +290,7 @@ pub fn layout( line_height: LineHeight, size: Option, font: Option, - horizontal_alignment: alignment::Horizontal, + horizontal_alignment: Option, vertical_alignment: alignment::Vertical, shaping: Shaping, wrapping: Wrapping, @@ -345,9 +345,9 @@ pub fn draw( let bounds = layout.bounds(); let x = match paragraph.horizontal_alignment() { - alignment::Horizontal::Left => bounds.x, - alignment::Horizontal::Center => bounds.center_x(), - alignment::Horizontal::Right => bounds.x + bounds.width, + None | Some(alignment::Horizontal::Left) => bounds.x, + Some(alignment::Horizontal::Center) => bounds.center_x(), + Some(alignment::Horizontal::Right) => bounds.x + bounds.width, }; let y = match paragraph.vertical_alignment() { diff --git a/graphics/src/geometry/text.rs b/graphics/src/geometry/text.rs index 90147f87..f8ac357d 100644 --- a/graphics/src/geometry/text.rs +++ b/graphics/src/geometry/text.rs @@ -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, diff --git a/graphics/src/text.rs b/graphics/src/text.rs index 7694ff1f..3731cdda 100644 --- a/graphics/src/text.rs +++ b/graphics/src/text.rs @@ -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, /// 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 { diff --git a/graphics/src/text/cache.rs b/graphics/src/text/cache.rs index e64d93f1..19ff9f61 100644 --- a/graphics/src/text/cache.rs +++ b/graphics/src/text/cache.rs @@ -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, diff --git a/graphics/src/text/editor.rs b/graphics/src/text/editor.rs index ca62abcf..214701e0 100644 --- a/graphics/src/text/editor.rs +++ b/graphics/src/text/editor.rs @@ -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( diff --git a/graphics/src/text/paragraph.rs b/graphics/src/text/paragraph.rs index 50afef1f..747bec59 100644 --- a/graphics/src/text/paragraph.rs +++ b/graphics/src/text/paragraph.rs @@ -18,7 +18,7 @@ struct Internal { font: Font, shaping: Shaping, wrapping: Wrapping, - horizontal_alignment: alignment::Horizontal, + horizontal_alignment: Option, 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(¶graph.buffer); + paragraph.bounds = new_bounds; - paragraph.min_bounds = text::measure(¶graph.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 { 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, +) -> 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, /// The vertical alignment of the [`Paragraph`]. pub vertical_alignment: alignment::Vertical, } diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index dbdff444..5d8a20f7 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -231,7 +231,7 @@ impl geometry::frame::Backend for Frame { size, line_height: line_height.to_absolute(size), font: text.font, - horizontal_alignment: text.horizontal_alignment, + horizontal_alignment: Some(text.horizontal_alignment), vertical_alignment: text.vertical_alignment, shaping: text.shaping, clip_bounds: Rectangle::with_size(Size::INFINITY), diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 0fc3d1f7..499e7998 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -92,7 +92,7 @@ impl Pipeline { editor.buffer(), Rectangle::new(position, editor.bounds()), color, - alignment::Horizontal::Left, + None, alignment::Vertical::Top, pixels, clip_mask, @@ -108,7 +108,7 @@ impl Pipeline { size: Pixels, line_height: Pixels, font: Font, - horizontal_alignment: alignment::Horizontal, + horizontal_alignment: Option, vertical_alignment: alignment::Vertical, shaping: Shaping, pixels: &mut tiny_skia::PixmapMut<'_>, @@ -177,7 +177,7 @@ impl Pipeline { ), ), color, - alignment::Horizontal::Left, + None, alignment::Vertical::Top, pixels, clip_mask, @@ -197,7 +197,7 @@ fn draw( buffer: &cosmic_text::Buffer, bounds: Rectangle, color: Color, - horizontal_alignment: alignment::Horizontal, + horizontal_alignment: Option, vertical_alignment: alignment::Vertical, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, @@ -206,9 +206,9 @@ fn draw( let bounds = bounds * transformation; let x = match horizontal_alignment { - alignment::Horizontal::Left => bounds.x, - alignment::Horizontal::Center => bounds.x - bounds.width / 2.0, - alignment::Horizontal::Right => bounds.x - bounds.width, + None | Some(alignment::Horizontal::Left) => bounds.x, + Some(alignment::Horizontal::Center) => bounds.x - bounds.width / 2.0, + Some(alignment::Horizontal::Right) => bounds.x - bounds.width, }; let y = match vertical_alignment { diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index 8d161015..5a562bf5 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -336,7 +336,7 @@ impl geometry::frame::Backend for Frame { size, line_height: line_height.to_absolute(size), font: text.font, - horizontal_alignment: text.horizontal_alignment, + horizontal_alignment: Some(text.horizontal_alignment), vertical_alignment: text.vertical_alignment, shaping: text.shaping, clip_bounds: self.clip_bounds, diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 2ef9992c..8d77d9c8 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -417,7 +417,7 @@ impl Renderer { size: Pixels(20.0), line_height: core::text::LineHeight::default(), font: Font::MONOSPACE, - horizontal_alignment: alignment::Horizontal::Left, + horizontal_alignment: None, vertical_alignment: alignment::Vertical::Top, shaping: core::text::Shaping::Basic, wrapping: core::text::Wrapping::Word, diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 274cc4b7..a7227350 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -540,7 +540,7 @@ fn prepare( ( editor.buffer(), Rectangle::new(*position, editor.bounds()), - alignment::Horizontal::Left, + None, alignment::Vertical::Top, *color, *clip_bounds, @@ -591,7 +591,7 @@ fn prepare( height.unwrap_or(layer_bounds.height), ), ), - alignment::Horizontal::Left, + None, alignment::Vertical::Top, raw.color, raw.clip_bounds, @@ -603,9 +603,11 @@ fn prepare( let bounds = bounds * transformation * layer_transformation; let left = match horizontal_alignment { - alignment::Horizontal::Left => bounds.x, - alignment::Horizontal::Center => bounds.x - bounds.width / 2.0, - alignment::Horizontal::Right => bounds.x - bounds.width, + None | Some(alignment::Horizontal::Left) => bounds.x, + Some(alignment::Horizontal::Center) => { + bounds.x - bounds.width / 2.0 + } + Some(alignment::Horizontal::Right) => bounds.x - bounds.width, }; let top = match vertical_alignment { diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 3c1ef276..7247641f 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -293,7 +293,7 @@ where self.text_line_height, self.text_size, self.font, - alignment::Horizontal::Left, + None, alignment::Vertical::Top, self.text_shaping, self.text_wrapping, @@ -416,7 +416,9 @@ where size, line_height: *line_height, bounds: bounds.size(), - horizontal_alignment: alignment::Horizontal::Center, + horizontal_alignment: Some( + alignment::Horizontal::Center, + ), vertical_alignment: alignment::Vertical::Center, shaping: *shaping, wrapping: text::Wrapping::default(), diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index 9d0539ff..91d7937f 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -552,7 +552,7 @@ where size: text_size, line_height: self.text_line_height, font: self.font.unwrap_or_else(|| renderer.default_font()), - horizontal_alignment: alignment::Horizontal::Left, + horizontal_alignment: None, vertical_alignment: alignment::Vertical::Center, shaping: self.text_shaping, wrapping: text::Wrapping::default(), diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index b751fcc3..f608c78f 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -371,7 +371,7 @@ where size: text_size, line_height: self.text_line_height, font, - horizontal_alignment: alignment::Horizontal::Left, + horizontal_alignment: None, vertical_alignment: alignment::Vertical::Center, shaping: self.text_shaping, wrapping: text::Wrapping::default(), @@ -639,7 +639,7 @@ where bounds.width, f32::from(line_height.to_absolute(size)), ), - horizontal_alignment: alignment::Horizontal::Right, + horizontal_alignment: Some(alignment::Horizontal::Right), vertical_alignment: alignment::Vertical::Center, shaping, wrapping: text::Wrapping::default(), @@ -669,7 +669,7 @@ where bounds.width - self.padding.horizontal(), f32::from(self.text_line_height.to_absolute(text_size)), ), - horizontal_alignment: alignment::Horizontal::Left, + horizontal_alignment: None, vertical_alignment: alignment::Vertical::Center, shaping: self.text_shaping, wrapping: text::Wrapping::default(), diff --git a/widget/src/radio.rs b/widget/src/radio.rs index 0df4d715..3ae2eca3 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -314,7 +314,7 @@ where self.text_line_height, self.text_size, self.font, - alignment::Horizontal::Left, + None, alignment::Vertical::Top, self.text_shaping, self.text_wrapping, diff --git a/widget/src/text/rich.rs b/widget/src/text/rich.rs index 4d4a2861..1e406136 100644 --- a/widget/src/text/rich.rs +++ b/widget/src/text/rich.rs @@ -31,7 +31,7 @@ pub struct Rich< width: Length, height: Length, font: Option, - align_x: alignment::Horizontal, + align_x: Option, align_y: alignment::Vertical, wrapping: Wrapping, class: Theme::Class<'a>, @@ -56,7 +56,7 @@ where width: Length::Shrink, height: Length::Shrink, font: None, - align_x: alignment::Horizontal::Left, + align_x: None, align_y: alignment::Vertical::Top, wrapping: Wrapping::default(), class: Theme::default(), @@ -116,7 +116,7 @@ where mut self, alignment: impl Into, ) -> Self { - self.align_x = alignment.into(); + self.align_x = Some(alignment.into()); self } @@ -476,7 +476,7 @@ fn layout( line_height: LineHeight, size: Option, font: Option, - horizontal_alignment: alignment::Horizontal, + horizontal_alignment: Option, vertical_alignment: alignment::Vertical, wrapping: Wrapping, ) -> layout::Node diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 7e40a56a..9f402a0c 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -955,7 +955,7 @@ where .unwrap_or_else(|| renderer.default_size()), line_height: self.line_height, font, - horizontal_alignment: alignment::Horizontal::Left, + horizontal_alignment: None, vertical_alignment: alignment::Vertical::Top, shaping: text::Shaping::Advanced, wrapping: self.wrapping, diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 3abead5b..96295312 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -319,7 +319,7 @@ where content: self.placeholder.as_str(), bounds: Size::new(f32::INFINITY, text_bounds.height), size: text_size, - horizontal_alignment: alignment::Horizontal::Left, + horizontal_alignment: None, vertical_alignment: alignment::Vertical::Center, shaping: text::Shaping::Advanced, wrapping: text::Wrapping::default(), @@ -344,7 +344,7 @@ where font: icon.font, size: icon.size.unwrap_or_else(|| renderer.default_size()), bounds: Size::new(f32::INFINITY, text_bounds.height), - horizontal_alignment: alignment::Horizontal::Center, + horizontal_alignment: Some(alignment::Horizontal::Center), vertical_alignment: alignment::Vertical::Center, shaping: text::Shaping::Advanced, wrapping: text::Wrapping::default(), @@ -1727,7 +1727,7 @@ fn replace_paragraph( content: &value.to_string(), bounds: Size::new(f32::INFINITY, text_bounds.height), size: text_size, - horizontal_alignment: alignment::Horizontal::Left, + horizontal_alignment: None, vertical_alignment: alignment::Vertical::Center, shaping: text::Shaping::Advanced, wrapping: text::Wrapping::default(), diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index b711432e..d588e897 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -93,7 +93,7 @@ pub struct Toggler< size: f32, text_size: Option, text_line_height: text::LineHeight, - text_alignment: alignment::Horizontal, + text_alignment: Option, text_shaping: text::Shaping, text_wrapping: text::Wrapping, spacing: f32, @@ -127,7 +127,7 @@ where size: Self::DEFAULT_SIZE, text_size: None, text_line_height: text::LineHeight::default(), - text_alignment: alignment::Horizontal::Left, + text_alignment: None, text_shaping: text::Shaping::default(), text_wrapping: text::Wrapping::default(), spacing: Self::DEFAULT_SIZE / 2.0, @@ -195,8 +195,11 @@ where } /// Sets the horizontal alignment of the text of the [`Toggler`] - pub fn text_alignment(mut self, alignment: alignment::Horizontal) -> Self { - self.text_alignment = alignment; + pub fn text_alignment( + mut self, + alignment: impl Into, + ) -> Self { + self.text_alignment = Some(alignment.into()); self } diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index 139d787a..c365d2f9 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -338,7 +338,7 @@ where .unwrap_or_else(|| renderer.default_size()), line_height: text::LineHeight::default(), font: renderer.default_font(), - horizontal_alignment: alignment::Horizontal::Left, + horizontal_alignment: None, vertical_alignment: alignment::Vertical::Top, shaping: text::Shaping::Advanced, wrapping: text::Wrapping::None,