1use std::{marker::PhantomData, mem, sync::Arc};
4
5use crate::{
6 widget::info::{ParallelSegmentOffsets, WidgetBoundsInfo},
7 window::WINDOW,
8};
9use zng_color::{
10 MixBlendMode, 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: MixBlendMode,
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: MixBlendMode::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: MixBlendMode::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 && let Some(g) = &group
724 {
725 if self.visible {
726 self.display_list.push_reuse_range(g);
727 }
728 return;
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: MixBlendMode, 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 != MixBlendMode::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(MixBlendMode::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(MixBlendMode::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(MixBlendMode::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(MixBlendMode::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 && let Some(r) = &self.renderer
1215 {
1216 self.display_list.push_mask(image.renderer_id(r), rect);
1217 pop = true;
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: MixBlendMode, 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(MixBlendMode::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 self.visible
1377 && let Some(r) = &self.renderer
1378 {
1379 let image_id = image.renderer_id(r);
1380 self.display_list.push_nine_patch_border(
1381 bounds,
1382 NinePatchSource::Image { image_id, rendering },
1383 widths,
1384 image.size(),
1385 slice,
1386 fill,
1387 repeat_horizontal,
1388 repeat_vertical,
1389 )
1390 }
1391 }
1392
1393 #[expect(clippy::too_many_arguments)]
1395 pub fn push_border_linear_gradient(
1396 &mut self,
1397 bounds: PxRect,
1398 widths: PxSideOffsets,
1399 slice: PxSideOffsets,
1400 fill: bool,
1401 repeat_horizontal: RepeatMode,
1402 repeat_vertical: RepeatMode,
1403 line: PxLine,
1404 stops: &[RenderGradientStop],
1405 extend_mode: RenderExtendMode,
1406 ) {
1407 debug_assert!(stops.len() >= 2);
1408 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1409 debug_assert!(
1410 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1411 "last color stop must be at offset 1.0"
1412 );
1413 expect_inner!(self.push_border_linear_gradient);
1414 warn_empty!(self.push_border_linear_gradient(bounds));
1415
1416 if self.visible && !stops.is_empty() {
1417 self.display_list.push_nine_patch_border(
1418 bounds,
1419 NinePatchSource::LinearGradient {
1420 start_point: line.start.cast(),
1421 end_point: line.end.cast(),
1422 extend_mode,
1423 stops: stops.to_vec().into_boxed_slice(),
1424 },
1425 widths,
1426 bounds.size,
1427 slice,
1428 fill,
1429 repeat_horizontal,
1430 repeat_vertical,
1431 );
1432 }
1433 }
1434
1435 #[expect(clippy::too_many_arguments)]
1437 pub fn push_border_radial_gradient(
1438 &mut self,
1439 bounds: PxRect,
1440 widths: PxSideOffsets,
1441 slice: PxSideOffsets,
1442 fill: bool,
1443 repeat_horizontal: RepeatMode,
1444 repeat_vertical: RepeatMode,
1445 center: PxPoint,
1446 radius: PxSize,
1447 stops: &[RenderGradientStop],
1448 extend_mode: RenderExtendMode,
1449 ) {
1450 debug_assert!(stops.len() >= 2);
1451 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1452 debug_assert!(
1453 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1454 "last color stop must be at offset 1.0"
1455 );
1456
1457 expect_inner!(self.push_border_radial_gradient);
1458 warn_empty!(self.push_border_radial_gradient(bounds));
1459
1460 if self.visible && !stops.is_empty() {
1461 self.display_list.push_nine_patch_border(
1462 bounds,
1463 NinePatchSource::RadialGradient {
1464 center: center.cast(),
1465 radius: radius.cast(),
1466 start_offset: 0.0,
1467 end_offset: 1.0,
1468 extend_mode,
1469 stops: stops.to_vec().into_boxed_slice(),
1470 },
1471 widths,
1472 bounds.size,
1473 slice,
1474 fill,
1475 repeat_horizontal,
1476 repeat_vertical,
1477 );
1478 }
1479 }
1480
1481 #[expect(clippy::too_many_arguments)]
1483 pub fn push_border_conic_gradient(
1484 &mut self,
1485 bounds: PxRect,
1486 widths: PxSideOffsets,
1487 slice: PxSideOffsets,
1488 fill: bool,
1489 repeat_horizontal: RepeatMode,
1490 repeat_vertical: RepeatMode,
1491 center: PxPoint,
1492 angle: AngleRadian,
1493 stops: &[RenderGradientStop],
1494 extend_mode: RenderExtendMode,
1495 ) {
1496 debug_assert!(stops.len() >= 2);
1497 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1498 debug_assert!(
1499 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1500 "last color stop must be at offset 1.0"
1501 );
1502
1503 expect_inner!(self.push_border_conic_gradient);
1504 warn_empty!(self.push_border_conic_gradient(bounds));
1505
1506 if self.visible && !stops.is_empty() {
1507 self.display_list.push_nine_patch_border(
1508 bounds,
1509 NinePatchSource::ConicGradient {
1510 center: center.cast(),
1511 angle,
1512 start_offset: 0.0,
1513 end_offset: 1.0,
1514 extend_mode,
1515 stops: stops.to_vec().into_boxed_slice(),
1516 },
1517 widths,
1518 bounds.size,
1519 slice,
1520 fill,
1521 repeat_horizontal,
1522 repeat_vertical,
1523 );
1524 }
1525 }
1526
1527 pub fn push_text(
1529 &mut self,
1530 clip_rect: PxRect,
1531 glyphs: &[GlyphInstance],
1532 font: &impl Font,
1533 color: FrameValue<Rgba>,
1534 synthesis: FontSynthesis,
1535 aa: FontAntiAliasing,
1536 ) {
1537 expect_inner!(self.push_text);
1538 warn_empty!(self.push_text(clip_rect));
1539
1540 if let Some(r) = &self.renderer
1541 && !glyphs.is_empty()
1542 && self.visible
1543 && !font.is_empty_fallback()
1544 {
1545 let font_id = font.renderer_id(r, synthesis);
1546
1547 let opts = GlyphOptions::new(
1548 match aa {
1549 FontAntiAliasing::Default => self.default_font_aa,
1550 aa => aa,
1551 },
1552 synthesis.contains(FontSynthesis::BOLD),
1553 synthesis.contains(FontSynthesis::OBLIQUE),
1554 );
1555 self.display_list.push_text(clip_rect, font_id, glyphs, color, opts);
1556 }
1557
1558 if self.auto_hit_test {
1559 self.hit_test().push_rect(clip_rect);
1560 }
1561 }
1562
1563 pub fn push_image(
1565 &mut self,
1566 clip_rect: PxRect,
1567 img_size: PxSize,
1568 tile_size: PxSize,
1569 tile_spacing: PxSize,
1570 image: &impl Img,
1571 rendering: ImageRendering,
1572 ) {
1573 expect_inner!(self.push_image);
1574 warn_empty!(self.push_image(clip_rect));
1575
1576 if let Some(r) = &self.renderer
1577 && self.visible
1578 {
1579 let image_key = image.renderer_id(r);
1580 self.display_list.push_image(
1581 clip_rect,
1582 image_key,
1583 img_size,
1584 tile_size,
1585 tile_spacing,
1586 rendering,
1587 image.alpha_type(),
1588 );
1589 }
1590
1591 if self.auto_hit_test {
1592 self.hit_test().push_rect(clip_rect);
1593 }
1594 }
1595
1596 pub fn push_color(&mut self, clip_rect: PxRect, color: FrameValue<Rgba>) {
1605 expect_inner!(self.push_color);
1606 warn_empty!(self.push_color(clip_rect));
1607
1608 if self.visible {
1609 self.display_list.push_color(clip_rect, color);
1610 }
1611
1612 if self.auto_hit_test {
1613 self.hit_test().push_rect(clip_rect);
1614 }
1615 }
1616
1617 #[expect(clippy::too_many_arguments)]
1625 pub fn push_linear_gradient(
1626 &mut self,
1627 clip_rect: PxRect,
1628 line: PxLine,
1629 stops: &[RenderGradientStop],
1630 extend_mode: RenderExtendMode,
1631 tile_origin: PxPoint,
1632 tile_size: PxSize,
1633 tile_spacing: PxSize,
1634 ) {
1635 debug_assert!(stops.len() >= 2);
1636 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1637 debug_assert!(
1638 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1639 "last color stop must be at offset 1.0"
1640 );
1641
1642 expect_inner!(self.push_linear_gradient);
1643 warn_empty!(self.push_linear_gradient(clip_rect));
1644
1645 if !stops.is_empty() && self.visible {
1646 self.display_list.push_linear_gradient(
1647 clip_rect,
1648 line.start.cast(),
1649 line.end.cast(),
1650 extend_mode,
1651 stops,
1652 tile_origin,
1653 tile_size,
1654 tile_spacing,
1655 );
1656 }
1657
1658 if self.auto_hit_test {
1659 self.hit_test().push_rect(clip_rect);
1660 }
1661 }
1662
1663 #[expect(clippy::too_many_arguments)]
1674 pub fn push_radial_gradient(
1675 &mut self,
1676 clip_rect: PxRect,
1677 center: PxPoint,
1678 radius: PxSize,
1679 stops: &[RenderGradientStop],
1680 extend_mode: RenderExtendMode,
1681 tile_origin: PxPoint,
1682 tile_size: PxSize,
1683 tile_spacing: PxSize,
1684 ) {
1685 debug_assert!(stops.len() >= 2);
1686 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1687 debug_assert!(
1688 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1689 "last color stop must be at offset 1.0"
1690 );
1691
1692 expect_inner!(self.push_radial_gradient);
1693 warn_empty!(self.push_radial_gradient(clip_rect));
1694
1695 if !stops.is_empty() && self.visible {
1696 self.display_list.push_radial_gradient(
1697 clip_rect,
1698 center.cast(),
1699 radius.cast(),
1700 0.0,
1701 1.0,
1702 extend_mode,
1703 stops,
1704 tile_origin,
1705 tile_size,
1706 tile_spacing,
1707 );
1708 }
1709
1710 if self.auto_hit_test {
1711 self.hit_test().push_rect(clip_rect);
1712 }
1713 }
1714
1715 #[expect(clippy::too_many_arguments)]
1723 pub fn push_conic_gradient(
1724 &mut self,
1725 clip_rect: PxRect,
1726 center: PxPoint,
1727 angle: AngleRadian,
1728 stops: &[RenderGradientStop],
1729 extend_mode: RenderExtendMode,
1730 tile_origin: PxPoint,
1731 tile_size: PxSize,
1732 tile_spacing: PxSize,
1733 ) {
1734 debug_assert!(stops.len() >= 2);
1735 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1736 debug_assert!(
1737 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1738 "last color stop must be at offset 1.0"
1739 );
1740
1741 expect_inner!(self.push_conic_gradient);
1742 warn_empty!(self.push_conic_gradient(clip_rect));
1743
1744 if !stops.is_empty() && self.visible {
1745 self.display_list.push_conic_gradient(
1746 clip_rect,
1747 center.cast(),
1748 angle,
1749 0.0,
1750 1.0,
1751 extend_mode,
1752 stops,
1753 tile_origin,
1754 tile_size,
1755 tile_spacing,
1756 );
1757 }
1758
1759 if self.auto_hit_test {
1760 self.hit_test().push_rect(clip_rect);
1761 }
1762 }
1763
1764 pub fn push_line(&mut self, clip_rect: PxRect, orientation: border::LineOrientation, color: Rgba, style: border::LineStyle) {
1766 expect_inner!(self.push_line);
1767 warn_empty!(self.push_line(clip_rect));
1768
1769 if self.visible {
1770 match style.render_command() {
1771 RenderLineCommand::Line(style) => {
1772 self.display_list.push_line(clip_rect, color, style, orientation);
1773 }
1774 RenderLineCommand::Border(style) => {
1775 use border::LineOrientation as LO;
1776 let widths = match orientation {
1777 LO::Vertical => PxSideOffsets::new(Px(0), Px(0), Px(0), clip_rect.width()),
1778 LO::Horizontal => PxSideOffsets::new(clip_rect.height(), Px(0), Px(0), Px(0)),
1779 };
1780 self.display_list.push_border(
1781 clip_rect,
1782 widths,
1783 zng_view_api::BorderSide { color, style },
1784 zng_view_api::BorderSide {
1785 color: colors::BLACK.transparent(),
1786 style: zng_view_api::BorderStyle::Hidden,
1787 },
1788 zng_view_api::BorderSide {
1789 color: colors::BLACK.transparent(),
1790 style: zng_view_api::BorderStyle::Hidden,
1791 },
1792 zng_view_api::BorderSide { color, style },
1793 PxCornerRadius::zero(),
1794 );
1795 }
1796 }
1797 }
1798
1799 if self.auto_hit_test {
1800 self.hit_test().push_rect(clip_rect);
1801 }
1802 }
1803
1804 pub fn push_debug_dot_overlay(&mut self, offset: PxPoint, color: impl Into<Rgba>) {
1808 if let Some(offset) = self.transform.transform_point(offset) {
1809 self.debug_dot_overlays.push((offset, color.into()));
1810 }
1811 }
1812
1813 pub fn push_debug_dot(&mut self, offset: PxPoint, color: impl Into<Rgba>) {
1817 if !self.visible {
1818 return;
1819 }
1820 let scale = self.scale_factor();
1821
1822 let radius = PxSize::splat(Px(6)) * scale;
1823 let color = color.into();
1824
1825 let center = radius.to_vector().to_point();
1826 let bounds = radius * 2.0.fct();
1827
1828 let offset = offset - radius.to_vector();
1829
1830 self.display_list.push_radial_gradient(
1831 PxRect::new(offset, bounds),
1832 center.cast(),
1833 radius.cast(),
1834 0.0,
1835 1.0,
1836 RenderExtendMode::Clamp,
1837 &[
1838 RenderGradientStop { offset: 0.0, color },
1839 RenderGradientStop { offset: 0.5, color },
1840 RenderGradientStop {
1841 offset: 0.6,
1842 color: colors::WHITE,
1843 },
1844 RenderGradientStop {
1845 offset: 0.7,
1846 color: colors::WHITE,
1847 },
1848 RenderGradientStop {
1849 offset: 0.8,
1850 color: colors::BLACK,
1851 },
1852 RenderGradientStop {
1853 offset: 1.0,
1854 color: colors::BLACK.transparent(),
1855 },
1856 ],
1857 PxPoint::zero(),
1858 bounds,
1859 PxSize::zero(),
1860 );
1861 }
1862
1863 pub fn push_extension_context_raw(
1865 &mut self,
1866 extension_id: ApiExtensionId,
1867 payload: ApiExtensionPayload,
1868 render: impl FnOnce(&mut Self),
1869 ) {
1870 self.display_list.push_extension(extension_id, payload);
1871 render(self);
1872 self.display_list.pop_extension(extension_id);
1873 }
1874
1875 pub fn push_extension_context<T: serde::Serialize>(
1877 &mut self,
1878 extension_id: ApiExtensionId,
1879 payload: &T,
1880 render: impl FnOnce(&mut Self),
1881 ) {
1882 self.push_extension_context_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap(), render)
1883 }
1884
1885 pub fn push_extension_item_raw(&mut self, extension_id: ApiExtensionId, payload: ApiExtensionPayload) {
1887 self.display_list.push_extension(extension_id, payload);
1888 }
1889
1890 pub fn push_extension_item<T: serde::Serialize>(&mut self, extension_id: ApiExtensionId, payload: &T) {
1892 self.push_extension_item_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap())
1893 }
1894
1895 pub fn parallel_split(&self) -> ParallelBuilder<Self> {
1904 if self.widget_data.is_some() {
1905 tracing::error!(
1906 "called `parallel_split` inside `{}` and before calling `push_inner`",
1907 self.widget_id
1908 );
1909 }
1910
1911 ParallelBuilder(Some(Self {
1912 render_widgets: self.render_widgets.clone(),
1913 render_update_widgets: self.render_update_widgets.clone(),
1914 frame_id: self.frame_id,
1915 widget_id: self.widget_id,
1916 transform: self.transform,
1917 transform_style: self.transform_style,
1918 default_font_aa: self.default_font_aa,
1919 renderer: self.renderer.clone(),
1920 scale_factor: self.scale_factor,
1921 display_list: self.display_list.parallel_split(),
1922 hit_testable: self.hit_testable,
1923 visible: self.visible,
1924 backface_visible: self.backface_visible,
1925 auto_hit_test: self.auto_hit_test,
1926 hit_clips: self.hit_clips.parallel_split(),
1927 auto_hide_rect: self.auto_hide_rect,
1928 widget_data: None,
1929 child_offset: self.child_offset,
1930 parent_inner_bounds: self.parent_inner_bounds,
1931 perspective: self.perspective,
1932 view_process_has_frame: self.view_process_has_frame,
1933 can_reuse: self.can_reuse,
1934 open_reuse: None,
1935 clear_color: None,
1936 widget_count: 0,
1937 widget_count_offsets: self.widget_count_offsets.parallel_split(),
1938 debug_dot_overlays: vec![],
1939 }))
1940 }
1941
1942 pub fn parallel_fold(&mut self, mut split: ParallelBuilder<Self>) {
1944 let split = split.take();
1945 if split.clear_color.is_some() {
1946 self.clear_color = split.clear_color;
1947 }
1948 self.hit_clips.parallel_fold(split.hit_clips);
1949 self.display_list.parallel_fold(split.display_list);
1950 self.widget_count_offsets
1951 .parallel_fold(split.widget_count_offsets, self.widget_count);
1952
1953 self.widget_count += split.widget_count;
1954 self.debug_dot_overlays.extend(split.debug_dot_overlays);
1955 }
1956
1957 #[expect(clippy::too_many_arguments)]
1959 pub fn with_nested_window(
1960 &mut self,
1961 render_widgets: Arc<RenderUpdates>,
1962 render_update_widgets: Arc<RenderUpdates>,
1963
1964 root_id: WidgetId,
1965 root_bounds: &WidgetBoundsInfo,
1966 info_tree: &WidgetInfoTree,
1967 default_font_aa: FontAntiAliasing,
1968
1969 render: impl FnOnce(&mut Self),
1970 ) {
1971 let mut nested = Self::new(
1973 render_widgets,
1974 render_update_widgets,
1975 self.frame_id,
1976 root_id,
1977 root_bounds,
1978 info_tree,
1979 self.renderer.clone(),
1980 self.scale_factor,
1981 default_font_aa,
1982 );
1983 nested.display_list = self.display_list.parallel_split();
1984 nested.hit_clips = self.hit_clips.parallel_split();
1985 render(&mut nested);
1989
1990 info_tree.root().bounds_info().set_rendered(
1992 Some(WidgetRenderInfo {
1993 visible: nested.visible,
1994 parent_perspective: nested.perspective,
1995 seg_id: 0,
1996 back: 0,
1997 front: nested.widget_count,
1998 }),
1999 info_tree,
2000 );
2001 info_tree.after_render(
2002 nested.frame_id,
2003 nested.scale_factor,
2004 Some(
2005 nested
2006 .renderer
2007 .as_ref()
2008 .and_then(|r| r.generation().ok())
2009 .unwrap_or(ViewProcessGen::INVALID),
2010 ),
2011 Some(nested.widget_count_offsets.clone()),
2012 );
2013
2014 self.hit_clips.parallel_fold(nested.hit_clips);
2016 self.display_list.parallel_fold(nested.display_list);
2017
2018 self.widget_count += nested.widget_count;
2022 self.debug_dot_overlays.extend(nested.debug_dot_overlays);
2023 }
2024
2025 pub fn render_widgets(&self) -> &Arc<RenderUpdates> {
2027 &self.render_widgets
2028 }
2029
2030 pub fn render_update_widgets(&self) -> &Arc<RenderUpdates> {
2032 &self.render_update_widgets
2033 }
2034
2035 pub fn finalize(self, info_tree: &WidgetInfoTree) -> BuiltFrame {
2037 info_tree.root().bounds_info().set_rendered(
2038 Some(WidgetRenderInfo {
2039 visible: self.visible,
2040 parent_perspective: self.perspective,
2041 seg_id: 0,
2042 back: 0,
2043 front: self.widget_count,
2044 }),
2045 info_tree,
2046 );
2047
2048 info_tree.after_render(
2049 self.frame_id,
2050 self.scale_factor,
2051 Some(
2052 self.renderer
2053 .as_ref()
2054 .and_then(|r| r.generation().ok())
2055 .unwrap_or(ViewProcessGen::INVALID),
2056 ),
2057 Some(self.widget_count_offsets),
2058 );
2059
2060 let display_list = self.display_list.finalize();
2061
2062 let clear_color = self.clear_color.unwrap_or_default();
2063
2064 BuiltFrame { display_list, clear_color }
2065 }
2066}
2067
2068pub struct ClipBuilder<'a> {
2072 builder: &'a mut FrameBuilder,
2073 render_count: usize,
2074 hit_test_count: usize,
2075}
2076impl ClipBuilder<'_> {
2077 pub fn push_clip_rect(&mut self, clip_rect: PxRect, clip_out: bool, hit_test: bool) {
2086 if self.builder.visible {
2087 self.builder.display_list.push_clip_rect(clip_rect, clip_out);
2088 self.render_count += 1;
2089 }
2090
2091 if hit_test || self.builder.auto_hit_test {
2092 self.builder.hit_clips.push_clip_rect(clip_rect.to_box2d(), clip_out);
2093 self.hit_test_count += 1;
2094 }
2095 }
2096
2097 pub fn push_clip_rounded_rect(&mut self, clip_rect: PxRect, corners: PxCornerRadius, clip_out: bool, hit_test: bool) {
2106 if self.builder.visible {
2107 self.builder.display_list.push_clip_rounded_rect(clip_rect, corners, clip_out);
2108 self.render_count += 1;
2109 }
2110
2111 if hit_test || self.builder.auto_hit_test {
2112 self.builder
2113 .hit_clips
2114 .push_clip_rounded_rect(clip_rect.to_box2d(), corners, clip_out);
2115 self.hit_test_count += 1;
2116 }
2117 }
2118}
2119
2120pub struct HitTestClipBuilder<'a> {
2124 hit_clips: &'a mut HitTestClips,
2125 count: usize,
2126}
2127impl HitTestClipBuilder<'_> {
2128 pub fn push_clip_rect(&mut self, rect: PxRect, clip_out: bool) {
2132 self.hit_clips.push_clip_rect(rect.to_box2d(), clip_out);
2133 self.count += 1;
2134 }
2135
2136 pub fn push_clip_rounded_rect(&mut self, rect: PxRect, corners: PxCornerRadius, clip_out: bool) {
2140 self.hit_clips.push_clip_rounded_rect(rect.to_box2d(), corners, clip_out);
2141 self.count += 1;
2142 }
2143
2144 pub fn push_clip_ellipse(&mut self, center: PxPoint, radii: PxSize, clip_out: bool) {
2148 self.hit_clips.push_clip_ellipse(center, radii, clip_out);
2149 self.count += 1;
2150 }
2151}
2152
2153pub struct HitTestBuilder<'a> {
2157 hit_clips: &'a mut HitTestClips,
2158 is_hit_testable: bool,
2159}
2160impl HitTestBuilder<'_> {
2161 pub fn is_hit_testable(&self) -> bool {
2163 self.is_hit_testable
2164 }
2165
2166 pub fn push_rect(&mut self, rect: PxRect) {
2168 if self.is_hit_testable && rect.size != PxSize::zero() {
2169 self.hit_clips.push_rect(rect.to_box2d());
2170 }
2171 }
2172
2173 pub fn push_rounded_rect(&mut self, rect: PxRect, corners: PxCornerRadius) {
2175 if self.is_hit_testable && rect.size != PxSize::zero() {
2176 self.hit_clips.push_rounded_rect(rect.to_box2d(), corners);
2177 }
2178 }
2179
2180 pub fn push_ellipse(&mut self, center: PxPoint, radii: PxSize) {
2182 if self.is_hit_testable && radii != PxSize::zero() {
2183 self.hit_clips.push_ellipse(center, radii);
2184 }
2185 }
2186
2187 pub fn push_clip_rect(&mut self, rect: PxRect, clip_out: bool, inner_hit_test: impl FnOnce(&mut Self)) {
2189 if !self.is_hit_testable {
2190 return;
2191 }
2192
2193 self.hit_clips.push_clip_rect(rect.to_box2d(), clip_out);
2194
2195 inner_hit_test(self);
2196
2197 self.hit_clips.pop_clip();
2198 }
2199
2200 pub fn push_clip_rounded_rect(
2202 &mut self,
2203 rect: PxRect,
2204 corners: PxCornerRadius,
2205 clip_out: bool,
2206 inner_hit_test: impl FnOnce(&mut Self),
2207 ) {
2208 self.push_clips(move |c| c.push_clip_rounded_rect(rect, corners, clip_out), inner_hit_test);
2209 }
2210
2211 pub fn push_clip_ellipse(&mut self, center: PxPoint, radii: PxSize, clip_out: bool, inner_hit_test: impl FnOnce(&mut Self)) {
2213 self.push_clips(move |c| c.push_clip_ellipse(center, radii, clip_out), inner_hit_test);
2214 }
2215
2216 pub fn push_clips(&mut self, clips: impl FnOnce(&mut HitTestClipBuilder), inner_hit_test: impl FnOnce(&mut Self)) {
2218 if !self.is_hit_testable {
2219 return;
2220 }
2221
2222 let mut count = {
2223 let mut builder = HitTestClipBuilder {
2224 hit_clips: &mut *self.hit_clips,
2225 count: 0,
2226 };
2227 clips(&mut builder);
2228 builder.count
2229 };
2230
2231 inner_hit_test(self);
2232
2233 while count > 0 {
2234 count -= 1;
2235 self.hit_clips.pop_clip();
2236 }
2237 }
2238
2239 pub fn push_transform(&mut self, transform: PxTransform, inner_hit_test: impl FnOnce(&mut Self)) {
2241 if !self.is_hit_testable {
2242 return;
2243 }
2244
2245 self.hit_clips.push_transform(FrameValue::Value(transform));
2246
2247 inner_hit_test(self);
2248
2249 self.hit_clips.pop_transform();
2250 }
2251
2252 pub fn push_border(&mut self, bounds: PxRect, widths: PxSideOffsets, corners: PxCornerRadius) {
2254 if !self.is_hit_testable {
2255 return;
2256 }
2257
2258 let bounds = bounds.to_box2d();
2259 let mut inner_bounds = bounds;
2260 inner_bounds.min.x += widths.left;
2261 inner_bounds.min.y += widths.top;
2262 inner_bounds.max.x -= widths.right;
2263 inner_bounds.max.y -= widths.bottom;
2264
2265 if inner_bounds.is_negative() {
2266 self.hit_clips.push_rounded_rect(bounds, corners);
2267 } else if corners == PxCornerRadius::zero() {
2268 self.hit_clips.push_clip_rect(inner_bounds, true);
2269 self.hit_clips.push_rect(bounds);
2270 self.hit_clips.pop_clip();
2271 } else {
2272 let inner_radii = corners.deflate(widths);
2273
2274 self.hit_clips.push_clip_rounded_rect(inner_bounds, inner_radii, true);
2275 self.hit_clips.push_rounded_rect(bounds, corners);
2276 self.hit_clips.pop_clip();
2277 }
2278 }
2279}
2280
2281#[non_exhaustive]
2283pub struct BuiltFrame {
2284 pub display_list: DisplayList,
2286 pub clear_color: Rgba,
2288}
2289
2290enum RenderLineCommand {
2291 Line(zng_view_api::LineStyle),
2292 Border(zng_view_api::BorderStyle),
2293}
2294impl border::LineStyle {
2295 fn render_command(self) -> RenderLineCommand {
2296 use RenderLineCommand::*;
2297 use border::LineStyle as LS;
2298 match self {
2299 LS::Solid => Line(zng_view_api::LineStyle::Solid),
2300 LS::Double => Border(zng_view_api::BorderStyle::Double),
2301 LS::Dotted => Line(zng_view_api::LineStyle::Dotted),
2302 LS::Dashed => Line(zng_view_api::LineStyle::Dashed),
2303 LS::Groove => Border(zng_view_api::BorderStyle::Groove),
2304 LS::Ridge => Border(zng_view_api::BorderStyle::Ridge),
2305 LS::Wavy(thickness) => Line(zng_view_api::LineStyle::Wavy(thickness)),
2306 LS::Hidden => Border(zng_view_api::BorderStyle::Hidden),
2307 }
2308 }
2309}
2310
2311pub struct FrameUpdate {
2318 render_update_widgets: Arc<RenderUpdates>,
2319
2320 transforms: Vec<FrameValueUpdate<PxTransform>>,
2321 floats: Vec<FrameValueUpdate<f32>>,
2322 colors: Vec<FrameValueUpdate<Rgba>>,
2323
2324 extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
2325
2326 current_clear_color: Rgba,
2327 clear_color: Option<Rgba>,
2328 frame_id: FrameId,
2329
2330 widget_id: WidgetId,
2331 transform: PxTransform,
2332 parent_child_offset: PxVector,
2333 perspective: Option<(f32, PxPoint)>,
2334 inner_transform: Option<PxTransform>,
2335 child_offset: PxVector,
2336 can_reuse_widget: bool,
2337 widget_bounds: WidgetBoundsInfo,
2338 parent_inner_bounds: Option<PxRect>,
2339
2340 auto_hit_test: bool,
2341 visible: bool,
2342}
2343impl FrameUpdate {
2344 pub fn new(
2352 render_update_widgets: Arc<RenderUpdates>,
2353 frame_id: FrameId,
2354 root_id: WidgetId,
2355 root_bounds: WidgetBoundsInfo,
2356 clear_color: Rgba,
2357 ) -> Self {
2358 FrameUpdate {
2359 render_update_widgets,
2360 widget_id: root_id,
2361 widget_bounds: root_bounds,
2362 transforms: vec![],
2363 floats: vec![],
2364 colors: vec![],
2365 extensions: vec![],
2366 clear_color: None,
2367 frame_id,
2368 current_clear_color: clear_color,
2369
2370 transform: PxTransform::identity(),
2371 perspective: None,
2372 parent_child_offset: PxVector::zero(),
2373 inner_transform: Some(PxTransform::identity()),
2374 child_offset: PxVector::zero(),
2375 can_reuse_widget: true,
2376
2377 auto_hit_test: false,
2378 parent_inner_bounds: None,
2379 visible: true,
2380 }
2381 }
2382
2383 pub fn frame_id(&self) -> FrameId {
2385 self.frame_id
2386 }
2387
2388 pub fn is_outer(&self) -> bool {
2395 self.inner_transform.is_some()
2396 }
2397
2398 pub fn transform(&self) -> &PxTransform {
2400 &self.transform
2401 }
2402
2403 pub fn set_clear_color(&mut self, color: Rgba) {
2405 if self.visible {
2406 self.clear_color = Some(color);
2407 }
2408 }
2409
2410 pub fn auto_hit_test(&self) -> bool {
2412 self.auto_hit_test
2413 }
2414 pub fn with_auto_hit_test(&mut self, auto_hit_test: bool, render_update: impl FnOnce(&mut Self)) {
2418 let prev = mem::replace(&mut self.auto_hit_test, auto_hit_test);
2419 render_update(self);
2420 self.auto_hit_test = prev;
2421 }
2422
2423 pub fn is_visible(&self) -> bool {
2425 self.visible
2426 }
2427
2428 pub fn hidden(&mut self, update: impl FnOnce(&mut Self)) {
2435 let parent_visible = mem::replace(&mut self.visible, false);
2436 update(self);
2437 self.visible = parent_visible;
2438 }
2439
2440 pub fn update_transform(&mut self, new_value: FrameValueUpdate<PxTransform>, hit_test: bool) {
2448 if self.visible {
2449 self.transforms.push(new_value);
2450 }
2451
2452 if hit_test || self.auto_hit_test {
2453 self.widget_bounds.update_hit_test_transform(new_value);
2454 }
2455 }
2456
2457 pub fn update_transform_opt(&mut self, new_value: Option<FrameValueUpdate<PxTransform>>, hit_test: bool) {
2459 if let Some(value) = new_value {
2460 self.update_transform(value, hit_test)
2461 }
2462 }
2463
2464 pub fn with_transform(&mut self, new_value: FrameValueUpdate<PxTransform>, hit_test: bool, render_update: impl FnOnce(&mut Self)) {
2473 self.with_transform_value(&new_value.value, render_update);
2474 self.update_transform(new_value, hit_test);
2475 }
2476
2477 pub fn with_transform_opt(
2481 &mut self,
2482 new_value: Option<FrameValueUpdate<PxTransform>>,
2483 hit_test: bool,
2484 render_update: impl FnOnce(&mut Self),
2485 ) {
2486 match new_value {
2487 Some(value) => self.with_transform(value, hit_test, render_update),
2488 None => render_update(self),
2489 }
2490 }
2491
2492 pub fn with_child(&mut self, offset: PxVector, render_update: impl FnOnce(&mut Self)) {
2496 self.child_offset = offset;
2497 render_update(self);
2498 self.child_offset = PxVector::zero();
2499 }
2500
2501 pub fn with_transform_value(&mut self, value: &PxTransform, render_update: impl FnOnce(&mut Self)) {
2507 let parent_transform = self.transform;
2508 self.transform = value.then(&parent_transform);
2509
2510 render_update(self);
2511 self.transform = parent_transform;
2512 }
2513
2514 pub fn with_inner_transform(&mut self, transform: &PxTransform, render_update: impl FnOnce(&mut Self)) {
2520 if let Some(inner_transform) = &mut self.inner_transform {
2521 let parent = *inner_transform;
2522 *inner_transform = inner_transform.then(transform);
2523
2524 render_update(self);
2525
2526 if let Some(inner_transform) = &mut self.inner_transform {
2527 *inner_transform = parent;
2528 }
2529 } else {
2530 tracing::error!("called `with_inner_transform` inside inner context of `{}`", self.widget_id);
2531 render_update(self);
2532 }
2533 }
2534
2535 pub fn can_reuse_widget(&self) -> bool {
2539 self.can_reuse_widget
2540 }
2541
2542 pub fn with_no_reuse(&mut self, render_update: impl FnOnce(&mut Self)) {
2546 let prev_can_reuse = self.can_reuse_widget;
2547 self.can_reuse_widget = false;
2548 render_update(self);
2549 self.can_reuse_widget = prev_can_reuse;
2550 }
2551
2552 pub fn update_widget(&mut self, render_update: impl FnOnce(&mut Self)) {
2559 let wgt_info = WIDGET.info();
2560 let id = wgt_info.id();
2561
2562 #[cfg(debug_assertions)]
2563 if self.inner_transform.is_some() && wgt_info.parent().is_some() {
2564 tracing::error!(
2565 "called `update_widget` for `{}` without calling `update_inner` for the parent `{}`",
2566 WIDGET.trace_id(),
2567 self.widget_id
2568 );
2569 }
2570
2571 let bounds = wgt_info.bounds_info();
2572 if bounds.is_collapsed() {
2573 let _ = WIDGET.take_update(UpdateFlags::LAYOUT | UpdateFlags::RENDER | UpdateFlags::RENDER_UPDATE);
2574 return;
2575 } else {
2576 #[cfg(debug_assertions)]
2577 if WIDGET.pending_update().contains(UpdateFlags::LAYOUT) {
2578 tracing::error!("called `update_widget` for `{}` with pending layout", WIDGET.trace_id());
2579 }
2580 }
2581
2582 let tree = wgt_info.tree();
2583
2584 let parent_can_reuse = self.can_reuse_widget;
2585 let parent_perspective = mem::replace(&mut self.perspective, wgt_info.perspective());
2586 let parent_bounds = mem::replace(&mut self.widget_bounds, bounds.clone());
2587
2588 if let Some((_, o)) = &mut self.perspective {
2589 *o -= self.child_offset;
2590 }
2591
2592 let render_info = bounds.render_info();
2593 if let Some(i) = &render_info
2594 && i.parent_perspective != self.perspective
2595 {
2596 self.can_reuse_widget = false;
2597 }
2598
2599 let outer_transform = PxTransform::from(self.child_offset).then(&self.transform);
2600
2601 if !WIDGET.take_update(UpdateFlags::RENDER_UPDATE)
2602 && self.can_reuse_widget
2603 && !self.render_update_widgets.delivery_list().enter_widget(id)
2604 && bounds.parent_child_offset() == self.child_offset
2605 {
2606 let _span = tracing::trace_span!("reuse-descendants", id=?self.widget_id).entered();
2607
2608 let prev_outer = bounds.outer_transform();
2609 if prev_outer != outer_transform {
2610 if let Some(undo_prev) = prev_outer.inverse() {
2611 let patch = undo_prev.then(&outer_transform);
2612
2613 let update = |info: WidgetInfo| {
2614 let bounds = info.bounds_info();
2615 bounds.set_outer_transform(bounds.outer_transform().then(&patch), tree);
2616 bounds.set_inner_transform(
2617 bounds.inner_transform().then(&patch),
2618 tree,
2619 info.id(),
2620 info.parent().map(|p| p.inner_bounds()),
2621 );
2622 };
2623 let targets = tree.get(id).unwrap().self_and_descendants();
2624 if PARALLEL_VAR.get().contains(Parallel::RENDER) {
2625 targets.par_bridge().for_each(update);
2626 } else {
2627 targets.for_each(update);
2628 }
2629
2630 return; }
2632 } else {
2633 return; }
2635
2636 self.can_reuse_widget = false;
2638 }
2639
2640 bounds.set_parent_child_offset(self.child_offset);
2641 bounds.set_outer_transform(outer_transform, tree);
2642 self.parent_child_offset = mem::take(&mut self.child_offset);
2643 self.inner_transform = Some(PxTransform::identity());
2644 let parent_id = self.widget_id;
2645 self.widget_id = id;
2646
2647 render_update(self);
2648
2649 if let Some(mut i) = render_info {
2650 i.parent_perspective = self.perspective;
2651 bounds.set_rendered(Some(i), tree);
2652 }
2653 self.parent_child_offset = PxVector::zero();
2654 self.inner_transform = None;
2655 self.widget_id = parent_id;
2656 self.can_reuse_widget = parent_can_reuse;
2657 self.perspective = parent_perspective;
2658 self.widget_bounds = parent_bounds;
2659 }
2660
2661 pub fn reuse_widget(&mut self) {
2666 if self.inner_transform.is_some() {
2667 tracing::error!(
2668 "called `reuse_widget` for `{}` without calling `update_inner` for the parent `{}`",
2669 WIDGET.trace_id(),
2670 self.widget_id
2671 );
2672 }
2673 }
2674
2675 pub fn update_inner(
2679 &mut self,
2680 layout_translation_key: FrameValueKey<PxTransform>,
2681 layout_translation_animating: bool,
2682 render_update: impl FnOnce(&mut Self),
2683 ) {
2684 let id = WIDGET.id();
2685 if let Some(mut inner_transform) = self.inner_transform.take() {
2686 let bounds = WIDGET.bounds();
2687 let tree = WINDOW.info();
2688
2689 let inner_offset = bounds.inner_offset();
2690 if let Some((p, mut o)) = self.perspective {
2691 o -= inner_offset;
2692 let x = o.x.0 as f32;
2693 let y = o.y.0 as f32;
2694 let p = PxTransform::translation(-x, -y)
2695 .then(&PxTransform::perspective(p))
2696 .then_translate(euclid::vec2(x, y));
2697 inner_transform = inner_transform.then(&p);
2698 }
2699 let inner_transform = inner_transform.then_translate((self.parent_child_offset + inner_offset).cast());
2700 self.update_transform(layout_translation_key.update(inner_transform, layout_translation_animating), false);
2701 let parent_transform = self.transform;
2702
2703 self.transform = inner_transform.then(&parent_transform);
2704
2705 bounds.set_inner_transform(self.transform, &tree, id, self.parent_inner_bounds);
2706 let parent_inner_bounds = self.parent_inner_bounds.replace(bounds.inner_bounds());
2707
2708 render_update(self);
2709
2710 self.transform = parent_transform;
2711 self.parent_inner_bounds = parent_inner_bounds;
2712 } else {
2713 tracing::error!("called `update_inner` more then once for `{}`", id);
2714 render_update(self)
2715 }
2716 }
2717
2718 pub fn update_f32(&mut self, new_value: FrameValueUpdate<f32>) {
2720 if self.visible {
2721 self.floats.push(new_value);
2722 }
2723 }
2724
2725 pub fn update_f32_opt(&mut self, new_value: Option<FrameValueUpdate<f32>>) {
2727 if let Some(value) = new_value {
2728 self.update_f32(value)
2729 }
2730 }
2731
2732 pub fn update_color(&mut self, new_value: FrameValueUpdate<Rgba>) {
2736 if self.visible {
2737 self.colors.push(new_value)
2738 }
2739 }
2740
2741 pub fn update_color_opt(&mut self, new_value: Option<FrameValueUpdate<Rgba>>) {
2743 if let Some(value) = new_value {
2744 self.update_color(value)
2745 }
2746 }
2747
2748 pub fn update_extension_raw(&mut self, extension_id: ApiExtensionId, extension_payload: ApiExtensionPayload) {
2750 self.extensions.push((extension_id, extension_payload))
2751 }
2752
2753 pub fn update_extension<T: serde::Serialize>(&mut self, extension_id: ApiExtensionId, payload: &T) {
2755 self.update_extension_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap())
2756 }
2757
2758 pub fn parallel_split(&self) -> ParallelBuilder<Self> {
2764 if self.inner_transform.is_some() && WIDGET.parent_id().is_some() {
2765 tracing::error!(
2766 "called `parallel_split` inside `{}` and before calling `update_inner`",
2767 self.widget_id
2768 );
2769 }
2770
2771 ParallelBuilder(Some(Self {
2772 render_update_widgets: self.render_update_widgets.clone(),
2773 current_clear_color: self.current_clear_color,
2774 frame_id: self.frame_id,
2775
2776 transforms: vec![],
2777 floats: vec![],
2778 colors: vec![],
2779 extensions: vec![],
2780 clear_color: None,
2781
2782 widget_id: self.widget_id,
2783 transform: self.transform,
2784 perspective: self.perspective,
2785 parent_child_offset: self.parent_child_offset,
2786 inner_transform: self.inner_transform,
2787 child_offset: self.child_offset,
2788 can_reuse_widget: self.can_reuse_widget,
2789 widget_bounds: self.widget_bounds.clone(),
2790 parent_inner_bounds: self.parent_inner_bounds,
2791 auto_hit_test: self.auto_hit_test,
2792 visible: self.visible,
2793 }))
2794 }
2795
2796 pub fn parallel_fold(&mut self, mut split: ParallelBuilder<Self>) {
2798 let mut split = split.take();
2799
2800 debug_assert_eq!(self.frame_id, split.frame_id);
2801 debug_assert_eq!(self.widget_id, split.widget_id);
2802
2803 fn take_or_append<T>(t: &mut Vec<T>, s: &mut Vec<T>) {
2804 if t.is_empty() {
2805 *t = mem::take(s);
2806 } else {
2807 t.append(s)
2808 }
2809 }
2810
2811 take_or_append(&mut self.transforms, &mut split.transforms);
2812 take_or_append(&mut self.floats, &mut split.floats);
2813 take_or_append(&mut self.colors, &mut split.colors);
2814 take_or_append(&mut self.extensions, &mut split.extensions);
2815
2816 if let Some(c) = self.clear_color.take() {
2817 self.clear_color = Some(c);
2818 }
2819 }
2820
2821 pub fn with_nested_window(
2823 &mut self,
2824 render_update_widgets: Arc<RenderUpdates>,
2825 root_id: WidgetId,
2826 root_bounds: WidgetBoundsInfo,
2827 update: impl FnOnce(&mut Self),
2828 ) {
2829 let mut nested = Self::new(render_update_widgets, self.frame_id, root_id, root_bounds, self.current_clear_color);
2830
2831 update(&mut nested);
2832
2833 fn take_or_append<T>(t: &mut Vec<T>, s: &mut Vec<T>) {
2835 if t.is_empty() {
2836 *t = mem::take(s);
2837 } else {
2838 t.append(s)
2839 }
2840 }
2841 take_or_append(&mut self.transforms, &mut nested.transforms);
2842 take_or_append(&mut self.floats, &mut nested.floats);
2843 take_or_append(&mut self.colors, &mut nested.colors);
2844 take_or_append(&mut self.extensions, &mut nested.extensions);
2845 }
2846
2847 pub fn render_update_widgets(&self) -> &Arc<RenderUpdates> {
2849 &self.render_update_widgets
2850 }
2851
2852 pub fn finalize(mut self, info_tree: &WidgetInfoTree) -> BuiltFrameUpdate {
2856 info_tree.after_render_update(self.frame_id);
2857
2858 if self.clear_color == Some(self.current_clear_color) {
2859 self.clear_color = None;
2860 }
2861
2862 BuiltFrameUpdate {
2863 clear_color: self.clear_color,
2864 transforms: self.transforms,
2865 floats: self.floats,
2866 colors: self.colors,
2867 extensions: self.extensions,
2868 }
2869 }
2870}
2871
2872#[non_exhaustive]
2874pub struct BuiltFrameUpdate {
2875 pub transforms: Vec<FrameValueUpdate<PxTransform>>,
2877 pub floats: Vec<FrameValueUpdate<f32>>,
2879 pub colors: Vec<FrameValueUpdate<Rgba>>,
2881 pub clear_color: Option<Rgba>,
2883 pub extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
2885}
2886
2887unique_id_32! {
2888 #[derive(Debug)]
2889 struct FrameBindingKeyId;
2890}
2891impl_unique_id_bytemuck!(FrameBindingKeyId);
2892
2893unique_id_32! {
2894 #[derive(Debug)]
2896 pub struct SpatialFrameId;
2897}
2898impl_unique_id_bytemuck!(SpatialFrameId);
2899
2900#[derive(Clone, Copy, PartialEq, Eq)]
2901enum ReferenceFrameIdInner {
2902 Unique(SpatialFrameId),
2903 UniqueIndex(SpatialFrameId, u32),
2904 Widget(WidgetId),
2905 WidgetIndex(WidgetId, u32),
2906 FrameValue(FrameValueKey<PxTransform>),
2907 FrameValueIndex(FrameValueKey<PxTransform>, u32),
2908}
2909impl ReferenceFrameIdInner {
2910 const _RESERVED: u64 = 1 << 63; const UNIQUE: u64 = 1 << 62;
2912 const WIDGET: u64 = 1 << 61;
2913 const FRAME_VALUE: u64 = 1 << 60;
2914}
2915impl From<ReferenceFrameIdInner> for RenderReferenceFrameId {
2916 fn from(value: ReferenceFrameIdInner) -> Self {
2917 match value {
2918 ReferenceFrameIdInner::UniqueIndex(id, index) => {
2919 RenderReferenceFrameId(id.get() as u64, index as u64 | ReferenceFrameIdInner::UNIQUE)
2920 }
2921 ReferenceFrameIdInner::WidgetIndex(id, index) => RenderReferenceFrameId(id.get(), index as u64 | ReferenceFrameIdInner::WIDGET),
2922 ReferenceFrameIdInner::FrameValue(key) => {
2923 RenderReferenceFrameId(((key.id.get() as u64) << 32) | u32::MAX as u64, ReferenceFrameIdInner::FRAME_VALUE)
2924 }
2925 ReferenceFrameIdInner::FrameValueIndex(key, index) => {
2926 RenderReferenceFrameId(((key.id.get() as u64) << 32) | index as u64, ReferenceFrameIdInner::FRAME_VALUE)
2927 }
2928 ReferenceFrameIdInner::Unique(id) => {
2929 RenderReferenceFrameId(id.get() as u64, (u32::MAX as u64 + 1) | ReferenceFrameIdInner::UNIQUE)
2930 }
2931 ReferenceFrameIdInner::Widget(id) => RenderReferenceFrameId(id.get(), (u32::MAX as u64 + 1) | ReferenceFrameIdInner::WIDGET),
2932 }
2933 }
2934}
2935
2936#[derive(Clone, Copy, PartialEq, Eq)]
2943pub struct ReferenceFrameId(ReferenceFrameIdInner);
2944impl ReferenceFrameId {
2945 fn from_widget(widget_id: WidgetId) -> Self {
2949 Self(ReferenceFrameIdInner::Widget(widget_id))
2950 }
2951
2952 pub fn from_widget_child(parent_id: WidgetId, child_index: u32) -> Self {
2956 Self(ReferenceFrameIdInner::WidgetIndex(parent_id, child_index))
2957 }
2958
2959 pub fn from_unique(id: SpatialFrameId) -> Self {
2961 Self(ReferenceFrameIdInner::Unique(id))
2962 }
2963
2964 pub fn from_unique_child(id: SpatialFrameId, child_index: u32) -> Self {
2966 Self(ReferenceFrameIdInner::UniqueIndex(id, child_index))
2967 }
2968
2969 pub fn from_frame_value(frame_value_key: FrameValueKey<PxTransform>) -> Self {
2971 Self(ReferenceFrameIdInner::FrameValue(frame_value_key))
2972 }
2973
2974 pub fn from_frame_value_child(frame_value_key: FrameValueKey<PxTransform>, child_index: u32) -> Self {
2976 Self(ReferenceFrameIdInner::FrameValueIndex(frame_value_key, child_index))
2977 }
2978}
2979impl From<ReferenceFrameId> for RenderReferenceFrameId {
2980 fn from(value: ReferenceFrameId) -> Self {
2981 value.0.into()
2982 }
2983}
2984impl From<FrameValueKey<PxTransform>> for ReferenceFrameId {
2985 fn from(value: FrameValueKey<PxTransform>) -> Self {
2986 Self::from_frame_value(value)
2987 }
2988}
2989impl From<SpatialFrameId> for ReferenceFrameId {
2990 fn from(id: SpatialFrameId) -> Self {
2991 Self::from_unique(id)
2992 }
2993}
2994impl From<(SpatialFrameId, u32)> for ReferenceFrameId {
2995 fn from((id, index): (SpatialFrameId, u32)) -> Self {
2996 Self::from_unique_child(id, index)
2997 }
2998}
2999impl From<(WidgetId, u32)> for ReferenceFrameId {
3000 fn from((id, index): (WidgetId, u32)) -> Self {
3001 Self::from_widget_child(id, index)
3002 }
3003}
3004impl From<(FrameValueKey<PxTransform>, u32)> for ReferenceFrameId {
3005 fn from((key, index): (FrameValueKey<PxTransform>, u32)) -> Self {
3006 Self::from_frame_value_child(key, index)
3007 }
3008}
3009
3010#[derive(Debug)]
3012pub struct FrameValueKey<T> {
3013 id: FrameBindingKeyId,
3014 _type: PhantomData<T>,
3015}
3016impl<T> PartialEq for FrameValueKey<T> {
3017 fn eq(&self, other: &Self) -> bool {
3018 self.id == other.id
3019 }
3020}
3021impl<T> Eq for FrameValueKey<T> {}
3022impl<T> Clone for FrameValueKey<T> {
3023 fn clone(&self) -> Self {
3024 *self
3025 }
3026}
3027impl<T> Copy for FrameValueKey<T> {}
3028impl<T> FrameValueKey<T> {
3029 pub fn new_unique() -> Self {
3031 FrameValueKey {
3032 id: FrameBindingKeyId::new_unique(),
3033 _type: PhantomData,
3034 }
3035 }
3036
3037 pub fn to_wr(self) -> zng_view_api::display_list::FrameValueId {
3039 Self::to_wr_child(self, u32::MAX)
3040 }
3041
3042 pub fn to_wr_child(self, child_index: u32) -> zng_view_api::display_list::FrameValueId {
3044 zng_view_api::display_list::FrameValueId::from_raw(((self.id.get() as u64) << 32) | child_index as u64)
3045 }
3046
3047 pub fn bind(self, value: T, animating: bool) -> FrameValue<T> {
3053 self.bind_child(u32::MAX, value, animating)
3054 }
3055
3056 pub fn bind_child(self, child_index: u32, value: T, animating: bool) -> FrameValue<T> {
3060 FrameValue::Bind {
3061 id: self.to_wr_child(child_index),
3062 value,
3063 animating,
3064 }
3065 }
3066
3067 pub fn update(self, value: T, animating: bool) -> FrameValueUpdate<T> {
3069 self.update_child(u32::MAX, value, animating)
3070 }
3071
3072 pub fn update_child(self, child_index: u32, value: T, animating: bool) -> FrameValueUpdate<T> {
3076 FrameValueUpdate::new(self.to_wr_child(child_index), value, animating)
3077 }
3078
3079 pub fn bind_var<VT: VarValue>(self, var: &Var<VT>, map: impl FnOnce(&VT) -> T) -> FrameValue<T> {
3083 self.bind_var_child(u32::MAX, var, map)
3084 }
3085
3086 pub fn bind_var_child<VT: VarValue>(self, child_index: u32, var: &Var<VT>, map: impl FnOnce(&VT) -> T) -> FrameValue<T> {
3090 if var.capabilities().contains(VarCapability::NEW) {
3091 FrameValue::Bind {
3092 id: self.to_wr_child(child_index),
3093 value: var.with(map),
3094 animating: var.is_animating(),
3095 }
3096 } else {
3097 FrameValue::Value(var.with(map))
3098 }
3099 }
3100
3101 pub fn bind_var_mapped<VT: VarValue>(&self, var: &Var<VT>, value: T) -> FrameValue<T> {
3103 self.bind_var_mapped_child(u32::MAX, var, value)
3104 }
3105
3106 pub fn bind_var_mapped_child<VT: VarValue>(&self, child_index: u32, var: &Var<VT>, value: T) -> FrameValue<T> {
3110 if var.capabilities().contains(VarCapability::NEW) {
3111 FrameValue::Bind {
3112 id: self.to_wr_child(child_index),
3113 value,
3114 animating: var.is_animating(),
3115 }
3116 } else {
3117 FrameValue::Value(value)
3118 }
3119 }
3120
3121 pub fn update_var<VT: VarValue>(self, var: &Var<VT>, map: impl FnOnce(&VT) -> T) -> Option<FrameValueUpdate<T>> {
3123 self.update_var_child(u32::MAX, var, map)
3124 }
3125
3126 pub fn update_var_child<VT: VarValue>(
3130 self,
3131 child_index: u32,
3132 var: &Var<VT>,
3133 map: impl FnOnce(&VT) -> T,
3134 ) -> Option<FrameValueUpdate<T>> {
3135 if var.capabilities().contains(VarCapability::NEW) {
3136 Some(FrameValueUpdate::new(
3137 self.to_wr_child(child_index),
3138 var.with(map),
3139 var.is_animating(),
3140 ))
3141 } else {
3142 None
3143 }
3144 }
3145
3146 pub fn update_var_mapped<VT: VarValue>(self, var: &Var<VT>, value: T) -> Option<FrameValueUpdate<T>> {
3148 self.update_var_mapped_child(u32::MAX, var, value)
3149 }
3150
3151 pub fn update_var_mapped_child<VT: VarValue>(self, child_index: u32, var: &Var<VT>, value: T) -> Option<FrameValueUpdate<T>> {
3155 if var.capabilities().contains(VarCapability::NEW) {
3156 Some(FrameValueUpdate::new(self.to_wr_child(child_index), value, var.is_animating()))
3157 } else {
3158 None
3159 }
3160 }
3161}
3162
3163bitflags::bitflags! {
3164 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3166 #[serde(transparent)]
3167 pub struct FontSynthesis: u8 {
3168 const DISABLED = 0;
3171 const BOLD = 1;
3173 const OBLIQUE = 2;
3175 const ENABLED = Self::BOLD.bits() | Self::OBLIQUE.bits();
3177 }
3178}
3179impl Default for FontSynthesis {
3180 fn default() -> Self {
3182 FontSynthesis::ENABLED
3183 }
3184}
3185impl_from_and_into_var! {
3186 fn from(enabled: bool) -> FontSynthesis {
3188 if enabled { FontSynthesis::ENABLED } else { FontSynthesis::DISABLED }
3189 }
3190}