1use std::cell::RefCell;
2use std::rc::Rc;
3use std::sync::Arc;
4
5use geometry_core::Rect;
6use layout_core::{LayoutError, LayoutStyle};
7use platform_core::Event;
8use renderer_core::TextStyle;
9use ui_tree::{Component, EventResult, RenderNode};
10
11use crate::impl_leaf_widget;
12use crate::layout_leaf::LayoutLeaf;
13
14pub struct Text {
15 content: Rc<dyn Fn() -> String>,
16 cached_content: RefCell<(String, Arc<str>)>,
17 cached_ink: RefCell<Option<(String, u32, f32, f32)>>,
20 style: Rc<dyn Fn() -> TextStyle>,
21 leaf: LayoutLeaf,
22}
23
24impl Text {
25 pub fn new(
26 content_fn: impl Fn() -> String + 'static,
27 layout_style: LayoutStyle,
28 style_fn: impl Fn() -> TextStyle + 'static,
29 ) -> Result<Self, LayoutError> {
30 let leaf = LayoutLeaf::register(layout_style.align_self_stretch())?;
32 Ok(Self {
33 content: Rc::new(content_fn),
34 cached_content: RefCell::new((String::new(), Arc::from(""))),
35 cached_ink: RefCell::new(None),
36 style: Rc::new(style_fn),
37 leaf,
38 })
39 }
40
41 pub fn auto(
45 content_fn: impl Fn() -> String + 'static,
46 layout_style: LayoutStyle,
47 style_fn: impl Fn() -> TextStyle + 'static,
48 ) -> Result<Self, LayoutError> {
49 let content_fn: Rc<dyn Fn() -> String> = Rc::new(content_fn);
50 let style: Rc<dyn Fn() -> TextStyle> = Rc::new(style_fn);
51
52 let measure_content = Rc::clone(&content_fn);
53 let measure_style = Rc::clone(&style);
54 let measure = Box::new(move |max_width: f32| {
55 let s = (measure_style)();
56 renderer_text::measure_text(&(measure_content)(), max_width, &s)
57 });
58
59 let (node, rect) =
60 crate::context::new_measured_leaf(layout_style.align_self_stretch(), measure)?;
61 Ok(Self {
62 content: content_fn,
63 cached_content: RefCell::new((String::new(), Arc::from(""))),
64 cached_ink: RefCell::new(None),
65 style,
66 leaf: LayoutLeaf { node, rect },
67 })
68 }
69
70 pub fn single_line(
71 content_fn: impl Fn() -> String + 'static,
72 style_fn: impl Fn() -> TextStyle + 'static,
73 ) -> Result<Self, LayoutError> {
74 let height = style_fn().font_size * 1.4;
75 Text::new(content_fn, LayoutStyle::new().height(height), style_fn)
76 }
77}
78
79impl Component for Text {
80 fn view(&self) -> RenderNode {
81 let r = self.leaf.rect.get();
82 let text: Arc<str> = {
83 let new_str = (self.content)();
84 let mut cache = self.cached_content.borrow_mut();
85 if cache.0 != new_str {
86 let rc = Arc::from(new_str.as_str());
87 *cache = (new_str, Arc::clone(&rc));
88 rc
89 } else {
90 Arc::clone(&cache.1)
91 }
92 };
93 let style = (self.style)();
94 let (ink_top, ink_height) = {
99 let width_bits = r.width.to_bits();
100 let mut cache = self.cached_ink.borrow_mut();
101 match cache.as_ref() {
102 Some((t, w, top, h)) if *t == *text && *w == width_bits => (*top, *h),
103 _ => {
104 let (top, h) = renderer_text::measure_ink_bounds(&text, r.width, &style);
105 *cache = Some((text.to_string(), width_bits, top, h));
106 (top, h)
107 }
108 }
109 };
110 let (_, line_height) = renderer_text::measure_text(&text, r.width, &style);
113 let y = if ink_height > 0.0 {
114 r.height / 2.0 - ink_top - ink_height / 2.0
115 } else {
116 0.0
117 };
118 self.leaf.at_layout_position(RenderNode::text(
119 text,
120 Rect {
121 x: 0.0,
122 y,
123 width: r.width,
124 height: line_height,
125 },
126 style,
127 ))
128 }
129
130 fn on_event(&mut self, _event: &Event) -> EventResult {
131 EventResult::Ignored
132 }
133
134 fn debug_name(&self) -> &'static str {
135 "Text"
136 }
137}
138
139impl_leaf_widget!(Text);