1use std::sync::{
2 Arc,
3 Mutex,
4 MutexGuard,
5};
6#[cfg(feature = "rc-dom")]
7use std::{
8 cell::{
9 Ref,
10 RefCell,
11 RefMut,
12 },
13 rc::Rc,
14};
15
16use dioxus_core::VirtualDom;
17use freya_native_core::{
18 prelude::{
19 DioxusState,
20 State,
21 },
22 real_dom::{
23 NodeRef,
24 RealDom,
25 },
26 NodeId,
27 SendAnyMap,
28};
29use torin::prelude::*;
30
31use super::{
32 mutations_writer::MutationsWriter,
33 CompositorDirtyNodes,
34 ImagesCache,
35 ParagraphElements,
36};
37use crate::{
38 accessibility::{
39 AccessibilityDirtyNodes,
40 AccessibilityGenerator,
41 },
42 custom_attributes::CustomAttributeValues,
43 elements::ParagraphElement,
44 event_loop_messages::TextGroupMeasurement,
45 layers::Layers,
46 render::{
47 CompositorCache,
48 CompositorDirtyArea,
49 },
50 states::{
51 AccessibilityNodeState,
52 CanvasState,
53 CursorState,
54 FontStyleState,
55 ImageState,
56 LayerState,
57 LayoutState,
58 StyleState,
59 SvgState,
60 TransformState,
61 ViewportState,
62 },
63};
64
65pub type DioxusDOM = RealDom<CustomAttributeValues>;
66pub type DioxusNode<'a> = NodeRef<'a, CustomAttributeValues>;
67
68pub struct SafeDOM {
71 #[cfg(not(feature = "rc-dom"))]
72 pub fdom: FreyaDOM,
73
74 #[cfg(feature = "rc-dom")]
75 pub fdom: Rc<RefCell<FreyaDOM>>,
76}
77
78#[cfg(feature = "rc-dom")]
79impl Clone for SafeDOM {
80 fn clone(&self) -> Self {
81 Self {
82 fdom: self.fdom.clone(),
83 }
84 }
85}
86
87impl SafeDOM {
88 #[cfg(not(feature = "rc-dom"))]
89 pub fn new(fdom: FreyaDOM) -> Self {
90 Self { fdom }
91 }
92
93 #[cfg(feature = "rc-dom")]
94 pub fn new(fdom: FreyaDOM) -> Self {
95 Self {
96 fdom: Rc::new(RefCell::new(fdom)),
97 }
98 }
99
100 #[cfg(not(feature = "rc-dom"))]
102 pub fn get(&self) -> &FreyaDOM {
103 &self.fdom
104 }
105
106 #[cfg(not(feature = "rc-dom"))]
108 pub fn try_get(&self) -> Option<&FreyaDOM> {
109 Some(&self.fdom)
110 }
111
112 #[cfg(not(feature = "rc-dom"))]
114 pub fn get_mut(&mut self) -> &mut FreyaDOM {
115 &mut self.fdom
116 }
117
118 #[cfg(feature = "rc-dom")]
120 pub fn get(&self) -> Ref<FreyaDOM> {
121 return self.fdom.borrow();
122 }
123
124 #[cfg(feature = "rc-dom")]
126 pub fn get_mut(&self) -> RefMut<FreyaDOM> {
127 return self.fdom.borrow_mut();
128 }
129}
130
131pub struct FreyaDOM {
133 rdom: DioxusDOM,
134 dioxus_integration_state: DioxusState,
135 torin: Arc<Mutex<Torin<NodeId>>>,
136 paragraphs: Arc<Mutex<ParagraphElements>>,
137 layers: Arc<Mutex<Layers>>,
138 compositor_dirty_nodes: Arc<Mutex<CompositorDirtyNodes>>,
139 compositor_dirty_area: Arc<Mutex<CompositorDirtyArea>>,
140 compositor_cache: Arc<Mutex<CompositorCache>>,
141 accessibility_dirty_nodes: Arc<Mutex<AccessibilityDirtyNodes>>,
142 accessibility_generator: Arc<AccessibilityGenerator>,
143 images_cache: Arc<Mutex<ImagesCache>>,
144}
145
146impl Default for FreyaDOM {
147 fn default() -> Self {
148 let mut rdom = RealDom::<CustomAttributeValues>::new([
149 CursorState::to_type_erased(),
150 FontStyleState::to_type_erased(),
151 CanvasState::to_type_erased(),
152 LayoutState::to_type_erased(),
153 StyleState::to_type_erased(),
154 TransformState::to_type_erased(),
155 AccessibilityNodeState::to_type_erased(),
156 ViewportState::to_type_erased(),
157 LayerState::to_type_erased(),
158 SvgState::to_type_erased(),
159 ImageState::to_type_erased(),
160 ]);
161 let dioxus_integration_state = DioxusState::create(&mut rdom);
162 Self {
163 rdom,
164 dioxus_integration_state,
165 torin: Arc::new(Mutex::new(Torin::new())),
166 paragraphs: Arc::default(),
167 layers: Arc::default(),
168 compositor_dirty_nodes: Arc::default(),
169 compositor_dirty_area: Arc::default(),
170 compositor_cache: Arc::default(),
171 accessibility_dirty_nodes: Arc::default(),
172 accessibility_generator: Arc::default(),
173 images_cache: Arc::default(),
174 }
175 }
176}
177
178impl FreyaDOM {
179 pub fn layout(&self) -> MutexGuard<Torin<NodeId>> {
180 self.torin.lock().unwrap()
181 }
182
183 pub fn layers(&self) -> MutexGuard<Layers> {
184 self.layers.lock().unwrap()
185 }
186
187 pub fn paragraphs(&self) -> MutexGuard<ParagraphElements> {
188 self.paragraphs.lock().unwrap()
189 }
190
191 pub fn compositor_dirty_nodes(&self) -> MutexGuard<CompositorDirtyNodes> {
192 self.compositor_dirty_nodes.lock().unwrap()
193 }
194
195 pub fn compositor_dirty_area(&self) -> MutexGuard<CompositorDirtyArea> {
196 self.compositor_dirty_area.lock().unwrap()
197 }
198
199 pub fn compositor_cache(&self) -> MutexGuard<CompositorCache> {
200 self.compositor_cache.lock().unwrap()
201 }
202
203 pub fn accessibility_dirty_nodes(&self) -> MutexGuard<AccessibilityDirtyNodes> {
204 self.accessibility_dirty_nodes.lock().unwrap()
205 }
206
207 pub fn accessibility_generator(&self) -> &Arc<AccessibilityGenerator> {
208 &self.accessibility_generator
209 }
210
211 pub fn images_cache(&self) -> MutexGuard<ImagesCache> {
212 self.images_cache.lock().unwrap()
213 }
214
215 pub fn init_dom(&mut self, vdom: &mut VirtualDom, scale_factor: f32) {
217 vdom.rebuild(&mut MutationsWriter {
219 native_writer: self
220 .dioxus_integration_state
221 .create_mutation_writer(&mut self.rdom),
222 layout: &mut self.torin.lock().unwrap(),
223 layers: &mut self.layers.lock().unwrap(),
224 paragraphs: &mut self.paragraphs.lock().unwrap(),
225 scale_factor,
226 compositor_dirty_nodes: &mut self.compositor_dirty_nodes.lock().unwrap(),
227 compositor_dirty_area: &mut self.compositor_dirty_area.lock().unwrap(),
228 compositor_cache: &mut self.compositor_cache.lock().unwrap(),
229 accessibility_dirty_nodes: &mut self.accessibility_dirty_nodes.lock().unwrap(),
230 images_cache: &mut self.images_cache.lock().unwrap(),
231 });
232
233 let mut ctx = SendAnyMap::new();
234 ctx.insert(self.torin.clone());
235 ctx.insert(self.layers.clone());
236 ctx.insert(self.paragraphs.clone());
237 ctx.insert(self.compositor_dirty_nodes.clone());
238 ctx.insert(self.accessibility_dirty_nodes.clone());
239 ctx.insert(self.rdom.root_id());
240 ctx.insert(self.accessibility_generator.clone());
241 ctx.insert(self.images_cache.clone());
242
243 self.rdom.update_state(ctx);
244 }
245
246 pub fn render_mutations(&mut self, vdom: &mut VirtualDom, scale_factor: f32) -> (bool, bool) {
248 vdom.render_immediate(&mut MutationsWriter {
250 native_writer: self
251 .dioxus_integration_state
252 .create_mutation_writer(&mut self.rdom),
253 layout: &mut self.torin.lock().unwrap(),
254 layers: &mut self.layers.lock().unwrap(),
255 paragraphs: &mut self.paragraphs.lock().unwrap(),
256 scale_factor,
257 compositor_dirty_nodes: &mut self.compositor_dirty_nodes.lock().unwrap(),
258 compositor_dirty_area: &mut self.compositor_dirty_area.lock().unwrap(),
259 compositor_cache: &mut self.compositor_cache.lock().unwrap(),
260 accessibility_dirty_nodes: &mut self.accessibility_dirty_nodes.lock().unwrap(),
261 images_cache: &mut self.images_cache.lock().unwrap(),
262 });
263
264 let mut ctx = SendAnyMap::new();
266 ctx.insert(self.torin.clone());
267 ctx.insert(self.layers.clone());
268 ctx.insert(self.paragraphs.clone());
269 ctx.insert(self.compositor_dirty_nodes.clone());
270 ctx.insert(self.accessibility_dirty_nodes.clone());
271 ctx.insert(self.rdom.root_id());
272 ctx.insert(self.accessibility_generator.clone());
273 ctx.insert(self.images_cache.clone());
274
275 let diff = self.rdom.update_state(ctx);
277
278 let must_repaint = !diff.is_empty();
279 let must_relayout = !self.layout().get_dirty_nodes().is_empty();
280
281 #[cfg(debug_assertions)]
282 if !diff.is_empty() {
283 tracing::info!(
284 "Updated {} nodes in RealDOM, now of size {}",
285 diff.len(),
286 self.rdom().tree_ref().len()
287 );
288 }
289
290 (must_repaint, must_relayout)
291 }
292
293 pub fn rdom(&self) -> &DioxusDOM {
295 &self.rdom
296 }
297
298 pub fn rdom_mut(&mut self) -> &mut DioxusDOM {
300 &mut self.rdom
301 }
302
303 pub fn state_mut(&mut self) -> &mut DioxusState {
304 &mut self.dioxus_integration_state
305 }
306
307 pub fn measure_paragraphs(&self, text_measurement: TextGroupMeasurement, scale_factor: f64) {
309 let paragraphs = self.paragraphs.lock().unwrap();
310 let group = paragraphs.get(&text_measurement.text_id);
311 let layout = self.layout();
312 if let Some(group) = group {
313 for node_id in group {
314 let node = self.rdom().get(*node_id);
315 let layout_node = layout.get(*node_id);
316
317 if let Some((node, layout_node)) = node.zip(layout_node) {
318 ParagraphElement::measure_paragraph(
319 &node,
320 layout_node,
321 &text_measurement,
322 scale_factor,
323 );
324 }
325 }
326 }
327 }
328}