Skip to main content

ferroui_pretext/
pretext_layout_backends.rs

1use std::cell::{Cell, RefCell};
2use std::collections::HashMap;
3use std::rc::Rc;
4
5use crate::pretext_layout_measurement::FontState;
6use crate::pretext_layout_shaping::{PreparedShapedCacheEntry, ShaperState};
7use crate::{
8    IPretextTextMeasurerFactory, IPretextTextShaperFactory, PretextError, PretextTextDirection,
9};
10
11/// An isolated preparation session. Replacing a backend affects subsequent
12/// preparation; already prepared metrics and shaped objects remain usable.
13pub struct PretextLayout {
14    pub(crate) text_measurer_factory: RefCell<Rc<dyn IPretextTextMeasurerFactory>>,
15    pub(crate) text_shaper_factory: RefCell<Option<Rc<dyn IPretextTextShaperFactory>>>,
16    pub(crate) font_states: RefCell<HashMap<String, Rc<FontState>>>,
17    pub(crate) shaper_states: RefCell<HashMap<String, Rc<ShaperState>>>,
18    pub(crate) prepared_shaped:
19        RefCell<HashMap<(usize, PretextTextDirection), PreparedShapedCacheEntry>>,
20    pub(crate) locale: RefCell<Option<String>>,
21    pub(crate) cache_generation: Cell<u64>,
22}
23
24impl PretextLayout {
25    pub fn new(factory: Rc<dyn IPretextTextMeasurerFactory>) -> Self {
26        Self {
27            text_measurer_factory: RefCell::new(factory),
28            text_shaper_factory: RefCell::new(None),
29            font_states: RefCell::new(HashMap::new()),
30            shaper_states: RefCell::new(HashMap::new()),
31            prepared_shaped: RefCell::new(HashMap::new()),
32            locale: RefCell::new(None),
33            cache_generation: Cell::new(0),
34        }
35    }
36
37    pub fn with_shaper_factory(self, factory: Rc<dyn IPretextTextShaperFactory>) -> Self {
38        self.set_text_shaper_factory(Some(factory));
39        self
40    }
41
42    /// Selects the highest-priority supported registered measurement backend.
43    /// Equal priorities retain registration order. Reflection is unnecessary.
44    pub fn from_factories(
45        factories: impl IntoIterator<Item = Rc<dyn IPretextTextMeasurerFactory>>,
46    ) -> Result<Self, PretextError> {
47        let mut selected: Option<Rc<dyn IPretextTextMeasurerFactory>> = None;
48        for factory in factories {
49            if factory.is_supported()
50                && selected
51                    .as_ref()
52                    .is_none_or(|current| factory.priority() > current.priority())
53            {
54                selected = Some(factory);
55            }
56        }
57        selected.map(Self::new).ok_or_else(|| {
58            PretextError::Backend("no supported text measurement backend is registered".into())
59        })
60    }
61
62    pub fn text_measurer_factory(&self) -> Rc<dyn IPretextTextMeasurerFactory> {
63        self.text_measurer_factory.borrow().clone()
64    }
65
66    pub fn text_shaper_factory(&self) -> Result<Rc<dyn IPretextTextShaperFactory>, PretextError> {
67        let explicit = self.text_shaper_factory.borrow().clone();
68        let factory = match explicit {
69            Some(factory) => Some(factory),
70            None => self.text_measurer_factory().shaper_factory(),
71        };
72        factory
73            .filter(|factory| factory.is_supported())
74            .ok_or(PretextError::NoShapingBackend)
75    }
76
77    pub fn set_text_measurer_factory(&self, factory: Rc<dyn IPretextTextMeasurerFactory>) {
78        let previous = self.text_measurer_factory.replace(factory);
79        self.clear_cache();
80        drop(previous);
81    }
82
83    pub fn set_text_shaper_factory(&self, factory: Option<Rc<dyn IPretextTextShaperFactory>>) {
84        let previous = self.text_shaper_factory.replace(factory);
85        self.clear_cache();
86        drop(previous);
87    }
88
89    pub fn clear_cache(&self) {
90        // Drop backend handles after releasing every RefCell borrow: backend
91        // destructors can invoke application callbacks.
92        let fonts = std::mem::take(&mut *self.font_states.borrow_mut());
93        let shapers = std::mem::take(&mut *self.shaper_states.borrow_mut());
94        let prepared = std::mem::take(&mut *self.prepared_shaped.borrow_mut());
95        self.cache_generation
96            .set(self.cache_generation.get().wrapping_add(1));
97        drop((fonts, shapers, prepared));
98    }
99
100    pub fn set_locale(&self, locale: Option<&str>) {
101        *self.locale.borrow_mut() = locale.map(str::to_owned);
102        self.clear_cache();
103    }
104
105    pub fn locale(&self) -> Option<String> {
106        self.locale.borrow().clone()
107    }
108}