use std::sync::{Arc, RwLock};
use crate::{TextRun, TextStyle};
pub trait TextMetrics: Send + Sync + 'static {
fn measure(&self, text: &str, max_width: f32, style: &TextStyle) -> (f32, f32);
fn measure_runs(&self, runs: &[TextRun], max_width: f32, base: &TextStyle) -> (f32, f32);
fn ink_bounds(&self, text: &str, max_width: f32, style: &TextStyle) -> (f32, f32);
fn line_height(&self, font_size: f32) -> f32;
}
static TEXT_METRICS: RwLock<Option<Arc<dyn TextMetrics>>> = RwLock::new(None);
pub fn set_text_metrics(metrics: impl TextMetrics) {
*TEXT_METRICS.write().expect("text metrics lock") = Some(Arc::new(metrics));
}
pub fn set_default_text_metrics(metrics: impl TextMetrics) -> bool {
let mut slot = TEXT_METRICS.write().expect("text metrics lock");
if slot.is_some() {
return false;
}
*slot = Some(Arc::new(metrics));
true
}
fn metrics() -> Arc<dyn TextMetrics> {
TEXT_METRICS
.read()
.expect("text metrics lock")
.as_ref()
.cloned()
.expect(
"no TextMetrics installed, so nothing can size text: install one with \
renderer_core::set_text_metrics (renderer_text::ShaperMetrics is the raster default, and \
renderer_text::set_measure_font_config installs it for you)",
)
}
pub fn measure_text(text: &str, max_width: f32, style: &TextStyle) -> (f32, f32) {
metrics().measure(text, max_width, style)
}
pub fn measure_rich_text(runs: &[TextRun], max_width: f32, base: &TextStyle) -> (f32, f32) {
metrics().measure_runs(runs, max_width, base)
}
pub fn measure_ink_bounds(text: &str, max_width: f32, style: &TextStyle) -> (f32, f32) {
metrics().ink_bounds(text, max_width, style)
}
pub fn line_height(font_size: f32) -> f32 {
metrics().line_height(font_size)
}
#[cfg(test)]
mod tests {
use super::*;
struct Fixed(f32);
impl TextMetrics for Fixed {
fn measure(&self, text: &str, _max_width: f32, _style: &TextStyle) -> (f32, f32) {
(text.chars().count() as f32 * self.0, self.0)
}
fn measure_runs(
&self,
_runs: &[TextRun],
_max_width: f32,
_base: &TextStyle,
) -> (f32, f32) {
(0.0, self.0)
}
fn ink_bounds(&self, _text: &str, _max_width: f32, _style: &TextStyle) -> (f32, f32) {
(0.0, self.0)
}
fn line_height(&self, _font_size: f32) -> f32 {
self.0
}
}
#[test]
fn a_frontends_own_metrics_survive_the_runtimes_default() {
let style = TextStyle::new(14.0, crate::Color::BLACK);
assert!(
set_default_text_metrics(Fixed(10.0)),
"the first default install takes"
);
assert_eq!(measure_text("ab", 1.0e6, &style).0, 20.0);
assert!(
!set_default_text_metrics(Fixed(1.0)),
"a second default must not displace metrics already in force — that is the whole point of it"
);
assert_eq!(line_height(14.0), 10.0);
set_text_metrics(Fixed(2.0));
assert_eq!(
line_height(14.0),
2.0,
"an explicit install replaces, so a frontend can change its mind"
);
}
}