1use glyphon::cosmic_text::Align;
2use std::cell::{Ref, RefCell};
3use std::rc::Rc;
4
5use rootvg_core::math::Size;
6
7use super::{TextProperties, WRITE_LOCK_PANIC_MSG};
8
9#[derive(Debug)]
10struct TextBufferInner {
11 raw_buffer: glyphon::Buffer,
12 props: TextProperties,
13 bounds_size: Size,
14 has_text: bool,
15}
16
17#[derive(Debug)]
18pub struct RcTextBuffer {
19 inner: Rc<RefCell<TextBufferInner>>,
20}
21
22impl RcTextBuffer {
23 pub fn new(text: &str, props: TextProperties, bounds_size: Size) -> Self {
24 let mut font_system = super::font_system().write().expect(WRITE_LOCK_PANIC_MSG);
25
26 let mut raw_buffer = glyphon::Buffer::new(font_system.raw_mut(), props.metrics);
27
28 raw_buffer.set_size(font_system.raw_mut(), bounds_size.width, bounds_size.height);
29 raw_buffer.set_wrap(font_system.raw_mut(), props.wrap);
30 raw_buffer.set_text(font_system.raw_mut(), text, props.attrs, props.shaping);
31
32 let has_text = !text.is_empty();
33 if has_text {
34 shape(&mut raw_buffer, font_system.raw_mut(), props.align);
35 }
36
37 Self {
38 inner: Rc::new(RefCell::new(TextBufferInner {
39 raw_buffer,
40 props,
41 bounds_size,
42 has_text,
43 })),
44 }
45 }
46
47 pub fn bounds_size(&self) -> Size {
48 RefCell::borrow(&self.inner).bounds_size
49 }
50
51 pub fn props<'a>(&'a self) -> Ref<'a, TextProperties> {
52 let inner = RefCell::borrow(&self.inner);
53 Ref::map(inner, |inner| &inner.props)
54 }
55
56 pub fn measure(&self) -> Size {
58 let inner = RefCell::borrow(&self.inner);
59 let buffer = &inner.raw_buffer;
60
61 let (width, total_lines) = buffer
62 .layout_runs()
63 .fold((0.0, 0usize), |(width, total_lines), run| {
64 (run.line_w.max(width), total_lines + 1)
65 });
66
67 Size::new(width, total_lines as f32 * buffer.metrics().line_height)
68 }
69
70 pub fn set_text_and_props(&mut self, text: &str, props: TextProperties) {
71 let mut font_system = super::font_system().write().expect(WRITE_LOCK_PANIC_MSG);
72
73 let mut inner = RefCell::borrow_mut(&self.inner);
74
75 if inner.props.metrics != props.metrics {
76 inner
77 .raw_buffer
78 .set_metrics(font_system.raw_mut(), props.metrics)
79 }
80
81 if inner.props.wrap != props.wrap {
82 inner.raw_buffer.set_wrap(font_system.raw_mut(), props.wrap);
83 }
84
85 inner
86 .raw_buffer
87 .set_text(font_system.raw_mut(), text, props.attrs, props.shaping);
88
89 inner.has_text = !text.is_empty();
90
91 if inner.has_text {
92 shape(&mut inner.raw_buffer, font_system.raw_mut(), props.align);
93 }
94
95 inner.props = props;
96 }
97
98 pub fn set_text(&mut self, text: &str) {
99 let mut font_system = super::font_system().write().expect(WRITE_LOCK_PANIC_MSG);
100
101 let mut inner = RefCell::borrow_mut(&self.inner);
102 let TextBufferInner {
103 raw_buffer,
104 props,
105 bounds_size: _,
106 has_text,
107 } = &mut *inner;
108
109 raw_buffer.set_text(font_system.raw_mut(), text, props.attrs, props.shaping);
110
111 *has_text = !text.is_empty();
112
113 if *has_text {
114 shape(raw_buffer, font_system.raw_mut(), props.align);
115 }
116 }
117
118 pub fn set_bounds(&mut self, bounds_size: Size) {
120 let mut inner = RefCell::borrow_mut(&self.inner);
121 let TextBufferInner {
122 raw_buffer,
123 props,
124 bounds_size: inner_bounds_size,
125 has_text,
126 } = &mut *inner;
127
128 if *inner_bounds_size == bounds_size {
129 return;
130 }
131 *inner_bounds_size = bounds_size;
132
133 let mut font_system = super::font_system().write().expect(WRITE_LOCK_PANIC_MSG);
134
135 raw_buffer.set_size(
136 font_system.raw_mut(),
137 bounds_size.width as f32,
138 bounds_size.height as f32,
139 );
140
141 if *has_text {
142 shape(raw_buffer, font_system.raw_mut(), props.align);
143 }
144 }
145
146 pub(crate) fn raw_buffer<'a>(&'a self) -> Ref<'a, glyphon::Buffer> {
147 let inner = RefCell::borrow(&self.inner);
148 Ref::map(inner, |inner| &inner.raw_buffer)
149 }
150}
151
152impl Clone for RcTextBuffer {
153 fn clone(&self) -> Self {
154 Self {
155 inner: Rc::clone(&self.inner),
156 }
157 }
158}
159
160impl PartialEq for RcTextBuffer {
161 fn eq(&self, other: &Self) -> bool {
162 Rc::ptr_eq(&self.inner, &other.inner)
163 }
164}
165
166fn shape(
167 buffer: &mut glyphon::Buffer,
168 font_system: &mut glyphon::FontSystem,
169 align: Option<Align>,
170) {
171 for line in buffer.lines.iter_mut() {
172 if line.align() != align {
173 line.set_align(align);
174 }
175 }
176
177 buffer.shape_until_scroll(font_system);
178}