Skip to main content

telar_ui_core/
rich_text.rs

1use std::rc::Rc;
2use std::sync::Arc;
3
4use geometry_core::Rect;
5use layout_core::{LayoutError, LayoutStyle};
6use platform_core::Event;
7use renderer_core::{TextRun, TextStyle};
8use ui_tree::{Component, EventResult, RenderNode};
9
10use crate::impl_leaf_widget;
11use crate::layout_leaf::LayoutLeaf;
12
13/// A paragraph of mixed-style text: a sequence of [`TextRun`]s (bold, italic, coloured links) shaped and
14/// wrapped as one, the multi-style counterpart of [`Text`](crate::Text). The shared paragraph metrics — font
15/// size, line height, wrapping, `max_lines` — come from a base [`TextStyle`]; each run overrides only weight,
16/// slant, and colour.
17pub struct RichText {
18    runs: Rc<dyn Fn() -> Vec<TextRun>>,
19    base: Rc<dyn Fn() -> TextStyle>,
20    leaf: LayoutLeaf,
21}
22
23impl RichText {
24    /// A rich paragraph whose leaf height is measured from its runs at the resolved width, so the box grows to
25    /// fit however many lines they wrap into and pushes following siblings down — like [`Text::auto`].
26    pub fn auto(
27        runs_fn: impl Fn() -> Vec<TextRun> + 'static,
28        layout_style: LayoutStyle,
29        base_fn: impl Fn() -> TextStyle + 'static,
30    ) -> Result<Self, LayoutError> {
31        let runs: Rc<dyn Fn() -> Vec<TextRun>> = Rc::new(runs_fn);
32        let base: Rc<dyn Fn() -> TextStyle> = Rc::new(base_fn);
33
34        let measure_runs = Rc::clone(&runs);
35        let measure_base = Rc::clone(&base);
36        let measure = Box::new(move |max_width: f32| {
37            renderer_text::measure_rich_text(&(measure_runs)(), max_width, &(measure_base)())
38        });
39
40        let (node, rect) =
41            crate::context::new_measured_leaf(layout_style.align_self_stretch(), measure)?;
42        Ok(Self {
43            runs,
44            base,
45            leaf: LayoutLeaf { node, rect },
46        })
47    }
48}
49
50impl Component for RichText {
51    fn view(&self) -> RenderNode {
52        let r = self.leaf.rect.get();
53        let runs: Arc<[TextRun]> = Arc::from((self.runs)());
54        let base = (self.base)();
55        self.leaf.at_layout_position(RenderNode::rich_text(
56            runs,
57            Rect {
58                x: 0.0,
59                y: 0.0,
60                width: r.width,
61                height: r.height,
62            },
63            base,
64        ))
65    }
66
67    fn on_event(&mut self, _event: &Event) -> EventResult {
68        EventResult::Ignored
69    }
70
71    fn debug_name(&self) -> &'static str {
72        "RichText"
73    }
74}
75
76impl_leaf_widget!(RichText);