text: add metrics and dynamic height
This commit is contained in:
parent
2568b7b1f5
commit
8b93957f31
4 changed files with 84 additions and 28 deletions
27
src/pango.rs
27
src/pango.rs
|
|
@ -85,6 +85,7 @@ extern "C" {
|
||||||
ink_rect: *mut PangoRectangle,
|
ink_rect: *mut PangoRectangle,
|
||||||
logical_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);
|
fn pango_extents_to_pixels(inclusive: *mut PangoRectangle, nearest: *mut PangoRectangle);
|
||||||
}
|
}
|
||||||
|
|
@ -104,6 +105,7 @@ pub enum PangoError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Default)]
|
||||||
struct PangoRectangle {
|
struct PangoRectangle {
|
||||||
x: c::c_int,
|
x: c::c_int,
|
||||||
y: c::c_int,
|
y: c::c_int,
|
||||||
|
|
@ -111,6 +113,8 @@ struct PangoRectangle {
|
||||||
height: c::c_int,
|
height: c::c_int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PANGO_SCALE: i32 = 1024;
|
||||||
|
|
||||||
pub struct CairoImageSurface {
|
pub struct CairoImageSurface {
|
||||||
s: *mut cairo_surface_t,
|
s: *mut cairo_surface_t,
|
||||||
}
|
}
|
||||||
|
|
@ -325,18 +329,29 @@ impl PangoLayout {
|
||||||
|
|
||||||
pub fn inc_pixel_rect(&self) -> Rect {
|
pub fn inc_pixel_rect(&self) -> Rect {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut rect = PangoRectangle {
|
let mut rect = PangoRectangle::default();
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
};
|
|
||||||
pango_layout_get_extents(self.l, &mut rect, ptr::null_mut());
|
pango_layout_get_extents(self.l, &mut rect, ptr::null_mut());
|
||||||
pango_extents_to_pixels(&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()
|
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) {
|
pub fn show_layout(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
pango_cairo_show_layout(self.c.c.c, self.l);
|
pango_cairo_show_layout(self.c.c.c, self.l);
|
||||||
|
|
|
||||||
58
src/text.rs
58
src/text.rs
|
|
@ -76,14 +76,21 @@ pub fn measure(
|
||||||
text: &str,
|
text: &str,
|
||||||
markup: bool,
|
markup: bool,
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
) -> Result<Rect, TextError> {
|
full: bool,
|
||||||
|
) -> Result<TextMeasurement, TextError> {
|
||||||
let data = create_data(font, 1, 1, scale)?;
|
let data = create_data(font, 1, 1, scale)?;
|
||||||
if markup {
|
if markup {
|
||||||
data.layout.set_markup(text);
|
data.layout.set_markup(text);
|
||||||
} else {
|
} else {
|
||||||
data.layout.set_text(text);
|
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(
|
pub fn render(
|
||||||
|
|
@ -96,13 +103,14 @@ pub fn render(
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
) -> Result<Rc<Texture>, TextError> {
|
) -> Result<Rc<Texture>, TextError> {
|
||||||
render2(
|
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(
|
fn render2(
|
||||||
ctx: &Rc<RenderContext>,
|
ctx: &Rc<RenderContext>,
|
||||||
x: i32,
|
x: i32,
|
||||||
|
y: Option<i32>,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
padding: i32,
|
padding: i32,
|
||||||
|
|
@ -128,8 +136,8 @@ fn render2(
|
||||||
data.cctx.set_operator(CAIRO_OPERATOR_SOURCE);
|
data.cctx.set_operator(CAIRO_OPERATOR_SOURCE);
|
||||||
data.cctx
|
data.cctx
|
||||||
.set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _);
|
.set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _);
|
||||||
data.cctx
|
let y = y.unwrap_or((height - font_height) / 2);
|
||||||
.move_to(x as f64, ((height - font_height) / 2) as f64);
|
data.cctx.move_to(x as f64, y as f64);
|
||||||
data.layout.show_layout();
|
data.layout.show_layout();
|
||||||
data.image.flush();
|
data.image.flush();
|
||||||
let bytes = match data.image.data() {
|
let bytes = match data.image.data() {
|
||||||
|
|
@ -144,19 +152,44 @@ fn render2(
|
||||||
|
|
||||||
pub fn render_fitting(
|
pub fn render_fitting(
|
||||||
ctx: &Rc<RenderContext>,
|
ctx: &Rc<RenderContext>,
|
||||||
height: i32,
|
height: Option<i32>,
|
||||||
font: &str,
|
font: &str,
|
||||||
text: &str,
|
text: &str,
|
||||||
color: Color,
|
color: Color,
|
||||||
markup: bool,
|
markup: bool,
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
) -> Result<Rc<Texture>, TextError> {
|
) -> Result<Rc<Texture>, TextError> {
|
||||||
let rect = measure(font, text, markup, scale)?;
|
render_fitting2(ctx, height, font, text, color, markup, scale, false).map(|(a, _)| a)
|
||||||
render2(
|
}
|
||||||
|
|
||||||
|
#[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<RenderContext>,
|
||||||
|
height: Option<i32>,
|
||||||
|
font: &str,
|
||||||
|
text: &str,
|
||||||
|
color: Color,
|
||||||
|
markup: bool,
|
||||||
|
scale: Option<f64>,
|
||||||
|
include_measurements: bool,
|
||||||
|
) -> Result<(Rc<Texture>, 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,
|
ctx,
|
||||||
rect.x1().neg(),
|
measurement.ink_rect.x1().neg(),
|
||||||
rect.width(),
|
y,
|
||||||
height,
|
measurement.ink_rect.width(),
|
||||||
|
height.unwrap_or(measurement.ink_rect.height()),
|
||||||
0,
|
0,
|
||||||
font,
|
font,
|
||||||
text,
|
text,
|
||||||
|
|
@ -164,5 +197,6 @@ pub fn render_fitting(
|
||||||
false,
|
false,
|
||||||
markup,
|
markup,
|
||||||
scale,
|
scale,
|
||||||
)
|
);
|
||||||
|
res.map(|r| (r, measurement))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ impl OutputNode {
|
||||||
};
|
};
|
||||||
let title = match text::render_fitting(
|
let title = match text::render_fitting(
|
||||||
&ctx,
|
&ctx,
|
||||||
texture_height,
|
Some(texture_height),
|
||||||
&font,
|
&font,
|
||||||
&ws.name,
|
&ws.name,
|
||||||
tc,
|
tc,
|
||||||
|
|
@ -219,14 +219,21 @@ impl OutputNode {
|
||||||
break 'set_status;
|
break 'set_status;
|
||||||
}
|
}
|
||||||
let tc = self.state.theme.colors.bar_text.get();
|
let tc = self.state.theme.colors.bar_text.get();
|
||||||
let title =
|
let title = match text::render_fitting(
|
||||||
match text::render_fitting(&ctx, texture_height, &font, &status, tc, true, scale) {
|
&ctx,
|
||||||
Ok(t) => t,
|
Some(texture_height),
|
||||||
Err(e) => {
|
&font,
|
||||||
log::error!("Could not render status {}: {}", status, ErrorFmt(e));
|
&status,
|
||||||
break 'set_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();
|
let mut width = title.width();
|
||||||
if let Some(scale) = scale {
|
if let Some(scale) = scale {
|
||||||
width = (width as f64 / scale).round() as _;
|
width = (width as f64 / scale).round() as _;
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ impl PlaceholderNode {
|
||||||
let font = format!("monospace {}", width / 10);
|
let font = format!("monospace {}", width / 10);
|
||||||
match text::render_fitting(
|
match text::render_fitting(
|
||||||
&ctx,
|
&ctx,
|
||||||
height,
|
Some(height),
|
||||||
&font,
|
&font,
|
||||||
"Fullscreen",
|
"Fullscreen",
|
||||||
self.toplevel.state.theme.colors.unfocused_title_text.get(),
|
self.toplevel.state.theme.colors.unfocused_title_text.get(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue