diff --git a/piet-cairo/src/text.rs b/piet-cairo/src/text.rs index e1f8e6ea..1cf9d31c 100644 --- a/piet-cairo/src/text.rs +++ b/piet-cairo/src/text.rs @@ -7,7 +7,7 @@ use std::rc::Rc; use glib::translate::{from_glib_full, ToGlibPtr}; -use pango::{AttrList, FontMapExt}; +use pango::{AttrList, FontMapExt, TabAlign, TabArray}; use pango_sys::pango_attr_insert_hyphens_new; use pangocairo::FontMap; @@ -49,9 +49,12 @@ pub struct CairoTextLayout { pub struct CairoTextLayoutBuilder { text: Rc, + defaults: util::LayoutDefaults, attributes: Vec, last_range_start_pos: usize, + + tab_width: Option, width_constraint: f64, pango_layout: PangoLayout, } @@ -193,6 +196,7 @@ impl Text for CairoText { defaults: util::LayoutDefaults::default(), attributes: Vec::new(), last_range_start_pos: 0, + tab_width: None, width_constraint: f64::INFINITY, pango_layout, } @@ -213,6 +217,11 @@ impl TextLayoutBuilder for CairoTextLayoutBuilder { self } + fn tab_width(mut self, width: f64) -> Self { + self.tab_width = Some(width); + self + } + fn alignment(self, alignment: TextAlignment) -> Self { /* * NOTE: Pango has `auto_dir` enabled by default. This means that @@ -277,6 +286,13 @@ impl TextLayoutBuilder for CairoTextLayoutBuilder { } fn build(self) -> Result { + if let Some(tab_width) = self.tab_width { + let tab_width = (tab_width * PANGO_SCALE) as i32; + let mut array = TabArray::new(1, false); + array.set_tab(0, TabAlign::Left, tab_width); + self.pango_layout.set_tabs(Some(&array)); + } + let pango_attributes = AttrList::new(); let add_attribute = |attribute| { if let Some(attribute) = attribute { diff --git a/piet-coregraphics/src/text.rs b/piet-coregraphics/src/text.rs index f71ad020..9aada5a0 100644 --- a/piet-coregraphics/src/text.rs +++ b/piet-coregraphics/src/text.rs @@ -514,6 +514,10 @@ impl TextLayoutBuilder for CoreGraphicsTextLayoutBuilder { self } + fn tab_width(self, _width: f64) -> Self { + self + } + fn alignment(mut self, alignment: piet::TextAlignment) -> Self { self.alignment = alignment; self diff --git a/piet-direct2d/src/dwrite.rs b/piet-direct2d/src/dwrite.rs index ff628e01..df231f39 100644 --- a/piet-direct2d/src/dwrite.rs +++ b/piet-direct2d/src/dwrite.rs @@ -341,6 +341,12 @@ impl TextLayout { } } + pub(crate) fn set_incremental_tab_stop(&self, distance: f32) { + unsafe { + self.0.SetIncrementalTabStop(distance); + } + } + /// Set the weight for a range of this layout. `start` and `len` are in utf16. pub(crate) fn set_weight(&mut self, range: Utf16Range, weight: FontWeight) { let weight = weight.to_raw() as DWRITE_FONT_WEIGHT; diff --git a/piet-direct2d/src/text.rs b/piet-direct2d/src/text.rs index 05538e2a..96efc1d5 100644 --- a/piet-direct2d/src/text.rs +++ b/piet-direct2d/src/text.rs @@ -84,6 +84,7 @@ pub struct D2DTextLayout { pub struct D2DTextLayoutBuilder { text: Rc, layout: Result, + tab_width: Option, len_utf16: usize, loaded_fonts: D2DLoadedFonts, default_font: FontFamily, @@ -161,6 +162,7 @@ impl Text for D2DText { D2DTextLayoutBuilder { layout, text, + tab_width: None, len_utf16: wide_str.len(), colors: Vec::new(), loaded_fonts: self.loaded_fonts.clone(), @@ -186,6 +188,11 @@ impl TextLayoutBuilder for D2DTextLayoutBuilder { self } + fn tab_width(mut self, width: f64) -> Self { + self.tab_width = Some(width as f32); + self + } + fn alignment(mut self, alignment: TextAlignment) -> Self { if let Ok(layout) = self.layout.as_mut() { layout.set_alignment(alignment); @@ -229,6 +236,10 @@ impl TextLayoutBuilder for D2DTextLayoutBuilder { let (default_line_height, default_baseline) = self.get_default_line_height_and_baseline(); let layout = self.layout?; + if let Some(tab_width) = self.tab_width { + layout.set_incremental_tab_stop(tab_width); + } + let mut layout = D2DTextLayout { text: self.text, colors: self.colors.into(), diff --git a/piet-svg/src/text.rs b/piet-svg/src/text.rs index 25b08fb6..570b4d7a 100644 --- a/piet-svg/src/text.rs +++ b/piet-svg/src/text.rs @@ -47,6 +47,10 @@ impl piet::TextLayoutBuilder for TextLayoutBuilder { self } + fn tab_width(self, _width: f64) -> Self { + self + } + fn alignment(self, _alignment: piet::TextAlignment) -> Self { self } diff --git a/piet-web/src/text.rs b/piet-web/src/text.rs index c54532af..b03bea7e 100644 --- a/piet-web/src/text.rs +++ b/piet-web/src/text.rs @@ -144,6 +144,10 @@ impl TextLayoutBuilder for WebTextLayoutBuilder { self } + fn tab_width(self, _width: f64) -> Self { + self + } + fn alignment(self, _alignment: piet::TextAlignment) -> Self { web_sys::console::log_1(&"TextLayout alignment unsupported on web".into()); self diff --git a/piet/src/null_renderer.rs b/piet/src/null_renderer.rs index 3b374ff5..6fb3bda8 100644 --- a/piet/src/null_renderer.rs +++ b/piet/src/null_renderer.rs @@ -157,6 +157,10 @@ impl TextLayoutBuilder for NullTextLayoutBuilder { self } + fn tab_width(self, _width: f64) -> Self { + self + } + fn alignment(self, _alignment: crate::TextAlignment) -> Self { self } diff --git a/piet/src/text.rs b/piet/src/text.rs index d9961811..cbe50d1a 100644 --- a/piet/src/text.rs +++ b/piet/src/text.rs @@ -175,6 +175,12 @@ pub trait TextLayoutBuilder: Sized { /// default behaviour. fn max_width(self, width: f64) -> Self; + /// Set the base tabulator width. + /// + /// The width specified here controls the max width for tab characters + /// in the final layout. + fn tab_width(self, width: f64) -> Self; + /// Set the [`TextAlignment`] to be used for this layout. /// /// [`TextAlignment`]: enum.TextAlignment.html