lgui-core 0.2.0

Platform-neutral runtime and UI primitives for LGUI
Documentation
use std::{cell::RefCell, sync::Arc};

use crate::core::UiRect;

use super::{TextLayout, TextLayoutRequest, TextMeasureRequest, TextMetrics};

pub trait TextSystem: Send + Sync + 'static {
    fn measure(&self, request: &TextMeasureRequest<'_>) -> Option<TextMetrics>;

    fn layout(&self, _request: &TextLayoutRequest<'_>) -> Option<TextLayout> {
        None
    }
}

#[derive(Clone)]
pub struct TextSystemHandle(Arc<dyn TextSystem>);

impl TextSystemHandle {
    pub fn new(system: impl TextSystem) -> Self {
        Self(Arc::new(system))
    }

    fn measure(&self, request: &TextMeasureRequest<'_>) -> Option<TextMetrics> {
        self.0.measure(request)
    }

    fn layout(&self, request: &TextLayoutRequest<'_>) -> Option<TextLayout> {
        self.0.layout(request)
    }
}

thread_local! {
    static TEXT_SYSTEM: RefCell<Option<TextSystemHandle>> = const { RefCell::new(None) };
}

pub(crate) struct TextSystemGuard {
    previous: Option<TextSystemHandle>,
}

impl Drop for TextSystemGuard {
    fn drop(&mut self) {
        TEXT_SYSTEM.with(|current| {
            *current.borrow_mut() = self.previous.take();
        });
    }
}

pub(crate) fn install_text_system(system: TextSystemHandle) -> TextSystemGuard {
    let previous = TEXT_SYSTEM.with(|current| current.borrow_mut().replace(system));
    TextSystemGuard { previous }
}

pub fn measure(request: &TextMeasureRequest<'_>) -> Option<TextMetrics> {
    TEXT_SYSTEM.with(|current| {
        current
            .borrow()
            .as_ref()
            .and_then(|system| system.measure(request))
    })
}

pub fn layout(request: &TextLayoutRequest<'_>) -> Option<TextLayout> {
    TEXT_SYSTEM.with(|current| {
        current
            .borrow()
            .as_ref()
            .and_then(|system| system.layout(request))
    })
}

pub fn measure_width(text: &str, rect: UiRect, font_height: f32, font_weight: i32) -> Option<f32> {
    let request = TextLayoutRequest::single_line(text, rect, font_height, font_weight);
    layout(&request).map(|layout| layout.width).or_else(|| {
        measure(&TextMeasureRequest {
            text,
            bounds: rect,
            font_height,
            font_weight,
        })
        .map(|metrics| metrics.width)
    })
}