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, _)| u32::from(b) 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 = u32::from(back) as i64 + z_patch;
607 let front = u32::from(front) 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 = u32::from(back) as i64 + z_patch;
635 let mut front = u32::from(front) 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 = builder.parent_inner_bounds.replace(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::new(
1543 match aa {
1544 FontAntiAliasing::Default => self.default_font_aa,
1545 aa => aa,
1546 },
1547 synthesis.contains(FontSynthesis::BOLD),
1548 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
2274#[non_exhaustive]
2276pub struct BuiltFrame {
2277 pub display_list: DisplayList,
2279 pub clear_color: Rgba,
2281}
2282
2283enum RenderLineCommand {
2284 Line(zng_view_api::LineStyle),
2285 Border(zng_view_api::BorderStyle),
2286}
2287impl border::LineStyle {
2288 fn render_command(self) -> RenderLineCommand {
2289 use RenderLineCommand::*;
2290 use border::LineStyle as LS;
2291 match self {
2292 LS::Solid => Line(zng_view_api::LineStyle::Solid),
2293 LS::Double => Border(zng_view_api::BorderStyle::Double),
2294 LS::Dotted => Line(zng_view_api::LineStyle::Dotted),
2295 LS::Dashed => Line(zng_view_api::LineStyle::Dashed),
2296 LS::Groove => Border(zng_view_api::BorderStyle::Groove),
2297 LS::Ridge => Border(zng_view_api::BorderStyle::Ridge),
2298 LS::Wavy(thickness) => Line(zng_view_api::LineStyle::Wavy(thickness)),
2299 LS::Hidden => Border(zng_view_api::BorderStyle::Hidden),
2300 }
2301 }
2302}
2303
2304pub struct FrameUpdate {
2311 render_update_widgets: Arc<RenderUpdates>,
2312
2313 transforms: Vec<FrameValueUpdate<PxTransform>>,
2314 floats: Vec<FrameValueUpdate<f32>>,
2315 colors: Vec<FrameValueUpdate<Rgba>>,
2316
2317 extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
2318
2319 current_clear_color: Rgba,
2320 clear_color: Option<Rgba>,
2321 frame_id: FrameId,
2322
2323 widget_id: WidgetId,
2324 transform: PxTransform,
2325 parent_child_offset: PxVector,
2326 perspective: Option<(f32, PxPoint)>,
2327 inner_transform: Option<PxTransform>,
2328 child_offset: PxVector,
2329 can_reuse_widget: bool,
2330 widget_bounds: WidgetBoundsInfo,
2331 parent_inner_bounds: Option<PxRect>,
2332
2333 auto_hit_test: bool,
2334 visible: bool,
2335}
2336impl FrameUpdate {
2337 pub fn new(
2345 render_update_widgets: Arc<RenderUpdates>,
2346 frame_id: FrameId,
2347 root_id: WidgetId,
2348 root_bounds: WidgetBoundsInfo,
2349 clear_color: Rgba,
2350 ) -> Self {
2351 FrameUpdate {
2352 render_update_widgets,
2353 widget_id: root_id,
2354 widget_bounds: root_bounds,
2355 transforms: vec![],
2356 floats: vec![],
2357 colors: vec![],
2358 extensions: vec![],
2359 clear_color: None,
2360 frame_id,
2361 current_clear_color: clear_color,
2362
2363 transform: PxTransform::identity(),
2364 perspective: None,
2365 parent_child_offset: PxVector::zero(),
2366 inner_transform: Some(PxTransform::identity()),
2367 child_offset: PxVector::zero(),
2368 can_reuse_widget: true,
2369
2370 auto_hit_test: false,
2371 parent_inner_bounds: None,
2372 visible: true,
2373 }
2374 }
2375
2376 pub fn frame_id(&self) -> FrameId {
2378 self.frame_id
2379 }
2380
2381 pub fn is_outer(&self) -> bool {
2388 self.inner_transform.is_some()
2389 }
2390
2391 pub fn transform(&self) -> &PxTransform {
2393 &self.transform
2394 }
2395
2396 pub fn set_clear_color(&mut self, color: Rgba) {
2398 if self.visible {
2399 self.clear_color = Some(color);
2400 }
2401 }
2402
2403 pub fn auto_hit_test(&self) -> bool {
2405 self.auto_hit_test
2406 }
2407 pub fn with_auto_hit_test(&mut self, auto_hit_test: bool, render_update: impl FnOnce(&mut Self)) {
2411 let prev = mem::replace(&mut self.auto_hit_test, auto_hit_test);
2412 render_update(self);
2413 self.auto_hit_test = prev;
2414 }
2415
2416 pub fn is_visible(&self) -> bool {
2418 self.visible
2419 }
2420
2421 pub fn hidden(&mut self, update: impl FnOnce(&mut Self)) {
2428 let parent_visible = mem::replace(&mut self.visible, false);
2429 update(self);
2430 self.visible = parent_visible;
2431 }
2432
2433 pub fn update_transform(&mut self, new_value: FrameValueUpdate<PxTransform>, hit_test: bool) {
2441 if self.visible {
2442 self.transforms.push(new_value);
2443 }
2444
2445 if hit_test || self.auto_hit_test {
2446 self.widget_bounds.update_hit_test_transform(new_value);
2447 }
2448 }
2449
2450 pub fn update_transform_opt(&mut self, new_value: Option<FrameValueUpdate<PxTransform>>, hit_test: bool) {
2452 if let Some(value) = new_value {
2453 self.update_transform(value, hit_test)
2454 }
2455 }
2456
2457 pub fn with_transform(&mut self, new_value: FrameValueUpdate<PxTransform>, hit_test: bool, render_update: impl FnOnce(&mut Self)) {
2466 self.with_transform_value(&new_value.value, render_update);
2467 self.update_transform(new_value, hit_test);
2468 }
2469
2470 pub fn with_transform_opt(
2474 &mut self,
2475 new_value: Option<FrameValueUpdate<PxTransform>>,
2476 hit_test: bool,
2477 render_update: impl FnOnce(&mut Self),
2478 ) {
2479 match new_value {
2480 Some(value) => self.with_transform(value, hit_test, render_update),
2481 None => render_update(self),
2482 }
2483 }
2484
2485 pub fn with_child(&mut self, offset: PxVector, render_update: impl FnOnce(&mut Self)) {
2489 self.child_offset = offset;
2490 render_update(self);
2491 self.child_offset = PxVector::zero();
2492 }
2493
2494 pub fn with_transform_value(&mut self, value: &PxTransform, render_update: impl FnOnce(&mut Self)) {
2500 let parent_transform = self.transform;
2501 self.transform = value.then(&parent_transform);
2502
2503 render_update(self);
2504 self.transform = parent_transform;
2505 }
2506
2507 pub fn with_inner_transform(&mut self, transform: &PxTransform, render_update: impl FnOnce(&mut Self)) {
2513 if let Some(inner_transform) = &mut self.inner_transform {
2514 let parent = *inner_transform;
2515 *inner_transform = inner_transform.then(transform);
2516
2517 render_update(self);
2518
2519 if let Some(inner_transform) = &mut self.inner_transform {
2520 *inner_transform = parent;
2521 }
2522 } else {
2523 tracing::error!("called `with_inner_transform` inside inner context of `{}`", self.widget_id);
2524 render_update(self);
2525 }
2526 }
2527
2528 pub fn can_reuse_widget(&self) -> bool {
2532 self.can_reuse_widget
2533 }
2534
2535 pub fn with_no_reuse(&mut self, render_update: impl FnOnce(&mut Self)) {
2539 let prev_can_reuse = self.can_reuse_widget;
2540 self.can_reuse_widget = false;
2541 render_update(self);
2542 self.can_reuse_widget = prev_can_reuse;
2543 }
2544
2545 pub fn update_widget(&mut self, render_update: impl FnOnce(&mut Self)) {
2552 let wgt_info = WIDGET.info();
2553 let id = wgt_info.id();
2554
2555 #[cfg(debug_assertions)]
2556 if self.inner_transform.is_some() && wgt_info.parent().is_some() {
2557 tracing::error!(
2558 "called `update_widget` for `{}` without calling `update_inner` for the parent `{}`",
2559 WIDGET.trace_id(),
2560 self.widget_id
2561 );
2562 }
2563
2564 let bounds = wgt_info.bounds_info();
2565 if bounds.is_collapsed() {
2566 let _ = WIDGET.take_update(UpdateFlags::LAYOUT | UpdateFlags::RENDER | UpdateFlags::RENDER_UPDATE);
2567 return;
2568 } else {
2569 #[cfg(debug_assertions)]
2570 if WIDGET.pending_update().contains(UpdateFlags::LAYOUT) {
2571 tracing::error!("called `update_widget` for `{}` with pending layout", WIDGET.trace_id());
2572 }
2573 }
2574
2575 let tree = wgt_info.tree();
2576
2577 let parent_can_reuse = self.can_reuse_widget;
2578 let parent_perspective = mem::replace(&mut self.perspective, wgt_info.perspective());
2579 let parent_bounds = mem::replace(&mut self.widget_bounds, bounds.clone());
2580
2581 if let Some((_, o)) = &mut self.perspective {
2582 *o -= self.child_offset;
2583 }
2584
2585 let render_info = bounds.render_info();
2586 if let Some(i) = &render_info {
2587 if i.parent_perspective != self.perspective {
2588 self.can_reuse_widget = false;
2589 }
2590 }
2591
2592 let outer_transform = PxTransform::from(self.child_offset).then(&self.transform);
2593
2594 if !WIDGET.take_update(UpdateFlags::RENDER_UPDATE)
2595 && self.can_reuse_widget
2596 && !self.render_update_widgets.delivery_list().enter_widget(id)
2597 && bounds.parent_child_offset() == self.child_offset
2598 {
2599 let _span = tracing::trace_span!("reuse-descendants", id=?self.widget_id).entered();
2600
2601 let prev_outer = bounds.outer_transform();
2602 if prev_outer != outer_transform {
2603 if let Some(undo_prev) = prev_outer.inverse() {
2604 let patch = undo_prev.then(&outer_transform);
2605
2606 let update = |info: WidgetInfo| {
2607 let bounds = info.bounds_info();
2608 bounds.set_outer_transform(bounds.outer_transform().then(&patch), tree);
2609 bounds.set_inner_transform(
2610 bounds.inner_transform().then(&patch),
2611 tree,
2612 info.id(),
2613 info.parent().map(|p| p.inner_bounds()),
2614 );
2615 };
2616 let targets = tree.get(id).unwrap().self_and_descendants();
2617 if PARALLEL_VAR.get().contains(Parallel::RENDER) {
2618 targets.par_bridge().for_each(update);
2619 } else {
2620 targets.for_each(update);
2621 }
2622
2623 return; }
2625 } else {
2626 return; }
2628
2629 self.can_reuse_widget = false;
2631 }
2632
2633 bounds.set_parent_child_offset(self.child_offset);
2634 bounds.set_outer_transform(outer_transform, tree);
2635 self.parent_child_offset = mem::take(&mut self.child_offset);
2636 self.inner_transform = Some(PxTransform::identity());
2637 let parent_id = self.widget_id;
2638 self.widget_id = id;
2639
2640 render_update(self);
2641
2642 if let Some(mut i) = render_info {
2643 i.parent_perspective = self.perspective;
2644 bounds.set_rendered(Some(i), tree);
2645 }
2646 self.parent_child_offset = PxVector::zero();
2647 self.inner_transform = None;
2648 self.widget_id = parent_id;
2649 self.can_reuse_widget = parent_can_reuse;
2650 self.perspective = parent_perspective;
2651 self.widget_bounds = parent_bounds;
2652 }
2653
2654 pub fn reuse_widget(&mut self) {
2659 if self.inner_transform.is_some() {
2660 tracing::error!(
2661 "called `reuse_widget` for `{}` without calling `update_inner` for the parent `{}`",
2662 WIDGET.trace_id(),
2663 self.widget_id
2664 );
2665 }
2666 }
2667
2668 pub fn update_inner(
2672 &mut self,
2673 layout_translation_key: FrameValueKey<PxTransform>,
2674 layout_translation_animating: bool,
2675 render_update: impl FnOnce(&mut Self),
2676 ) {
2677 let id = WIDGET.id();
2678 if let Some(mut inner_transform) = self.inner_transform.take() {
2679 let bounds = WIDGET.bounds();
2680 let tree = WINDOW.info();
2681
2682 let inner_offset = bounds.inner_offset();
2683 if let Some((p, mut o)) = self.perspective {
2684 o -= inner_offset;
2685 let x = o.x.0 as f32;
2686 let y = o.y.0 as f32;
2687 let p = PxTransform::translation(-x, -y)
2688 .then(&PxTransform::perspective(p))
2689 .then_translate(euclid::vec2(x, y));
2690 inner_transform = inner_transform.then(&p);
2691 }
2692 let inner_transform = inner_transform.then_translate((self.parent_child_offset + inner_offset).cast());
2693 self.update_transform(layout_translation_key.update(inner_transform, layout_translation_animating), false);
2694 let parent_transform = self.transform;
2695
2696 self.transform = inner_transform.then(&parent_transform);
2697
2698 bounds.set_inner_transform(self.transform, &tree, id, self.parent_inner_bounds);
2699 let parent_inner_bounds = self.parent_inner_bounds.replace(bounds.inner_bounds());
2700
2701 render_update(self);
2702
2703 self.transform = parent_transform;
2704 self.parent_inner_bounds = parent_inner_bounds;
2705 } else {
2706 tracing::error!("called `update_inner` more then once for `{}`", id);
2707 render_update(self)
2708 }
2709 }
2710
2711 pub fn update_f32(&mut self, new_value: FrameValueUpdate<f32>) {
2713 if self.visible {
2714 self.floats.push(new_value);
2715 }
2716 }
2717
2718 pub fn update_f32_opt(&mut self, new_value: Option<FrameValueUpdate<f32>>) {
2720 if let Some(value) = new_value {
2721 self.update_f32(value)
2722 }
2723 }
2724
2725 pub fn update_color(&mut self, new_value: FrameValueUpdate<Rgba>) {
2729 if self.visible {
2730 self.colors.push(new_value)
2731 }
2732 }
2733
2734 pub fn update_color_opt(&mut self, new_value: Option<FrameValueUpdate<Rgba>>) {
2736 if let Some(value) = new_value {
2737 self.update_color(value)
2738 }
2739 }
2740
2741 pub fn update_extension_raw(&mut self, extension_id: ApiExtensionId, extension_payload: ApiExtensionPayload) {
2743 self.extensions.push((extension_id, extension_payload))
2744 }
2745
2746 pub fn update_extension<T: serde::Serialize>(&mut self, extension_id: ApiExtensionId, payload: &T) {
2748 self.update_extension_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap())
2749 }
2750
2751 pub fn parallel_split(&self) -> ParallelBuilder<Self> {
2757 if self.inner_transform.is_some() && WIDGET.parent_id().is_some() {
2758 tracing::error!(
2759 "called `parallel_split` inside `{}` and before calling `update_inner`",
2760 self.widget_id
2761 );
2762 }
2763
2764 ParallelBuilder(Some(Self {
2765 render_update_widgets: self.render_update_widgets.clone(),
2766 current_clear_color: self.current_clear_color,
2767 frame_id: self.frame_id,
2768
2769 transforms: vec![],
2770 floats: vec![],
2771 colors: vec![],
2772 extensions: vec![],
2773 clear_color: None,
2774
2775 widget_id: self.widget_id,
2776 transform: self.transform,
2777 perspective: self.perspective,
2778 parent_child_offset: self.parent_child_offset,
2779 inner_transform: self.inner_transform,
2780 child_offset: self.child_offset,
2781 can_reuse_widget: self.can_reuse_widget,
2782 widget_bounds: self.widget_bounds.clone(),
2783 parent_inner_bounds: self.parent_inner_bounds,
2784 auto_hit_test: self.auto_hit_test,
2785 visible: self.visible,
2786 }))
2787 }
2788
2789 pub fn parallel_fold(&mut self, mut split: ParallelBuilder<Self>) {
2791 let mut split = split.take();
2792
2793 debug_assert_eq!(self.frame_id, split.frame_id);
2794 debug_assert_eq!(self.widget_id, split.widget_id);
2795
2796 fn take_or_append<T>(t: &mut Vec<T>, s: &mut Vec<T>) {
2797 if t.is_empty() {
2798 *t = mem::take(s);
2799 } else {
2800 t.append(s)
2801 }
2802 }
2803
2804 take_or_append(&mut self.transforms, &mut split.transforms);
2805 take_or_append(&mut self.floats, &mut split.floats);
2806 take_or_append(&mut self.colors, &mut split.colors);
2807 take_or_append(&mut self.extensions, &mut split.extensions);
2808
2809 if let Some(c) = self.clear_color.take() {
2810 self.clear_color = Some(c);
2811 }
2812 }
2813
2814 pub fn with_nested_window(
2816 &mut self,
2817 render_update_widgets: Arc<RenderUpdates>,
2818 root_id: WidgetId,
2819 root_bounds: WidgetBoundsInfo,
2820 update: impl FnOnce(&mut Self),
2821 ) {
2822 let mut nested = Self::new(render_update_widgets, self.frame_id, root_id, root_bounds, self.current_clear_color);
2823
2824 update(&mut nested);
2825
2826 fn take_or_append<T>(t: &mut Vec<T>, s: &mut Vec<T>) {
2828 if t.is_empty() {
2829 *t = mem::take(s);
2830 } else {
2831 t.append(s)
2832 }
2833 }
2834 take_or_append(&mut self.transforms, &mut nested.transforms);
2835 take_or_append(&mut self.floats, &mut nested.floats);
2836 take_or_append(&mut self.colors, &mut nested.colors);
2837 take_or_append(&mut self.extensions, &mut nested.extensions);
2838 }
2839
2840 pub fn render_update_widgets(&self) -> &Arc<RenderUpdates> {
2842 &self.render_update_widgets
2843 }
2844
2845 pub fn finalize(mut self, info_tree: &WidgetInfoTree) -> BuiltFrameUpdate {
2849 info_tree.after_render_update(self.frame_id);
2850
2851 if self.clear_color == Some(self.current_clear_color) {
2852 self.clear_color = None;
2853 }
2854
2855 BuiltFrameUpdate {
2856 clear_color: self.clear_color,
2857 transforms: self.transforms,
2858 floats: self.floats,
2859 colors: self.colors,
2860 extensions: self.extensions,
2861 }
2862 }
2863}
2864
2865#[non_exhaustive]
2867pub struct BuiltFrameUpdate {
2868 pub transforms: Vec<FrameValueUpdate<PxTransform>>,
2870 pub floats: Vec<FrameValueUpdate<f32>>,
2872 pub colors: Vec<FrameValueUpdate<Rgba>>,
2874 pub clear_color: Option<Rgba>,
2876 pub extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
2878}
2879
2880unique_id_32! {
2881 #[derive(Debug)]
2882 struct FrameBindingKeyId;
2883}
2884impl_unique_id_bytemuck!(FrameBindingKeyId);
2885
2886unique_id_32! {
2887 #[derive(Debug)]
2889 pub struct SpatialFrameId;
2890}
2891impl_unique_id_bytemuck!(SpatialFrameId);
2892
2893#[derive(Clone, Copy, PartialEq, Eq)]
2894enum ReferenceFrameIdInner {
2895 Unique(SpatialFrameId),
2896 UniqueIndex(SpatialFrameId, u32),
2897 Widget(WidgetId),
2898 WidgetIndex(WidgetId, u32),
2899 FrameValue(FrameValueKey<PxTransform>),
2900 FrameValueIndex(FrameValueKey<PxTransform>, u32),
2901}
2902impl ReferenceFrameIdInner {
2903 const _RESERVED: u64 = 1 << 63; const UNIQUE: u64 = 1 << 62;
2905 const WIDGET: u64 = 1 << 61;
2906 const FRAME_VALUE: u64 = 1 << 60;
2907}
2908impl From<ReferenceFrameIdInner> for RenderReferenceFrameId {
2909 fn from(value: ReferenceFrameIdInner) -> Self {
2910 match value {
2911 ReferenceFrameIdInner::UniqueIndex(id, index) => {
2912 RenderReferenceFrameId(id.get() as u64, index as u64 | ReferenceFrameIdInner::UNIQUE)
2913 }
2914 ReferenceFrameIdInner::WidgetIndex(id, index) => RenderReferenceFrameId(id.get(), index as u64 | ReferenceFrameIdInner::WIDGET),
2915 ReferenceFrameIdInner::FrameValue(key) => {
2916 RenderReferenceFrameId(((key.id.get() as u64) << 32) | u32::MAX as u64, ReferenceFrameIdInner::FRAME_VALUE)
2917 }
2918 ReferenceFrameIdInner::FrameValueIndex(key, index) => {
2919 RenderReferenceFrameId(((key.id.get() as u64) << 32) | index as u64, ReferenceFrameIdInner::FRAME_VALUE)
2920 }
2921 ReferenceFrameIdInner::Unique(id) => {
2922 RenderReferenceFrameId(id.get() as u64, (u32::MAX as u64 + 1) | ReferenceFrameIdInner::UNIQUE)
2923 }
2924 ReferenceFrameIdInner::Widget(id) => RenderReferenceFrameId(id.get(), (u32::MAX as u64 + 1) | ReferenceFrameIdInner::WIDGET),
2925 }
2926 }
2927}
2928
2929#[derive(Clone, Copy, PartialEq, Eq)]
2936pub struct ReferenceFrameId(ReferenceFrameIdInner);
2937impl ReferenceFrameId {
2938 fn from_widget(widget_id: WidgetId) -> Self {
2942 Self(ReferenceFrameIdInner::Widget(widget_id))
2943 }
2944
2945 pub fn from_widget_child(parent_id: WidgetId, child_index: u32) -> Self {
2949 Self(ReferenceFrameIdInner::WidgetIndex(parent_id, child_index))
2950 }
2951
2952 pub fn from_unique(id: SpatialFrameId) -> Self {
2954 Self(ReferenceFrameIdInner::Unique(id))
2955 }
2956
2957 pub fn from_unique_child(id: SpatialFrameId, child_index: u32) -> Self {
2959 Self(ReferenceFrameIdInner::UniqueIndex(id, child_index))
2960 }
2961
2962 pub fn from_frame_value(frame_value_key: FrameValueKey<PxTransform>) -> Self {
2964 Self(ReferenceFrameIdInner::FrameValue(frame_value_key))
2965 }
2966
2967 pub fn from_frame_value_child(frame_value_key: FrameValueKey<PxTransform>, child_index: u32) -> Self {
2969 Self(ReferenceFrameIdInner::FrameValueIndex(frame_value_key, child_index))
2970 }
2971}
2972impl From<ReferenceFrameId> for RenderReferenceFrameId {
2973 fn from(value: ReferenceFrameId) -> Self {
2974 value.0.into()
2975 }
2976}
2977impl From<FrameValueKey<PxTransform>> for ReferenceFrameId {
2978 fn from(value: FrameValueKey<PxTransform>) -> Self {
2979 Self::from_frame_value(value)
2980 }
2981}
2982impl From<SpatialFrameId> for ReferenceFrameId {
2983 fn from(id: SpatialFrameId) -> Self {
2984 Self::from_unique(id)
2985 }
2986}
2987impl From<(SpatialFrameId, u32)> for ReferenceFrameId {
2988 fn from((id, index): (SpatialFrameId, u32)) -> Self {
2989 Self::from_unique_child(id, index)
2990 }
2991}
2992impl From<(WidgetId, u32)> for ReferenceFrameId {
2993 fn from((id, index): (WidgetId, u32)) -> Self {
2994 Self::from_widget_child(id, index)
2995 }
2996}
2997impl From<(FrameValueKey<PxTransform>, u32)> for ReferenceFrameId {
2998 fn from((key, index): (FrameValueKey<PxTransform>, u32)) -> Self {
2999 Self::from_frame_value_child(key, index)
3000 }
3001}
3002
3003#[derive(Debug)]
3005pub struct FrameValueKey<T> {
3006 id: FrameBindingKeyId,
3007 _type: PhantomData<T>,
3008}
3009impl<T> PartialEq for FrameValueKey<T> {
3010 fn eq(&self, other: &Self) -> bool {
3011 self.id == other.id
3012 }
3013}
3014impl<T> Eq for FrameValueKey<T> {}
3015impl<T> Clone for FrameValueKey<T> {
3016 fn clone(&self) -> Self {
3017 *self
3018 }
3019}
3020impl<T> Copy for FrameValueKey<T> {}
3021impl<T> FrameValueKey<T> {
3022 pub fn new_unique() -> Self {
3024 FrameValueKey {
3025 id: FrameBindingKeyId::new_unique(),
3026 _type: PhantomData,
3027 }
3028 }
3029
3030 pub fn to_wr(self) -> zng_view_api::display_list::FrameValueId {
3032 Self::to_wr_child(self, u32::MAX)
3033 }
3034
3035 pub fn to_wr_child(self, child_index: u32) -> zng_view_api::display_list::FrameValueId {
3037 zng_view_api::display_list::FrameValueId::from_raw(((self.id.get() as u64) << 32) | child_index as u64)
3038 }
3039
3040 pub fn bind(self, value: T, animating: bool) -> FrameValue<T> {
3045 self.bind_child(u32::MAX, value, animating)
3046 }
3047
3048 pub fn bind_child(self, child_index: u32, value: T, animating: bool) -> FrameValue<T> {
3052 FrameValue::Bind {
3053 id: self.to_wr_child(child_index),
3054 value,
3055 animating,
3056 }
3057 }
3058
3059 pub fn update(self, value: T, animating: bool) -> FrameValueUpdate<T> {
3061 self.update_child(u32::MAX, value, animating)
3062 }
3063
3064 pub fn update_child(self, child_index: u32, value: T, animating: bool) -> FrameValueUpdate<T> {
3068 FrameValueUpdate::new(self.to_wr_child(child_index), value, animating)
3069 }
3070
3071 pub fn bind_var<VT: VarValue>(self, var: &impl Var<VT>, map: impl FnOnce(&VT) -> T) -> FrameValue<T> {
3075 self.bind_var_child(u32::MAX, var, map)
3076 }
3077
3078 pub fn bind_var_child<VT: VarValue>(self, child_index: u32, var: &impl Var<VT>, map: impl FnOnce(&VT) -> T) -> FrameValue<T> {
3082 if var.capabilities().contains(VarCapability::NEW) {
3083 FrameValue::Bind {
3084 id: self.to_wr_child(child_index),
3085 value: var.with(map),
3086 animating: var.is_animating(),
3087 }
3088 } else {
3089 FrameValue::Value(var.with(map))
3090 }
3091 }
3092
3093 pub fn bind_var_mapped<VT: VarValue>(&self, var: &impl Var<VT>, value: T) -> FrameValue<T> {
3095 self.bind_var_mapped_child(u32::MAX, var, value)
3096 }
3097
3098 pub fn bind_var_mapped_child<VT: VarValue>(&self, child_index: u32, var: &impl Var<VT>, value: T) -> FrameValue<T> {
3102 if var.capabilities().contains(VarCapability::NEW) {
3103 FrameValue::Bind {
3104 id: self.to_wr_child(child_index),
3105 value,
3106 animating: var.is_animating(),
3107 }
3108 } else {
3109 FrameValue::Value(value)
3110 }
3111 }
3112
3113 pub fn update_var<VT: VarValue>(self, var: &impl Var<VT>, map: impl FnOnce(&VT) -> T) -> Option<FrameValueUpdate<T>> {
3115 self.update_var_child(u32::MAX, var, map)
3116 }
3117
3118 pub fn update_var_child<VT: VarValue>(
3122 self,
3123 child_index: u32,
3124 var: &impl Var<VT>,
3125 map: impl FnOnce(&VT) -> T,
3126 ) -> Option<FrameValueUpdate<T>> {
3127 if var.capabilities().contains(VarCapability::NEW) {
3128 Some(FrameValueUpdate::new(
3129 self.to_wr_child(child_index),
3130 var.with(map),
3131 var.is_animating(),
3132 ))
3133 } else {
3134 None
3135 }
3136 }
3137
3138 pub fn update_var_mapped<VT: VarValue>(self, var: &impl Var<VT>, value: T) -> Option<FrameValueUpdate<T>> {
3140 self.update_var_mapped_child(u32::MAX, var, value)
3141 }
3142
3143 pub fn update_var_mapped_child<VT: VarValue>(self, child_index: u32, var: &impl Var<VT>, value: T) -> Option<FrameValueUpdate<T>> {
3147 if var.capabilities().contains(VarCapability::NEW) {
3148 Some(FrameValueUpdate::new(self.to_wr_child(child_index), value, var.is_animating()))
3149 } else {
3150 None
3151 }
3152 }
3153}
3154
3155bitflags::bitflags! {
3156 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3158 #[serde(transparent)]
3159 pub struct FontSynthesis: u8 {
3160 const DISABLED = 0;
3163 const BOLD = 1;
3165 const OBLIQUE = 2;
3167 const ENABLED = Self::BOLD.bits() | Self::OBLIQUE.bits();
3169 }
3170}
3171impl Default for FontSynthesis {
3172 fn default() -> Self {
3174 FontSynthesis::ENABLED
3175 }
3176}
3177impl_from_and_into_var! {
3178 fn from(enabled: bool) -> FontSynthesis {
3180 if enabled {
3181 FontSynthesis::ENABLED
3182 } else {
3183 FontSynthesis::DISABLED
3184 }
3185 }
3186}