1use std::{marker::PhantomData, mem, sync::Arc};
4
5use crate::{
6 widget::info::{ParallelSegmentOffsets, WidgetBoundsInfo},
7 window::WINDOW,
8};
9use zng_color::{
10 RenderMixBlendMode, Rgba, colors,
11 filter::RenderFilter,
12 gradient::{RenderExtendMode, RenderGradientStop},
13};
14use zng_layout::unit::{
15 AngleRadian, Factor, FactorUnits, Px, PxCornerRadius, PxLine, PxPoint, PxRect, PxSideOffsets, PxSize, PxTransform, PxVector, euclid,
16};
17use zng_task::rayon::iter::{ParallelBridge, ParallelIterator};
18use zng_unique_id::{impl_unique_id_bytemuck, unique_id_32};
19use zng_var::{Var, VarCapability, VarValue, impl_from_and_into_var};
20use zng_view_api::{
21 ReferenceFrameId as RenderReferenceFrameId, ViewProcessGen,
22 api_extension::{ApiExtensionId, ApiExtensionPayload},
23 config::FontAntiAliasing,
24 display_list::{DisplayList, DisplayListBuilder, FilterOp, NinePatchSource, ReuseStart},
25 font::{GlyphInstance, GlyphOptions},
26 window::FrameId,
27};
28
29use crate::{
30 update::{RenderUpdates, UpdateFlags},
31 view_process::ViewRenderer,
32 widget::{
33 WIDGET, WidgetId,
34 base::{PARALLEL_VAR, Parallel},
35 border::{self, BorderSides},
36 info::{HitTestClips, ParallelBuilder, WidgetInfo, WidgetInfoTree, WidgetRenderInfo},
37 },
38};
39
40pub use zng_view_api::{
41 ImageRendering, RepeatMode, TransformStyle,
42 display_list::{FrameValue, FrameValueUpdate, ReuseRange},
43};
44
45pub trait Font {
49 fn is_empty_fallback(&self) -> bool;
51
52 fn renderer_id(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId;
56}
57
58pub trait Img {
64 fn renderer_id(&self, renderer: &ViewRenderer) -> zng_view_api::image::ImageTextureId;
69
70 fn alpha_type(&self) -> zng_view_api::AlphaType {
74 zng_view_api::AlphaType::PremultipliedAlpha
75 }
76
77 fn size(&self) -> PxSize;
79}
80
81macro_rules! expect_inner {
82 ($self:ident.$fn_name:ident) => {
83 if $self.is_outer() {
84 tracing::error!("called `{}` in outer context of `{}`", stringify!($fn_name), $self.widget_id);
85 }
86 };
87}
88
89macro_rules! warn_empty {
90 ($self:ident.$fn_name:ident($rect:tt)) => {
91 #[cfg(debug_assertions)]
92 if $rect.is_empty() {
93 tracing::warn!(
94 "called `{}` with empty `{:?}` in `{:?}`",
95 stringify!($fn_name),
96 $rect,
97 $self.widget_id
98 )
99 }
100 };
101}
102
103struct WidgetData {
104 parent_child_offset: PxVector,
105 inner_is_set: bool, inner_transform: PxTransform,
107 filter: RenderFilter,
108 blend: RenderMixBlendMode,
109 backdrop_filter: RenderFilter,
110}
111
112pub struct FrameBuilder {
114 render_widgets: Arc<RenderUpdates>,
115 render_update_widgets: Arc<RenderUpdates>,
116
117 frame_id: FrameId,
118 widget_id: WidgetId,
119 transform: PxTransform,
120 transform_style: TransformStyle,
121
122 default_font_aa: FontAntiAliasing,
123
124 renderer: Option<ViewRenderer>,
125
126 scale_factor: Factor,
127
128 display_list: DisplayListBuilder,
129
130 hit_testable: bool,
131 visible: bool,
132 backface_visible: bool,
133 auto_hit_test: bool,
134 hit_clips: HitTestClips,
135
136 perspective: Option<(f32, PxPoint)>,
137
138 auto_hide_rect: PxRect,
139 widget_data: Option<WidgetData>,
140 child_offset: PxVector,
141 parent_inner_bounds: Option<PxRect>,
142
143 view_process_has_frame: bool,
144 can_reuse: bool,
145 open_reuse: Option<ReuseStart>,
146
147 clear_color: Option<Rgba>,
148
149 widget_count: usize,
150 widget_count_offsets: ParallelSegmentOffsets,
151
152 debug_dot_overlays: Vec<(PxPoint, Rgba)>,
153}
154impl FrameBuilder {
155 #[expect(clippy::too_many_arguments)]
169 pub fn new(
170 render_widgets: Arc<RenderUpdates>,
171 render_update_widgets: Arc<RenderUpdates>,
172 frame_id: FrameId,
173 root_id: WidgetId,
174 root_bounds: &WidgetBoundsInfo,
175 info_tree: &WidgetInfoTree,
176 renderer: Option<ViewRenderer>,
177 scale_factor: Factor,
178 default_font_aa: FontAntiAliasing,
179 ) -> Self {
180 let display_list = DisplayListBuilder::new(frame_id);
181
182 let root_size = root_bounds.outer_size();
183 let auto_hide_rect = PxRect::from_size(root_size).inflate(root_size.width, root_size.height);
184 root_bounds.set_outer_transform(PxTransform::identity(), info_tree);
185
186 let vp_gen = renderer
187 .as_ref()
188 .and_then(|r| r.generation().ok())
189 .unwrap_or(ViewProcessGen::INVALID);
190 let view_process_has_frame = vp_gen != ViewProcessGen::INVALID && vp_gen == info_tree.view_process_gen();
191
192 FrameBuilder {
193 render_widgets,
194 render_update_widgets,
195 frame_id,
196 widget_id: root_id,
197 transform: PxTransform::identity(),
198 transform_style: TransformStyle::Flat,
199 default_font_aa: match default_font_aa {
200 FontAntiAliasing::Default => FontAntiAliasing::Subpixel,
201 aa => aa,
202 },
203 renderer,
204 scale_factor,
205 display_list,
206 hit_testable: true,
207 visible: true,
208 backface_visible: true,
209 auto_hit_test: false,
210 hit_clips: HitTestClips::default(),
211 widget_data: Some(WidgetData {
212 filter: vec![],
213 blend: RenderMixBlendMode::Normal,
214 backdrop_filter: vec![],
215 parent_child_offset: PxVector::zero(),
216 inner_is_set: false,
217 inner_transform: PxTransform::identity(),
218 }),
219 child_offset: PxVector::zero(),
220 parent_inner_bounds: None,
221 perspective: None,
222 view_process_has_frame,
223 can_reuse: view_process_has_frame,
224 open_reuse: None,
225 auto_hide_rect,
226
227 widget_count: 0,
228 widget_count_offsets: ParallelSegmentOffsets::default(),
229
230 clear_color: Some(colors::BLACK.transparent()),
231
232 debug_dot_overlays: vec![],
233 }
234 }
235
236 #[expect(clippy::too_many_arguments)]
238 pub fn new_renderless(
239 render_widgets: Arc<RenderUpdates>,
240 render_update_widgets: Arc<RenderUpdates>,
241 frame_id: FrameId,
242 root_id: WidgetId,
243 root_bounds: &WidgetBoundsInfo,
244 info_tree: &WidgetInfoTree,
245 scale_factor: Factor,
246 default_font_aa: FontAntiAliasing,
247 ) -> Self {
248 Self::new(
249 render_widgets,
250 render_update_widgets,
251 frame_id,
252 root_id,
253 root_bounds,
254 info_tree,
255 None,
256 scale_factor,
257 default_font_aa,
258 )
259 }
260
261 pub fn scale_factor(&self) -> Factor {
265 self.scale_factor
266 }
267
268 pub fn is_renderless(&self) -> bool {
272 self.renderer.is_none()
273 }
274
275 pub fn set_clear_color(&mut self, color: Rgba) {
283 self.clear_color = Some(color);
284 }
285
286 pub fn renderer(&self) -> Option<&ViewRenderer> {
290 self.renderer.as_ref()
291 }
292
293 pub fn frame_id(&self) -> FrameId {
295 self.frame_id
296 }
297
298 pub fn widget_id(&self) -> WidgetId {
300 self.widget_id
301 }
302
303 pub fn transform(&self) -> &PxTransform {
305 &self.transform
306 }
307
308 pub fn is_hit_testable(&self) -> bool {
315 self.hit_testable
316 }
317
318 pub fn is_visible(&self) -> bool {
320 self.visible
321 }
322
323 pub fn auto_hit_test(&self) -> bool {
329 self.auto_hit_test
330 }
331
332 pub fn with_default_font_aa(&mut self, aa: FontAntiAliasing, render: impl FnOnce(&mut Self)) {
334 let parent = mem::replace(&mut self.default_font_aa, aa);
335 render(self);
336 self.default_font_aa = parent;
337 }
338
339 pub fn with_hit_tests_disabled(&mut self, render: impl FnOnce(&mut Self)) {
344 let prev = mem::replace(&mut self.hit_testable, false);
345 render(self);
346 self.hit_testable = prev;
347 }
348
349 pub fn with_auto_hit_test(&mut self, auto_hit_test: bool, render: impl FnOnce(&mut Self)) {
355 let prev = mem::replace(&mut self.auto_hit_test, auto_hit_test);
356 render(self);
357 self.auto_hit_test = prev;
358 }
359
360 pub fn auto_hide_rect(&self) -> PxRect {
364 self.auto_hide_rect
365 }
366
367 pub fn with_auto_hide_rect(&mut self, auto_hide_rect: PxRect, render: impl FnOnce(&mut Self)) {
371 let parent_rect = mem::replace(&mut self.auto_hide_rect, auto_hide_rect);
372 render(self);
373 self.auto_hide_rect = parent_rect;
374 }
375
376 pub fn push_widget(&mut self, render: impl FnOnce(&mut Self)) {
390 #[derive(Default)]
392 struct PushWidget {
393 parent_visible: bool,
394 parent_perspective: Option<(f32, PxPoint)>,
395 parent_can_reuse: bool,
396 widget_z: usize,
397 outer_transform: PxTransform,
398 undo_prev_outer_transform: Option<PxTransform>,
399 reuse: Option<ReuseRange>,
400 reused: bool,
401 display_count: usize,
402 child_offset: PxVector,
403
404 wgt_info: Option<WidgetInfo>,
405 collapsed: bool,
406 }
407 impl PushWidget {
408 fn before(&mut self, builder: &mut FrameBuilder) {
409 let wgt_info = WIDGET.info();
410 let id = wgt_info.id();
411
412 #[cfg(debug_assertions)]
413 if builder.widget_data.is_some() && WIDGET.parent_id().is_some() {
414 tracing::error!(
415 "called `push_widget` for `{}` without calling `push_inner` for the parent `{}`",
416 WIDGET.trace_id(),
417 builder.widget_id
418 );
419 }
420
421 let bounds = wgt_info.bounds_info();
422 let tree = wgt_info.tree();
423
424 if bounds.is_collapsed() {
425 for info in wgt_info.self_and_descendants() {
427 info.bounds_info().set_rendered(None, tree);
428 }
429 let _ = WIDGET.take_update(UpdateFlags::LAYOUT | UpdateFlags::RENDER | UpdateFlags::RENDER_UPDATE);
431 let _ = WIDGET.take_render_reuse(&builder.render_widgets, &builder.render_update_widgets);
432 self.collapsed = true;
433 return;
434 } else {
435 #[cfg(debug_assertions)]
436 if WIDGET.pending_update().contains(UpdateFlags::LAYOUT) {
437 tracing::error!("called `push_widget` for `{}` with pending layout", WIDGET.trace_id());
440 }
441 }
442
443 let mut try_reuse = true;
444
445 let prev_outer = bounds.outer_transform();
446 self.outer_transform = PxTransform::from(builder.child_offset).then(&builder.transform);
447 bounds.set_outer_transform(self.outer_transform, tree);
448
449 if bounds.parent_child_offset() != builder.child_offset {
450 bounds.set_parent_child_offset(builder.child_offset);
451 try_reuse = false;
452 }
453 let outer_bounds = bounds.outer_bounds();
454
455 self.parent_visible = builder.visible;
456
457 if bounds.can_auto_hide() {
458 let mut outer_bounds = outer_bounds;
462 if outer_bounds.size.width < Px(1) {
463 outer_bounds.size.width = Px(1);
464 }
465 if outer_bounds.size.height < Px(1) {
466 outer_bounds.size.height = Px(1);
467 }
468 match builder.auto_hide_rect.intersection(&outer_bounds) {
469 Some(cull) => {
470 let partial = cull != outer_bounds;
471 if partial || bounds.is_partially_culled() {
472 try_reuse = false;
474 bounds.set_is_partially_culled(partial);
475 }
476 }
477 None => {
478 builder.visible = false;
480 }
481 }
482 } else {
483 bounds.set_is_partially_culled(false);
484 }
485
486 self.parent_perspective = builder.perspective;
487 builder.perspective = wgt_info.perspective();
488 if let Some((_, o)) = &mut builder.perspective {
489 *o -= builder.child_offset;
490 }
491
492 let can_reuse = builder.view_process_has_frame
493 && match bounds.render_info() {
494 Some(i) => i.visible == builder.visible && i.parent_perspective == builder.perspective,
495 None => false,
497 };
498 self.parent_can_reuse = mem::replace(&mut builder.can_reuse, can_reuse);
499
500 try_reuse &= can_reuse;
501
502 builder.widget_count += 1;
503 self.widget_z = builder.widget_count;
504
505 self.reuse = WIDGET.take_render_reuse(&builder.render_widgets, &builder.render_update_widgets);
506 if !try_reuse {
507 self.reuse = None;
508 }
509
510 if self.reuse.is_some() {
511 if let Some(undo_prev) = prev_outer.inverse() {
513 self.undo_prev_outer_transform = Some(undo_prev);
514 } else {
515 self.reuse = None; }
517 }
518
519 let index = builder.hit_clips.push_child(id);
520 bounds.set_hit_index(index);
521
522 self.reused = true;
523 self.display_count = builder.display_list.len();
524
525 self.child_offset = mem::take(&mut builder.child_offset);
526
527 self.wgt_info = Some(wgt_info);
528 }
529 fn push(&mut self, builder: &mut FrameBuilder, render: impl FnOnce(&mut FrameBuilder)) {
530 if self.collapsed {
531 return;
532 }
533
534 builder.push_reuse(&mut self.reuse, |frame| {
536 self.reused = false;
539 self.undo_prev_outer_transform = None;
540
541 frame.widget_data = Some(WidgetData {
542 filter: vec![],
543 blend: RenderMixBlendMode::Normal,
544 backdrop_filter: vec![],
545 parent_child_offset: self.child_offset,
546 inner_is_set: frame.perspective.is_some(),
547 inner_transform: PxTransform::identity(),
548 });
549 let parent_widget = mem::replace(&mut frame.widget_id, self.wgt_info.as_ref().unwrap().id());
550
551 render(frame);
552
553 frame.widget_id = parent_widget;
554 frame.widget_data = None;
555 });
556 }
557 fn after(self, builder: &mut FrameBuilder) {
558 if self.collapsed {
559 return;
560 }
561
562 WIDGET.set_render_reuse(self.reuse);
563
564 let wgt_info = self.wgt_info.unwrap();
565 let id = wgt_info.id();
566 let tree = wgt_info.tree();
567 let bounds = wgt_info.bounds_info();
568
569 if self.reused {
570 let _span = tracing::trace_span!("reuse-descendants", ?id).entered();
573
574 let transform_patch = self.undo_prev_outer_transform.and_then(|t| {
575 let t = t.then(&self.outer_transform);
576 if t != PxTransform::identity() { Some(t) } else { None }
577 });
578
579 let current_wgt = tree.get(id).unwrap();
580 let z_patch = self.widget_z as i64 - current_wgt.z_index().map(|(b, _)| b.0 as i64).unwrap_or(0);
581
582 let update_transforms = transform_patch.is_some();
583 let seg_id = builder.widget_count_offsets.id();
584
585 if update_transforms {
587 let transform_patch = transform_patch.unwrap();
588
589 let update_transforms_and_z = |info: WidgetInfo| {
591 let b = info.bounds_info();
592
593 if b != bounds {
594 b.set_outer_transform(b.outer_transform().then(&transform_patch), tree);
596 }
597 b.set_inner_transform(
598 b.inner_transform().then(&transform_patch),
599 tree,
600 info.id(),
601 info.parent().map(|p| p.inner_bounds()),
602 );
603
604 if let Some(i) = b.render_info() {
605 let (back, front) = info.z_index().unwrap();
606 let back = back.0 as i64 + z_patch;
607 let front = front.0 as i64 + z_patch;
608
609 b.set_rendered(
610 Some(WidgetRenderInfo {
611 visible: i.visible,
612 parent_perspective: i.parent_perspective,
613 seg_id,
614 back: back.try_into().unwrap(),
615 front: front.try_into().unwrap(),
616 }),
617 tree,
618 );
619 }
620 };
621
622 let targets = current_wgt.self_and_descendants();
623 if PARALLEL_VAR.get().contains(Parallel::RENDER) {
624 targets.par_bridge().for_each(update_transforms_and_z);
625 } else {
626 targets.for_each(update_transforms_and_z);
627 }
628 } else {
629 let update_z = |info: WidgetInfo| {
630 let bounds = info.bounds_info();
631
632 if let Some(i) = bounds.render_info() {
633 let (back, front) = info.z_index().unwrap();
634 let mut back = back.0 as i64 + z_patch;
635 let mut front = front.0 as i64 + z_patch;
636 if back < 0 {
637 tracing::error!("incorrect back Z-index ({back}) after patch ({z_patch})");
638 back = 0;
639 }
640 if front < 0 {
641 tracing::error!("incorrect front Z-index ({front}) after patch ({z_patch})");
642 front = 0;
643 }
644 bounds.set_rendered(
645 Some(WidgetRenderInfo {
646 visible: i.visible,
647 parent_perspective: i.parent_perspective,
648 seg_id,
649 back: back as _,
650 front: front as _,
651 }),
652 tree,
653 );
654 }
655 };
656
657 let targets = current_wgt.self_and_descendants();
658 if PARALLEL_VAR.get().contains(Parallel::RENDER) {
659 targets.par_bridge().for_each(update_z);
660 } else {
661 targets.for_each(update_z);
662 }
663 }
664
665 builder.widget_count = bounds.render_info().map(|i| i.front).unwrap_or(builder.widget_count);
667 } else {
668 bounds.set_rendered(
670 Some(WidgetRenderInfo {
671 visible: builder.display_list.len() > self.display_count,
672 parent_perspective: builder.perspective,
673 seg_id: builder.widget_count_offsets.id(),
674 back: self.widget_z,
675 front: builder.widget_count,
676 }),
677 tree,
678 );
679 }
680
681 builder.visible = self.parent_visible;
682 builder.perspective = self.parent_perspective;
683 builder.can_reuse = self.parent_can_reuse;
684 }
685 }
686 let mut push = PushWidget::default();
687 push.before(self);
688 push.push(self, render);
689 push.after(self);
690 }
691
692 pub fn can_reuse(&self) -> bool {
698 self.can_reuse
699 }
700
701 pub fn with_no_reuse(&mut self, render: impl FnOnce(&mut Self)) {
705 let prev_can_reuse = self.can_reuse;
706 self.can_reuse = false;
707 render(self);
708 self.can_reuse = prev_can_reuse;
709 }
710
711 pub fn push_reuse(&mut self, group: &mut Option<ReuseRange>, generate: impl FnOnce(&mut Self)) {
722 if self.can_reuse {
723 if let Some(g) = &group {
724 if self.visible {
725 self.display_list.push_reuse_range(g);
726 }
727 return;
728 }
729 }
730 *group = None;
731 let parent_group = self.open_reuse.replace(self.display_list.start_reuse_range());
732
733 generate(self);
734
735 let start = self.open_reuse.take().unwrap();
736 let range = self.display_list.finish_reuse_range(start);
737 *group = Some(range);
738 self.open_reuse = parent_group;
739 }
740
741 pub fn hide(&mut self, render: impl FnOnce(&mut Self)) {
753 let parent_visible = mem::replace(&mut self.visible, false);
754 render(self);
755 self.visible = parent_visible;
756 }
757
758 pub fn with_backface_visibility(&mut self, visible: bool, render: impl FnOnce(&mut Self)) {
762 if self.backface_visible != visible {
763 let parent = self.backface_visible;
764 self.display_list.set_backface_visibility(visible);
765 render(self);
766 self.display_list.set_backface_visibility(parent);
767 self.backface_visible = parent;
768 } else {
769 render(self);
770 }
771 }
772
773 pub fn is_outer(&self) -> bool {
780 self.widget_data.is_some()
781 }
782
783 pub fn push_inner_filter(&mut self, filter: RenderFilter, render: impl FnOnce(&mut Self)) {
792 if let Some(data) = self.widget_data.as_mut() {
793 let mut filter = filter;
794 filter.reverse();
795 data.filter.extend(filter.iter().copied());
796
797 render(self);
798 } else {
799 tracing::error!("called `push_inner_filter` inside inner context of `{}`", self.widget_id);
800 render(self);
801 }
802 }
803
804 pub fn push_inner_opacity(&mut self, bind: FrameValue<f32>, render: impl FnOnce(&mut Self)) {
813 if let Some(data) = self.widget_data.as_mut() {
814 data.filter.push(FilterOp::Opacity(bind));
815
816 render(self);
817 } else {
818 tracing::error!("called `push_inner_opacity` inside inner context of `{}`", self.widget_id);
819 render(self);
820 }
821 }
822
823 pub fn push_inner_backdrop_filter(&mut self, filter: RenderFilter, render: impl FnOnce(&mut Self)) {
832 if let Some(data) = self.widget_data.as_mut() {
833 let mut filter = filter;
834 filter.reverse();
835 data.backdrop_filter.extend(filter.iter().copied());
836
837 render(self);
838 } else {
839 tracing::error!("called `push_inner_backdrop_filter` inside inner context of `{}`", self.widget_id);
840 render(self);
841 }
842 }
843
844 pub fn push_inner_blend(&mut self, mode: RenderMixBlendMode, render: impl FnOnce(&mut Self)) {
853 if let Some(data) = self.widget_data.as_mut() {
854 data.blend = mode;
855
856 render(self);
857 } else {
858 tracing::error!("called `push_inner_blend` inside inner context of `{}`", self.widget_id);
859 render(self);
860 }
861 }
862
863 pub fn push_child(&mut self, offset: PxVector, render: impl FnOnce(&mut Self)) {
874 if self.widget_data.is_some() {
875 tracing::error!("called `push_child` outside inner context of `{}`", self.widget_id);
876 }
877
878 self.child_offset = offset;
879 render(self);
880 self.child_offset = PxVector::zero();
881 }
882
883 pub fn push_inner_transform(&mut self, transform: &PxTransform, render: impl FnOnce(&mut Self)) {
892 if let Some(data) = &mut self.widget_data {
893 let parent_transform = data.inner_transform;
894 let parent_is_set = mem::replace(&mut data.inner_is_set, true);
895 data.inner_transform = data.inner_transform.then(transform);
896
897 render(self);
898
899 if let Some(data) = &mut self.widget_data {
900 data.inner_transform = parent_transform;
901 data.inner_is_set = parent_is_set;
902 }
903 } else {
904 tracing::error!("called `push_inner_transform` inside inner context of `{}`", self.widget_id);
905 render(self);
906 }
907 }
908
909 pub fn push_inner(
916 &mut self,
917 layout_translation_key: FrameValueKey<PxTransform>,
918 layout_translation_animating: bool,
919 render: impl FnOnce(&mut Self),
920 ) {
921 #[derive(Default)]
923 struct PushInner {
924 invalid: bool,
925 parent_transform: PxTransform,
926 parent_transform_style: TransformStyle,
927 parent_hit_clips: HitTestClips,
928 parent_parent_inner_bounds: Option<PxRect>,
929 visible: bool,
930 ctx_outside_ref_frame: i32,
931 ctx_inside_ref_frame: i32,
932
933 wgt_info: Option<WidgetInfo>,
934 }
935 impl PushInner {
936 fn before(
937 &mut self,
938 builder: &mut FrameBuilder,
939 layout_translation_key: FrameValueKey<PxTransform>,
940 layout_translation_animating: bool,
941 ) {
942 if let Some(mut data) = builder.widget_data.take() {
943 self.parent_transform = builder.transform;
944 self.parent_transform_style = builder.transform_style;
945 self.parent_hit_clips = mem::take(&mut builder.hit_clips);
946
947 let wgt_info = WIDGET.info();
948 let id = wgt_info.id();
949 let bounds = wgt_info.bounds_info();
950 let tree = wgt_info.tree();
951
952 let inner_offset = bounds.inner_offset();
953 let mut inner_transform = data.inner_transform;
954 if let Some((d, mut o)) = builder.perspective {
955 o -= inner_offset;
956 let x = o.x.0 as f32;
957 let y = o.y.0 as f32;
958 let p = PxTransform::translation(-x, -y)
959 .then(&PxTransform::perspective(d))
960 .then_translate(euclid::vec2(x, y));
961 inner_transform = inner_transform.then(&p);
962 }
963 let inner_transform = inner_transform.then_translate((data.parent_child_offset + inner_offset).cast());
964
965 builder.transform = inner_transform.then(&self.parent_transform);
966
967 bounds.set_inner_transform(builder.transform, tree, id, builder.parent_inner_bounds);
968
969 self.parent_parent_inner_bounds = mem::replace(&mut builder.parent_inner_bounds, Some(bounds.inner_bounds()));
970
971 if builder.visible {
972 self.visible = true;
973 builder.transform_style = wgt_info.transform_style();
974
975 let has_3d_ctx = matches!(builder.transform_style, TransformStyle::Preserve3D);
976 let has_filters = !data.filter.is_empty() || data.blend != RenderMixBlendMode::Normal;
977
978 macro_rules! push_reference_frame {
981 () => {
982 builder.display_list.push_reference_frame(
983 ReferenceFrameId::from_widget(builder.widget_id).into(),
984 layout_translation_key.bind(inner_transform, layout_translation_animating),
985 builder.transform_style.into(),
986 !data.inner_is_set,
987 );
988 if !data.backdrop_filter.is_empty() {
989 builder
990 .display_list
991 .push_backdrop_filter(PxRect::from_size(bounds.inner_size()), &data.backdrop_filter);
992 }
993 };
994 }
995
996 if has_filters {
997 data.filter.reverse();
1004
1005 if has_3d_ctx {
1006 if matches!(
1011 (builder.transform_style, bounds.transform_style()),
1012 (TransformStyle::Preserve3D, TransformStyle::Flat)
1013 ) {
1014 push_reference_frame!();
1016 builder
1017 .display_list
1018 .push_stacking_context(RenderMixBlendMode::Normal, builder.transform_style, &[]);
1019 builder
1020 .display_list
1021 .push_stacking_context(data.blend, TransformStyle::Flat, &data.filter);
1022 self.ctx_inside_ref_frame = 2;
1023 } else if wgt_info
1024 .parent()
1025 .map(|p| matches!(p.bounds_info().transform_style(), TransformStyle::Flat))
1026 .unwrap_or(false)
1027 {
1028 builder
1031 .display_list
1032 .push_stacking_context(data.blend, TransformStyle::Flat, &data.filter);
1033 self.ctx_outside_ref_frame = 1;
1034 push_reference_frame!();
1035 builder
1036 .display_list
1037 .push_stacking_context(RenderMixBlendMode::Normal, builder.transform_style, &[]);
1038 self.ctx_inside_ref_frame = 1;
1039 } else {
1040 tracing::warn!(
1043 "widget `{id}` cannot have filters because it is `Preserve3D` inside `Preserve3D`, filters & blend ignored"
1044 );
1045
1046 push_reference_frame!();
1047 builder
1048 .display_list
1049 .push_stacking_context(RenderMixBlendMode::Normal, builder.transform_style, &[]);
1050 self.ctx_inside_ref_frame = 1;
1051 }
1052 } else {
1053 push_reference_frame!();
1055 builder
1056 .display_list
1057 .push_stacking_context(data.blend, TransformStyle::Flat, &data.filter);
1058 self.ctx_inside_ref_frame = 1;
1059 }
1060 } else if has_3d_ctx {
1061 push_reference_frame!();
1063 builder
1064 .display_list
1065 .push_stacking_context(RenderMixBlendMode::Normal, builder.transform_style, &[]);
1066 self.ctx_inside_ref_frame = 1;
1067 } else {
1068 push_reference_frame!();
1070 }
1071 }
1072
1073 self.wgt_info = Some(wgt_info);
1074 } else {
1075 tracing::error!("called `push_inner` more then once for `{}`", builder.widget_id);
1076 self.invalid = true;
1077 }
1078 }
1079 fn push(&mut self, builder: &mut FrameBuilder, render: impl FnOnce(&mut FrameBuilder)) {
1080 if self.invalid {
1081 return;
1082 }
1083 render(builder)
1084 }
1085 fn after(mut self, builder: &mut FrameBuilder) {
1086 if self.invalid {
1087 return;
1088 }
1089
1090 if self.visible {
1091 while self.ctx_inside_ref_frame > 0 {
1092 builder.display_list.pop_stacking_context();
1093 self.ctx_inside_ref_frame -= 1;
1094 }
1095
1096 builder.display_list.pop_reference_frame();
1097
1098 while self.ctx_outside_ref_frame > 0 {
1099 builder.display_list.pop_stacking_context();
1100 self.ctx_outside_ref_frame -= 1;
1101 }
1102 }
1103
1104 let wgt_info = self.wgt_info.unwrap();
1105 let bounds = wgt_info.bounds_info();
1106
1107 builder.transform = self.parent_transform;
1109 builder.transform_style = self.parent_transform_style;
1110 builder.parent_inner_bounds = self.parent_parent_inner_bounds;
1111
1112 let hit_clips = mem::replace(&mut builder.hit_clips, self.parent_hit_clips);
1113 bounds.set_hit_clips(hit_clips);
1114
1115 if !builder.debug_dot_overlays.is_empty() && wgt_info.parent().is_none() {
1116 for (offset, color) in mem::take(&mut builder.debug_dot_overlays) {
1117 builder.push_debug_dot(offset, color);
1118 }
1119 }
1120 }
1121 }
1122 let mut push_inner = PushInner::default();
1123 push_inner.before(self, layout_translation_key, layout_translation_animating);
1124 push_inner.push(self, render);
1125 push_inner.after(self);
1126 }
1127
1128 pub fn is_inner(&self) -> bool {
1135 self.widget_data.is_none()
1136 }
1137
1138 pub fn hit_test(&mut self) -> HitTestBuilder {
1143 expect_inner!(self.hit_test);
1144
1145 HitTestBuilder {
1146 hit_clips: &mut self.hit_clips,
1147 is_hit_testable: self.hit_testable,
1148 }
1149 }
1150
1151 pub fn push_clip_rect(&mut self, clip_rect: PxRect, clip_out: bool, hit_test: bool, render: impl FnOnce(&mut FrameBuilder)) {
1160 self.push_clips(move |c| c.push_clip_rect(clip_rect, clip_out, hit_test), render)
1161 }
1162
1163 pub fn push_clip_rounded_rect(
1172 &mut self,
1173 clip_rect: PxRect,
1174 corners: PxCornerRadius,
1175 clip_out: bool,
1176 hit_test: bool,
1177 render: impl FnOnce(&mut FrameBuilder),
1178 ) {
1179 self.push_clips(move |c| c.push_clip_rounded_rect(clip_rect, corners, clip_out, hit_test), render)
1180 }
1181
1182 pub fn push_clips(&mut self, clips: impl FnOnce(&mut ClipBuilder), render: impl FnOnce(&mut FrameBuilder)) {
1184 expect_inner!(self.push_clips);
1185
1186 let (mut render_count, mut hit_test_count) = {
1187 let mut clip_builder = ClipBuilder {
1188 builder: self,
1189 render_count: 0,
1190 hit_test_count: 0,
1191 };
1192 clips(&mut clip_builder);
1193 (clip_builder.render_count, clip_builder.hit_test_count)
1194 };
1195
1196 render(self);
1197
1198 while hit_test_count > 0 {
1199 hit_test_count -= 1;
1200
1201 self.hit_clips.pop_clip();
1202 }
1203 while render_count > 0 {
1204 render_count -= 1;
1205
1206 self.display_list.pop_clip();
1207 }
1208 }
1209
1210 pub fn push_mask(&mut self, image: &impl Img, rect: PxRect, render: impl FnOnce(&mut Self)) {
1212 let mut pop = false;
1213 if self.visible {
1214 if let Some(r) = &self.renderer {
1215 self.display_list.push_mask(image.renderer_id(r), rect);
1216 pop = true;
1217 }
1218 }
1219
1220 render(self);
1221
1222 if pop {
1223 self.display_list.pop_mask();
1224 }
1225 }
1226
1227 pub fn push_reference_frame(
1241 &mut self,
1242 key: ReferenceFrameId,
1243 transform: FrameValue<PxTransform>,
1244 is_2d_scale_translation: bool,
1245 hit_test: bool,
1246 render: impl FnOnce(&mut Self),
1247 ) {
1248 let transform_value = transform.value();
1249
1250 let prev_transform = self.transform;
1251 self.transform = transform_value.then(&prev_transform);
1252
1253 if self.visible {
1254 self.display_list
1255 .push_reference_frame(key.into(), transform, self.transform_style, is_2d_scale_translation);
1256 }
1257
1258 let hit_test = hit_test || self.auto_hit_test;
1259
1260 if hit_test {
1261 self.hit_clips.push_transform(transform);
1262 }
1263
1264 render(self);
1265
1266 if self.visible {
1267 self.display_list.pop_reference_frame();
1268 }
1269 self.transform = prev_transform;
1270
1271 if hit_test {
1272 self.hit_clips.pop_transform();
1273 }
1274 }
1275
1276 pub fn push_filter(&mut self, blend: RenderMixBlendMode, filter: &RenderFilter, render: impl FnOnce(&mut Self)) {
1284 expect_inner!(self.push_filter);
1285
1286 if self.visible {
1287 self.display_list.push_stacking_context(blend, self.transform_style, filter);
1288
1289 render(self);
1290
1291 self.display_list.pop_stacking_context();
1292 } else {
1293 render(self);
1294 }
1295 }
1296
1297 pub fn push_opacity(&mut self, bind: FrameValue<f32>, render: impl FnOnce(&mut Self)) {
1304 expect_inner!(self.push_opacity);
1305
1306 if self.visible {
1307 self.display_list
1308 .push_stacking_context(RenderMixBlendMode::Normal, self.transform_style, &[FilterOp::Opacity(bind)]);
1309
1310 render(self);
1311
1312 self.display_list.pop_stacking_context();
1313 } else {
1314 render(self);
1315 }
1316 }
1317
1318 pub fn push_backdrop_filter(&mut self, clip_rect: PxRect, filter: &RenderFilter) {
1326 expect_inner!(self.push_backdrop_filter);
1327 warn_empty!(self.push_backdrop_filter(clip_rect));
1328
1329 if self.visible {
1330 self.display_list.push_backdrop_filter(clip_rect, filter);
1331 }
1332
1333 if self.auto_hit_test {
1334 self.hit_test().push_rect(clip_rect);
1335 }
1336 }
1337
1338 pub fn push_border(&mut self, bounds: PxRect, widths: PxSideOffsets, sides: BorderSides, radius: PxCornerRadius) {
1340 expect_inner!(self.push_border);
1341 warn_empty!(self.push_border(bounds));
1342
1343 if self.visible {
1344 self.display_list.push_border(
1345 bounds,
1346 widths,
1347 sides.top.into(),
1348 sides.right.into(),
1349 sides.bottom.into(),
1350 sides.left.into(),
1351 radius,
1352 );
1353 }
1354
1355 if self.auto_hit_test {
1356 self.hit_test().push_border(bounds, widths, radius);
1357 }
1358 }
1359
1360 #[expect(clippy::too_many_arguments)]
1362 pub fn push_border_image(
1363 &mut self,
1364 bounds: PxRect,
1365 widths: PxSideOffsets,
1366 slice: PxSideOffsets,
1367 fill: bool,
1368 repeat_horizontal: RepeatMode,
1369 repeat_vertical: RepeatMode,
1370 image: &impl Img,
1371 rendering: ImageRendering,
1372 ) {
1373 expect_inner!(self.push_border_image);
1374 warn_empty!(self.push_border_image(bounds));
1375
1376 if let (true, Some(r)) = (self.visible, &self.renderer) {
1377 let image_id = image.renderer_id(r);
1378 self.display_list.push_nine_patch_border(
1379 bounds,
1380 NinePatchSource::Image { image_id, rendering },
1381 widths,
1382 image.size(),
1383 slice,
1384 fill,
1385 repeat_horizontal,
1386 repeat_vertical,
1387 )
1388 }
1389 }
1390
1391 #[expect(clippy::too_many_arguments)]
1393 pub fn push_border_linear_gradient(
1394 &mut self,
1395 bounds: PxRect,
1396 widths: PxSideOffsets,
1397 slice: PxSideOffsets,
1398 fill: bool,
1399 repeat_horizontal: RepeatMode,
1400 repeat_vertical: RepeatMode,
1401 line: PxLine,
1402 stops: &[RenderGradientStop],
1403 extend_mode: RenderExtendMode,
1404 ) {
1405 debug_assert!(stops.len() >= 2);
1406 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1407 debug_assert!(
1408 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1409 "last color stop must be at offset 1.0"
1410 );
1411 expect_inner!(self.push_border_linear_gradient);
1412 warn_empty!(self.push_border_linear_gradient(bounds));
1413
1414 if self.visible && !stops.is_empty() {
1415 self.display_list.push_nine_patch_border(
1416 bounds,
1417 NinePatchSource::LinearGradient {
1418 start_point: line.start.cast(),
1419 end_point: line.end.cast(),
1420 extend_mode,
1421 stops: stops.to_vec().into_boxed_slice(),
1422 },
1423 widths,
1424 bounds.size,
1425 slice,
1426 fill,
1427 repeat_horizontal,
1428 repeat_vertical,
1429 );
1430 }
1431 }
1432
1433 #[expect(clippy::too_many_arguments)]
1435 pub fn push_border_radial_gradient(
1436 &mut self,
1437 bounds: PxRect,
1438 widths: PxSideOffsets,
1439 slice: PxSideOffsets,
1440 fill: bool,
1441 repeat_horizontal: RepeatMode,
1442 repeat_vertical: RepeatMode,
1443 center: PxPoint,
1444 radius: PxSize,
1445 stops: &[RenderGradientStop],
1446 extend_mode: RenderExtendMode,
1447 ) {
1448 debug_assert!(stops.len() >= 2);
1449 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1450 debug_assert!(
1451 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1452 "last color stop must be at offset 1.0"
1453 );
1454
1455 expect_inner!(self.push_border_radial_gradient);
1456 warn_empty!(self.push_border_radial_gradient(bounds));
1457
1458 if self.visible && !stops.is_empty() {
1459 self.display_list.push_nine_patch_border(
1460 bounds,
1461 NinePatchSource::RadialGradient {
1462 center: center.cast(),
1463 radius: radius.cast(),
1464 start_offset: 0.0,
1465 end_offset: 1.0,
1466 extend_mode,
1467 stops: stops.to_vec().into_boxed_slice(),
1468 },
1469 widths,
1470 bounds.size,
1471 slice,
1472 fill,
1473 repeat_horizontal,
1474 repeat_vertical,
1475 );
1476 }
1477 }
1478
1479 #[expect(clippy::too_many_arguments)]
1481 pub fn push_border_conic_gradient(
1482 &mut self,
1483 bounds: PxRect,
1484 widths: PxSideOffsets,
1485 slice: PxSideOffsets,
1486 fill: bool,
1487 repeat_horizontal: RepeatMode,
1488 repeat_vertical: RepeatMode,
1489 center: PxPoint,
1490 angle: AngleRadian,
1491 stops: &[RenderGradientStop],
1492 extend_mode: RenderExtendMode,
1493 ) {
1494 debug_assert!(stops.len() >= 2);
1495 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1496 debug_assert!(
1497 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1498 "last color stop must be at offset 1.0"
1499 );
1500
1501 expect_inner!(self.push_border_conic_gradient);
1502 warn_empty!(self.push_border_conic_gradient(bounds));
1503
1504 if self.visible && !stops.is_empty() {
1505 self.display_list.push_nine_patch_border(
1506 bounds,
1507 NinePatchSource::ConicGradient {
1508 center: center.cast(),
1509 angle,
1510 start_offset: 0.0,
1511 end_offset: 1.0,
1512 extend_mode,
1513 stops: stops.to_vec().into_boxed_slice(),
1514 },
1515 widths,
1516 bounds.size,
1517 slice,
1518 fill,
1519 repeat_horizontal,
1520 repeat_vertical,
1521 );
1522 }
1523 }
1524
1525 pub fn push_text(
1527 &mut self,
1528 clip_rect: PxRect,
1529 glyphs: &[GlyphInstance],
1530 font: &impl Font,
1531 color: FrameValue<Rgba>,
1532 synthesis: FontSynthesis,
1533 aa: FontAntiAliasing,
1534 ) {
1535 expect_inner!(self.push_text);
1536 warn_empty!(self.push_text(clip_rect));
1537
1538 if let Some(r) = &self.renderer {
1539 if !glyphs.is_empty() && self.visible && !font.is_empty_fallback() {
1540 let font_id = font.renderer_id(r, synthesis);
1541
1542 let opts = GlyphOptions {
1543 aa: match aa {
1544 FontAntiAliasing::Default => self.default_font_aa,
1545 aa => aa,
1546 },
1547 synthetic_bold: synthesis.contains(FontSynthesis::BOLD),
1548 synthetic_oblique: synthesis.contains(FontSynthesis::OBLIQUE),
1549 };
1550 self.display_list.push_text(clip_rect, font_id, glyphs, color, opts);
1551 }
1552 }
1553
1554 if self.auto_hit_test {
1555 self.hit_test().push_rect(clip_rect);
1556 }
1557 }
1558
1559 pub fn push_image(
1561 &mut self,
1562 clip_rect: PxRect,
1563 img_size: PxSize,
1564 tile_size: PxSize,
1565 tile_spacing: PxSize,
1566 image: &impl Img,
1567 rendering: ImageRendering,
1568 ) {
1569 expect_inner!(self.push_image);
1570 warn_empty!(self.push_image(clip_rect));
1571
1572 if let Some(r) = &self.renderer {
1573 if self.visible {
1574 let image_key = image.renderer_id(r);
1575 self.display_list.push_image(
1576 clip_rect,
1577 image_key,
1578 img_size,
1579 tile_size,
1580 tile_spacing,
1581 rendering,
1582 image.alpha_type(),
1583 );
1584 }
1585 }
1586
1587 if self.auto_hit_test {
1588 self.hit_test().push_rect(clip_rect);
1589 }
1590 }
1591
1592 pub fn push_color(&mut self, clip_rect: PxRect, color: FrameValue<Rgba>) {
1601 expect_inner!(self.push_color);
1602 warn_empty!(self.push_color(clip_rect));
1603
1604 if self.visible {
1605 self.display_list.push_color(clip_rect, color);
1606 }
1607
1608 if self.auto_hit_test {
1609 self.hit_test().push_rect(clip_rect);
1610 }
1611 }
1612
1613 #[expect(clippy::too_many_arguments)]
1621 pub fn push_linear_gradient(
1622 &mut self,
1623 clip_rect: PxRect,
1624 line: PxLine,
1625 stops: &[RenderGradientStop],
1626 extend_mode: RenderExtendMode,
1627 tile_origin: PxPoint,
1628 tile_size: PxSize,
1629 tile_spacing: PxSize,
1630 ) {
1631 debug_assert!(stops.len() >= 2);
1632 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1633 debug_assert!(
1634 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1635 "last color stop must be at offset 1.0"
1636 );
1637
1638 expect_inner!(self.push_linear_gradient);
1639 warn_empty!(self.push_linear_gradient(clip_rect));
1640
1641 if !stops.is_empty() && self.visible {
1642 self.display_list.push_linear_gradient(
1643 clip_rect,
1644 line.start.cast(),
1645 line.end.cast(),
1646 extend_mode,
1647 stops,
1648 tile_origin,
1649 tile_size,
1650 tile_spacing,
1651 );
1652 }
1653
1654 if self.auto_hit_test {
1655 self.hit_test().push_rect(clip_rect);
1656 }
1657 }
1658
1659 #[expect(clippy::too_many_arguments)]
1670 pub fn push_radial_gradient(
1671 &mut self,
1672 clip_rect: PxRect,
1673 center: PxPoint,
1674 radius: PxSize,
1675 stops: &[RenderGradientStop],
1676 extend_mode: RenderExtendMode,
1677 tile_origin: PxPoint,
1678 tile_size: PxSize,
1679 tile_spacing: PxSize,
1680 ) {
1681 debug_assert!(stops.len() >= 2);
1682 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1683 debug_assert!(
1684 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1685 "last color stop must be at offset 1.0"
1686 );
1687
1688 expect_inner!(self.push_radial_gradient);
1689 warn_empty!(self.push_radial_gradient(clip_rect));
1690
1691 if !stops.is_empty() && self.visible {
1692 self.display_list.push_radial_gradient(
1693 clip_rect,
1694 center.cast(),
1695 radius.cast(),
1696 0.0,
1697 1.0,
1698 extend_mode,
1699 stops,
1700 tile_origin,
1701 tile_size,
1702 tile_spacing,
1703 );
1704 }
1705
1706 if self.auto_hit_test {
1707 self.hit_test().push_rect(clip_rect);
1708 }
1709 }
1710
1711 #[expect(clippy::too_many_arguments)]
1719 pub fn push_conic_gradient(
1720 &mut self,
1721 clip_rect: PxRect,
1722 center: PxPoint,
1723 angle: AngleRadian,
1724 stops: &[RenderGradientStop],
1725 extend_mode: RenderExtendMode,
1726 tile_origin: PxPoint,
1727 tile_size: PxSize,
1728 tile_spacing: PxSize,
1729 ) {
1730 debug_assert!(stops.len() >= 2);
1731 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1732 debug_assert!(
1733 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1734 "last color stop must be at offset 1.0"
1735 );
1736
1737 expect_inner!(self.push_conic_gradient);
1738 warn_empty!(self.push_conic_gradient(clip_rect));
1739
1740 if !stops.is_empty() && self.visible {
1741 self.display_list.push_conic_gradient(
1742 clip_rect,
1743 center.cast(),
1744 angle,
1745 0.0,
1746 1.0,
1747 extend_mode,
1748 stops,
1749 tile_origin,
1750 tile_size,
1751 tile_spacing,
1752 );
1753 }
1754
1755 if self.auto_hit_test {
1756 self.hit_test().push_rect(clip_rect);
1757 }
1758 }
1759
1760 pub fn push_line(&mut self, clip_rect: PxRect, orientation: border::LineOrientation, color: Rgba, style: border::LineStyle) {
1762 expect_inner!(self.push_line);
1763 warn_empty!(self.push_line(clip_rect));
1764
1765 if self.visible {
1766 match style.render_command() {
1767 RenderLineCommand::Line(style) => {
1768 self.display_list.push_line(clip_rect, color, style, orientation);
1769 }
1770 RenderLineCommand::Border(style) => {
1771 use border::LineOrientation as LO;
1772 let widths = match orientation {
1773 LO::Vertical => PxSideOffsets::new(Px(0), Px(0), Px(0), clip_rect.width()),
1774 LO::Horizontal => PxSideOffsets::new(clip_rect.height(), Px(0), Px(0), Px(0)),
1775 };
1776 self.display_list.push_border(
1777 clip_rect,
1778 widths,
1779 zng_view_api::BorderSide { color, style },
1780 zng_view_api::BorderSide {
1781 color: colors::BLACK.transparent(),
1782 style: zng_view_api::BorderStyle::Hidden,
1783 },
1784 zng_view_api::BorderSide {
1785 color: colors::BLACK.transparent(),
1786 style: zng_view_api::BorderStyle::Hidden,
1787 },
1788 zng_view_api::BorderSide { color, style },
1789 PxCornerRadius::zero(),
1790 );
1791 }
1792 }
1793 }
1794
1795 if self.auto_hit_test {
1796 self.hit_test().push_rect(clip_rect);
1797 }
1798 }
1799
1800 pub fn push_debug_dot_overlay(&mut self, offset: PxPoint, color: impl Into<Rgba>) {
1804 if let Some(offset) = self.transform.transform_point(offset) {
1805 self.debug_dot_overlays.push((offset, color.into()));
1806 }
1807 }
1808
1809 pub fn push_debug_dot(&mut self, offset: PxPoint, color: impl Into<Rgba>) {
1813 if !self.visible {
1814 return;
1815 }
1816 let scale = self.scale_factor();
1817
1818 let radius = PxSize::splat(Px(6)) * scale;
1819 let color = color.into();
1820
1821 let center = radius.to_vector().to_point();
1822 let bounds = radius * 2.0.fct();
1823
1824 let offset = offset - radius.to_vector();
1825
1826 self.display_list.push_radial_gradient(
1827 PxRect::new(offset, bounds),
1828 center.cast(),
1829 radius.cast(),
1830 0.0,
1831 1.0,
1832 RenderExtendMode::Clamp,
1833 &[
1834 RenderGradientStop { offset: 0.0, color },
1835 RenderGradientStop { offset: 0.5, color },
1836 RenderGradientStop {
1837 offset: 0.6,
1838 color: colors::WHITE,
1839 },
1840 RenderGradientStop {
1841 offset: 0.7,
1842 color: colors::WHITE,
1843 },
1844 RenderGradientStop {
1845 offset: 0.8,
1846 color: colors::BLACK,
1847 },
1848 RenderGradientStop {
1849 offset: 1.0,
1850 color: colors::BLACK.transparent(),
1851 },
1852 ],
1853 PxPoint::zero(),
1854 bounds,
1855 PxSize::zero(),
1856 );
1857 }
1858
1859 pub fn push_extension_context_raw(
1861 &mut self,
1862 extension_id: ApiExtensionId,
1863 payload: ApiExtensionPayload,
1864 render: impl FnOnce(&mut Self),
1865 ) {
1866 self.display_list.push_extension(extension_id, payload);
1867 render(self);
1868 self.display_list.pop_extension(extension_id);
1869 }
1870
1871 pub fn push_extension_context<T: serde::Serialize>(
1873 &mut self,
1874 extension_id: ApiExtensionId,
1875 payload: &T,
1876 render: impl FnOnce(&mut Self),
1877 ) {
1878 self.push_extension_context_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap(), render)
1879 }
1880
1881 pub fn push_extension_item_raw(&mut self, extension_id: ApiExtensionId, payload: ApiExtensionPayload) {
1883 self.display_list.push_extension(extension_id, payload);
1884 }
1885
1886 pub fn push_extension_item<T: serde::Serialize>(&mut self, extension_id: ApiExtensionId, payload: &T) {
1888 self.push_extension_item_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap())
1889 }
1890
1891 pub fn parallel_split(&self) -> ParallelBuilder<Self> {
1897 if self.widget_data.is_some() && WIDGET.parent_id().is_some() {
1898 tracing::error!(
1899 "called `parallel_split` inside `{}` and before calling `push_inner`",
1900 self.widget_id
1901 );
1902 }
1903
1904 ParallelBuilder(Some(Self {
1905 render_widgets: self.render_widgets.clone(),
1906 render_update_widgets: self.render_update_widgets.clone(),
1907 frame_id: self.frame_id,
1908 widget_id: self.widget_id,
1909 transform: self.transform,
1910 transform_style: self.transform_style,
1911 default_font_aa: self.default_font_aa,
1912 renderer: self.renderer.clone(),
1913 scale_factor: self.scale_factor,
1914 display_list: self.display_list.parallel_split(),
1915 hit_testable: self.hit_testable,
1916 visible: self.visible,
1917 backface_visible: self.backface_visible,
1918 auto_hit_test: self.auto_hit_test,
1919 hit_clips: self.hit_clips.parallel_split(),
1920 auto_hide_rect: self.auto_hide_rect,
1921 widget_data: None,
1922 child_offset: self.child_offset,
1923 parent_inner_bounds: self.parent_inner_bounds,
1924 perspective: self.perspective,
1925 view_process_has_frame: self.view_process_has_frame,
1926 can_reuse: self.can_reuse,
1927 open_reuse: None,
1928 clear_color: None,
1929 widget_count: 0,
1930 widget_count_offsets: self.widget_count_offsets.parallel_split(),
1931 debug_dot_overlays: vec![],
1932 }))
1933 }
1934
1935 pub fn parallel_fold(&mut self, mut split: ParallelBuilder<Self>) {
1937 let split = split.take();
1938 if split.clear_color.is_some() {
1939 self.clear_color = split.clear_color;
1940 }
1941 self.hit_clips.parallel_fold(split.hit_clips);
1942 self.display_list.parallel_fold(split.display_list);
1943 self.widget_count_offsets
1944 .parallel_fold(split.widget_count_offsets, self.widget_count);
1945
1946 self.widget_count += split.widget_count;
1947 self.debug_dot_overlays.extend(split.debug_dot_overlays);
1948 }
1949
1950 #[expect(clippy::too_many_arguments)]
1952 pub fn with_nested_window(
1953 &mut self,
1954 render_widgets: Arc<RenderUpdates>,
1955 render_update_widgets: Arc<RenderUpdates>,
1956
1957 root_id: WidgetId,
1958 root_bounds: &WidgetBoundsInfo,
1959 info_tree: &WidgetInfoTree,
1960 default_font_aa: FontAntiAliasing,
1961
1962 render: impl FnOnce(&mut Self),
1963 ) {
1964 let mut nested = Self::new(
1966 render_widgets,
1967 render_update_widgets,
1968 self.frame_id,
1969 root_id,
1970 root_bounds,
1971 info_tree,
1972 self.renderer.clone(),
1973 self.scale_factor,
1974 default_font_aa,
1975 );
1976 nested.display_list = self.display_list.parallel_split();
1977 nested.hit_clips = self.hit_clips.parallel_split();
1978 render(&mut nested);
1982
1983 info_tree.root().bounds_info().set_rendered(
1985 Some(WidgetRenderInfo {
1986 visible: nested.visible,
1987 parent_perspective: nested.perspective,
1988 seg_id: 0,
1989 back: 0,
1990 front: nested.widget_count,
1991 }),
1992 info_tree,
1993 );
1994 info_tree.after_render(
1995 nested.frame_id,
1996 nested.scale_factor,
1997 Some(
1998 nested
1999 .renderer
2000 .as_ref()
2001 .and_then(|r| r.generation().ok())
2002 .unwrap_or(ViewProcessGen::INVALID),
2003 ),
2004 Some(nested.widget_count_offsets.clone()),
2005 );
2006
2007 self.hit_clips.parallel_fold(nested.hit_clips);
2009 self.display_list.parallel_fold(nested.display_list);
2010
2011 self.widget_count += nested.widget_count;
2015 self.debug_dot_overlays.extend(nested.debug_dot_overlays);
2016 }
2017
2018 pub fn render_widgets(&self) -> &Arc<RenderUpdates> {
2020 &self.render_widgets
2021 }
2022
2023 pub fn render_update_widgets(&self) -> &Arc<RenderUpdates> {
2025 &self.render_update_widgets
2026 }
2027
2028 pub fn finalize(self, info_tree: &WidgetInfoTree) -> BuiltFrame {
2030 info_tree.root().bounds_info().set_rendered(
2031 Some(WidgetRenderInfo {
2032 visible: self.visible,
2033 parent_perspective: self.perspective,
2034 seg_id: 0,
2035 back: 0,
2036 front: self.widget_count,
2037 }),
2038 info_tree,
2039 );
2040
2041 info_tree.after_render(
2042 self.frame_id,
2043 self.scale_factor,
2044 Some(
2045 self.renderer
2046 .as_ref()
2047 .and_then(|r| r.generation().ok())
2048 .unwrap_or(ViewProcessGen::INVALID),
2049 ),
2050 Some(self.widget_count_offsets),
2051 );
2052
2053 let display_list = self.display_list.finalize();
2054
2055 let clear_color = self.clear_color.unwrap_or_default();
2056
2057 BuiltFrame { display_list, clear_color }
2058 }
2059}
2060
2061pub struct ClipBuilder<'a> {
2065 builder: &'a mut FrameBuilder,
2066 render_count: usize,
2067 hit_test_count: usize,
2068}
2069impl ClipBuilder<'_> {
2070 pub fn push_clip_rect(&mut self, clip_rect: PxRect, clip_out: bool, hit_test: bool) {
2079 if self.builder.visible {
2080 self.builder.display_list.push_clip_rect(clip_rect, clip_out);
2081 self.render_count += 1;
2082 }
2083
2084 if hit_test || self.builder.auto_hit_test {
2085 self.builder.hit_clips.push_clip_rect(clip_rect.to_box2d(), clip_out);
2086 self.hit_test_count += 1;
2087 }
2088 }
2089
2090 pub fn push_clip_rounded_rect(&mut self, clip_rect: PxRect, corners: PxCornerRadius, clip_out: bool, hit_test: bool) {
2099 if self.builder.visible {
2100 self.builder.display_list.push_clip_rounded_rect(clip_rect, corners, clip_out);
2101 self.render_count += 1;
2102 }
2103
2104 if hit_test || self.builder.auto_hit_test {
2105 self.builder
2106 .hit_clips
2107 .push_clip_rounded_rect(clip_rect.to_box2d(), corners, clip_out);
2108 self.hit_test_count += 1;
2109 }
2110 }
2111}
2112
2113pub struct HitTestClipBuilder<'a> {
2117 hit_clips: &'a mut HitTestClips,
2118 count: usize,
2119}
2120impl HitTestClipBuilder<'_> {
2121 pub fn push_clip_rect(&mut self, rect: PxRect, clip_out: bool) {
2125 self.hit_clips.push_clip_rect(rect.to_box2d(), clip_out);
2126 self.count += 1;
2127 }
2128
2129 pub fn push_clip_rounded_rect(&mut self, rect: PxRect, corners: PxCornerRadius, clip_out: bool) {
2133 self.hit_clips.push_clip_rounded_rect(rect.to_box2d(), corners, clip_out);
2134 self.count += 1;
2135 }
2136
2137 pub fn push_clip_ellipse(&mut self, center: PxPoint, radii: PxSize, clip_out: bool) {
2141 self.hit_clips.push_clip_ellipse(center, radii, clip_out);
2142 self.count += 1;
2143 }
2144}
2145
2146pub struct HitTestBuilder<'a> {
2150 hit_clips: &'a mut HitTestClips,
2151 is_hit_testable: bool,
2152}
2153impl HitTestBuilder<'_> {
2154 pub fn is_hit_testable(&self) -> bool {
2156 self.is_hit_testable
2157 }
2158
2159 pub fn push_rect(&mut self, rect: PxRect) {
2161 if self.is_hit_testable && rect.size != PxSize::zero() {
2162 self.hit_clips.push_rect(rect.to_box2d());
2163 }
2164 }
2165
2166 pub fn push_rounded_rect(&mut self, rect: PxRect, corners: PxCornerRadius) {
2168 if self.is_hit_testable && rect.size != PxSize::zero() {
2169 self.hit_clips.push_rounded_rect(rect.to_box2d(), corners);
2170 }
2171 }
2172
2173 pub fn push_ellipse(&mut self, center: PxPoint, radii: PxSize) {
2175 if self.is_hit_testable && radii != PxSize::zero() {
2176 self.hit_clips.push_ellipse(center, radii);
2177 }
2178 }
2179
2180 pub fn push_clip_rect(&mut self, rect: PxRect, clip_out: bool, inner_hit_test: impl FnOnce(&mut Self)) {
2182 if !self.is_hit_testable {
2183 return;
2184 }
2185
2186 self.hit_clips.push_clip_rect(rect.to_box2d(), clip_out);
2187
2188 inner_hit_test(self);
2189
2190 self.hit_clips.pop_clip();
2191 }
2192
2193 pub fn push_clip_rounded_rect(
2195 &mut self,
2196 rect: PxRect,
2197 corners: PxCornerRadius,
2198 clip_out: bool,
2199 inner_hit_test: impl FnOnce(&mut Self),
2200 ) {
2201 self.push_clips(move |c| c.push_clip_rounded_rect(rect, corners, clip_out), inner_hit_test);
2202 }
2203
2204 pub fn push_clip_ellipse(&mut self, center: PxPoint, radii: PxSize, clip_out: bool, inner_hit_test: impl FnOnce(&mut Self)) {
2206 self.push_clips(move |c| c.push_clip_ellipse(center, radii, clip_out), inner_hit_test);
2207 }
2208
2209 pub fn push_clips(&mut self, clips: impl FnOnce(&mut HitTestClipBuilder), inner_hit_test: impl FnOnce(&mut Self)) {
2211 if !self.is_hit_testable {
2212 return;
2213 }
2214
2215 let mut count = {
2216 let mut builder = HitTestClipBuilder {
2217 hit_clips: &mut *self.hit_clips,
2218 count: 0,
2219 };
2220 clips(&mut builder);
2221 builder.count
2222 };
2223
2224 inner_hit_test(self);
2225
2226 while count > 0 {
2227 count -= 1;
2228 self.hit_clips.pop_clip();
2229 }
2230 }
2231
2232 pub fn push_transform(&mut self, transform: PxTransform, inner_hit_test: impl FnOnce(&mut Self)) {
2234 if !self.is_hit_testable {
2235 return;
2236 }
2237
2238 self.hit_clips.push_transform(FrameValue::Value(transform));
2239
2240 inner_hit_test(self);
2241
2242 self.hit_clips.pop_transform();
2243 }
2244
2245 pub fn push_border(&mut self, bounds: PxRect, widths: PxSideOffsets, corners: PxCornerRadius) {
2247 if !self.is_hit_testable {
2248 return;
2249 }
2250
2251 let bounds = bounds.to_box2d();
2252 let mut inner_bounds = bounds;
2253 inner_bounds.min.x += widths.left;
2254 inner_bounds.min.y += widths.top;
2255 inner_bounds.max.x -= widths.right;
2256 inner_bounds.max.y -= widths.bottom;
2257
2258 if inner_bounds.is_negative() {
2259 self.hit_clips.push_rounded_rect(bounds, corners);
2260 } else if corners == PxCornerRadius::zero() {
2261 self.hit_clips.push_clip_rect(inner_bounds, true);
2262 self.hit_clips.push_rect(bounds);
2263 self.hit_clips.pop_clip();
2264 } else {
2265 let inner_radii = corners.deflate(widths);
2266
2267 self.hit_clips.push_clip_rounded_rect(inner_bounds, inner_radii, true);
2268 self.hit_clips.push_rounded_rect(bounds, corners);
2269 self.hit_clips.pop_clip();
2270 }
2271 }
2272}
2273
2274pub struct BuiltFrame {
2276 pub display_list: DisplayList,
2278 pub clear_color: Rgba,
2280}
2281
2282enum RenderLineCommand {
2283 Line(zng_view_api::LineStyle),
2284 Border(zng_view_api::BorderStyle),
2285}
2286impl border::LineStyle {
2287 fn render_command(self) -> RenderLineCommand {
2288 use RenderLineCommand::*;
2289 use border::LineStyle as LS;
2290 match self {
2291 LS::Solid => Line(zng_view_api::LineStyle::Solid),
2292 LS::Double => Border(zng_view_api::BorderStyle::Double),
2293 LS::Dotted => Line(zng_view_api::LineStyle::Dotted),
2294 LS::Dashed => Line(zng_view_api::LineStyle::Dashed),
2295 LS::Groove => Border(zng_view_api::BorderStyle::Groove),
2296 LS::Ridge => Border(zng_view_api::BorderStyle::Ridge),
2297 LS::Wavy(thickness) => Line(zng_view_api::LineStyle::Wavy(thickness)),
2298 LS::Hidden => Border(zng_view_api::BorderStyle::Hidden),
2299 }
2300 }
2301}
2302
2303pub struct FrameUpdate {
2310 render_update_widgets: Arc<RenderUpdates>,
2311
2312 transforms: Vec<FrameValueUpdate<PxTransform>>,
2313 floats: Vec<FrameValueUpdate<f32>>,
2314 colors: Vec<FrameValueUpdate<Rgba>>,
2315
2316 extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
2317
2318 current_clear_color: Rgba,
2319 clear_color: Option<Rgba>,
2320 frame_id: FrameId,
2321
2322 widget_id: WidgetId,
2323 transform: PxTransform,
2324 parent_child_offset: PxVector,
2325 perspective: Option<(f32, PxPoint)>,
2326 inner_transform: Option<PxTransform>,
2327 child_offset: PxVector,
2328 can_reuse_widget: bool,
2329 widget_bounds: WidgetBoundsInfo,
2330 parent_inner_bounds: Option<PxRect>,
2331
2332 auto_hit_test: bool,
2333 visible: bool,
2334}
2335impl FrameUpdate {
2336 pub fn new(
2344 render_update_widgets: Arc<RenderUpdates>,
2345 frame_id: FrameId,
2346 root_id: WidgetId,
2347 root_bounds: WidgetBoundsInfo,
2348 clear_color: Rgba,
2349 ) -> Self {
2350 FrameUpdate {
2351 render_update_widgets,
2352 widget_id: root_id,
2353 widget_bounds: root_bounds,
2354 transforms: vec![],
2355 floats: vec![],
2356 colors: vec![],
2357 extensions: vec![],
2358 clear_color: None,
2359 frame_id,
2360 current_clear_color: clear_color,
2361
2362 transform: PxTransform::identity(),
2363 perspective: None,
2364 parent_child_offset: PxVector::zero(),
2365 inner_transform: Some(PxTransform::identity()),
2366 child_offset: PxVector::zero(),
2367 can_reuse_widget: true,
2368
2369 auto_hit_test: false,
2370 parent_inner_bounds: None,
2371 visible: true,
2372 }
2373 }
2374
2375 pub fn frame_id(&self) -> FrameId {
2377 self.frame_id
2378 }
2379
2380 pub fn is_outer(&self) -> bool {
2387 self.inner_transform.is_some()
2388 }
2389
2390 pub fn transform(&self) -> &PxTransform {
2392 &self.transform
2393 }
2394
2395 pub fn set_clear_color(&mut self, color: Rgba) {
2397 if self.visible {
2398 self.clear_color = Some(color);
2399 }
2400 }
2401
2402 pub fn auto_hit_test(&self) -> bool {
2404 self.auto_hit_test
2405 }
2406 pub fn with_auto_hit_test(&mut self, auto_hit_test: bool, render_update: impl FnOnce(&mut Self)) {
2410 let prev = mem::replace(&mut self.auto_hit_test, auto_hit_test);
2411 render_update(self);
2412 self.auto_hit_test = prev;
2413 }
2414
2415 pub fn is_visible(&self) -> bool {
2417 self.visible
2418 }
2419
2420 pub fn hidden(&mut self, update: impl FnOnce(&mut Self)) {
2427 let parent_visible = mem::replace(&mut self.visible, false);
2428 update(self);
2429 self.visible = parent_visible;
2430 }
2431
2432 pub fn update_transform(&mut self, new_value: FrameValueUpdate<PxTransform>, hit_test: bool) {
2440 if self.visible {
2441 self.transforms.push(new_value);
2442 }
2443
2444 if hit_test || self.auto_hit_test {
2445 self.widget_bounds.update_hit_test_transform(new_value);
2446 }
2447 }
2448
2449 pub fn update_transform_opt(&mut self, new_value: Option<FrameValueUpdate<PxTransform>>, hit_test: bool) {
2451 if let Some(value) = new_value {
2452 self.update_transform(value, hit_test)
2453 }
2454 }
2455
2456 pub fn with_transform(&mut self, new_value: FrameValueUpdate<PxTransform>, hit_test: bool, render_update: impl FnOnce(&mut Self)) {
2465 self.with_transform_value(&new_value.value, render_update);
2466 self.update_transform(new_value, hit_test);
2467 }
2468
2469 pub fn with_transform_opt(
2473 &mut self,
2474 new_value: Option<FrameValueUpdate<PxTransform>>,
2475 hit_test: bool,
2476 render_update: impl FnOnce(&mut Self),
2477 ) {
2478 match new_value {
2479 Some(value) => self.with_transform(value, hit_test, render_update),
2480 None => render_update(self),
2481 }
2482 }
2483
2484 pub fn with_child(&mut self, offset: PxVector, render_update: impl FnOnce(&mut Self)) {
2488 self.child_offset = offset;
2489 render_update(self);
2490 self.child_offset = PxVector::zero();
2491 }
2492
2493 pub fn with_transform_value(&mut self, value: &PxTransform, render_update: impl FnOnce(&mut Self)) {
2499 let parent_transform = self.transform;
2500 self.transform = value.then(&parent_transform);
2501
2502 render_update(self);
2503 self.transform = parent_transform;
2504 }
2505
2506 pub fn with_inner_transform(&mut self, transform: &PxTransform, render_update: impl FnOnce(&mut Self)) {
2512 if let Some(inner_transform) = &mut self.inner_transform {
2513 let parent = *inner_transform;
2514 *inner_transform = inner_transform.then(transform);
2515
2516 render_update(self);
2517
2518 if let Some(inner_transform) = &mut self.inner_transform {
2519 *inner_transform = parent;
2520 }
2521 } else {
2522 tracing::error!("called `with_inner_transform` inside inner context of `{}`", self.widget_id);
2523 render_update(self);
2524 }
2525 }
2526
2527 pub fn can_reuse_widget(&self) -> bool {
2531 self.can_reuse_widget
2532 }
2533
2534 pub fn with_no_reuse(&mut self, render_update: impl FnOnce(&mut Self)) {
2538 let prev_can_reuse = self.can_reuse_widget;
2539 self.can_reuse_widget = false;
2540 render_update(self);
2541 self.can_reuse_widget = prev_can_reuse;
2542 }
2543
2544 pub fn update_widget(&mut self, render_update: impl FnOnce(&mut Self)) {
2551 let wgt_info = WIDGET.info();
2552 let id = wgt_info.id();
2553
2554 #[cfg(debug_assertions)]
2555 if self.inner_transform.is_some() && wgt_info.parent().is_some() {
2556 tracing::error!(
2557 "called `update_widget` for `{}` without calling `update_inner` for the parent `{}`",
2558 WIDGET.trace_id(),
2559 self.widget_id
2560 );
2561 }
2562
2563 let bounds = wgt_info.bounds_info();
2564 if bounds.is_collapsed() {
2565 let _ = WIDGET.take_update(UpdateFlags::LAYOUT | UpdateFlags::RENDER | UpdateFlags::RENDER_UPDATE);
2566 return;
2567 } else {
2568 #[cfg(debug_assertions)]
2569 if WIDGET.pending_update().contains(UpdateFlags::LAYOUT) {
2570 tracing::error!("called `update_widget` for `{}` with pending layout", WIDGET.trace_id());
2571 }
2572 }
2573
2574 let tree = wgt_info.tree();
2575
2576 let parent_can_reuse = self.can_reuse_widget;
2577 let parent_perspective = mem::replace(&mut self.perspective, wgt_info.perspective());
2578 let parent_bounds = mem::replace(&mut self.widget_bounds, bounds.clone());
2579
2580 if let Some((_, o)) = &mut self.perspective {
2581 *o -= self.child_offset;
2582 }
2583
2584 let render_info = bounds.render_info();
2585 if let Some(i) = &render_info {
2586 if i.parent_perspective != self.perspective {
2587 self.can_reuse_widget = false;
2588 }
2589 }
2590
2591 let outer_transform = PxTransform::from(self.child_offset).then(&self.transform);
2592
2593 if !WIDGET.take_update(UpdateFlags::RENDER_UPDATE)
2594 && self.can_reuse_widget
2595 && !self.render_update_widgets.delivery_list().enter_widget(id)
2596 && bounds.parent_child_offset() == self.child_offset
2597 {
2598 let _span = tracing::trace_span!("reuse-descendants", id=?self.widget_id).entered();
2599
2600 let prev_outer = bounds.outer_transform();
2601 if prev_outer != outer_transform {
2602 if let Some(undo_prev) = prev_outer.inverse() {
2603 let patch = undo_prev.then(&outer_transform);
2604
2605 let update = |info: WidgetInfo| {
2606 let bounds = info.bounds_info();
2607 bounds.set_outer_transform(bounds.outer_transform().then(&patch), tree);
2608 bounds.set_inner_transform(
2609 bounds.inner_transform().then(&patch),
2610 tree,
2611 info.id(),
2612 info.parent().map(|p| p.inner_bounds()),
2613 );
2614 };
2615 let targets = tree.get(id).unwrap().self_and_descendants();
2616 if PARALLEL_VAR.get().contains(Parallel::RENDER) {
2617 targets.par_bridge().for_each(update);
2618 } else {
2619 targets.for_each(update);
2620 }
2621
2622 return; }
2624 } else {
2625 return; }
2627
2628 self.can_reuse_widget = false;
2630 }
2631
2632 bounds.set_parent_child_offset(self.child_offset);
2633 bounds.set_outer_transform(outer_transform, tree);
2634 self.parent_child_offset = mem::take(&mut self.child_offset);
2635 self.inner_transform = Some(PxTransform::identity());
2636 let parent_id = self.widget_id;
2637 self.widget_id = id;
2638
2639 render_update(self);
2640
2641 if let Some(mut i) = render_info {
2642 i.parent_perspective = self.perspective;
2643 bounds.set_rendered(Some(i), tree);
2644 }
2645 self.parent_child_offset = PxVector::zero();
2646 self.inner_transform = None;
2647 self.widget_id = parent_id;
2648 self.can_reuse_widget = parent_can_reuse;
2649 self.perspective = parent_perspective;
2650 self.widget_bounds = parent_bounds;
2651 }
2652
2653 pub fn reuse_widget(&mut self) {
2658 if self.inner_transform.is_some() {
2659 tracing::error!(
2660 "called `reuse_widget` for `{}` without calling `update_inner` for the parent `{}`",
2661 WIDGET.trace_id(),
2662 self.widget_id
2663 );
2664 }
2665 }
2666
2667 pub fn update_inner(
2671 &mut self,
2672 layout_translation_key: FrameValueKey<PxTransform>,
2673 layout_translation_animating: bool,
2674 render_update: impl FnOnce(&mut Self),
2675 ) {
2676 let id = WIDGET.id();
2677 if let Some(mut inner_transform) = self.inner_transform.take() {
2678 let bounds = WIDGET.bounds();
2679 let tree = WINDOW.info();
2680
2681 let inner_offset = bounds.inner_offset();
2682 if let Some((p, mut o)) = self.perspective {
2683 o -= inner_offset;
2684 let x = o.x.0 as f32;
2685 let y = o.y.0 as f32;
2686 let p = PxTransform::translation(-x, -y)
2687 .then(&PxTransform::perspective(p))
2688 .then_translate(euclid::vec2(x, y));
2689 inner_transform = inner_transform.then(&p);
2690 }
2691 let inner_transform = inner_transform.then_translate((self.parent_child_offset + inner_offset).cast());
2692 self.update_transform(layout_translation_key.update(inner_transform, layout_translation_animating), false);
2693 let parent_transform = self.transform;
2694
2695 self.transform = inner_transform.then(&parent_transform);
2696
2697 bounds.set_inner_transform(self.transform, &tree, id, self.parent_inner_bounds);
2698 let parent_inner_bounds = mem::replace(&mut self.parent_inner_bounds, Some(bounds.inner_bounds()));
2699
2700 render_update(self);
2701
2702 self.transform = parent_transform;
2703 self.parent_inner_bounds = parent_inner_bounds;
2704 } else {
2705 tracing::error!("called `update_inner` more then once for `{}`", id);
2706 render_update(self)
2707 }
2708 }
2709
2710 pub fn update_f32(&mut self, new_value: FrameValueUpdate<f32>) {
2712 if self.visible {
2713 self.floats.push(new_value);
2714 }
2715 }
2716
2717 pub fn update_f32_opt(&mut self, new_value: Option<FrameValueUpdate<f32>>) {
2719 if let Some(value) = new_value {
2720 self.update_f32(value)
2721 }
2722 }
2723
2724 pub fn update_color(&mut self, new_value: FrameValueUpdate<Rgba>) {
2728 if self.visible {
2729 self.colors.push(new_value)
2730 }
2731 }
2732
2733 pub fn update_color_opt(&mut self, new_value: Option<FrameValueUpdate<Rgba>>) {
2735 if let Some(value) = new_value {
2736 self.update_color(value)
2737 }
2738 }
2739
2740 pub fn update_extension_raw(&mut self, extension_id: ApiExtensionId, extension_payload: ApiExtensionPayload) {
2742 self.extensions.push((extension_id, extension_payload))
2743 }
2744
2745 pub fn update_extension<T: serde::Serialize>(&mut self, extension_id: ApiExtensionId, payload: &T) {
2747 self.update_extension_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap())
2748 }
2749
2750 pub fn parallel_split(&self) -> ParallelBuilder<Self> {
2756 if self.inner_transform.is_some() && WIDGET.parent_id().is_some() {
2757 tracing::error!(
2758 "called `parallel_split` inside `{}` and before calling `update_inner`",
2759 self.widget_id
2760 );
2761 }
2762
2763 ParallelBuilder(Some(Self {
2764 render_update_widgets: self.render_update_widgets.clone(),
2765 current_clear_color: self.current_clear_color,
2766 frame_id: self.frame_id,
2767
2768 transforms: vec![],
2769 floats: vec![],
2770 colors: vec![],
2771 extensions: vec![],
2772 clear_color: None,
2773
2774 widget_id: self.widget_id,
2775 transform: self.transform,
2776 perspective: self.perspective,
2777 parent_child_offset: self.parent_child_offset,
2778 inner_transform: self.inner_transform,
2779 child_offset: self.child_offset,
2780 can_reuse_widget: self.can_reuse_widget,
2781 widget_bounds: self.widget_bounds.clone(),
2782 parent_inner_bounds: self.parent_inner_bounds,
2783 auto_hit_test: self.auto_hit_test,
2784 visible: self.visible,
2785 }))
2786 }
2787
2788 pub fn parallel_fold(&mut self, mut split: ParallelBuilder<Self>) {
2790 let mut split = split.take();
2791
2792 debug_assert_eq!(self.frame_id, split.frame_id);
2793 debug_assert_eq!(self.widget_id, split.widget_id);
2794
2795 fn take_or_append<T>(t: &mut Vec<T>, s: &mut Vec<T>) {
2796 if t.is_empty() {
2797 *t = mem::take(s);
2798 } else {
2799 t.append(s)
2800 }
2801 }
2802
2803 take_or_append(&mut self.transforms, &mut split.transforms);
2804 take_or_append(&mut self.floats, &mut split.floats);
2805 take_or_append(&mut self.colors, &mut split.colors);
2806 take_or_append(&mut self.extensions, &mut split.extensions);
2807
2808 if let Some(c) = self.clear_color.take() {
2809 self.clear_color = Some(c);
2810 }
2811 }
2812
2813 pub fn with_nested_window(
2815 &mut self,
2816 render_update_widgets: Arc<RenderUpdates>,
2817 root_id: WidgetId,
2818 root_bounds: WidgetBoundsInfo,
2819 update: impl FnOnce(&mut Self),
2820 ) {
2821 let mut nested = Self::new(render_update_widgets, self.frame_id, root_id, root_bounds, self.current_clear_color);
2822
2823 update(&mut nested);
2824
2825 fn take_or_append<T>(t: &mut Vec<T>, s: &mut Vec<T>) {
2827 if t.is_empty() {
2828 *t = mem::take(s);
2829 } else {
2830 t.append(s)
2831 }
2832 }
2833 take_or_append(&mut self.transforms, &mut nested.transforms);
2834 take_or_append(&mut self.floats, &mut nested.floats);
2835 take_or_append(&mut self.colors, &mut nested.colors);
2836 take_or_append(&mut self.extensions, &mut nested.extensions);
2837 }
2838
2839 pub fn render_update_widgets(&self) -> &Arc<RenderUpdates> {
2841 &self.render_update_widgets
2842 }
2843
2844 pub fn finalize(mut self, info_tree: &WidgetInfoTree) -> BuiltFrameUpdate {
2848 info_tree.after_render_update(self.frame_id);
2849
2850 if self.clear_color == Some(self.current_clear_color) {
2851 self.clear_color = None;
2852 }
2853
2854 BuiltFrameUpdate {
2855 clear_color: self.clear_color,
2856 transforms: self.transforms,
2857 floats: self.floats,
2858 colors: self.colors,
2859 extensions: self.extensions,
2860 }
2861 }
2862}
2863
2864pub struct BuiltFrameUpdate {
2866 pub transforms: Vec<FrameValueUpdate<PxTransform>>,
2868 pub floats: Vec<FrameValueUpdate<f32>>,
2870 pub colors: Vec<FrameValueUpdate<Rgba>>,
2872 pub clear_color: Option<Rgba>,
2874 pub extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
2876}
2877
2878unique_id_32! {
2879 #[derive(Debug)]
2880 struct FrameBindingKeyId;
2881}
2882impl_unique_id_bytemuck!(FrameBindingKeyId);
2883
2884unique_id_32! {
2885 #[derive(Debug)]
2887 pub struct SpatialFrameId;
2888}
2889impl_unique_id_bytemuck!(SpatialFrameId);
2890
2891#[derive(Clone, Copy, PartialEq, Eq)]
2892enum ReferenceFrameIdInner {
2893 Unique(SpatialFrameId),
2894 UniqueIndex(SpatialFrameId, u32),
2895 Widget(WidgetId),
2896 WidgetIndex(WidgetId, u32),
2897 FrameValue(FrameValueKey<PxTransform>),
2898 FrameValueIndex(FrameValueKey<PxTransform>, u32),
2899}
2900impl ReferenceFrameIdInner {
2901 const _RESERVED: u64 = 1 << 63; const UNIQUE: u64 = 1 << 62;
2903 const WIDGET: u64 = 1 << 61;
2904 const FRAME_VALUE: u64 = 1 << 60;
2905}
2906impl From<ReferenceFrameIdInner> for RenderReferenceFrameId {
2907 fn from(value: ReferenceFrameIdInner) -> Self {
2908 match value {
2909 ReferenceFrameIdInner::UniqueIndex(id, index) => {
2910 RenderReferenceFrameId(id.get() as u64, index as u64 | ReferenceFrameIdInner::UNIQUE)
2911 }
2912 ReferenceFrameIdInner::WidgetIndex(id, index) => RenderReferenceFrameId(id.get(), index as u64 | ReferenceFrameIdInner::WIDGET),
2913 ReferenceFrameIdInner::FrameValue(key) => {
2914 RenderReferenceFrameId(((key.id.get() as u64) << 32) | u32::MAX as u64, ReferenceFrameIdInner::FRAME_VALUE)
2915 }
2916 ReferenceFrameIdInner::FrameValueIndex(key, index) => {
2917 RenderReferenceFrameId(((key.id.get() as u64) << 32) | index as u64, ReferenceFrameIdInner::FRAME_VALUE)
2918 }
2919 ReferenceFrameIdInner::Unique(id) => {
2920 RenderReferenceFrameId(id.get() as u64, (u32::MAX as u64 + 1) | ReferenceFrameIdInner::UNIQUE)
2921 }
2922 ReferenceFrameIdInner::Widget(id) => RenderReferenceFrameId(id.get(), (u32::MAX as u64 + 1) | ReferenceFrameIdInner::WIDGET),
2923 }
2924 }
2925}
2926
2927#[derive(Clone, Copy, PartialEq, Eq)]
2934pub struct ReferenceFrameId(ReferenceFrameIdInner);
2935impl ReferenceFrameId {
2936 fn from_widget(widget_id: WidgetId) -> Self {
2940 Self(ReferenceFrameIdInner::Widget(widget_id))
2941 }
2942
2943 pub fn from_widget_child(parent_id: WidgetId, child_index: u32) -> Self {
2947 Self(ReferenceFrameIdInner::WidgetIndex(parent_id, child_index))
2948 }
2949
2950 pub fn from_unique(id: SpatialFrameId) -> Self {
2952 Self(ReferenceFrameIdInner::Unique(id))
2953 }
2954
2955 pub fn from_unique_child(id: SpatialFrameId, child_index: u32) -> Self {
2957 Self(ReferenceFrameIdInner::UniqueIndex(id, child_index))
2958 }
2959
2960 pub fn from_frame_value(frame_value_key: FrameValueKey<PxTransform>) -> Self {
2962 Self(ReferenceFrameIdInner::FrameValue(frame_value_key))
2963 }
2964
2965 pub fn from_frame_value_child(frame_value_key: FrameValueKey<PxTransform>, child_index: u32) -> Self {
2967 Self(ReferenceFrameIdInner::FrameValueIndex(frame_value_key, child_index))
2968 }
2969}
2970impl From<ReferenceFrameId> for RenderReferenceFrameId {
2971 fn from(value: ReferenceFrameId) -> Self {
2972 value.0.into()
2973 }
2974}
2975impl From<FrameValueKey<PxTransform>> for ReferenceFrameId {
2976 fn from(value: FrameValueKey<PxTransform>) -> Self {
2977 Self::from_frame_value(value)
2978 }
2979}
2980impl From<SpatialFrameId> for ReferenceFrameId {
2981 fn from(id: SpatialFrameId) -> Self {
2982 Self::from_unique(id)
2983 }
2984}
2985impl From<(SpatialFrameId, u32)> for ReferenceFrameId {
2986 fn from((id, index): (SpatialFrameId, u32)) -> Self {
2987 Self::from_unique_child(id, index)
2988 }
2989}
2990impl From<(WidgetId, u32)> for ReferenceFrameId {
2991 fn from((id, index): (WidgetId, u32)) -> Self {
2992 Self::from_widget_child(id, index)
2993 }
2994}
2995impl From<(FrameValueKey<PxTransform>, u32)> for ReferenceFrameId {
2996 fn from((key, index): (FrameValueKey<PxTransform>, u32)) -> Self {
2997 Self::from_frame_value_child(key, index)
2998 }
2999}
3000
3001#[derive(Debug)]
3003pub struct FrameValueKey<T> {
3004 id: FrameBindingKeyId,
3005 _type: PhantomData<T>,
3006}
3007impl<T> PartialEq for FrameValueKey<T> {
3008 fn eq(&self, other: &Self) -> bool {
3009 self.id == other.id
3010 }
3011}
3012impl<T> Eq for FrameValueKey<T> {}
3013impl<T> Clone for FrameValueKey<T> {
3014 fn clone(&self) -> Self {
3015 *self
3016 }
3017}
3018impl<T> Copy for FrameValueKey<T> {}
3019impl<T> FrameValueKey<T> {
3020 pub fn new_unique() -> Self {
3022 FrameValueKey {
3023 id: FrameBindingKeyId::new_unique(),
3024 _type: PhantomData,
3025 }
3026 }
3027
3028 pub fn to_wr(self) -> zng_view_api::display_list::FrameValueId {
3030 Self::to_wr_child(self, u32::MAX)
3031 }
3032
3033 pub fn to_wr_child(self, child_index: u32) -> zng_view_api::display_list::FrameValueId {
3035 zng_view_api::display_list::FrameValueId::from_raw(((self.id.get() as u64) << 32) | child_index as u64)
3036 }
3037
3038 pub fn bind(self, value: T, animating: bool) -> FrameValue<T> {
3043 self.bind_child(u32::MAX, value, animating)
3044 }
3045
3046 pub fn bind_child(self, child_index: u32, value: T, animating: bool) -> FrameValue<T> {
3050 FrameValue::Bind {
3051 id: self.to_wr_child(child_index),
3052 value,
3053 animating,
3054 }
3055 }
3056
3057 pub fn update(self, value: T, animating: bool) -> FrameValueUpdate<T> {
3059 self.update_child(u32::MAX, value, animating)
3060 }
3061
3062 pub fn update_child(self, child_index: u32, value: T, animating: bool) -> FrameValueUpdate<T> {
3066 FrameValueUpdate {
3067 id: self.to_wr_child(child_index),
3068 value,
3069 animating,
3070 }
3071 }
3072
3073 pub fn bind_var<VT: VarValue>(self, var: &impl Var<VT>, map: impl FnOnce(&VT) -> T) -> FrameValue<T> {
3077 self.bind_var_child(u32::MAX, var, map)
3078 }
3079
3080 pub fn bind_var_child<VT: VarValue>(self, child_index: u32, var: &impl Var<VT>, map: impl FnOnce(&VT) -> T) -> FrameValue<T> {
3084 if var.capabilities().contains(VarCapability::NEW) {
3085 FrameValue::Bind {
3086 id: self.to_wr_child(child_index),
3087 value: var.with(map),
3088 animating: var.is_animating(),
3089 }
3090 } else {
3091 FrameValue::Value(var.with(map))
3092 }
3093 }
3094
3095 pub fn bind_var_mapped<VT: VarValue>(&self, var: &impl Var<VT>, value: T) -> FrameValue<T> {
3097 self.bind_var_mapped_child(u32::MAX, var, value)
3098 }
3099
3100 pub fn bind_var_mapped_child<VT: VarValue>(&self, child_index: u32, var: &impl Var<VT>, value: T) -> FrameValue<T> {
3104 if var.capabilities().contains(VarCapability::NEW) {
3105 FrameValue::Bind {
3106 id: self.to_wr_child(child_index),
3107 value,
3108 animating: var.is_animating(),
3109 }
3110 } else {
3111 FrameValue::Value(value)
3112 }
3113 }
3114
3115 pub fn update_var<VT: VarValue>(self, var: &impl Var<VT>, map: impl FnOnce(&VT) -> T) -> Option<FrameValueUpdate<T>> {
3117 self.update_var_child(u32::MAX, var, map)
3118 }
3119
3120 pub fn update_var_child<VT: VarValue>(
3124 self,
3125 child_index: u32,
3126 var: &impl Var<VT>,
3127 map: impl FnOnce(&VT) -> T,
3128 ) -> Option<FrameValueUpdate<T>> {
3129 if var.capabilities().contains(VarCapability::NEW) {
3130 Some(FrameValueUpdate {
3131 id: self.to_wr_child(child_index),
3132 value: var.with(map),
3133 animating: var.is_animating(),
3134 })
3135 } else {
3136 None
3137 }
3138 }
3139
3140 pub fn update_var_mapped<VT: VarValue>(self, var: &impl Var<VT>, value: T) -> Option<FrameValueUpdate<T>> {
3142 self.update_var_mapped_child(u32::MAX, var, value)
3143 }
3144
3145 pub fn update_var_mapped_child<VT: VarValue>(self, child_index: u32, var: &impl Var<VT>, value: T) -> Option<FrameValueUpdate<T>> {
3149 if var.capabilities().contains(VarCapability::NEW) {
3150 Some(FrameValueUpdate {
3151 id: self.to_wr_child(child_index),
3152 value,
3153 animating: var.is_animating(),
3154 })
3155 } else {
3156 None
3157 }
3158 }
3159}
3160
3161bitflags::bitflags! {
3162 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3164 #[serde(transparent)]
3165 pub struct FontSynthesis: u8 {
3166 const DISABLED = 0;
3169 const BOLD = 1;
3171 const OBLIQUE = 2;
3173 const ENABLED = Self::BOLD.bits() | Self::OBLIQUE.bits();
3175 }
3176}
3177impl Default for FontSynthesis {
3178 fn default() -> Self {
3180 FontSynthesis::ENABLED
3181 }
3182}
3183impl_from_and_into_var! {
3184 fn from(enabled: bool) -> FontSynthesis {
3186 if enabled {
3187 FontSynthesis::ENABLED
3188 } else {
3189 FontSynthesis::DISABLED
3190 }
3191 }
3192}