1#![warn(missing_docs)]
5use super::items::*;
8use crate::graphics::{
9 Color, FontRequest, Image, IntRect, adjust_rect_and_border_for_inner_drawing,
10};
11use crate::item_tree::ItemTreeRc;
12use crate::item_tree::{ItemVisitor, ItemVisitorVTable, VisitChildrenResult};
13use crate::lengths::{
14 LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector,
15 PhysicalBorderRadius, PhysicalPx, ScaleFactor, SizeLengths,
16};
17pub use crate::partial_renderer::CachedRenderingData;
18use crate::window::WindowAdapterRc;
19use crate::{Brush, SharedString};
20#[cfg(feature = "std")]
21use alloc::boxed::Box;
22#[cfg(feature = "std")]
23use core::cell::RefCell;
24use core::pin::Pin;
25#[cfg(feature = "std")]
26use std::collections::HashMap;
27use vtable::VRc;
28
29#[cfg(feature = "std")]
38pub struct ItemCache<T> {
39 map: RefCell<HashMap<*const vtable::Dyn, HashMap<u32, crate::graphics::CachedGraphicsData<T>>>>,
41 window_scale_factor_tracker: Pin<Box<crate::properties::PropertyTracker>>,
43}
44
45#[cfg(feature = "std")]
46impl<T> Default for ItemCache<T> {
47 fn default() -> Self {
48 Self { map: Default::default(), window_scale_factor_tracker: Box::pin(Default::default()) }
49 }
50}
51
52#[cfg(feature = "std")]
53impl<T: Clone> ItemCache<T> {
54 pub fn get_or_update_cache_entry(&self, item_rc: &ItemRc, update_fn: impl FnOnce() -> T) -> T {
58 let component = &(**item_rc.item_tree()) as *const _;
59 let mut borrowed = self.map.borrow_mut();
60 match borrowed.entry(component).or_default().entry(item_rc.index()) {
61 std::collections::hash_map::Entry::Occupied(mut entry) => {
62 let mut tracker = entry.get_mut().dependency_tracker.take();
63 drop(borrowed);
64 let maybe_new_data = tracker
65 .get_or_insert_with(|| Box::pin(Default::default()))
66 .as_ref()
67 .evaluate_if_dirty(update_fn);
68 let mut borrowed = self.map.borrow_mut();
69 let e = borrowed.get_mut(&component).unwrap().get_mut(&item_rc.index()).unwrap();
70 e.dependency_tracker = tracker;
71 if let Some(new_data) = maybe_new_data {
72 e.data = new_data.clone();
73 new_data
74 } else {
75 e.data.clone()
76 }
77 }
78 std::collections::hash_map::Entry::Vacant(_) => {
79 drop(borrowed);
80 let new_entry = crate::graphics::CachedGraphicsData::new(update_fn);
81 let data = new_entry.data.clone();
82 self.map
83 .borrow_mut()
84 .get_mut(&component)
85 .unwrap()
86 .insert(item_rc.index(), new_entry);
87 data
88 }
89 }
90 }
91}
92
93#[cfg(feature = "std")]
94impl<T> ItemCache<T> {
95 pub fn with_entry<U>(
98 &self,
99 item_rc: &ItemRc,
100 callback: impl FnOnce(&T) -> Option<U>,
101 ) -> Option<U> {
102 let component = &(**item_rc.item_tree()) as *const _;
103 self.map
104 .borrow()
105 .get(&component)
106 .and_then(|per_component_entries| per_component_entries.get(&item_rc.index()))
107 .and_then(|entry| callback(&entry.data))
108 }
109
110 pub fn clear_cache_if_scale_factor_changed(&self, window: &crate::api::Window) {
112 if self.window_scale_factor_tracker.is_dirty() {
113 self.window_scale_factor_tracker
114 .as_ref()
115 .evaluate_as_dependency_root(|| window.scale_factor());
116 self.clear_all();
117 }
118 }
119
120 pub fn clear_all(&self) {
122 self.map.borrow_mut().clear();
123 }
124
125 pub fn component_destroyed(&self, component: crate::item_tree::ItemTreeRef) {
129 let component_ptr: *const _ =
130 crate::item_tree::ItemTreeRef::as_ptr(component).cast().as_ptr();
131 self.map.borrow_mut().remove(&component_ptr);
132 }
133
134 pub fn release(&self, item_rc: &ItemRc) {
136 let component = &(**item_rc.item_tree()) as *const _;
137 if let Some(sub) = self.map.borrow_mut().get_mut(&component) {
138 sub.remove(&item_rc.index());
139 }
140 }
141
142 pub fn is_empty(&self) -> bool {
144 self.map.borrow().is_empty()
145 }
146
147 pub fn retain(&self, mut f: impl FnMut(&T) -> bool) {
149 self.map.borrow_mut().retain(|_, per_component| {
150 per_component.retain(|_, entry| f(&entry.data));
151 !per_component.is_empty()
152 });
153 }
154
155 pub fn get_or_update_cache_entry_ref(
163 &self,
164 item_rc: &ItemRc,
165 update_fn: impl FnOnce() -> T,
166 ) -> std::cell::RefMut<'_, T> {
167 let component = &(**item_rc.item_tree()) as *const _;
168 let index = item_rc.index();
169
170 {
171 let mut borrowed = self.map.borrow_mut();
172 match borrowed.entry(component).or_default().entry(index) {
173 std::collections::hash_map::Entry::Occupied(mut entry) => {
174 let mut tracker = entry.get_mut().dependency_tracker.take();
175 drop(borrowed);
176 let maybe_new_data = tracker
177 .get_or_insert_with(|| Box::pin(Default::default()))
178 .as_ref()
179 .evaluate_if_dirty(update_fn);
180 let mut borrowed = self.map.borrow_mut();
181 let e = borrowed.get_mut(&component).unwrap().get_mut(&index).unwrap();
182 e.dependency_tracker = tracker;
183 if let Some(new_data) = maybe_new_data {
184 e.data = new_data;
185 }
186 }
187 std::collections::hash_map::Entry::Vacant(_) => {
188 drop(borrowed);
189 let new_entry = crate::graphics::CachedGraphicsData::new(update_fn);
190 self.map.borrow_mut().get_mut(&component).unwrap().insert(index, new_entry);
191 }
192 }
193 }
194
195 std::cell::RefMut::map(self.map.borrow_mut(), |map| {
196 &mut map.get_mut(&component).unwrap().get_mut(&index).unwrap().data
197 })
198 }
199}
200
201pub fn render_item_children(
203 renderer: &mut dyn ItemRenderer,
204 component: &ItemTreeRc,
205 index: isize,
206 window_adapter: &WindowAdapterRc,
207) {
208 let mut actual_visitor =
209 |component: &ItemTreeRc, index: u32, item: Pin<ItemRef>| -> VisitChildrenResult {
210 renderer.save_state();
211 let item_rc = ItemRc::new(component.clone(), index);
212
213 let (do_draw, item_origin, item_size) = renderer.filter_item(&item_rc, window_adapter);
214
215 renderer.translate(item_origin.to_vector());
216
217 let render_result = if renderer.global_alpha_transparent() {
220 RenderingResult::ContinueRenderingWithoutChildren
222 } else if do_draw
223 || item.as_ref().clips_children()
224 || ItemRef::downcast_pin::<BoxShadow>(item).is_some()
226 || ItemRef::downcast_pin::<Transform>(item).is_some()
228 || ItemRef::downcast_pin::<Opacity>(item).is_some()
229 || ItemRef::downcast_pin::<Layer>(item).is_some()
230 {
231 let size = item_size.unwrap_or_else(|| {
232 crate::properties::evaluate_no_tracking(|| item_rc.geometry()).size
233 });
234 item.as_ref().render(&mut (renderer as &mut dyn ItemRenderer), &item_rc, size)
235 } else {
236 RenderingResult::ContinueRenderingChildren
237 };
238
239 if matches!(render_result, RenderingResult::ContinueRenderingChildren) {
240 render_item_children(renderer, component, index as isize, window_adapter);
241 }
242 renderer.restore_state();
243 VisitChildrenResult::CONTINUE
244 };
245 vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
246 VRc::borrow_pin(component).as_ref().visit_children_item(
247 index,
248 crate::item_tree::TraversalOrder::BackToFront,
249 actual_visitor,
250 );
251}
252
253pub fn render_component_items(
256 component: &ItemTreeRc,
257 renderer: &mut dyn ItemRenderer,
258 origin: LogicalPoint,
259 window_adapter: &WindowAdapterRc,
260) {
261 renderer.save_state();
262 renderer.translate(origin.to_vector());
263
264 render_item_children(renderer, component, -1, window_adapter);
265
266 renderer.restore_state();
267}
268
269pub fn item_children_bounding_rect(
272 item_rc: &ItemRc,
273 window_adapter: &WindowAdapterRc,
274) -> LogicalRect {
275 item_children_bounding_rect_transformed(item_rc, window_adapter, Default::default())
276}
277
278fn item_children_bounding_rect_transformed(
279 item_rc: &ItemRc,
280 window_adapter: &WindowAdapterRc,
281 transform: crate::lengths::ItemTransform,
282) -> LogicalRect {
283 let mut bounding_rect = LogicalRect::zero();
284
285 let mut actual_visitor =
286 |component: &ItemTreeRc, index: u32, _ref: Pin<ItemRef>| -> VisitChildrenResult {
287 let item_rc = ItemRc::new(component.clone(), index);
288 let bounds_with_children =
289 item_with_children_bounding_rect_transformed(&item_rc, window_adapter, transform);
290
291 bounding_rect = bounding_rect.union(&bounds_with_children);
292
293 VisitChildrenResult::CONTINUE
294 };
295
296 vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
297 VRc::borrow_pin(item_rc.item_tree()).as_ref().visit_children_item(
298 item_rc.index() as isize,
299 crate::item_tree::TraversalOrder::BackToFront,
300 actual_visitor,
301 );
302
303 bounding_rect
304}
305
306fn item_with_children_bounding_rect_transformed(
307 item_rc: &ItemRc,
308 window_adapter: &WindowAdapterRc,
309 transform: crate::lengths::ItemTransform,
310) -> LogicalRect {
311 let item_geom = item_rc.geometry();
312
313 if item_rc.borrow().as_ref().clips_children() {
314 transform.outer_transformed_rect(&item_geom.cast()).cast()
315 } else {
316 let bounding = item_rc.bounding_rect(&item_geom, window_adapter);
317 let bounding = transform.outer_transformed_rect(&bounding.cast());
318 let children_relative_transform = item_rc
319 .children_transform()
320 .unwrap_or_default()
321 .then_translate(item_geom.origin.to_vector().cast());
322
323 let children_absolute_transform = transform.then(&children_relative_transform);
324
325 item_children_bounding_rect_transformed(
326 item_rc,
327 window_adapter,
328 children_absolute_transform,
329 )
330 .union(&bounding.cast())
331 }
332}
333
334#[allow(missing_docs)]
336pub trait RenderRectangle {
337 fn background(self: Pin<&Self>) -> Brush;
338}
339
340#[allow(missing_docs)]
342pub trait RenderBorderRectangle {
343 fn background(self: Pin<&Self>) -> Brush;
344 fn border_width(self: Pin<&Self>) -> LogicalLength;
345 fn border_radius(self: Pin<&Self>) -> LogicalBorderRadius;
346 fn border_color(self: Pin<&Self>) -> Brush;
347}
348
349pub struct BorderRectLayout {
354 pub brush_size: euclid::Size2D<f32, PhysicalPx>,
356 pub background_rect: euclid::Rect<f32, PhysicalPx>,
358 pub background_radius: PhysicalBorderRadius,
360 pub border_rect: euclid::Rect<f32, PhysicalPx>,
362 pub border_radius: PhysicalBorderRadius,
364 pub border_width: euclid::Length<f32, PhysicalPx>,
366 pub border_color: Brush,
368}
369
370impl BorderRectLayout {
371 pub fn new(
374 rect: Pin<&dyn RenderBorderRectangle>,
375 size: LogicalSize,
376 scale_factor: ScaleFactor,
377 ) -> Option<Self> {
378 let mut geometry = euclid::Rect::from_size(size.cast() * scale_factor);
380 if geometry.is_empty() {
381 return None;
382 }
383 let brush_size = geometry.size;
384
385 let border_color = rect.border_color();
386 let opaque_border = border_color.is_opaque();
387 let mut border_width = if border_color.is_transparent() {
388 euclid::Length::new(0.)
389 } else {
390 rect.border_width().cast() * scale_factor
391 };
392
393 let fill_radius = (rect.border_radius().cast() * scale_factor)
399 .outer(border_width / 2. + euclid::Length::new(0.01));
400 let border_radius = fill_radius.inner(border_width / 2.);
401
402 let (background_rect, background_radius) = if opaque_border {
403 adjust_rect_and_border_for_inner_drawing(&mut geometry, &mut border_width);
406 (geometry, border_radius)
407 } else {
408 let background = (geometry, fill_radius);
411 adjust_rect_and_border_for_inner_drawing(&mut geometry, &mut border_width);
412 background
413 };
414
415 Some(Self {
416 brush_size,
417 background_rect,
418 background_radius,
419 border_rect: geometry,
420 border_radius,
421 border_width,
422 border_color,
423 })
424 }
425}
426
427pub fn clip_content_box(
431 size: LogicalSize,
432 radius: LogicalBorderRadius,
433 border_width: LogicalLength,
434) -> (LogicalRect, LogicalBorderRadius) {
435 let two = 2 as crate::Coord;
438 let border_width = border_width
439 .max(LogicalLength::default())
440 .min(size.width_length() / two)
441 .min(size.height_length() / two);
442 let rect = LogicalRect::new(
443 LogicalPoint::from_lengths(border_width, border_width),
444 LogicalSize::from_lengths(
445 size.width_length() - border_width * two,
446 size.height_length() - border_width * two,
447 ),
448 );
449 (rect, radius.inner(border_width))
450}
451
452#[allow(missing_docs)]
454pub trait RenderImage {
455 fn target_size(self: Pin<&Self>) -> LogicalSize;
456 fn source(self: Pin<&Self>) -> Image;
457 fn source_clip(self: Pin<&Self>) -> Option<IntRect>;
458 fn image_fit(self: Pin<&Self>) -> ImageFit;
459 fn rendering(self: Pin<&Self>) -> ImageRendering;
460 fn colorize(self: Pin<&Self>) -> Brush;
461 fn alignment(self: Pin<&Self>) -> (ImageHorizontalAlignment, ImageVerticalAlignment);
462 fn tiling(self: Pin<&Self>) -> (ImageTiling, ImageTiling);
463}
464
465#[allow(missing_docs)]
467pub trait HasFont {
468 fn font_request(self: Pin<&Self>, self_rc: &crate::items::ItemRc) -> FontRequest;
469}
470
471#[allow(missing_docs)]
472pub enum PlainOrStyledText {
473 Plain(SharedString),
474 Styled(crate::styled_text::StyledText),
475}
476
477#[allow(missing_docs)]
479pub trait RenderString: HasFont {
480 fn text(self: Pin<&Self>) -> PlainOrStyledText;
481 fn max_lines(self: Pin<&Self>) -> i32 {
482 0
483 }
484 fn line_limit(self: Pin<&Self>) -> Option<usize> {
487 usize::try_from(self.max_lines()).ok().filter(|max_lines| *max_lines > 0)
488 }
489 fn stroke(self: Pin<&Self>) -> (Brush, LogicalLength, TextStrokeStyle) {
492 Default::default()
493 }
494 fn link_color(self: Pin<&Self>) -> Color {
496 Default::default()
497 }
498}
499
500#[allow(missing_docs)]
502pub trait RenderText: RenderString {
503 fn target_size(self: Pin<&Self>) -> LogicalSize;
504 fn color(self: Pin<&Self>) -> Brush;
505 fn alignment(self: Pin<&Self>) -> (TextHorizontalAlignment, TextVerticalAlignment);
506 fn wrap(self: Pin<&Self>) -> TextWrap;
507 fn overflow(self: Pin<&Self>) -> TextOverflow;
508 fn is_markdown(self: Pin<&Self>) -> bool;
509}
510
511impl HasFont for (SharedString, Brush) {
512 fn font_request(self: Pin<&Self>, self_rc: &crate::items::ItemRc) -> FontRequest {
513 crate::items::WindowItem::resolved_font_request(
514 self_rc,
515 SharedString::default(),
516 0,
517 LogicalLength::default(),
518 LogicalLength::default(),
519 0.0,
520 false,
521 )
522 }
523}
524
525impl RenderString for (SharedString, Brush) {
526 fn text(self: Pin<&Self>) -> PlainOrStyledText {
527 PlainOrStyledText::Plain(self.0.clone())
528 }
529}
530
531impl RenderText for (SharedString, Brush) {
532 fn target_size(self: Pin<&Self>) -> LogicalSize {
533 LogicalSize::default()
534 }
535
536 fn color(self: Pin<&Self>) -> Brush {
537 self.1.clone()
538 }
539
540 fn alignment(
541 self: Pin<&Self>,
542 ) -> (crate::items::TextHorizontalAlignment, crate::items::TextVerticalAlignment) {
543 Default::default()
544 }
545
546 fn wrap(self: Pin<&Self>) -> crate::items::TextWrap {
547 Default::default()
548 }
549
550 fn overflow(self: Pin<&Self>) -> crate::items::TextOverflow {
551 Default::default()
552 }
553
554 fn is_markdown(self: Pin<&Self>) -> bool {
555 false
556 }
557}
558
559#[allow(missing_docs)]
564pub trait ItemRenderer {
565 fn draw_rectangle(
566 &mut self,
567 rect: Pin<&dyn RenderRectangle>,
568 _self_rc: &ItemRc,
569 _size: LogicalSize,
570 _cache: &CachedRenderingData,
571 );
572 fn draw_border_rectangle(
573 &mut self,
574 rect: Pin<&dyn RenderBorderRectangle>,
575 _self_rc: &ItemRc,
576 _size: LogicalSize,
577 _cache: &CachedRenderingData,
578 );
579 fn draw_window_background(
580 &mut self,
581 rect: Pin<&dyn RenderRectangle>,
582 self_rc: &ItemRc,
583 size: LogicalSize,
584 cache: &CachedRenderingData,
585 );
586 fn draw_image(
587 &mut self,
588 image: Pin<&dyn RenderImage>,
589 _self_rc: &ItemRc,
590 _size: LogicalSize,
591 _cache: &CachedRenderingData,
592 );
593 fn draw_text(
594 &mut self,
595 text: Pin<&dyn RenderText>,
596 _self_rc: &ItemRc,
597 _size: LogicalSize,
598 _cache: &CachedRenderingData,
599 );
600 fn draw_text_input(
601 &mut self,
602 text_input: Pin<&TextInput>,
603 _self_rc: &ItemRc,
604 _size: LogicalSize,
605 );
606 #[cfg(feature = "path")]
607 fn draw_path(&mut self, path: Pin<&Path>, _self_rc: &ItemRc, _size: LogicalSize);
608 fn draw_box_shadow(
609 &mut self,
610 box_shadow: Pin<&BoxShadow>,
611 _self_rc: &ItemRc,
612 _size: LogicalSize,
613 );
614 fn visit_opacity(
615 &mut self,
616 opacity_item: Pin<&Opacity>,
617 _self_rc: &ItemRc,
618 _size: LogicalSize,
619 ) -> RenderingResult {
620 self.apply_opacity(opacity_item.opacity());
621 RenderingResult::ContinueRenderingChildren
622 }
623 fn visit_layer(
624 &mut self,
625 _layer_item: Pin<&Layer>,
626 _self_rc: &ItemRc,
627 _size: LogicalSize,
628 ) -> RenderingResult {
629 RenderingResult::ContinueRenderingChildren
631 }
632
633 fn visit_clip(
637 &mut self,
638 clip_item: Pin<&Clip>,
639 _item_rc: &ItemRc,
640 size: LogicalSize,
641 ) -> RenderingResult {
642 if clip_item.clip() {
643 let (clip_rect, clip_radius) =
644 clip_content_box(size, clip_item.logical_border_radius(), clip_item.border_width());
645 let clip_region_valid = self.combine_clip(clip_rect, clip_radius);
646
647 if !clip_region_valid {
650 return RenderingResult::ContinueRenderingWithoutChildren;
651 }
652 }
653 RenderingResult::ContinueRenderingChildren
654 }
655
656 fn combine_clip(&mut self, rect: LogicalRect, radius: LogicalBorderRadius) -> bool;
661 fn get_current_clip(&self) -> LogicalRect;
663
664 fn translate(&mut self, distance: LogicalVector);
665 fn current_transform(&self) -> crate::lengths::ItemTransform {
668 todo!("this renderer does not track transforms for partial rendering")
669 }
670 fn rotate(&mut self, angle_in_degrees: f32);
671 fn scale(&mut self, scale_x_factor: f32, scale_y_factor: f32);
672 fn apply_opacity(&mut self, opacity: f32);
674 fn global_alpha_transparent(&self) -> bool {
676 false
677 }
678
679 fn save_state(&mut self);
680 fn restore_state(&mut self);
681
682 fn scale_factor(&self) -> ScaleFactor;
684
685 fn draw_cached_pixmap(
690 &mut self,
691 item_cache: &ItemRc,
692 update_fn: &dyn Fn(&mut dyn FnMut(u32, u32, &[u8])),
693 );
694
695 fn draw_string(&mut self, string: &str, color: crate::Color);
698
699 fn draw_image_direct(&mut self, image: crate::graphics::Image);
700
701 fn filter_item(
707 &mut self,
708 item: &ItemRc,
709 window_adapter: &WindowAdapterRc,
710 ) -> (bool, LogicalPoint, Option<LogicalSize>) {
711 let item_geometry = item.geometry();
712 let bounding_rect = crate::properties::evaluate_no_tracking(|| {
715 item.bounding_rect(&item_geometry, window_adapter)
716 });
717 (
718 self.get_current_clip().intersects(&bounding_rect),
719 item_geometry.origin,
720 Some(item_geometry.size),
721 )
722 }
723
724 fn window(&self) -> &crate::window::WindowInner;
725
726 fn as_any(&mut self) -> Option<&mut dyn core::any::Any>;
728}
729
730#[cfg(feature = "std")]
737pub trait LayerRenderer<'cache>: ItemRenderer {
738 type LayerTarget;
740 type Image: Clone;
742
743 fn layer_cache(
745 &self,
746 ) -> &'cache ItemCache<Option<(euclid::Point2D<f32, crate::lengths::PhysicalPx>, Self::Image)>>;
747
748 fn create_layer_target(
750 &mut self,
751 item_rc: &ItemRc,
752 physical_size: euclid::Size2D<f32, crate::lengths::PhysicalPx>,
753 ) -> Option<Self::LayerTarget>;
754
755 fn render_into_layer(
760 &mut self,
761 target: Self::LayerTarget,
762 item_rc: &ItemRc,
763 bounding_rect: LogicalRect,
764 ) -> Self::Image;
765}
766
767#[cfg(feature = "std")]
769pub fn render_layer<'cache, R>(
770 renderer: &mut R,
771 item_rc: &ItemRc,
772) -> Option<(euclid::Point2D<f32, crate::lengths::PhysicalPx>, R::Image)>
773where
774 R: LayerRenderer<'cache> + ?Sized + 'cache,
775{
776 let cache = renderer.layer_cache();
777 let scale_factor = renderer.scale_factor();
778
779 let compute_bounds = |r: &R| -> LogicalRect {
780 item_children_bounding_rect(item_rc, &r.window().window_adapter())
781 .intersection(
782 &r.get_current_clip().union(&LogicalRect::from_size(item_rc.geometry().size)),
783 )
784 .unwrap_or_default()
785 };
786
787 cache.get_or_update_cache_entry(item_rc, || {
788 let bounding_rect = crate::properties::evaluate_no_tracking(|| compute_bounds(renderer));
791 let physical_origin = bounding_rect.origin.cast() * scale_factor;
792 let layer_size = bounding_rect.size.cast() * scale_factor;
793
794 let Some(target) = renderer.create_layer_target(item_rc, layer_size) else {
795 let _ = compute_bounds(renderer);
800 return None;
801 };
802
803 let image = renderer.render_into_layer(target, item_rc, bounding_rect);
804 Some((physical_origin, image))
805 })
806}
807
808pub trait ItemRendererFeatures {
810 const SUPPORTS_TRANSFORMATIONS: bool;
812}