Skip to main content

lgui_core/text/
service.rs

1use std::{cell::RefCell, sync::Arc};
2
3use crate::core::UiRect;
4
5use super::{TextLayout, TextLayoutRequest, TextMeasureRequest, TextMetrics};
6
7pub trait TextSystem: Send + Sync + 'static {
8    fn measure(&self, request: &TextMeasureRequest<'_>) -> Option<TextMetrics>;
9
10    fn layout(&self, _request: &TextLayoutRequest<'_>) -> Option<TextLayout> {
11        None
12    }
13}
14
15#[derive(Clone)]
16pub struct TextSystemHandle(Arc<dyn TextSystem>);
17
18impl TextSystemHandle {
19    pub fn new(system: impl TextSystem) -> Self {
20        Self(Arc::new(system))
21    }
22
23    fn measure(&self, request: &TextMeasureRequest<'_>) -> Option<TextMetrics> {
24        self.0.measure(request)
25    }
26
27    fn layout(&self, request: &TextLayoutRequest<'_>) -> Option<TextLayout> {
28        self.0.layout(request)
29    }
30}
31
32thread_local! {
33    static TEXT_SYSTEM: RefCell<Option<TextSystemHandle>> = const { RefCell::new(None) };
34}
35
36pub(crate) struct TextSystemGuard {
37    previous: Option<TextSystemHandle>,
38}
39
40impl Drop for TextSystemGuard {
41    fn drop(&mut self) {
42        TEXT_SYSTEM.with(|current| {
43            *current.borrow_mut() = self.previous.take();
44        });
45    }
46}
47
48pub(crate) fn install_text_system(system: TextSystemHandle) -> TextSystemGuard {
49    let previous = TEXT_SYSTEM.with(|current| current.borrow_mut().replace(system));
50    TextSystemGuard { previous }
51}
52
53pub fn measure(request: &TextMeasureRequest<'_>) -> Option<TextMetrics> {
54    TEXT_SYSTEM.with(|current| {
55        current
56            .borrow()
57            .as_ref()
58            .and_then(|system| system.measure(request))
59    })
60}
61
62pub fn layout(request: &TextLayoutRequest<'_>) -> Option<TextLayout> {
63    TEXT_SYSTEM.with(|current| {
64        current
65            .borrow()
66            .as_ref()
67            .and_then(|system| system.layout(request))
68    })
69}
70
71pub fn measure_width(text: &str, rect: UiRect, font_height: f32, font_weight: i32) -> Option<f32> {
72    let request = TextLayoutRequest::single_line(text, rect, font_height, font_weight);
73    layout(&request).map(|layout| layout.width).or_else(|| {
74        measure(&TextMeasureRequest {
75            text,
76            bounds: rect,
77            font_height,
78            font_weight,
79        })
80        .map(|metrics| metrics.width)
81    })
82}