diff --git a/src/pango.rs b/src/pango.rs index 8219a216..087fb10e 100644 --- a/src/pango.rs +++ b/src/pango.rs @@ -85,6 +85,7 @@ extern "C" { ink_rect: *mut PangoRectangle, logical_rect: *mut PangoRectangle, ); + fn pango_layout_get_baseline(layout: *mut PangoLayout_) -> c::c_int; fn pango_extents_to_pixels(inclusive: *mut PangoRectangle, nearest: *mut PangoRectangle); } @@ -104,6 +105,7 @@ pub enum PangoError { } #[repr(C)] +#[derive(Default)] struct PangoRectangle { x: c::c_int, y: c::c_int, @@ -111,6 +113,8 @@ struct PangoRectangle { height: c::c_int, } +const PANGO_SCALE: i32 = 1024; + pub struct CairoImageSurface { s: *mut cairo_surface_t, } @@ -325,18 +329,29 @@ impl PangoLayout { pub fn inc_pixel_rect(&self) -> Rect { unsafe { - let mut rect = PangoRectangle { - x: 0, - y: 0, - width: 0, - height: 0, - }; + let mut rect = PangoRectangle::default(); pango_layout_get_extents(self.l, &mut rect, ptr::null_mut()); pango_extents_to_pixels(&mut rect, ptr::null_mut()); Rect::new_sized(rect.x, rect.y, rect.width, rect.height).unwrap() } } + pub fn logical_pixel_rect(&self) -> Rect { + unsafe { + let mut rect = PangoRectangle::default(); + pango_layout_get_extents(self.l, ptr::null_mut(), &mut rect); + pango_extents_to_pixels(&mut rect, ptr::null_mut()); + Rect::new_sized(rect.x, rect.y, rect.width, rect.height).unwrap() + } + } + + pub fn pixel_baseline(&self) -> i32 { + unsafe { + let res = pango_layout_get_baseline(self.l); + (res as i32 + PANGO_SCALE - 1) / PANGO_SCALE + } + } + pub fn show_layout(&self) { unsafe { pango_cairo_show_layout(self.c.c.c, self.l); diff --git a/src/text.rs b/src/text.rs index 87c97c58..3b9a1da2 100644 --- a/src/text.rs +++ b/src/text.rs @@ -76,14 +76,21 @@ pub fn measure( text: &str, markup: bool, scale: Option, -) -> Result { + full: bool, +) -> Result { let data = create_data(font, 1, 1, scale)?; if markup { data.layout.set_markup(text); } else { data.layout.set_text(text); } - Ok(data.layout.inc_pixel_rect()) + let mut res = TextMeasurement::default(); + res.ink_rect = data.layout.inc_pixel_rect(); + if full { + res.logical_rect = data.layout.logical_pixel_rect(); + res.baseline = data.layout.pixel_baseline(); + } + Ok(res) } pub fn render( @@ -96,13 +103,14 @@ pub fn render( scale: Option, ) -> Result, TextError> { render2( - ctx, 1, width, height, 1, font, text, color, true, false, scale, + ctx, 1, None, width, height, 1, font, text, color, true, false, scale, ) } fn render2( ctx: &Rc, x: i32, + y: Option, width: i32, height: i32, padding: i32, @@ -128,8 +136,8 @@ fn render2( data.cctx.set_operator(CAIRO_OPERATOR_SOURCE); data.cctx .set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _); - data.cctx - .move_to(x as f64, ((height - font_height) / 2) as f64); + let y = y.unwrap_or((height - font_height) / 2); + data.cctx.move_to(x as f64, y as f64); data.layout.show_layout(); data.image.flush(); let bytes = match data.image.data() { @@ -144,19 +152,44 @@ fn render2( pub fn render_fitting( ctx: &Rc, - height: i32, + height: Option, font: &str, text: &str, color: Color, markup: bool, scale: Option, ) -> Result, TextError> { - let rect = measure(font, text, markup, scale)?; - render2( + render_fitting2(ctx, height, font, text, color, markup, scale, false).map(|(a, _)| a) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct TextMeasurement { + pub ink_rect: Rect, + pub logical_rect: Rect, + pub baseline: i32, +} + +pub fn render_fitting2( + ctx: &Rc, + height: Option, + font: &str, + text: &str, + color: Color, + markup: bool, + scale: Option, + include_measurements: bool, +) -> Result<(Rc, TextMeasurement), TextError> { + let measurement = measure(font, text, markup, scale, include_measurements)?; + let y = match height { + Some(_) => None, + _ => Some(measurement.ink_rect.y1().neg()), + }; + let res = render2( ctx, - rect.x1().neg(), - rect.width(), - height, + measurement.ink_rect.x1().neg(), + y, + measurement.ink_rect.width(), + height.unwrap_or(measurement.ink_rect.height()), 0, font, text, @@ -164,5 +197,6 @@ pub fn render_fitting( false, markup, scale, - ) + ); + res.map(|r| (r, measurement)) } diff --git a/src/tree/output.rs b/src/tree/output.rs index 0de35223..fcf8749f 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -168,7 +168,7 @@ impl OutputNode { }; let title = match text::render_fitting( &ctx, - texture_height, + Some(texture_height), &font, &ws.name, tc, @@ -219,14 +219,21 @@ impl OutputNode { break 'set_status; } let tc = self.state.theme.colors.bar_text.get(); - let title = - match text::render_fitting(&ctx, texture_height, &font, &status, tc, true, scale) { - Ok(t) => t, - Err(e) => { - log::error!("Could not render status {}: {}", status, ErrorFmt(e)); - break 'set_status; - } - }; + let title = match text::render_fitting( + &ctx, + Some(texture_height), + &font, + &status, + tc, + true, + scale, + ) { + Ok(t) => t, + Err(e) => { + log::error!("Could not render status {}: {}", status, ErrorFmt(e)); + break 'set_status; + } + }; let mut width = title.width(); if let Some(scale) = scale { width = (width as f64 / scale).round() as _; diff --git a/src/tree/placeholder.rs b/src/tree/placeholder.rs index dc41a8e4..94eab6ad 100644 --- a/src/tree/placeholder.rs +++ b/src/tree/placeholder.rs @@ -61,7 +61,7 @@ impl PlaceholderNode { let font = format!("monospace {}", width / 10); match text::render_fitting( &ctx, - height, + Some(height), &font, "Fullscreen", self.toplevel.state.theme.colors.unfocused_title_text.get(),