1#![warn(missing_docs)]
5use super::items::*;
8use crate::graphics::{FontRequest, Image, IntRect};
9use crate::item_tree::ItemTreeRc;
10use crate::item_tree::{ItemVisitor, ItemVisitorVTable, VisitChildrenResult};
11use crate::lengths::{
12 LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector,
13};
14pub use crate::partial_renderer::CachedRenderingData;
15use crate::window::WindowAdapter;
16use crate::{Brush, SharedString};
17#[cfg(feature = "std")]
18use alloc::boxed::Box;
19use alloc::rc::Rc;
20#[cfg(feature = "std")]
21use core::cell::RefCell;
22use core::pin::Pin;
23#[cfg(feature = "std")]
24use std::collections::HashMap;
25use vtable::VRc;
26
27#[cfg(feature = "std")]
36pub struct ItemCache<T> {
37 map: RefCell<HashMap<*const vtable::Dyn, HashMap<u32, crate::graphics::CachedGraphicsData<T>>>>,
39 window_scale_factor_tracker: Pin<Box<crate::properties::PropertyTracker>>,
41}
42
43#[cfg(feature = "std")]
44impl<T> Default for ItemCache<T> {
45 fn default() -> Self {
46 Self { map: Default::default(), window_scale_factor_tracker: Box::pin(Default::default()) }
47 }
48}
49
50#[cfg(feature = "std")]
51impl<T: Clone> ItemCache<T> {
52 pub fn get_or_update_cache_entry(&self, item_rc: &ItemRc, update_fn: impl FnOnce() -> T) -> T {
56 let component = &(**item_rc.item_tree()) as *const _;
57 let mut borrowed = self.map.borrow_mut();
58 match borrowed.entry(component).or_default().entry(item_rc.index()) {
59 std::collections::hash_map::Entry::Occupied(mut entry) => {
60 let mut tracker = entry.get_mut().dependency_tracker.take();
61 drop(borrowed);
62 let maybe_new_data = tracker
63 .get_or_insert_with(|| Box::pin(Default::default()))
64 .as_ref()
65 .evaluate_if_dirty(update_fn);
66 let mut borrowed = self.map.borrow_mut();
67 let e = borrowed.get_mut(&component).unwrap().get_mut(&item_rc.index()).unwrap();
68 e.dependency_tracker = tracker;
69 if let Some(new_data) = maybe_new_data {
70 e.data = new_data.clone();
71 new_data
72 } else {
73 e.data.clone()
74 }
75 }
76 std::collections::hash_map::Entry::Vacant(_) => {
77 drop(borrowed);
78 let new_entry = crate::graphics::CachedGraphicsData::new(update_fn);
79 let data = new_entry.data.clone();
80 self.map
81 .borrow_mut()
82 .get_mut(&component)
83 .unwrap()
84 .insert(item_rc.index(), new_entry);
85 data
86 }
87 }
88 }
89
90 pub fn with_entry<U>(
93 &self,
94 item_rc: &ItemRc,
95 callback: impl FnOnce(&T) -> Option<U>,
96 ) -> Option<U> {
97 let component = &(**item_rc.item_tree()) as *const _;
98 self.map
99 .borrow()
100 .get(&component)
101 .and_then(|per_component_entries| per_component_entries.get(&item_rc.index()))
102 .and_then(|entry| callback(&entry.data))
103 }
104
105 pub fn clear_cache_if_scale_factor_changed(&self, window: &crate::api::Window) {
107 if self.window_scale_factor_tracker.is_dirty() {
108 self.window_scale_factor_tracker
109 .as_ref()
110 .evaluate_as_dependency_root(|| window.scale_factor());
111 self.clear_all();
112 }
113 }
114
115 pub fn clear_all(&self) {
117 self.map.borrow_mut().clear();
118 }
119
120 pub fn component_destroyed(&self, component: crate::item_tree::ItemTreeRef) {
124 let component_ptr: *const _ =
125 crate::item_tree::ItemTreeRef::as_ptr(component).cast().as_ptr();
126 self.map.borrow_mut().remove(&component_ptr);
127 }
128
129 pub fn release(&self, item_rc: &ItemRc) {
131 let component = &(**item_rc.item_tree()) as *const _;
132 if let Some(sub) = self.map.borrow_mut().get_mut(&component) {
133 sub.remove(&item_rc.index());
134 }
135 }
136
137 pub fn is_empty(&self) -> bool {
139 self.map.borrow().is_empty()
140 }
141}
142
143pub fn render_item_children(
145 renderer: &mut dyn ItemRenderer,
146 component: &ItemTreeRc,
147 index: isize,
148 window_adapter: &Rc<dyn WindowAdapter>,
149) {
150 let mut actual_visitor =
151 |component: &ItemTreeRc, index: u32, item: Pin<ItemRef>| -> VisitChildrenResult {
152 renderer.save_state();
153 let item_rc = ItemRc::new(component.clone(), index);
154
155 let (do_draw, item_geometry) = renderer.filter_item(&item_rc, window_adapter);
156
157 let item_origin = item_geometry.origin;
158 renderer.translate(item_origin.to_vector());
159
160 let render_result = if do_draw
163 || item.as_ref().clips_children()
164 || ItemRef::downcast_pin::<BoxShadow>(item).is_some()
166 {
167 item.as_ref().render(
168 &mut (renderer as &mut dyn ItemRenderer),
169 &item_rc,
170 item_geometry.size,
171 )
172 } else {
173 RenderingResult::ContinueRenderingChildren
174 };
175
176 if matches!(render_result, RenderingResult::ContinueRenderingChildren) {
177 render_item_children(renderer, component, index as isize, window_adapter);
178 }
179 renderer.restore_state();
180 VisitChildrenResult::CONTINUE
181 };
182 vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
183 VRc::borrow_pin(component).as_ref().visit_children_item(
184 index,
185 crate::item_tree::TraversalOrder::BackToFront,
186 actual_visitor,
187 );
188}
189
190pub fn render_component_items(
193 component: &ItemTreeRc,
194 renderer: &mut dyn ItemRenderer,
195 origin: LogicalPoint,
196 window_adapter: &Rc<dyn WindowAdapter>,
197) {
198 renderer.save_state();
199 renderer.translate(origin.to_vector());
200
201 render_item_children(renderer, component, -1, window_adapter);
202
203 renderer.restore_state();
204}
205
206pub fn item_children_bounding_rect(
209 component: &ItemTreeRc,
210 index: isize,
211 clip_rect: &LogicalRect,
212) -> LogicalRect {
213 item_children_bounding_rect_inner(component, index, clip_rect, Default::default())
214}
215
216fn item_children_bounding_rect_inner(
217 component: &ItemTreeRc,
218 index: isize,
219 clip_rect: &LogicalRect,
220 transform: crate::lengths::ItemTransform,
221) -> LogicalRect {
222 let mut bounding_rect = LogicalRect::zero();
223
224 let mut actual_visitor =
225 |component: &ItemTreeRc, index: u32, item: Pin<ItemRef>| -> VisitChildrenResult {
226 let item_geometry = transform.outer_transformed_rect(
227 &ItemTreeRc::borrow_pin(component).as_ref().item_geometry(index).cast(),
228 );
229 let children_transform = ItemRc::new(component.clone(), index)
230 .children_transform()
231 .unwrap_or_default()
232 .then_translate(item_geometry.origin.to_vector());
233
234 let offset: LogicalPoint = item_geometry.origin.cast();
235 let local_clip_rect = clip_rect.translate(-offset.to_vector());
236
237 if let Some(clipped_item_geometry) = item_geometry.intersection(&clip_rect.cast()) {
238 bounding_rect = bounding_rect.union(&clipped_item_geometry.cast());
239 }
240
241 if !item.as_ref().clips_children() {
242 bounding_rect = bounding_rect.union(&item_children_bounding_rect_inner(
243 component,
244 index as isize,
245 &local_clip_rect,
246 transform.then(&children_transform),
247 ));
248 }
249 VisitChildrenResult::CONTINUE
250 };
251 vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
252 VRc::borrow_pin(component).as_ref().visit_children_item(
253 index,
254 crate::item_tree::TraversalOrder::BackToFront,
255 actual_visitor,
256 );
257
258 bounding_rect
259}
260
261#[allow(missing_docs)]
263pub trait RenderRectangle {
264 fn background(self: Pin<&Self>) -> Brush;
265}
266
267#[allow(missing_docs)]
269pub trait RenderBorderRectangle {
270 fn background(self: Pin<&Self>) -> Brush;
271 fn border_width(self: Pin<&Self>) -> LogicalLength;
272 fn border_radius(self: Pin<&Self>) -> LogicalBorderRadius;
273 fn border_color(self: Pin<&Self>) -> Brush;
274}
275
276#[allow(missing_docs)]
278pub trait RenderImage {
279 fn target_size(self: Pin<&Self>) -> LogicalSize;
280 fn source(self: Pin<&Self>) -> Image;
281 fn source_clip(self: Pin<&Self>) -> Option<IntRect>;
282 fn image_fit(self: Pin<&Self>) -> ImageFit;
283 fn rendering(self: Pin<&Self>) -> ImageRendering;
284 fn colorize(self: Pin<&Self>) -> Brush;
285 fn alignment(self: Pin<&Self>) -> (ImageHorizontalAlignment, ImageVerticalAlignment);
286 fn tiling(self: Pin<&Self>) -> (ImageTiling, ImageTiling);
287}
288
289#[allow(missing_docs)]
291pub trait RenderText {
292 fn target_size(self: Pin<&Self>) -> LogicalSize;
293 fn text(self: Pin<&Self>) -> SharedString;
294 fn font_request(self: Pin<&Self>, self_rc: &ItemRc) -> FontRequest;
295 fn color(self: Pin<&Self>) -> Brush;
296 fn alignment(self: Pin<&Self>) -> (TextHorizontalAlignment, TextVerticalAlignment);
297 fn wrap(self: Pin<&Self>) -> TextWrap;
298 fn overflow(self: Pin<&Self>) -> TextOverflow;
299 fn letter_spacing(self: Pin<&Self>) -> LogicalLength;
300 fn stroke(self: Pin<&Self>) -> (Brush, LogicalLength, TextStrokeStyle);
301 fn is_markdown(self: Pin<&Self>) -> bool;
302}
303
304impl RenderText for (SharedString, Brush) {
305 fn target_size(self: Pin<&Self>) -> LogicalSize {
306 LogicalSize::default()
307 }
308
309 fn text(self: Pin<&Self>) -> SharedString {
310 self.0.clone()
311 }
312
313 fn font_request(self: Pin<&Self>, _self_rc: &ItemRc) -> crate::graphics::FontRequest {
314 Default::default()
315 }
316
317 fn color(self: Pin<&Self>) -> Brush {
318 self.1.clone()
319 }
320
321 fn alignment(
322 self: Pin<&Self>,
323 ) -> (crate::items::TextHorizontalAlignment, crate::items::TextVerticalAlignment) {
324 Default::default()
325 }
326
327 fn wrap(self: Pin<&Self>) -> crate::items::TextWrap {
328 Default::default()
329 }
330
331 fn overflow(self: Pin<&Self>) -> crate::items::TextOverflow {
332 Default::default()
333 }
334
335 fn letter_spacing(self: Pin<&Self>) -> LogicalLength {
336 LogicalLength::default()
337 }
338
339 fn stroke(self: Pin<&Self>) -> (Brush, LogicalLength, TextStrokeStyle) {
340 Default::default()
341 }
342
343 fn is_markdown(self: Pin<&Self>) -> bool {
344 false
345 }
346}
347
348#[allow(missing_docs)]
353pub trait ItemRenderer {
354 fn draw_rectangle(
355 &mut self,
356 rect: Pin<&dyn RenderRectangle>,
357 _self_rc: &ItemRc,
358 _size: LogicalSize,
359 _cache: &CachedRenderingData,
360 );
361 fn draw_border_rectangle(
362 &mut self,
363 rect: Pin<&dyn RenderBorderRectangle>,
364 _self_rc: &ItemRc,
365 _size: LogicalSize,
366 _cache: &CachedRenderingData,
367 );
368 fn draw_window_background(
369 &mut self,
370 rect: Pin<&dyn RenderRectangle>,
371 self_rc: &ItemRc,
372 size: LogicalSize,
373 cache: &CachedRenderingData,
374 );
375 fn draw_image(
376 &mut self,
377 image: Pin<&dyn RenderImage>,
378 _self_rc: &ItemRc,
379 _size: LogicalSize,
380 _cache: &CachedRenderingData,
381 );
382 fn draw_text(
383 &mut self,
384 text: Pin<&dyn RenderText>,
385 _self_rc: &ItemRc,
386 _size: LogicalSize,
387 _cache: &CachedRenderingData,
388 );
389 fn draw_text_input(
390 &mut self,
391 text_input: Pin<&TextInput>,
392 _self_rc: &ItemRc,
393 _size: LogicalSize,
394 );
395 #[cfg(feature = "std")]
396 fn draw_path(&mut self, path: Pin<&Path>, _self_rc: &ItemRc, _size: LogicalSize);
397 fn draw_box_shadow(
398 &mut self,
399 box_shadow: Pin<&BoxShadow>,
400 _self_rc: &ItemRc,
401 _size: LogicalSize,
402 );
403 fn visit_opacity(
404 &mut self,
405 opacity_item: Pin<&Opacity>,
406 _self_rc: &ItemRc,
407 _size: LogicalSize,
408 ) -> RenderingResult {
409 self.apply_opacity(opacity_item.opacity());
410 RenderingResult::ContinueRenderingChildren
411 }
412 fn visit_layer(
413 &mut self,
414 _layer_item: Pin<&Layer>,
415 _self_rc: &ItemRc,
416 _size: LogicalSize,
417 ) -> RenderingResult {
418 RenderingResult::ContinueRenderingChildren
420 }
421
422 fn visit_clip(
426 &mut self,
427 clip_item: Pin<&Clip>,
428 _item_rc: &ItemRc,
429 size: LogicalSize,
430 ) -> RenderingResult {
431 if clip_item.clip() {
432 let clip_region_valid = self.combine_clip(
433 LogicalRect::new(LogicalPoint::default(), size),
434 clip_item.logical_border_radius(),
435 clip_item.border_width(),
436 );
437
438 if !clip_region_valid {
441 return RenderingResult::ContinueRenderingWithoutChildren;
442 }
443 }
444 RenderingResult::ContinueRenderingChildren
445 }
446
447 fn combine_clip(
453 &mut self,
454 rect: LogicalRect,
455 radius: LogicalBorderRadius,
456 border_width: LogicalLength,
457 ) -> bool;
458 fn get_current_clip(&self) -> LogicalRect;
460
461 fn translate(&mut self, distance: LogicalVector);
462 fn translation(&self) -> LogicalVector {
463 unimplemented!()
464 }
465 fn rotate(&mut self, angle_in_degrees: f32);
466 fn scale(&mut self, scale_x_factor: f32, scale_y_factor: f32);
467 fn apply_opacity(&mut self, opacity: f32);
469
470 fn save_state(&mut self);
471 fn restore_state(&mut self);
472
473 fn scale_factor(&self) -> f32;
475
476 fn draw_cached_pixmap(
481 &mut self,
482 item_cache: &ItemRc,
483 update_fn: &dyn Fn(&mut dyn FnMut(u32, u32, &[u8])),
484 );
485
486 fn draw_string(&mut self, string: &str, color: crate::Color);
489
490 fn draw_image_direct(&mut self, image: crate::graphics::Image);
491
492 fn filter_item(
497 &mut self,
498 item: &ItemRc,
499 window_adapter: &Rc<dyn WindowAdapter>,
500 ) -> (bool, LogicalRect) {
501 let item_geometry = item.geometry();
502 let bounding_rect = crate::properties::evaluate_no_tracking(|| {
505 item.bounding_rect(&item_geometry, window_adapter)
506 });
507 (self.get_current_clip().intersects(&bounding_rect), item_geometry)
508 }
509
510 fn window(&self) -> &crate::window::WindowInner;
511
512 fn as_any(&mut self) -> Option<&mut dyn core::any::Any>;
514}
515
516pub trait ItemRendererFeatures {
518 const SUPPORTS_TRANSFORMATIONS: bool;
520}