1#![warn(missing_docs)]
5use super::items::*;
8use crate::graphics::{Color, 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::WindowAdapterRc;
16use crate::{Brush, SharedString};
17#[cfg(feature = "std")]
18use alloc::boxed::Box;
19#[cfg(feature = "std")]
20use core::cell::RefCell;
21use core::pin::Pin;
22#[cfg(feature = "std")]
23use std::collections::HashMap;
24use vtable::VRc;
25
26#[cfg(feature = "std")]
35pub struct ItemCache<T> {
36 map: RefCell<HashMap<*const vtable::Dyn, HashMap<u32, crate::graphics::CachedGraphicsData<T>>>>,
38 window_scale_factor_tracker: Pin<Box<crate::properties::PropertyTracker>>,
40}
41
42#[cfg(feature = "std")]
43impl<T> Default for ItemCache<T> {
44 fn default() -> Self {
45 Self { map: Default::default(), window_scale_factor_tracker: Box::pin(Default::default()) }
46 }
47}
48
49#[cfg(feature = "std")]
50impl<T: Clone> ItemCache<T> {
51 pub fn get_or_update_cache_entry(&self, item_rc: &ItemRc, update_fn: impl FnOnce() -> T) -> T {
55 let component = &(**item_rc.item_tree()) as *const _;
56 let mut borrowed = self.map.borrow_mut();
57 match borrowed.entry(component).or_default().entry(item_rc.index()) {
58 std::collections::hash_map::Entry::Occupied(mut entry) => {
59 let mut tracker = entry.get_mut().dependency_tracker.take();
60 drop(borrowed);
61 let maybe_new_data = tracker
62 .get_or_insert_with(|| Box::pin(Default::default()))
63 .as_ref()
64 .evaluate_if_dirty(update_fn);
65 let mut borrowed = self.map.borrow_mut();
66 let e = borrowed.get_mut(&component).unwrap().get_mut(&item_rc.index()).unwrap();
67 e.dependency_tracker = tracker;
68 if let Some(new_data) = maybe_new_data {
69 e.data = new_data.clone();
70 new_data
71 } else {
72 e.data.clone()
73 }
74 }
75 std::collections::hash_map::Entry::Vacant(_) => {
76 drop(borrowed);
77 let new_entry = crate::graphics::CachedGraphicsData::new(update_fn);
78 let data = new_entry.data.clone();
79 self.map
80 .borrow_mut()
81 .get_mut(&component)
82 .unwrap()
83 .insert(item_rc.index(), new_entry);
84 data
85 }
86 }
87 }
88}
89
90#[cfg(feature = "std")]
91impl<T> ItemCache<T> {
92 pub fn with_entry<U>(
95 &self,
96 item_rc: &ItemRc,
97 callback: impl FnOnce(&T) -> Option<U>,
98 ) -> Option<U> {
99 let component = &(**item_rc.item_tree()) as *const _;
100 self.map
101 .borrow()
102 .get(&component)
103 .and_then(|per_component_entries| per_component_entries.get(&item_rc.index()))
104 .and_then(|entry| callback(&entry.data))
105 }
106
107 pub fn clear_cache_if_scale_factor_changed(&self, window: &crate::api::Window) {
109 if self.window_scale_factor_tracker.is_dirty() {
110 self.window_scale_factor_tracker
111 .as_ref()
112 .evaluate_as_dependency_root(|| window.scale_factor());
113 self.clear_all();
114 }
115 }
116
117 pub fn clear_all(&self) {
119 self.map.borrow_mut().clear();
120 }
121
122 pub fn component_destroyed(&self, component: crate::item_tree::ItemTreeRef) {
126 let component_ptr: *const _ =
127 crate::item_tree::ItemTreeRef::as_ptr(component).cast().as_ptr();
128 self.map.borrow_mut().remove(&component_ptr);
129 }
130
131 pub fn release(&self, item_rc: &ItemRc) {
133 let component = &(**item_rc.item_tree()) as *const _;
134 if let Some(sub) = self.map.borrow_mut().get_mut(&component) {
135 sub.remove(&item_rc.index());
136 }
137 }
138
139 pub fn is_empty(&self) -> bool {
141 self.map.borrow().is_empty()
142 }
143
144 pub fn get_or_update_cache_entry_ref(
152 &self,
153 item_rc: &ItemRc,
154 update_fn: impl FnOnce() -> T,
155 ) -> std::cell::RefMut<'_, T> {
156 let component = &(**item_rc.item_tree()) as *const _;
157 let index = item_rc.index();
158
159 {
160 let mut borrowed = self.map.borrow_mut();
161 match borrowed.entry(component).or_default().entry(index) {
162 std::collections::hash_map::Entry::Occupied(mut entry) => {
163 let mut tracker = entry.get_mut().dependency_tracker.take();
164 drop(borrowed);
165 let maybe_new_data = tracker
166 .get_or_insert_with(|| Box::pin(Default::default()))
167 .as_ref()
168 .evaluate_if_dirty(update_fn);
169 let mut borrowed = self.map.borrow_mut();
170 let e = borrowed.get_mut(&component).unwrap().get_mut(&index).unwrap();
171 e.dependency_tracker = tracker;
172 if let Some(new_data) = maybe_new_data {
173 e.data = new_data;
174 }
175 }
176 std::collections::hash_map::Entry::Vacant(_) => {
177 drop(borrowed);
178 let new_entry = crate::graphics::CachedGraphicsData::new(update_fn);
179 self.map.borrow_mut().get_mut(&component).unwrap().insert(index, new_entry);
180 }
181 }
182 }
183
184 std::cell::RefMut::map(self.map.borrow_mut(), |map| {
185 &mut map.get_mut(&component).unwrap().get_mut(&index).unwrap().data
186 })
187 }
188}
189
190pub fn render_item_children(
192 renderer: &mut dyn ItemRenderer,
193 component: &ItemTreeRc,
194 index: isize,
195 window_adapter: &WindowAdapterRc,
196) {
197 let mut actual_visitor =
198 |component: &ItemTreeRc, index: u32, item: Pin<ItemRef>| -> VisitChildrenResult {
199 renderer.save_state();
200 let item_rc = ItemRc::new(component.clone(), index);
201
202 let (do_draw, item_geometry) = renderer.filter_item(&item_rc, window_adapter);
203
204 let item_origin = item_geometry.origin;
205 renderer.translate(item_origin.to_vector());
206
207 let render_result = if do_draw
210 || item.as_ref().clips_children()
211 || ItemRef::downcast_pin::<BoxShadow>(item).is_some()
213 || ItemRef::downcast_pin::<Transform>(item).is_some()
215 || ItemRef::downcast_pin::<Opacity>(item).is_some()
216 || ItemRef::downcast_pin::<Layer>(item).is_some()
217 {
218 item.as_ref().render(
219 &mut (renderer as &mut dyn ItemRenderer),
220 &item_rc,
221 item_geometry.size,
222 )
223 } else {
224 RenderingResult::ContinueRenderingChildren
225 };
226
227 if matches!(render_result, RenderingResult::ContinueRenderingChildren) {
228 render_item_children(renderer, component, index as isize, window_adapter);
229 }
230 renderer.restore_state();
231 VisitChildrenResult::CONTINUE
232 };
233 vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
234 VRc::borrow_pin(component).as_ref().visit_children_item(
235 index,
236 crate::item_tree::TraversalOrder::BackToFront,
237 actual_visitor,
238 );
239}
240
241pub fn render_component_items(
244 component: &ItemTreeRc,
245 renderer: &mut dyn ItemRenderer,
246 origin: LogicalPoint,
247 window_adapter: &WindowAdapterRc,
248) {
249 renderer.save_state();
250 renderer.translate(origin.to_vector());
251
252 render_item_children(renderer, component, -1, window_adapter);
253
254 renderer.restore_state();
255}
256
257pub fn item_children_bounding_rect(
260 item_rc: &ItemRc,
261 window_adapter: &WindowAdapterRc,
262) -> LogicalRect {
263 item_children_bounding_rect_transformed(item_rc, window_adapter, Default::default())
264}
265
266fn item_children_bounding_rect_transformed(
267 item_rc: &ItemRc,
268 window_adapter: &WindowAdapterRc,
269 transform: crate::lengths::ItemTransform,
270) -> LogicalRect {
271 let mut bounding_rect = LogicalRect::zero();
272
273 let mut actual_visitor =
274 |component: &ItemTreeRc, index: u32, _ref: Pin<ItemRef>| -> VisitChildrenResult {
275 let item_rc = ItemRc::new(component.clone(), index);
276 let bounds_with_children =
277 item_with_children_bounding_rect_transformed(&item_rc, window_adapter, transform);
278
279 bounding_rect = bounding_rect.union(&bounds_with_children);
280
281 VisitChildrenResult::CONTINUE
282 };
283
284 vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
285 VRc::borrow_pin(item_rc.item_tree()).as_ref().visit_children_item(
286 item_rc.index() as isize,
287 crate::item_tree::TraversalOrder::BackToFront,
288 actual_visitor,
289 );
290
291 bounding_rect
292}
293
294fn item_with_children_bounding_rect_transformed(
295 item_rc: &ItemRc,
296 window_adapter: &WindowAdapterRc,
297 transform: crate::lengths::ItemTransform,
298) -> LogicalRect {
299 let item_geom = item_rc.geometry();
300
301 if item_rc.borrow().as_ref().clips_children() {
302 transform.outer_transformed_rect(&item_geom.cast()).cast()
303 } else {
304 let bounding = item_rc.bounding_rect(&item_geom, window_adapter);
305 let bounding = transform.outer_transformed_rect(&bounding.cast());
306 let children_relative_transform = item_rc
307 .children_transform()
308 .unwrap_or_default()
309 .then_translate(item_geom.origin.to_vector().cast());
310
311 let children_absolute_transform = transform.then(&children_relative_transform);
312
313 item_children_bounding_rect_transformed(
314 item_rc,
315 window_adapter,
316 children_absolute_transform,
317 )
318 .union(&bounding.cast())
319 }
320}
321
322#[allow(missing_docs)]
324pub trait RenderRectangle {
325 fn background(self: Pin<&Self>) -> Brush;
326}
327
328#[allow(missing_docs)]
330pub trait RenderBorderRectangle {
331 fn background(self: Pin<&Self>) -> Brush;
332 fn border_width(self: Pin<&Self>) -> LogicalLength;
333 fn border_radius(self: Pin<&Self>) -> LogicalBorderRadius;
334 fn border_color(self: Pin<&Self>) -> Brush;
335}
336
337#[allow(missing_docs)]
339pub trait RenderImage {
340 fn target_size(self: Pin<&Self>) -> LogicalSize;
341 fn source(self: Pin<&Self>) -> Image;
342 fn source_clip(self: Pin<&Self>) -> Option<IntRect>;
343 fn image_fit(self: Pin<&Self>) -> ImageFit;
344 fn rendering(self: Pin<&Self>) -> ImageRendering;
345 fn colorize(self: Pin<&Self>) -> Brush;
346 fn alignment(self: Pin<&Self>) -> (ImageHorizontalAlignment, ImageVerticalAlignment);
347 fn tiling(self: Pin<&Self>) -> (ImageTiling, ImageTiling);
348}
349
350#[allow(missing_docs)]
352pub trait HasFont {
353 fn font_request(self: Pin<&Self>, self_rc: &crate::items::ItemRc) -> FontRequest;
354}
355
356#[allow(missing_docs)]
357pub enum PlainOrStyledText {
358 Plain(SharedString),
359 Styled(crate::styled_text::StyledText),
360}
361
362#[allow(missing_docs)]
364pub trait RenderString: HasFont {
365 fn text(self: Pin<&Self>) -> PlainOrStyledText;
366}
367
368#[allow(missing_docs)]
370pub trait RenderText: RenderString {
371 fn target_size(self: Pin<&Self>) -> LogicalSize;
372 fn color(self: Pin<&Self>) -> Brush;
373 fn alignment(self: Pin<&Self>) -> (TextHorizontalAlignment, TextVerticalAlignment);
374 fn wrap(self: Pin<&Self>) -> TextWrap;
375 fn overflow(self: Pin<&Self>) -> TextOverflow;
376 fn stroke(self: Pin<&Self>) -> (Brush, LogicalLength, TextStrokeStyle);
377 fn is_markdown(self: Pin<&Self>) -> bool;
378 fn link_color(self: Pin<&Self>) -> Color;
379}
380
381impl HasFont for (SharedString, Brush) {
382 fn font_request(self: Pin<&Self>, self_rc: &crate::items::ItemRc) -> FontRequest {
383 crate::items::WindowItem::resolved_font_request(
384 self_rc,
385 SharedString::default(),
386 0,
387 LogicalLength::default(),
388 LogicalLength::default(),
389 false,
390 )
391 }
392}
393
394impl RenderString for (SharedString, Brush) {
395 fn text(self: Pin<&Self>) -> PlainOrStyledText {
396 PlainOrStyledText::Plain(self.0.clone())
397 }
398}
399
400impl RenderText for (SharedString, Brush) {
401 fn target_size(self: Pin<&Self>) -> LogicalSize {
402 LogicalSize::default()
403 }
404
405 fn color(self: Pin<&Self>) -> Brush {
406 self.1.clone()
407 }
408
409 fn link_color(self: Pin<&Self>) -> Color {
410 Default::default()
411 }
412
413 fn alignment(
414 self: Pin<&Self>,
415 ) -> (crate::items::TextHorizontalAlignment, crate::items::TextVerticalAlignment) {
416 Default::default()
417 }
418
419 fn wrap(self: Pin<&Self>) -> crate::items::TextWrap {
420 Default::default()
421 }
422
423 fn overflow(self: Pin<&Self>) -> crate::items::TextOverflow {
424 Default::default()
425 }
426
427 fn stroke(self: Pin<&Self>) -> (Brush, LogicalLength, TextStrokeStyle) {
428 Default::default()
429 }
430
431 fn is_markdown(self: Pin<&Self>) -> bool {
432 false
433 }
434}
435
436#[allow(missing_docs)]
441pub trait ItemRenderer {
442 fn draw_rectangle(
443 &mut self,
444 rect: Pin<&dyn RenderRectangle>,
445 _self_rc: &ItemRc,
446 _size: LogicalSize,
447 _cache: &CachedRenderingData,
448 );
449 fn draw_border_rectangle(
450 &mut self,
451 rect: Pin<&dyn RenderBorderRectangle>,
452 _self_rc: &ItemRc,
453 _size: LogicalSize,
454 _cache: &CachedRenderingData,
455 );
456 fn draw_window_background(
457 &mut self,
458 rect: Pin<&dyn RenderRectangle>,
459 self_rc: &ItemRc,
460 size: LogicalSize,
461 cache: &CachedRenderingData,
462 );
463 fn draw_image(
464 &mut self,
465 image: Pin<&dyn RenderImage>,
466 _self_rc: &ItemRc,
467 _size: LogicalSize,
468 _cache: &CachedRenderingData,
469 );
470 fn draw_text(
471 &mut self,
472 text: Pin<&dyn RenderText>,
473 _self_rc: &ItemRc,
474 _size: LogicalSize,
475 _cache: &CachedRenderingData,
476 );
477 fn draw_text_input(
478 &mut self,
479 text_input: Pin<&TextInput>,
480 _self_rc: &ItemRc,
481 _size: LogicalSize,
482 );
483 #[cfg(feature = "path")]
484 fn draw_path(&mut self, path: Pin<&Path>, _self_rc: &ItemRc, _size: LogicalSize);
485 fn draw_box_shadow(
486 &mut self,
487 box_shadow: Pin<&BoxShadow>,
488 _self_rc: &ItemRc,
489 _size: LogicalSize,
490 );
491 fn visit_opacity(
492 &mut self,
493 opacity_item: Pin<&Opacity>,
494 _self_rc: &ItemRc,
495 _size: LogicalSize,
496 ) -> RenderingResult {
497 self.apply_opacity(opacity_item.opacity());
498 RenderingResult::ContinueRenderingChildren
499 }
500 fn visit_layer(
501 &mut self,
502 _layer_item: Pin<&Layer>,
503 _self_rc: &ItemRc,
504 _size: LogicalSize,
505 ) -> RenderingResult {
506 RenderingResult::ContinueRenderingChildren
508 }
509
510 fn visit_clip(
514 &mut self,
515 clip_item: Pin<&Clip>,
516 _item_rc: &ItemRc,
517 size: LogicalSize,
518 ) -> RenderingResult {
519 if clip_item.clip() {
520 let clip_region_valid = self.combine_clip(
521 LogicalRect::new(LogicalPoint::default(), size),
522 clip_item.logical_border_radius(),
523 clip_item.border_width(),
524 );
525
526 if !clip_region_valid {
529 return RenderingResult::ContinueRenderingWithoutChildren;
530 }
531 }
532 RenderingResult::ContinueRenderingChildren
533 }
534
535 fn combine_clip(
541 &mut self,
542 rect: LogicalRect,
543 radius: LogicalBorderRadius,
544 border_width: LogicalLength,
545 ) -> bool;
546 fn get_current_clip(&self) -> LogicalRect;
548
549 fn translate(&mut self, distance: LogicalVector);
550 fn translation(&self) -> LogicalVector {
551 unimplemented!()
552 }
553 fn rotate(&mut self, angle_in_degrees: f32);
554 fn scale(&mut self, scale_x_factor: f32, scale_y_factor: f32);
555 fn apply_opacity(&mut self, opacity: f32);
557
558 fn save_state(&mut self);
559 fn restore_state(&mut self);
560
561 fn scale_factor(&self) -> f32;
563
564 fn draw_cached_pixmap(
569 &mut self,
570 item_cache: &ItemRc,
571 update_fn: &dyn Fn(&mut dyn FnMut(u32, u32, &[u8])),
572 );
573
574 fn draw_string(&mut self, string: &str, color: crate::Color);
577
578 fn draw_image_direct(&mut self, image: crate::graphics::Image);
579
580 fn filter_item(
585 &mut self,
586 item: &ItemRc,
587 window_adapter: &WindowAdapterRc,
588 ) -> (bool, LogicalRect) {
589 let item_geometry = item.geometry();
590 let bounding_rect = crate::properties::evaluate_no_tracking(|| {
593 item.bounding_rect(&item_geometry, window_adapter)
594 });
595 (self.get_current_clip().intersects(&bounding_rect), item_geometry)
596 }
597
598 fn window(&self) -> &crate::window::WindowInner;
599
600 fn as_any(&mut self) -> Option<&mut dyn core::any::Any>;
602}
603
604pub trait ItemRendererFeatures {
606 const SUPPORTS_TRANSFORMATIONS: bool;
608}