1use std::any::Any;
7use std::rc::Rc;
8
9use inset_embedder::{Canvas, Matrix4, Offset, Size, TextBaseline};
10use inset_foundation::{App, Handle, Listenable, Listener};
11use inset_painting::ClipContext;
12
13use crate::box_::{AnyRenderBox, BoxConstraints, BoxHitTestResult, RenderBox, RenderBoxData};
14use crate::object::{
15 AnyRenderObject, RenderHandle, RenderObject, RenderObjectData, RenderObjectWithChildData,
16 RenderObjectWithChildMixin,
17};
18use crate::painting_context::PaintingContext;
19use crate::pipeline_owner::PipelineOwner;
20use crate::proxy_box::{RenderProxyBoxMixin, same_instance};
21
22pub trait CustomPainter: 'static {
61 fn repaint(&self) -> Option<&Rc<dyn Listenable>> {
67 None
68 }
69
70 fn paint(&self, app: &mut App, canvas: &mut Canvas, size: Size);
91
92 fn should_repaint(&self, app: &App, old_delegate: &dyn CustomPainter) -> bool;
119
120 fn hit_test(&self, app: &App, position: Offset) -> Option<bool> {
134 let _ = (app, position);
135 None
136 }
137
138 fn as_any(&self) -> &dyn Any;
140}
141
142impl Listenable for dyn CustomPainter {
143 fn add_listener(&self, app: &mut App, listener: Listener) {
147 if let Some(repaint) = self.repaint() {
148 repaint.add_listener(app, listener);
149 }
150 }
151
152 fn remove_listener(&self, app: &mut App, listener: &Listener) {
155 if let Some(repaint) = self.repaint() {
156 repaint.remove_listener(app, listener);
157 }
158 }
159}
160
161pub struct RenderCustomPaint {
164 render_object: RenderObjectData,
165 render_box: RenderBoxData,
166 child: RenderObjectWithChildData<AnyRenderBox>,
167 painter: Option<Rc<dyn CustomPainter>>,
168 foreground_painter: Option<Rc<dyn CustomPainter>>,
169 preferred_size: Size,
170 is_complex: bool,
171 will_change: bool,
172}
173
174impl RenderCustomPaint {
175 pub fn new(
177 app: &mut App,
178 painter: Option<Rc<dyn CustomPainter>>,
179 foreground_painter: Option<Rc<dyn CustomPainter>>,
180 preferred_size: Size,
181 is_complex: bool,
182 will_change: bool,
183 child: Option<AnyRenderBox>,
184 ) -> RenderHandle<RenderCustomPaint> {
185 let this = RenderHandle::new_box(
186 app,
187 RenderCustomPaint {
188 render_object: RenderObjectData::new(),
189 render_box: RenderBoxData::new(),
190 child: RenderObjectWithChildData::new(),
191 painter,
192 foreground_painter,
193 preferred_size,
194 is_complex,
195 will_change,
196 },
197 );
198 this.set_child(app, child);
199 this
200 }
201
202 pub fn painter(self: RenderHandle<Self>, app: &App) -> Option<Rc<dyn CustomPainter>> {
206 self.get(app).painter.clone()
207 }
208
209 pub fn set_painter(
222 self: RenderHandle<Self>,
223 app: &mut App,
224 value: Option<Rc<dyn CustomPainter>>,
225 ) {
226 let old_painter = self.get(app).painter.clone();
227 if same_instance(old_painter.as_ref(), value.as_ref()) {
228 return;
229 }
230 self.get_mut(app).painter = value.clone();
231 self.did_update_painter(app, value.as_ref(), old_painter.as_ref());
232 }
233
234 pub fn foreground_painter(
238 self: RenderHandle<Self>,
239 app: &App,
240 ) -> Option<Rc<dyn CustomPainter>> {
241 self.get(app).foreground_painter.clone()
242 }
243
244 pub fn set_foreground_painter(
257 self: RenderHandle<Self>,
258 app: &mut App,
259 value: Option<Rc<dyn CustomPainter>>,
260 ) {
261 let old_painter = self.get(app).foreground_painter.clone();
262 if same_instance(old_painter.as_ref(), value.as_ref()) {
263 return;
264 }
265 self.get_mut(app).foreground_painter = value.clone();
266 self.did_update_painter(app, value.as_ref(), old_painter.as_ref());
267 }
268
269 fn did_update_painter(
270 self: RenderHandle<Self>,
271 app: &mut App,
272 new_painter: Option<&Rc<dyn CustomPainter>>,
273 old_painter: Option<&Rc<dyn CustomPainter>>,
274 ) {
275 let repaint = match (new_painter, old_painter) {
277 (None, old_painter) => {
278 debug_assert!(old_painter.is_some()); true
280 }
281 (Some(new_painter), Some(old_painter)) => {
282 new_painter.as_any().type_id() != old_painter.as_any().type_id()
283 || new_painter.should_repaint(app, &**old_painter)
284 }
285 (Some(_), None) => true,
286 };
287 if repaint {
288 self.mark_needs_paint(app);
289 }
290 if self.attached(app) {
291 if let Some(old_painter) = old_painter {
292 old_painter.remove_listener(app, &self.paint_listener());
293 }
294 if let Some(new_painter) = new_painter {
295 new_painter.add_listener(app, self.paint_listener());
296 }
297 }
298 }
300
301 fn paint_listener(self: RenderHandle<Self>) -> Listener {
303 Listener::handle_method(self.handle(), mark_needs_paint)
304 }
305
306 pub fn preferred_size(self: RenderHandle<Self>, app: &App) -> Size {
314 self.get(app).preferred_size
315 }
316
317 pub fn set_preferred_size(self: RenderHandle<Self>, app: &mut App, value: Size) {
319 if self.preferred_size(app) == value {
320 return;
321 }
322 self.get_mut(app).preferred_size = value;
323 self.mark_needs_layout(app);
324 }
325
326 pub fn is_complex(self: RenderHandle<Self>, app: &App) -> bool {
334 self.get(app).is_complex
335 }
336
337 pub fn set_is_complex(self: RenderHandle<Self>, app: &mut App, value: bool) {
339 self.get_mut(app).is_complex = value;
340 }
341
342 pub fn will_change(self: RenderHandle<Self>, app: &App) -> bool {
350 self.get(app).will_change
351 }
352
353 pub fn set_will_change(self: RenderHandle<Self>, app: &mut App, value: bool) {
355 self.get_mut(app).will_change = value;
356 }
357
358 fn paint_with_painter(
359 self: RenderHandle<Self>,
360 app: &mut App,
361 canvas: &mut Canvas,
362 offset: Offset,
363 painter: &dyn CustomPainter,
364 ) {
365 canvas.save();
366 let debug_previous_canvas_save_count = canvas.save_count();
367 if offset != Offset::ZERO {
368 canvas.translate(offset.dx() as f32, offset.dy() as f32);
369 }
370 let size = self.size(app);
371 painter.paint(app, canvas, size);
372 debug_assert_eq!(
376 debug_previous_canvas_save_count,
377 canvas.save_count(),
378 "the custom painter had mismatching save and restore calls"
379 );
380 canvas.restore();
381 }
382
383 fn set_raster_cache_hints(self: RenderHandle<Self>, app: &App, context: &mut PaintingContext) {
384 if self.get(app).is_complex {
385 context.set_is_complex_hint();
386 }
387 if self.get(app).will_change {
388 context.set_will_change_hint();
389 }
390 }
391}
392
393fn mark_needs_paint(this: Handle<RenderCustomPaint>, app: &mut App) {
394 RenderHandle::from_handle(this).mark_needs_paint(app);
395}
396
397impl RenderObjectWithChildMixin for RenderCustomPaint {
398 type ChildType = AnyRenderBox;
399
400 fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
401 &self.get(app).child
402 }
403
404 fn child_data_mut(
405 self: RenderHandle<Self>,
406 app: &mut App,
407 ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
408 &mut self.get_mut(app).child
409 }
410}
411
412impl RenderProxyBoxMixin for RenderCustomPaint {
413 fn compute_size_for_no_child(
414 self: RenderHandle<Self>,
415 app: &App,
416 constraints: BoxConstraints,
417 ) -> Size {
418 constraints.constrain(self.get(app).preferred_size)
419 }
420}
421
422impl RenderObject for RenderCustomPaint {
423 crate::render_object_accessors!();
424
425 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
426 if let Some(child) = self.child(app) {
427 child.as_object().attach(app, owner);
428 }
429 if let Some(painter) = self.painter(app) {
430 painter.add_listener(app, self.paint_listener());
431 }
432 if let Some(foreground_painter) = self.foreground_painter(app) {
433 foreground_painter.add_listener(app, self.paint_listener());
434 }
435 }
436
437 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
438 if let Some(painter) = self.painter(app) {
439 painter.remove_listener(app, &self.paint_listener());
440 }
441 if let Some(foreground_painter) = self.foreground_painter(app) {
442 foreground_painter.remove_listener(app, &self.paint_listener());
443 }
444 if let Some(child) = self.child(app) {
445 child.as_object().detach(app);
446 }
447 }
448
449 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
450 RenderProxyBoxMixin::perform_layout(self, app);
451 }
452
453 fn paint(
454 self: RenderHandle<Self>,
455 app: &mut App,
456 context: &mut PaintingContext,
457 offset: Offset,
458 ) {
459 if let Some(painter) = self.painter(app) {
460 self.paint_with_painter(app, context.canvas(), offset, &*painter);
461 self.set_raster_cache_hints(app, context);
462 }
463 RenderProxyBoxMixin::paint(self, app, context, offset);
464 if let Some(foreground_painter) = self.foreground_painter(app) {
465 self.paint_with_painter(app, context.canvas(), offset, &*foreground_painter);
466 self.set_raster_cache_hints(app, context);
467 }
468 }
469
470 fn visit_children(
471 self: RenderHandle<Self>,
472 app: &App,
473 visitor: &mut dyn FnMut(AnyRenderObject),
474 ) {
475 if let Some(child) = self.child(app) {
476 visitor(child.as_object());
477 }
478 }
479}
480
481impl RenderBox for RenderCustomPaint {
482 crate::render_box_accessors!();
483
484 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
485 RenderProxyBoxMixin::setup_parent_data(self, app, child);
486 }
487
488 fn apply_paint_transform(
489 self: RenderHandle<Self>,
490 app: &App,
491 child: AnyRenderObject,
492 transform: &mut Matrix4,
493 ) {
494 RenderProxyBoxMixin::apply_paint_transform(self, app, child, transform);
495 }
496
497 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
498 if self.child(app).is_none() {
499 let width = self.preferred_size(app).width();
500 return if width.is_finite() { width } else { 0.0 };
501 }
502 RenderProxyBoxMixin::compute_min_intrinsic_width(self, app, height)
503 }
504
505 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
506 if self.child(app).is_none() {
507 let width = self.preferred_size(app).width();
508 return if width.is_finite() { width } else { 0.0 };
509 }
510 RenderProxyBoxMixin::compute_max_intrinsic_width(self, app, height)
511 }
512
513 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
514 if self.child(app).is_none() {
515 let height = self.preferred_size(app).height();
516 return if height.is_finite() { height } else { 0.0 };
517 }
518 RenderProxyBoxMixin::compute_min_intrinsic_height(self, app, width)
519 }
520
521 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
522 if self.child(app).is_none() {
523 let height = self.preferred_size(app).height();
524 return if height.is_finite() { height } else { 0.0 };
525 }
526 RenderProxyBoxMixin::compute_max_intrinsic_height(self, app, width)
527 }
528
529 fn compute_distance_to_actual_baseline(
530 self: RenderHandle<Self>,
531 app: &mut App,
532 baseline: TextBaseline,
533 ) -> Option<f64> {
534 RenderProxyBoxMixin::compute_distance_to_actual_baseline(self, app, baseline)
535 }
536
537 fn compute_dry_baseline(
538 self: RenderHandle<Self>,
539 app: &mut App,
540 constraints: BoxConstraints,
541 baseline: TextBaseline,
542 ) -> Option<f64> {
543 RenderProxyBoxMixin::compute_dry_baseline(self, app, constraints, baseline)
544 }
545
546 fn compute_dry_layout(
547 self: RenderHandle<Self>,
548 app: &mut App,
549 constraints: BoxConstraints,
550 ) -> Size {
551 RenderProxyBoxMixin::compute_dry_layout(self, app, constraints)
552 }
553
554 fn hit_test_self(self: RenderHandle<Self>, app: &App, position: Offset) -> bool {
555 match &self.get(app).painter {
556 Some(painter) => painter.hit_test(app, position).unwrap_or(true),
557 None => false,
558 }
559 }
560
561 fn hit_test_children(
562 self: RenderHandle<Self>,
563 app: &mut App,
564 result: &mut BoxHitTestResult<'_>,
565 position: Offset,
566 ) -> bool {
567 if let Some(foreground_painter) = self.foreground_painter(app)
568 && foreground_painter.hit_test(app, position).unwrap_or(false)
569 {
570 return true;
571 }
572 RenderProxyBoxMixin::hit_test_children(self, app, result, position)
573 }
574}
575
576#[cfg(test)]
577mod tests {
578 use inset_foundation::AppCell;
579 use std::cell::Cell;
580
581 use inset_embedder::{Color, Paint, valo};
582 use inset_foundation::ValueNotifier;
583 use inset_gestures::HitTestResult;
584
585 use super::*;
586 use crate::box_::BoxHitTestResult;
587 use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer, PictureLayer};
588 use crate::object::AnyRenderObject;
589 use crate::proxy_box::RenderRepaintBoundary;
590
591 const RED: Color = Color::from_argb(255, 255, 0, 0);
592 const BLUE: Color = Color::from_argb(255, 0, 0, 255);
593 const GREEN: Color = Color::from_argb(255, 0, 255, 0);
594
595 struct Recorder {
598 color: Color,
599 paints: Rc<Cell<u32>>,
600 hit: Option<bool>,
601 repaint: Option<Rc<dyn Listenable>>,
602 should_repaint: bool,
603 }
604
605 impl Recorder {
606 fn new(color: Color) -> Recorder {
607 Recorder {
608 color,
609 paints: Rc::new(Cell::new(0)),
610 hit: None,
611 repaint: None,
612 should_repaint: false,
613 }
614 }
615 }
616
617 impl CustomPainter for Recorder {
618 fn repaint(&self) -> Option<&Rc<dyn Listenable>> {
619 self.repaint.as_ref()
620 }
621
622 fn paint(&self, _app: &mut App, canvas: &mut Canvas, size: Size) {
623 self.paints.set(self.paints.get() + 1);
624 canvas.draw_rect(
625 Offset::ZERO & size,
626 &Paint {
627 color: self.color.into(),
628 ..Paint::default()
629 },
630 );
631 }
632
633 fn should_repaint(&self, _app: &App, _old_delegate: &dyn CustomPainter) -> bool {
634 self.should_repaint
635 }
636
637 fn hit_test(&self, _app: &App, _position: Offset) -> Option<bool> {
638 self.hit
639 }
640
641 fn as_any(&self) -> &dyn Any {
642 self
643 }
644 }
645
646 fn schedule_root_paint(app: &mut App, node: AnyRenderObject) {
647 let root = OffsetLayer::new(app, Offset::ZERO);
648 root.as_layer().attach(app, node.id());
649 node.schedule_initial_paint(app, root.as_container_layer());
650 }
651
652 fn first_frame(app: &mut App, root: AnyRenderBox) -> Handle<PipelineOwner> {
654 let owner = PipelineOwner::new(app, None, None);
655 owner.set_root_node(app, Some(root.as_object()));
656 root.layout(app, BoxConstraints::tight(Size::new(100.0, 100.0)), false);
657 schedule_root_paint(app, root.as_object());
658 owner.flush_compositing_bits(app);
659 owner.flush_paint(app);
660 owner
661 }
662
663 fn pump_frame(app: &mut App, owner: Handle<PipelineOwner>) {
664 owner.flush_layout(app);
665 owner.flush_compositing_bits(app);
666 owner.flush_paint(app);
667 }
668
669 fn drawn_colors(app: &App, boundary: AnyRenderObject) -> Vec<valo::Color> {
671 let layer = boundary.debug_layer(app).expect("painted");
672 layer
673 .depth_first_iterate_children(app)
674 .into_iter()
675 .filter_map(|child| {
676 app.handle::<PictureLayer>(child.id())
677 .and_then(|picture| picture.picture(app).map(|p| p.ops().to_vec()))
678 })
679 .flatten()
680 .filter_map(|op| match op {
681 valo::Op::DrawRect { paint, .. } => Some(paint.color),
682 _ => None,
683 })
684 .collect()
685 }
686
687 fn hit_test(app: &mut App, target: AnyRenderBox, position: Offset) -> (bool, usize) {
688 let mut result = HitTestResult::new();
689 let is_hit = target.hit_test(app, &mut BoxHitTestResult::wrap(&mut result), position);
690 (is_hit, result.path().len())
691 }
692
693 #[test]
694 fn both_painters_paint_around_the_child() {
695 let cell = AppCell::new();
696 let mut app = cell.borrow_mut();
697 let background = Rc::new(Recorder::new(RED));
698 let foreground = Rc::new(Recorder::new(BLUE));
699 let child = crate::proxy_box::RenderColoredBox::new(&mut app, GREEN, true, None);
700 let custom = RenderCustomPaint::new(
701 &mut app,
702 Some(background.clone()),
703 Some(foreground.clone()),
704 Size::ZERO,
705 false,
706 false,
707 Some(child.as_box()),
708 );
709 let root = RenderRepaintBoundary::new(&mut app, Some(custom.as_box()));
710 first_frame(&mut app, root.as_box());
711
712 assert_eq!((background.paints.get(), foreground.paints.get()), (1, 1));
713 assert_eq!(
714 drawn_colors(&app, root.as_object()),
715 [RED.into(), GREEN.into(), BLUE.into()],
716 "background, child, foreground"
717 );
718 }
719
720 #[test]
721 fn a_painter_repaints_when_its_repaint_listenable_notifies() {
722 let cell = AppCell::new();
723 let mut app = cell.borrow_mut();
724 let notifier = app.create(ValueNotifier::new(0));
725 let painter = Rc::new(Recorder {
726 repaint: Some(Rc::new(notifier)),
727 ..Recorder::new(RED)
728 });
729 let custom = RenderCustomPaint::new(
730 &mut app,
731 Some(painter.clone()),
732 None,
733 Size::new(10.0, 10.0),
734 false,
735 false,
736 None,
737 );
738 let root = RenderRepaintBoundary::new(&mut app, Some(custom.as_box()));
739 let owner = first_frame(&mut app, root.as_box());
740 assert_eq!(painter.paints.get(), 1);
741 assert!(!custom.as_object().debug_needs_paint(&app));
742
743 notifier.set_value(&mut app, 1);
744 assert!(custom.as_object().debug_needs_paint(&app));
745 pump_frame(&mut app, owner);
746 assert_eq!(painter.paints.get(), 2);
747 }
748
749 #[test]
751 fn replacing_the_painter_asks_it_whether_to_repaint() {
752 let cell = AppCell::new();
753 let mut app = cell.borrow_mut();
754 let notifier = app.create(ValueNotifier::new(0));
755 let painter = Rc::new(Recorder {
756 repaint: Some(Rc::new(notifier)),
757 ..Recorder::new(RED)
758 });
759 let custom = RenderCustomPaint::new(
760 &mut app,
761 Some(painter.clone()),
762 None,
763 Size::new(10.0, 10.0),
764 false,
765 false,
766 None,
767 );
768 let root = RenderRepaintBoundary::new(&mut app, Some(custom.as_box()));
769 let owner = first_frame(&mut app, root.as_box());
770
771 let quiet = Rc::new(Recorder::new(BLUE));
772 custom.set_painter(&mut app, Some(quiet.clone()));
773 assert!(
774 !custom.as_object().debug_needs_paint(&app),
775 "should_repaint is false"
776 );
777 notifier.set_value(&mut app, 1);
778 assert!(
779 !custom.as_object().debug_needs_paint(&app),
780 "the old painter's listenable was unsubscribed"
781 );
782
783 let eager = Rc::new(Recorder {
784 should_repaint: true,
785 ..Recorder::new(BLUE)
786 });
787 custom.set_painter(&mut app, Some(eager.clone()));
788 assert!(custom.as_object().debug_needs_paint(&app));
789 pump_frame(&mut app, owner);
790 assert_eq!((quiet.paints.get(), eager.paints.get()), (0, 1));
791
792 custom.set_painter(&mut app, None);
793 assert!(
794 custom.as_object().debug_needs_paint(&app),
795 "a removal repaints"
796 );
797 }
798
799 #[test]
800 fn preferred_size_is_used_without_a_child() {
801 let cell = AppCell::new();
802 let mut app = cell.borrow_mut();
803 let custom = RenderCustomPaint::new(
804 &mut app,
805 None,
806 None,
807 Size::new(20.0, 30.0),
808 false,
809 false,
810 None,
811 );
812 custom.layout(&mut app, BoxConstraints::new(), false);
813 assert_eq!(custom.size(&app), Size::new(20.0, 30.0));
814
815 custom.layout(
816 &mut app,
817 BoxConstraints::loose(Size::new(10.0, 10.0)),
818 false,
819 );
820 assert_eq!(
821 custom.size(&app),
822 Size::new(10.0, 10.0),
823 "the preferred size is constrained"
824 );
825 }
826
827 #[test]
828 fn a_child_sizes_the_custom_paint() {
829 let cell = AppCell::new();
830 let mut app = cell.borrow_mut();
831 let child = crate::proxy_box::RenderConstrainedBox::new(
832 &mut app,
833 BoxConstraints::tight(Size::new(40.0, 40.0)),
834 None,
835 );
836 let custom = RenderCustomPaint::new(
837 &mut app,
838 None,
839 None,
840 Size::new(20.0, 30.0),
841 false,
842 false,
843 Some(child.as_box()),
844 );
845 custom.layout(&mut app, BoxConstraints::new(), false);
846 assert_eq!(custom.size(&app), Size::new(40.0, 40.0));
847 }
848
849 #[test]
850 fn the_painters_answer_hit_tests() {
851 let cell = AppCell::new();
852 let mut app = cell.borrow_mut();
853 let background = Rc::new(Recorder::new(RED));
854 let custom = RenderCustomPaint::new(
855 &mut app,
856 Some(background),
857 None,
858 Size::new(100.0, 100.0),
859 false,
860 false,
861 None,
862 );
863 custom.layout(&mut app, BoxConstraints::new(), false);
864 assert_eq!(
865 hit_test(&mut app, custom.as_box(), Offset::new(50.0, 50.0)),
866 (true, 1),
867 "a background painter is hit by default"
868 );
869
870 let missing = Rc::new(Recorder {
871 hit: Some(false),
872 ..Recorder::new(RED)
873 });
874 custom.set_painter(&mut app, Some(missing));
875 assert_eq!(
876 hit_test(&mut app, custom.as_box(), Offset::new(50.0, 50.0)),
877 (false, 0)
878 );
879
880 custom.set_painter(&mut app, None);
881 assert_eq!(
882 hit_test(&mut app, custom.as_box(), Offset::new(50.0, 50.0)),
883 (false, 0),
884 "no painter, no hit"
885 );
886
887 let foreground = Rc::new(Recorder {
888 hit: Some(true),
889 ..Recorder::new(BLUE)
890 });
891 custom.set_foreground_painter(&mut app, Some(foreground));
892 assert_eq!(
893 hit_test(&mut app, custom.as_box(), Offset::new(50.0, 50.0)),
894 (true, 1),
895 "a foreground painter answers for the children"
896 );
897 }
898}