ferroui_pretext/
pretext_layout_backends.rs1use 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
11pub 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 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 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}