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 size(&self) -> PxSize;
72}
73
74macro_rules! expect_inner {
75 ($self:ident.$fn_name:ident) => {
76 if $self.is_outer() {
77 tracing::error!("called `{}` in outer context of `{}`", stringify!($fn_name), $self.widget_id);
78 }
79 };
80}
81
82macro_rules! warn_empty {
83 ($self:ident.$fn_name:ident($rect:tt)) => {
84 #[cfg(debug_assertions)]
85 if $rect.is_empty() {
86 tracing::warn!(
87 "called `{}` with empty `{:?}` in `{:?}`",
88 stringify!($fn_name),
89 $rect,
90 $self.widget_id
91 )
92 }
93 };
94}
95
96struct WidgetData {
97 parent_child_offset: PxVector,
98 inner_is_set: bool, inner_transform: PxTransform,
100 filter: RenderFilter,
101 blend: MixBlendMode,
102 backdrop_filter: RenderFilter,
103}
104
105pub struct FrameBuilder {
107 render_widgets: Arc<RenderUpdates>,
108 render_update_widgets: Arc<RenderUpdates>,
109
110 frame_id: FrameId,
111 widget_id: WidgetId,
112 transform: PxTransform,
113 transform_style: TransformStyle,
114
115 default_font_aa: FontAntiAliasing,
116
117 renderer: Option<ViewRenderer>,
118
119 scale_factor: Factor,
120
121 display_list: DisplayListBuilder,
122
123 hit_testable: bool,
124 visible: bool,
125 backface_visible: bool,
126 auto_hit_test: bool,
127 hit_clips: HitTestClips,
128
129 perspective: Option<(f32, PxPoint)>,
130
131 auto_hide_rect: PxRect,
132 widget_data: Option<WidgetData>,
133 child_offset: PxVector,
134 parent_inner_bounds: Option<PxRect>,
135
136 view_process_has_frame: bool,
137 can_reuse: bool,
138 open_reuse: Option<ReuseStart>,
139
140 clear_color: Option<Rgba>,
141
142 widget_count: usize,
143 widget_count_offsets: ParallelSegmentOffsets,
144
145 debug_dot_overlays: Vec<(PxPoint, Rgba)>,
146}
147impl FrameBuilder {
148 #[expect(clippy::too_many_arguments)]
162 pub fn new(
163 render_widgets: Arc<RenderUpdates>,
164 render_update_widgets: Arc<RenderUpdates>,
165 frame_id: FrameId,
166 root_id: WidgetId,
167 root_bounds: &WidgetBoundsInfo,
168 info_tree: &WidgetInfoTree,
169 renderer: Option<ViewRenderer>,
170 scale_factor: Factor,
171 default_font_aa: FontAntiAliasing,
172 ) -> Self {
173 let display_list = DisplayListBuilder::new(frame_id);
174
175 let root_size = root_bounds.outer_size();
176 let auto_hide_rect = PxRect::from_size(root_size).inflate(root_size.width, root_size.height);
177 root_bounds.set_outer_transform(PxTransform::identity(), info_tree);
178
179 let vp_gen = renderer
180 .as_ref()
181 .and_then(|r| r.generation().ok())
182 .unwrap_or(ViewProcessGen::INVALID);
183 let view_process_has_frame = vp_gen != ViewProcessGen::INVALID && vp_gen == info_tree.view_process_gen();
184
185 FrameBuilder {
186 render_widgets,
187 render_update_widgets,
188 frame_id,
189 widget_id: root_id,
190 transform: PxTransform::identity(),
191 transform_style: TransformStyle::Flat,
192 default_font_aa: match default_font_aa {
193 FontAntiAliasing::Default => FontAntiAliasing::Subpixel,
194 aa => aa,
195 },
196 renderer,
197 scale_factor,
198 display_list,
199 hit_testable: true,
200 visible: true,
201 backface_visible: true,
202 auto_hit_test: false,
203 hit_clips: HitTestClips::default(),
204 widget_data: Some(WidgetData {
205 filter: vec![],
206 blend: MixBlendMode::Normal,
207 backdrop_filter: vec![],
208 parent_child_offset: PxVector::zero(),
209 inner_is_set: false,
210 inner_transform: PxTransform::identity(),
211 }),
212 child_offset: PxVector::zero(),
213 parent_inner_bounds: None,
214 perspective: None,
215 view_process_has_frame,
216 can_reuse: view_process_has_frame,
217 open_reuse: None,
218 auto_hide_rect,
219
220 widget_count: 0,
221 widget_count_offsets: ParallelSegmentOffsets::default(),
222
223 clear_color: Some(colors::BLACK.transparent()),
224
225 debug_dot_overlays: vec![],
226 }
227 }
228
229 #[expect(clippy::too_many_arguments)]
231 pub fn new_renderless(
232 render_widgets: Arc<RenderUpdates>,
233 render_update_widgets: Arc<RenderUpdates>,
234 frame_id: FrameId,
235 root_id: WidgetId,
236 root_bounds: &WidgetBoundsInfo,
237 info_tree: &WidgetInfoTree,
238 scale_factor: Factor,
239 default_font_aa: FontAntiAliasing,
240 ) -> Self {
241 Self::new(
242 render_widgets,
243 render_update_widgets,
244 frame_id,
245 root_id,
246 root_bounds,
247 info_tree,
248 None,
249 scale_factor,
250 default_font_aa,
251 )
252 }
253
254 pub fn scale_factor(&self) -> Factor {
258 self.scale_factor
259 }
260
261 pub fn is_renderless(&self) -> bool {
265 self.renderer.is_none()
266 }
267
268 pub fn set_clear_color(&mut self, color: Rgba) {
276 self.clear_color = Some(color);
277 }
278
279 pub fn renderer(&self) -> Option<&ViewRenderer> {
283 self.renderer.as_ref()
284 }
285
286 pub fn frame_id(&self) -> FrameId {
288 self.frame_id
289 }
290
291 pub fn widget_id(&self) -> WidgetId {
293 self.widget_id
294 }
295
296 pub fn transform(&self) -> &PxTransform {
298 &self.transform
299 }
300
301 pub fn is_hit_testable(&self) -> bool {
308 self.hit_testable
309 }
310
311 pub fn is_visible(&self) -> bool {
313 self.visible
314 }
315
316 pub fn auto_hit_test(&self) -> bool {
322 self.auto_hit_test
323 }
324
325 pub fn with_default_font_aa(&mut self, aa: FontAntiAliasing, render: impl FnOnce(&mut Self)) {
327 let parent = mem::replace(&mut self.default_font_aa, aa);
328 render(self);
329 self.default_font_aa = parent;
330 }
331
332 pub fn with_hit_tests_disabled(&mut self, render: impl FnOnce(&mut Self)) {
337 let prev = mem::replace(&mut self.hit_testable, false);
338 render(self);
339 self.hit_testable = prev;
340 }
341
342 pub fn with_auto_hit_test(&mut self, auto_hit_test: bool, render: impl FnOnce(&mut Self)) {
348 let prev = mem::replace(&mut self.auto_hit_test, auto_hit_test);
349 render(self);
350 self.auto_hit_test = prev;
351 }
352
353 pub fn auto_hide_rect(&self) -> PxRect {
357 self.auto_hide_rect
358 }
359
360 pub fn with_auto_hide_rect(&mut self, auto_hide_rect: PxRect, render: impl FnOnce(&mut Self)) {
364 let parent_rect = mem::replace(&mut self.auto_hide_rect, auto_hide_rect);
365 render(self);
366 self.auto_hide_rect = parent_rect;
367 }
368
369 pub fn push_widget(&mut self, render: impl FnOnce(&mut Self)) {
383 #[derive(Default)]
385 struct PushWidget {
386 parent_visible: bool,
387 parent_perspective: Option<(f32, PxPoint)>,
388 parent_can_reuse: bool,
389 widget_z: usize,
390 outer_transform: PxTransform,
391 undo_prev_outer_transform: Option<PxTransform>,
392 reuse: Option<ReuseRange>,
393 reused: bool,
394 display_count: usize,
395 child_offset: PxVector,
396
397 wgt_info: Option<WidgetInfo>,
398 collapsed: bool,
399 }
400 impl PushWidget {
401 fn before(&mut self, builder: &mut FrameBuilder) {
402 let wgt_info = WIDGET.info();
403 let id = wgt_info.id();
404
405 #[cfg(debug_assertions)]
406 if builder.widget_data.is_some() && WIDGET.parent_id().is_some() {
407 tracing::error!(
408 "called `push_widget` for `{}` without calling `push_inner` for the parent `{}`",
409 WIDGET.trace_id(),
410 builder.widget_id
411 );
412 }
413
414 let bounds = wgt_info.bounds_info();
415 let tree = wgt_info.tree();
416
417 if bounds.is_collapsed() {
418 for info in wgt_info.self_and_descendants() {
420 info.bounds_info().set_rendered(None, tree);
421 }
422 let _ = WIDGET.take_update(UpdateFlags::LAYOUT | UpdateFlags::RENDER | UpdateFlags::RENDER_UPDATE);
424 let _ = WIDGET.take_render_reuse(&builder.render_widgets, &builder.render_update_widgets);
425 self.collapsed = true;
426 return;
427 } else {
428 #[cfg(debug_assertions)]
429 if WIDGET.pending_update().contains(UpdateFlags::LAYOUT) {
430 tracing::error!("called `push_widget` for `{}` with pending layout", WIDGET.trace_id());
433 }
434 }
435
436 let mut try_reuse = true;
437
438 let prev_outer = bounds.outer_transform();
439 self.outer_transform = PxTransform::from(builder.child_offset).then(&builder.transform);
440 bounds.set_outer_transform(self.outer_transform, tree);
441
442 if bounds.parent_child_offset() != builder.child_offset {
443 bounds.set_parent_child_offset(builder.child_offset);
444 try_reuse = false;
445 }
446 let outer_bounds = bounds.outer_bounds();
447
448 self.parent_visible = builder.visible;
449
450 if bounds.can_auto_hide() {
451 let mut outer_bounds = outer_bounds;
455 if outer_bounds.size.width < Px(1) {
456 outer_bounds.size.width = Px(1);
457 }
458 if outer_bounds.size.height < Px(1) {
459 outer_bounds.size.height = Px(1);
460 }
461 match builder.auto_hide_rect.intersection(&outer_bounds) {
462 Some(cull) => {
463 let partial = cull != outer_bounds;
464 if partial || bounds.is_partially_culled() {
465 try_reuse = false;
467 bounds.set_is_partially_culled(partial);
468 }
469 }
470 None => {
471 builder.visible = false;
473 }
474 }
475 } else {
476 bounds.set_is_partially_culled(false);
477 }
478
479 self.parent_perspective = builder.perspective;
480 builder.perspective = wgt_info.perspective();
481 if let Some((_, o)) = &mut builder.perspective {
482 *o -= builder.child_offset;
483 }
484
485 let can_reuse = builder.view_process_has_frame
486 && match bounds.render_info() {
487 Some(i) => i.visible == builder.visible && i.parent_perspective == builder.perspective,
488 None => false,
490 };
491 self.parent_can_reuse = mem::replace(&mut builder.can_reuse, can_reuse);
492
493 try_reuse &= can_reuse;
494
495 builder.widget_count += 1;
496 self.widget_z = builder.widget_count;
497
498 self.reuse = WIDGET.take_render_reuse(&builder.render_widgets, &builder.render_update_widgets);
499 if !try_reuse {
500 self.reuse = None;
501 }
502
503 if self.reuse.is_some() {
504 if let Some(undo_prev) = prev_outer.inverse() {
506 self.undo_prev_outer_transform = Some(undo_prev);
507 } else {
508 self.reuse = None; }
510 }
511
512 let index = builder.hit_clips.push_child(id);
513 bounds.set_hit_index(index);
514
515 self.reused = true;
516 self.display_count = builder.display_list.len();
517
518 self.child_offset = mem::take(&mut builder.child_offset);
519
520 self.wgt_info = Some(wgt_info);
521 }
522 fn push(&mut self, builder: &mut FrameBuilder, render: impl FnOnce(&mut FrameBuilder)) {
523 if self.collapsed {
524 return;
525 }
526
527 builder.push_reuse(&mut self.reuse, |frame| {
529 self.reused = false;
532 self.undo_prev_outer_transform = None;
533
534 frame.widget_data = Some(WidgetData {
535 filter: vec![],
536 blend: MixBlendMode::Normal,
537 backdrop_filter: vec![],
538 parent_child_offset: self.child_offset,
539 inner_is_set: frame.perspective.is_some(),
540 inner_transform: PxTransform::identity(),
541 });
542 let parent_widget = mem::replace(&mut frame.widget_id, self.wgt_info.as_ref().unwrap().id());
543
544 render(frame);
545
546 frame.widget_id = parent_widget;
547 frame.widget_data = None;
548 });
549 }
550 fn after(self, builder: &mut FrameBuilder) {
551 if self.collapsed {
552 return;
553 }
554
555 WIDGET.set_render_reuse(self.reuse);
556
557 let wgt_info = self.wgt_info.unwrap();
558 let id = wgt_info.id();
559 let tree = wgt_info.tree();
560 let bounds = wgt_info.bounds_info();
561
562 if self.reused {
563 let _span = tracing::trace_span!("reuse-descendants", ?id).entered();
566
567 let transform_patch = self.undo_prev_outer_transform.and_then(|t| {
568 let t = t.then(&self.outer_transform);
569 if t != PxTransform::identity() { Some(t) } else { None }
570 });
571
572 let current_wgt = tree.get(id).unwrap();
573 let z_patch = self.widget_z as i64 - current_wgt.z_index().map(|(b, _)| u32::from(b) as i64).unwrap_or(0);
574
575 let update_transforms = transform_patch.is_some();
576 let seg_id = builder.widget_count_offsets.id();
577
578 if update_transforms {
580 let transform_patch = transform_patch.unwrap();
581
582 let update_transforms_and_z = |info: WidgetInfo| {
584 let b = info.bounds_info();
585
586 if b != bounds {
587 b.set_outer_transform(b.outer_transform().then(&transform_patch), tree);
589 }
590 b.set_inner_transform(
591 b.inner_transform().then(&transform_patch),
592 tree,
593 info.id(),
594 info.parent().map(|p| p.inner_bounds()),
595 );
596
597 if let Some(i) = b.render_info() {
598 let (back, front) = info.z_index().unwrap();
599 let back = u32::from(back) as i64 + z_patch;
600 let front = u32::from(front) as i64 + z_patch;
601
602 b.set_rendered(
603 Some(WidgetRenderInfo {
604 visible: i.visible,
605 parent_perspective: i.parent_perspective,
606 seg_id,
607 back: back.try_into().unwrap(),
608 front: front.try_into().unwrap(),
609 }),
610 tree,
611 );
612 }
613 };
614
615 let targets = current_wgt.self_and_descendants();
616 if PARALLEL_VAR.get().contains(Parallel::RENDER) {
617 targets.par_bridge().for_each(update_transforms_and_z);
618 } else {
619 targets.for_each(update_transforms_and_z);
620 }
621 } else {
622 let update_z = |info: WidgetInfo| {
623 let bounds = info.bounds_info();
624
625 if let Some(i) = bounds.render_info() {
626 let (back, front) = info.z_index().unwrap();
627 let mut back = u32::from(back) as i64 + z_patch;
628 let mut front = u32::from(front) as i64 + z_patch;
629 if back < 0 {
630 tracing::error!("incorrect back Z-index ({back}) after patch ({z_patch})");
631 back = 0;
632 }
633 if front < 0 {
634 tracing::error!("incorrect front Z-index ({front}) after patch ({z_patch})");
635 front = 0;
636 }
637 bounds.set_rendered(
638 Some(WidgetRenderInfo {
639 visible: i.visible,
640 parent_perspective: i.parent_perspective,
641 seg_id,
642 back: back as _,
643 front: front as _,
644 }),
645 tree,
646 );
647 }
648 };
649
650 let targets = current_wgt.self_and_descendants();
651 if PARALLEL_VAR.get().contains(Parallel::RENDER) {
652 targets.par_bridge().for_each(update_z);
653 } else {
654 targets.for_each(update_z);
655 }
656 }
657
658 builder.widget_count = bounds.render_info().map(|i| i.front).unwrap_or(builder.widget_count);
660 } else {
661 bounds.set_rendered(
663 Some(WidgetRenderInfo {
664 visible: builder.display_list.len() > self.display_count,
665 parent_perspective: builder.perspective,
666 seg_id: builder.widget_count_offsets.id(),
667 back: self.widget_z,
668 front: builder.widget_count,
669 }),
670 tree,
671 );
672 }
673
674 builder.visible = self.parent_visible;
675 builder.perspective = self.parent_perspective;
676 builder.can_reuse = self.parent_can_reuse;
677 }
678 }
679 let mut push = PushWidget::default();
680 push.before(self);
681 push.push(self, render);
682 push.after(self);
683 }
684
685 pub fn can_reuse(&self) -> bool {
691 self.can_reuse
692 }
693
694 pub fn with_no_reuse(&mut self, render: impl FnOnce(&mut Self)) {
698 let prev_can_reuse = self.can_reuse;
699 self.can_reuse = false;
700 render(self);
701 self.can_reuse = prev_can_reuse;
702 }
703
704 pub fn push_reuse(&mut self, group: &mut Option<ReuseRange>, generate: impl FnOnce(&mut Self)) {
715 if self.can_reuse
716 && let Some(g) = &group
717 {
718 if self.visible {
719 self.display_list.push_reuse_range(g);
720 }
721 return;
722 }
723 *group = None;
724 let parent_group = self.open_reuse.replace(self.display_list.start_reuse_range());
725
726 generate(self);
727
728 let start = self.open_reuse.take().unwrap();
729 let range = self.display_list.finish_reuse_range(start);
730 *group = Some(range);
731 self.open_reuse = parent_group;
732 }
733
734 pub fn hide(&mut self, render: impl FnOnce(&mut Self)) {
746 let parent_visible = mem::replace(&mut self.visible, false);
747 render(self);
748 self.visible = parent_visible;
749 }
750
751 pub fn with_backface_visibility(&mut self, visible: bool, render: impl FnOnce(&mut Self)) {
755 if self.backface_visible != visible {
756 let parent = self.backface_visible;
757 self.display_list.set_backface_visibility(visible);
758 render(self);
759 self.display_list.set_backface_visibility(parent);
760 self.backface_visible = parent;
761 } else {
762 render(self);
763 }
764 }
765
766 pub fn is_outer(&self) -> bool {
773 self.widget_data.is_some()
774 }
775
776 pub fn push_inner_filter(&mut self, filter: RenderFilter, render: impl FnOnce(&mut Self)) {
785 if let Some(data) = self.widget_data.as_mut() {
786 let mut filter = filter;
787 filter.reverse();
788 data.filter.extend(filter.iter().copied());
789
790 render(self);
791 } else {
792 tracing::error!("called `push_inner_filter` inside inner context of `{}`", self.widget_id);
793 render(self);
794 }
795 }
796
797 pub fn push_inner_opacity(&mut self, bind: FrameValue<f32>, render: impl FnOnce(&mut Self)) {
806 if let Some(data) = self.widget_data.as_mut() {
807 data.filter.push(FilterOp::Opacity(bind));
808
809 render(self);
810 } else {
811 tracing::error!("called `push_inner_opacity` inside inner context of `{}`", self.widget_id);
812 render(self);
813 }
814 }
815
816 pub fn push_inner_backdrop_filter(&mut self, filter: RenderFilter, render: impl FnOnce(&mut Self)) {
825 if let Some(data) = self.widget_data.as_mut() {
826 let mut filter = filter;
827 filter.reverse();
828 data.backdrop_filter.extend(filter.iter().copied());
829
830 render(self);
831 } else {
832 tracing::error!("called `push_inner_backdrop_filter` inside inner context of `{}`", self.widget_id);
833 render(self);
834 }
835 }
836
837 pub fn push_inner_blend(&mut self, mode: MixBlendMode, render: impl FnOnce(&mut Self)) {
846 if let Some(data) = self.widget_data.as_mut() {
847 data.blend = mode;
848
849 render(self);
850 } else {
851 tracing::error!("called `push_inner_blend` inside inner context of `{}`", self.widget_id);
852 render(self);
853 }
854 }
855
856 pub fn push_child(&mut self, offset: PxVector, render: impl FnOnce(&mut Self)) {
867 if self.widget_data.is_some() {
868 tracing::error!("called `push_child` outside inner context of `{}`", self.widget_id);
869 }
870
871 self.child_offset = offset;
872 render(self);
873 self.child_offset = PxVector::zero();
874 }
875
876 pub fn push_inner_transform(&mut self, transform: &PxTransform, render: impl FnOnce(&mut Self)) {
885 if let Some(data) = &mut self.widget_data {
886 let parent_transform = data.inner_transform;
887 let parent_is_set = mem::replace(&mut data.inner_is_set, true);
888 data.inner_transform = data.inner_transform.then(transform);
889
890 render(self);
891
892 if let Some(data) = &mut self.widget_data {
893 data.inner_transform = parent_transform;
894 data.inner_is_set = parent_is_set;
895 }
896 } else {
897 tracing::error!("called `push_inner_transform` inside inner context of `{}`", self.widget_id);
898 render(self);
899 }
900 }
901
902 pub fn push_inner(
909 &mut self,
910 layout_translation_key: FrameValueKey<PxTransform>,
911 layout_translation_animating: bool,
912 render: impl FnOnce(&mut Self),
913 ) {
914 #[derive(Default)]
916 struct PushInner {
917 invalid: bool,
918 parent_transform: PxTransform,
919 parent_transform_style: TransformStyle,
920 parent_hit_clips: HitTestClips,
921 parent_parent_inner_bounds: Option<PxRect>,
922 visible: bool,
923 ctx_outside_ref_frame: i32,
924 ctx_inside_ref_frame: i32,
925
926 wgt_info: Option<WidgetInfo>,
927 }
928 impl PushInner {
929 fn before(
930 &mut self,
931 builder: &mut FrameBuilder,
932 layout_translation_key: FrameValueKey<PxTransform>,
933 layout_translation_animating: bool,
934 ) {
935 if let Some(mut data) = builder.widget_data.take() {
936 self.parent_transform = builder.transform;
937 self.parent_transform_style = builder.transform_style;
938 self.parent_hit_clips = mem::take(&mut builder.hit_clips);
939
940 let wgt_info = WIDGET.info();
941 let id = wgt_info.id();
942 let bounds = wgt_info.bounds_info();
943 let tree = wgt_info.tree();
944
945 let inner_offset = bounds.inner_offset();
946 let mut inner_transform = data.inner_transform;
947 if let Some((d, mut o)) = builder.perspective {
948 o -= inner_offset;
949 let x = o.x.0 as f32;
950 let y = o.y.0 as f32;
951 let p = PxTransform::translation(-x, -y)
952 .then(&PxTransform::perspective(d))
953 .then_translate(euclid::vec2(x, y));
954 inner_transform = inner_transform.then(&p);
955 }
956 let inner_transform = inner_transform.then_translate((data.parent_child_offset + inner_offset).cast());
957
958 builder.transform = inner_transform.then(&self.parent_transform);
959
960 bounds.set_inner_transform(builder.transform, tree, id, builder.parent_inner_bounds);
961
962 self.parent_parent_inner_bounds = builder.parent_inner_bounds.replace(bounds.inner_bounds());
963
964 if builder.visible {
965 self.visible = true;
966 builder.transform_style = wgt_info.transform_style();
967
968 let has_3d_ctx = matches!(builder.transform_style, TransformStyle::Preserve3D);
969 let has_filters = !data.filter.is_empty() || data.blend != MixBlendMode::Normal;
970
971 macro_rules! push_reference_frame {
974 () => {
975 builder.display_list.push_reference_frame(
976 ReferenceFrameId::from_widget(builder.widget_id).into(),
977 layout_translation_key.bind(inner_transform, layout_translation_animating),
978 builder.transform_style.into(),
979 !data.inner_is_set,
980 );
981 if !data.backdrop_filter.is_empty() {
982 builder
983 .display_list
984 .push_backdrop_filter(PxRect::from_size(bounds.inner_size()), &data.backdrop_filter);
985 }
986 };
987 }
988
989 if has_filters {
990 data.filter.reverse();
997
998 if has_3d_ctx {
999 if matches!(
1004 (builder.transform_style, bounds.transform_style()),
1005 (TransformStyle::Preserve3D, TransformStyle::Flat)
1006 ) {
1007 push_reference_frame!();
1009 builder
1010 .display_list
1011 .push_stacking_context(MixBlendMode::Normal, builder.transform_style, &[]);
1012 builder
1013 .display_list
1014 .push_stacking_context(data.blend, TransformStyle::Flat, &data.filter);
1015 self.ctx_inside_ref_frame = 2;
1016 } else if wgt_info
1017 .parent()
1018 .map(|p| matches!(p.bounds_info().transform_style(), TransformStyle::Flat))
1019 .unwrap_or(false)
1020 {
1021 builder
1024 .display_list
1025 .push_stacking_context(data.blend, TransformStyle::Flat, &data.filter);
1026 self.ctx_outside_ref_frame = 1;
1027 push_reference_frame!();
1028 builder
1029 .display_list
1030 .push_stacking_context(MixBlendMode::Normal, builder.transform_style, &[]);
1031 self.ctx_inside_ref_frame = 1;
1032 } else {
1033 tracing::warn!(
1036 "widget `{id}` cannot have filters because it is `Preserve3D` inside `Preserve3D`, filters & blend ignored"
1037 );
1038
1039 push_reference_frame!();
1040 builder
1041 .display_list
1042 .push_stacking_context(MixBlendMode::Normal, builder.transform_style, &[]);
1043 self.ctx_inside_ref_frame = 1;
1044 }
1045 } else {
1046 push_reference_frame!();
1048 builder
1049 .display_list
1050 .push_stacking_context(data.blend, TransformStyle::Flat, &data.filter);
1051 self.ctx_inside_ref_frame = 1;
1052 }
1053 } else if has_3d_ctx {
1054 push_reference_frame!();
1056 builder
1057 .display_list
1058 .push_stacking_context(MixBlendMode::Normal, builder.transform_style, &[]);
1059 self.ctx_inside_ref_frame = 1;
1060 } else {
1061 push_reference_frame!();
1063 }
1064 }
1065
1066 self.wgt_info = Some(wgt_info);
1067 } else {
1068 tracing::error!("called `push_inner` more then once for `{}`", builder.widget_id);
1069 self.invalid = true;
1070 }
1071 }
1072 fn push(&mut self, builder: &mut FrameBuilder, render: impl FnOnce(&mut FrameBuilder)) {
1073 if self.invalid {
1074 return;
1075 }
1076 render(builder)
1077 }
1078 fn after(mut self, builder: &mut FrameBuilder) {
1079 if self.invalid {
1080 return;
1081 }
1082
1083 if self.visible {
1084 while self.ctx_inside_ref_frame > 0 {
1085 builder.display_list.pop_stacking_context();
1086 self.ctx_inside_ref_frame -= 1;
1087 }
1088
1089 builder.display_list.pop_reference_frame();
1090
1091 while self.ctx_outside_ref_frame > 0 {
1092 builder.display_list.pop_stacking_context();
1093 self.ctx_outside_ref_frame -= 1;
1094 }
1095 }
1096
1097 let wgt_info = self.wgt_info.unwrap();
1098 let bounds = wgt_info.bounds_info();
1099
1100 builder.transform = self.parent_transform;
1102 builder.transform_style = self.parent_transform_style;
1103 builder.parent_inner_bounds = self.parent_parent_inner_bounds;
1104
1105 let hit_clips = mem::replace(&mut builder.hit_clips, self.parent_hit_clips);
1106 bounds.set_hit_clips(hit_clips);
1107
1108 if !builder.debug_dot_overlays.is_empty() && wgt_info.parent().is_none() {
1109 for (offset, color) in mem::take(&mut builder.debug_dot_overlays) {
1110 builder.push_debug_dot(offset, color);
1111 }
1112 }
1113 }
1114 }
1115 let mut push_inner = PushInner::default();
1116 push_inner.before(self, layout_translation_key, layout_translation_animating);
1117 push_inner.push(self, render);
1118 push_inner.after(self);
1119 }
1120
1121 pub fn is_inner(&self) -> bool {
1128 self.widget_data.is_none()
1129 }
1130
1131 pub fn hit_test(&mut self) -> HitTestBuilder<'_> {
1136 expect_inner!(self.hit_test);
1137
1138 HitTestBuilder {
1139 hit_clips: &mut self.hit_clips,
1140 is_hit_testable: self.hit_testable,
1141 }
1142 }
1143
1144 pub fn push_clip_rect(&mut self, clip_rect: PxRect, clip_out: bool, hit_test: bool, render: impl FnOnce(&mut FrameBuilder)) {
1153 self.push_clips(move |c| c.push_clip_rect(clip_rect, clip_out, hit_test), render)
1154 }
1155
1156 pub fn push_clip_rounded_rect(
1165 &mut self,
1166 clip_rect: PxRect,
1167 corners: PxCornerRadius,
1168 clip_out: bool,
1169 hit_test: bool,
1170 render: impl FnOnce(&mut FrameBuilder),
1171 ) {
1172 self.push_clips(move |c| c.push_clip_rounded_rect(clip_rect, corners, clip_out, hit_test), render)
1173 }
1174
1175 pub fn push_clips(&mut self, clips: impl FnOnce(&mut ClipBuilder), render: impl FnOnce(&mut FrameBuilder)) {
1177 expect_inner!(self.push_clips);
1178
1179 let (mut render_count, mut hit_test_count) = {
1180 let mut clip_builder = ClipBuilder {
1181 builder: self,
1182 render_count: 0,
1183 hit_test_count: 0,
1184 };
1185 clips(&mut clip_builder);
1186 (clip_builder.render_count, clip_builder.hit_test_count)
1187 };
1188
1189 render(self);
1190
1191 while hit_test_count > 0 {
1192 hit_test_count -= 1;
1193
1194 self.hit_clips.pop_clip();
1195 }
1196 while render_count > 0 {
1197 render_count -= 1;
1198
1199 self.display_list.pop_clip();
1200 }
1201 }
1202
1203 pub fn push_mask(&mut self, image: &impl Img, rect: PxRect, render: impl FnOnce(&mut Self)) {
1205 let mut pop = false;
1206 if self.visible
1207 && let Some(r) = &self.renderer
1208 {
1209 self.display_list.push_mask(image.renderer_id(r), rect);
1210 pop = true;
1211 }
1212
1213 render(self);
1214
1215 if pop {
1216 self.display_list.pop_mask();
1217 }
1218 }
1219
1220 pub fn push_reference_frame(
1234 &mut self,
1235 key: ReferenceFrameId,
1236 transform: FrameValue<PxTransform>,
1237 is_2d_scale_translation: bool,
1238 hit_test: bool,
1239 render: impl FnOnce(&mut Self),
1240 ) {
1241 let transform_value = transform.value();
1242
1243 let prev_transform = self.transform;
1244 self.transform = transform_value.then(&prev_transform);
1245
1246 if self.visible {
1247 self.display_list
1248 .push_reference_frame(key.into(), transform, self.transform_style, is_2d_scale_translation);
1249 }
1250
1251 let hit_test = hit_test || self.auto_hit_test;
1252
1253 if hit_test {
1254 self.hit_clips.push_transform(transform);
1255 }
1256
1257 render(self);
1258
1259 if self.visible {
1260 self.display_list.pop_reference_frame();
1261 }
1262 self.transform = prev_transform;
1263
1264 if hit_test {
1265 self.hit_clips.pop_transform();
1266 }
1267 }
1268
1269 pub fn push_filter(&mut self, blend: MixBlendMode, filter: &RenderFilter, render: impl FnOnce(&mut Self)) {
1277 expect_inner!(self.push_filter);
1278
1279 if self.visible {
1280 self.display_list.push_stacking_context(blend, self.transform_style, filter);
1281
1282 render(self);
1283
1284 self.display_list.pop_stacking_context();
1285 } else {
1286 render(self);
1287 }
1288 }
1289
1290 pub fn push_opacity(&mut self, bind: FrameValue<f32>, render: impl FnOnce(&mut Self)) {
1297 expect_inner!(self.push_opacity);
1298
1299 if self.visible {
1300 self.display_list
1301 .push_stacking_context(MixBlendMode::Normal, self.transform_style, &[FilterOp::Opacity(bind)]);
1302
1303 render(self);
1304
1305 self.display_list.pop_stacking_context();
1306 } else {
1307 render(self);
1308 }
1309 }
1310
1311 pub fn push_backdrop_filter(&mut self, clip_rect: PxRect, filter: &RenderFilter) {
1319 expect_inner!(self.push_backdrop_filter);
1320 warn_empty!(self.push_backdrop_filter(clip_rect));
1321
1322 if self.visible {
1323 self.display_list.push_backdrop_filter(clip_rect, filter);
1324 }
1325
1326 if self.auto_hit_test {
1327 self.hit_test().push_rect(clip_rect);
1328 }
1329 }
1330
1331 pub fn push_border(&mut self, bounds: PxRect, widths: PxSideOffsets, sides: BorderSides, radius: PxCornerRadius) {
1333 expect_inner!(self.push_border);
1334 warn_empty!(self.push_border(bounds));
1335
1336 if self.visible {
1337 self.display_list.push_border(
1338 bounds,
1339 widths,
1340 sides.top.into(),
1341 sides.right.into(),
1342 sides.bottom.into(),
1343 sides.left.into(),
1344 radius,
1345 );
1346 }
1347
1348 if self.auto_hit_test {
1349 self.hit_test().push_border(bounds, widths, radius);
1350 }
1351 }
1352
1353 #[expect(clippy::too_many_arguments)]
1355 pub fn push_border_image(
1356 &mut self,
1357 bounds: PxRect,
1358 widths: PxSideOffsets,
1359 slice: PxSideOffsets,
1360 fill: bool,
1361 repeat_horizontal: RepeatMode,
1362 repeat_vertical: RepeatMode,
1363 image: &impl Img,
1364 rendering: ImageRendering,
1365 ) {
1366 expect_inner!(self.push_border_image);
1367 warn_empty!(self.push_border_image(bounds));
1368
1369 if self.visible
1370 && let Some(r) = &self.renderer
1371 {
1372 let image_id = image.renderer_id(r);
1373 self.display_list.push_nine_patch_border(
1374 bounds,
1375 NinePatchSource::Image { image_id, rendering },
1376 widths,
1377 image.size(),
1378 slice,
1379 fill,
1380 repeat_horizontal,
1381 repeat_vertical,
1382 )
1383 }
1384 }
1385
1386 #[expect(clippy::too_many_arguments)]
1388 pub fn push_border_linear_gradient(
1389 &mut self,
1390 bounds: PxRect,
1391 widths: PxSideOffsets,
1392 slice: PxSideOffsets,
1393 fill: bool,
1394 repeat_horizontal: RepeatMode,
1395 repeat_vertical: RepeatMode,
1396 line: PxLine,
1397 stops: &[RenderGradientStop],
1398 extend_mode: RenderExtendMode,
1399 ) {
1400 debug_assert!(stops.len() >= 2);
1401 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1402 debug_assert!(
1403 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1404 "last color stop must be at offset 1.0"
1405 );
1406 expect_inner!(self.push_border_linear_gradient);
1407 warn_empty!(self.push_border_linear_gradient(bounds));
1408
1409 if self.visible && !stops.is_empty() {
1410 self.display_list.push_nine_patch_border(
1411 bounds,
1412 NinePatchSource::LinearGradient {
1413 start_point: line.start.cast(),
1414 end_point: line.end.cast(),
1415 extend_mode,
1416 stops: stops.to_vec().into_boxed_slice(),
1417 },
1418 widths,
1419 bounds.size,
1420 slice,
1421 fill,
1422 repeat_horizontal,
1423 repeat_vertical,
1424 );
1425 }
1426 }
1427
1428 #[expect(clippy::too_many_arguments)]
1430 pub fn push_border_radial_gradient(
1431 &mut self,
1432 bounds: PxRect,
1433 widths: PxSideOffsets,
1434 slice: PxSideOffsets,
1435 fill: bool,
1436 repeat_horizontal: RepeatMode,
1437 repeat_vertical: RepeatMode,
1438 center: PxPoint,
1439 radius: PxSize,
1440 stops: &[RenderGradientStop],
1441 extend_mode: RenderExtendMode,
1442 ) {
1443 debug_assert!(stops.len() >= 2);
1444 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1445 debug_assert!(
1446 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1447 "last color stop must be at offset 1.0"
1448 );
1449
1450 expect_inner!(self.push_border_radial_gradient);
1451 warn_empty!(self.push_border_radial_gradient(bounds));
1452
1453 if self.visible && !stops.is_empty() {
1454 self.display_list.push_nine_patch_border(
1455 bounds,
1456 NinePatchSource::RadialGradient {
1457 center: center.cast(),
1458 radius: radius.cast(),
1459 start_offset: 0.0,
1460 end_offset: 1.0,
1461 extend_mode,
1462 stops: stops.to_vec().into_boxed_slice(),
1463 },
1464 widths,
1465 bounds.size,
1466 slice,
1467 fill,
1468 repeat_horizontal,
1469 repeat_vertical,
1470 );
1471 }
1472 }
1473
1474 #[expect(clippy::too_many_arguments)]
1476 pub fn push_border_conic_gradient(
1477 &mut self,
1478 bounds: PxRect,
1479 widths: PxSideOffsets,
1480 slice: PxSideOffsets,
1481 fill: bool,
1482 repeat_horizontal: RepeatMode,
1483 repeat_vertical: RepeatMode,
1484 center: PxPoint,
1485 angle: AngleRadian,
1486 stops: &[RenderGradientStop],
1487 extend_mode: RenderExtendMode,
1488 ) {
1489 debug_assert!(stops.len() >= 2);
1490 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1491 debug_assert!(
1492 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1493 "last color stop must be at offset 1.0"
1494 );
1495
1496 expect_inner!(self.push_border_conic_gradient);
1497 warn_empty!(self.push_border_conic_gradient(bounds));
1498
1499 if self.visible && !stops.is_empty() {
1500 self.display_list.push_nine_patch_border(
1501 bounds,
1502 NinePatchSource::ConicGradient {
1503 center: center.cast(),
1504 angle,
1505 start_offset: 0.0,
1506 end_offset: 1.0,
1507 extend_mode,
1508 stops: stops.to_vec().into_boxed_slice(),
1509 },
1510 widths,
1511 bounds.size,
1512 slice,
1513 fill,
1514 repeat_horizontal,
1515 repeat_vertical,
1516 );
1517 }
1518 }
1519
1520 pub fn push_text(
1522 &mut self,
1523 clip_rect: PxRect,
1524 glyphs: &[GlyphInstance],
1525 font: &impl Font,
1526 color: FrameValue<Rgba>,
1527 synthesis: FontSynthesis,
1528 aa: FontAntiAliasing,
1529 ) {
1530 expect_inner!(self.push_text);
1531 warn_empty!(self.push_text(clip_rect));
1532
1533 if let Some(r) = &self.renderer
1534 && !glyphs.is_empty()
1535 && self.visible
1536 && !font.is_empty_fallback()
1537 {
1538 let font_id = font.renderer_id(r, synthesis);
1539
1540 let opts = GlyphOptions::new(
1541 match aa {
1542 FontAntiAliasing::Default => self.default_font_aa,
1543 aa => aa,
1544 },
1545 synthesis.contains(FontSynthesis::BOLD),
1546 synthesis.contains(FontSynthesis::OBLIQUE),
1547 );
1548 self.display_list.push_text(clip_rect, font_id, glyphs, color, opts);
1549 }
1550
1551 if self.auto_hit_test {
1552 self.hit_test().push_rect(clip_rect);
1553 }
1554 }
1555
1556 #[allow(clippy::too_many_arguments)]
1563 pub fn push_image(
1564 &mut self,
1565 clip_rect: PxRect,
1566 image_size: PxSize,
1567 tile_size: PxSize,
1568 tile_spacing: PxSize,
1569 image: &impl Img,
1570 rendering: ImageRendering,
1571 ) {
1572 expect_inner!(self.push_image);
1573 warn_empty!(self.push_image(clip_rect));
1574
1575 if let Some(r) = &self.renderer
1576 && self.visible
1577 {
1578 let image_key = image.renderer_id(r);
1579 self.display_list
1580 .push_image(clip_rect, image_key, image_size, tile_size, tile_spacing, rendering);
1581 }
1582
1583 if self.auto_hit_test {
1584 self.hit_test().push_rect(clip_rect);
1585 }
1586 }
1587
1588 pub fn push_color(&mut self, clip_rect: PxRect, color: FrameValue<Rgba>) {
1597 expect_inner!(self.push_color);
1598 warn_empty!(self.push_color(clip_rect));
1599
1600 if self.visible {
1601 self.display_list.push_color(clip_rect, color);
1602 }
1603
1604 if self.auto_hit_test {
1605 self.hit_test().push_rect(clip_rect);
1606 }
1607 }
1608
1609 #[expect(clippy::too_many_arguments)]
1617 pub fn push_linear_gradient(
1618 &mut self,
1619 clip_rect: PxRect,
1620 line: PxLine,
1621 stops: &[RenderGradientStop],
1622 extend_mode: RenderExtendMode,
1623 tile_origin: PxPoint,
1624 tile_size: PxSize,
1625 tile_spacing: PxSize,
1626 ) {
1627 debug_assert!(stops.len() >= 2);
1628 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1629 debug_assert!(
1630 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1631 "last color stop must be at offset 1.0"
1632 );
1633
1634 expect_inner!(self.push_linear_gradient);
1635 warn_empty!(self.push_linear_gradient(clip_rect));
1636
1637 if !stops.is_empty() && self.visible {
1638 self.display_list.push_linear_gradient(
1639 clip_rect,
1640 line.start.cast(),
1641 line.end.cast(),
1642 extend_mode,
1643 stops,
1644 tile_origin,
1645 tile_size,
1646 tile_spacing,
1647 );
1648 }
1649
1650 if self.auto_hit_test {
1651 self.hit_test().push_rect(clip_rect);
1652 }
1653 }
1654
1655 #[expect(clippy::too_many_arguments)]
1666 pub fn push_radial_gradient(
1667 &mut self,
1668 clip_rect: PxRect,
1669 center: PxPoint,
1670 radius: PxSize,
1671 stops: &[RenderGradientStop],
1672 extend_mode: RenderExtendMode,
1673 tile_origin: PxPoint,
1674 tile_size: PxSize,
1675 tile_spacing: PxSize,
1676 ) {
1677 debug_assert!(stops.len() >= 2);
1678 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1679 debug_assert!(
1680 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1681 "last color stop must be at offset 1.0"
1682 );
1683
1684 expect_inner!(self.push_radial_gradient);
1685 warn_empty!(self.push_radial_gradient(clip_rect));
1686
1687 if !stops.is_empty() && self.visible {
1688 self.display_list.push_radial_gradient(
1689 clip_rect,
1690 center.cast(),
1691 radius.cast(),
1692 0.0,
1693 1.0,
1694 extend_mode,
1695 stops,
1696 tile_origin,
1697 tile_size,
1698 tile_spacing,
1699 );
1700 }
1701
1702 if self.auto_hit_test {
1703 self.hit_test().push_rect(clip_rect);
1704 }
1705 }
1706
1707 #[expect(clippy::too_many_arguments)]
1715 pub fn push_conic_gradient(
1716 &mut self,
1717 clip_rect: PxRect,
1718 center: PxPoint,
1719 angle: AngleRadian,
1720 stops: &[RenderGradientStop],
1721 extend_mode: RenderExtendMode,
1722 tile_origin: PxPoint,
1723 tile_size: PxSize,
1724 tile_spacing: PxSize,
1725 ) {
1726 debug_assert!(stops.len() >= 2);
1727 debug_assert!(stops[0].offset.abs() < 0.00001, "first color stop must be at offset 0.0");
1728 debug_assert!(
1729 (stops[stops.len() - 1].offset - 1.0).abs() < 0.00001,
1730 "last color stop must be at offset 1.0"
1731 );
1732
1733 expect_inner!(self.push_conic_gradient);
1734 warn_empty!(self.push_conic_gradient(clip_rect));
1735
1736 if !stops.is_empty() && self.visible {
1737 self.display_list.push_conic_gradient(
1738 clip_rect,
1739 center.cast(),
1740 angle,
1741 0.0,
1742 1.0,
1743 extend_mode,
1744 stops,
1745 tile_origin,
1746 tile_size,
1747 tile_spacing,
1748 );
1749 }
1750
1751 if self.auto_hit_test {
1752 self.hit_test().push_rect(clip_rect);
1753 }
1754 }
1755
1756 pub fn push_line(&mut self, clip_rect: PxRect, orientation: border::LineOrientation, color: Rgba, style: border::LineStyle) {
1758 expect_inner!(self.push_line);
1759 warn_empty!(self.push_line(clip_rect));
1760
1761 if self.visible {
1762 match style.render_command() {
1763 RenderLineCommand::Line(style) => {
1764 self.display_list.push_line(clip_rect, color, style, orientation);
1765 }
1766 RenderLineCommand::Border(style) => {
1767 use border::LineOrientation as LO;
1768 let widths = match orientation {
1769 LO::Vertical => PxSideOffsets::new(Px(0), Px(0), Px(0), clip_rect.width()),
1770 LO::Horizontal => PxSideOffsets::new(clip_rect.height(), Px(0), Px(0), Px(0)),
1771 };
1772 self.display_list.push_border(
1773 clip_rect,
1774 widths,
1775 zng_view_api::BorderSide { color, style },
1776 zng_view_api::BorderSide {
1777 color: colors::BLACK.transparent(),
1778 style: zng_view_api::BorderStyle::Hidden,
1779 },
1780 zng_view_api::BorderSide {
1781 color: colors::BLACK.transparent(),
1782 style: zng_view_api::BorderStyle::Hidden,
1783 },
1784 zng_view_api::BorderSide { color, style },
1785 PxCornerRadius::zero(),
1786 );
1787 }
1788 }
1789 }
1790
1791 if self.auto_hit_test {
1792 self.hit_test().push_rect(clip_rect);
1793 }
1794 }
1795
1796 pub fn push_debug_dot_overlay(&mut self, offset: PxPoint, color: impl Into<Rgba>) {
1800 if let Some(offset) = self.transform.transform_point(offset) {
1801 self.debug_dot_overlays.push((offset, color.into()));
1802 }
1803 }
1804
1805 pub fn push_debug_dot(&mut self, offset: PxPoint, color: impl Into<Rgba>) {
1809 if !self.visible {
1810 return;
1811 }
1812 let scale = self.scale_factor();
1813
1814 let radius = PxSize::splat(Px(6)) * scale;
1815 let color = color.into();
1816
1817 let center = radius.to_vector().to_point();
1818 let bounds = radius * 2.0.fct();
1819
1820 let offset = offset - radius.to_vector();
1821
1822 self.display_list.push_radial_gradient(
1823 PxRect::new(offset, bounds),
1824 center.cast(),
1825 radius.cast(),
1826 0.0,
1827 1.0,
1828 RenderExtendMode::Clamp,
1829 &[
1830 RenderGradientStop { offset: 0.0, color },
1831 RenderGradientStop { offset: 0.5, color },
1832 RenderGradientStop {
1833 offset: 0.6,
1834 color: colors::WHITE,
1835 },
1836 RenderGradientStop {
1837 offset: 0.7,
1838 color: colors::WHITE,
1839 },
1840 RenderGradientStop {
1841 offset: 0.8,
1842 color: colors::BLACK,
1843 },
1844 RenderGradientStop {
1845 offset: 1.0,
1846 color: colors::BLACK.transparent(),
1847 },
1848 ],
1849 PxPoint::zero(),
1850 bounds,
1851 PxSize::zero(),
1852 );
1853 }
1854
1855 pub fn push_extension_context_raw(
1857 &mut self,
1858 extension_id: ApiExtensionId,
1859 payload: ApiExtensionPayload,
1860 render: impl FnOnce(&mut Self),
1861 ) {
1862 self.display_list.push_extension(extension_id, payload);
1863 render(self);
1864 self.display_list.pop_extension(extension_id);
1865 }
1866
1867 pub fn push_extension_context<T: serde::Serialize>(
1869 &mut self,
1870 extension_id: ApiExtensionId,
1871 payload: &T,
1872 render: impl FnOnce(&mut Self),
1873 ) {
1874 self.push_extension_context_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap(), render)
1875 }
1876
1877 pub fn push_extension_item_raw(&mut self, extension_id: ApiExtensionId, payload: ApiExtensionPayload) {
1879 self.display_list.push_extension(extension_id, payload);
1880 }
1881
1882 pub fn push_extension_item<T: serde::Serialize>(&mut self, extension_id: ApiExtensionId, payload: &T) {
1884 self.push_extension_item_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap())
1885 }
1886
1887 pub fn parallel_split(&self) -> ParallelBuilder<Self> {
1896 if self.widget_data.is_some() {
1897 tracing::error!(
1898 "called `parallel_split` inside `{}` and before calling `push_inner`",
1899 self.widget_id
1900 );
1901 }
1902
1903 ParallelBuilder(Some(Self {
1904 render_widgets: self.render_widgets.clone(),
1905 render_update_widgets: self.render_update_widgets.clone(),
1906 frame_id: self.frame_id,
1907 widget_id: self.widget_id,
1908 transform: self.transform,
1909 transform_style: self.transform_style,
1910 default_font_aa: self.default_font_aa,
1911 renderer: self.renderer.clone(),
1912 scale_factor: self.scale_factor,
1913 display_list: self.display_list.parallel_split(),
1914 hit_testable: self.hit_testable,
1915 visible: self.visible,
1916 backface_visible: self.backface_visible,
1917 auto_hit_test: self.auto_hit_test,
1918 hit_clips: self.hit_clips.parallel_split(),
1919 auto_hide_rect: self.auto_hide_rect,
1920 widget_data: None,
1921 child_offset: self.child_offset,
1922 parent_inner_bounds: self.parent_inner_bounds,
1923 perspective: self.perspective,
1924 view_process_has_frame: self.view_process_has_frame,
1925 can_reuse: self.can_reuse,
1926 open_reuse: None,
1927 clear_color: None,
1928 widget_count: 0,
1929 widget_count_offsets: self.widget_count_offsets.parallel_split(),
1930 debug_dot_overlays: vec![],
1931 }))
1932 }
1933
1934 pub fn parallel_fold(&mut self, mut split: ParallelBuilder<Self>) {
1936 let split = split.take();
1937 if split.clear_color.is_some() {
1938 self.clear_color = split.clear_color;
1939 }
1940 self.hit_clips.parallel_fold(split.hit_clips);
1941 self.display_list.parallel_fold(split.display_list);
1942 self.widget_count_offsets
1943 .parallel_fold(split.widget_count_offsets, self.widget_count);
1944
1945 self.widget_count += split.widget_count;
1946 self.debug_dot_overlays.extend(split.debug_dot_overlays);
1947 }
1948
1949 #[expect(clippy::too_many_arguments)]
1951 pub fn with_nested_window(
1952 &mut self,
1953 render_widgets: Arc<RenderUpdates>,
1954 render_update_widgets: Arc<RenderUpdates>,
1955
1956 root_id: WidgetId,
1957 root_bounds: &WidgetBoundsInfo,
1958 info_tree: &WidgetInfoTree,
1959 default_font_aa: FontAntiAliasing,
1960
1961 render: impl FnOnce(&mut Self),
1962 ) {
1963 let mut nested = Self::new(
1965 render_widgets,
1966 render_update_widgets,
1967 self.frame_id,
1968 root_id,
1969 root_bounds,
1970 info_tree,
1971 self.renderer.clone(),
1972 self.scale_factor,
1973 default_font_aa,
1974 );
1975 nested.display_list = self.display_list.parallel_split();
1976 nested.hit_clips = self.hit_clips.parallel_split();
1977 render(&mut nested);
1981
1982 info_tree.root().bounds_info().set_rendered(
1984 Some(WidgetRenderInfo {
1985 visible: nested.visible,
1986 parent_perspective: nested.perspective,
1987 seg_id: 0,
1988 back: 0,
1989 front: nested.widget_count,
1990 }),
1991 info_tree,
1992 );
1993 info_tree.after_render(
1994 nested.frame_id,
1995 nested.scale_factor,
1996 Some(
1997 nested
1998 .renderer
1999 .as_ref()
2000 .and_then(|r| r.generation().ok())
2001 .unwrap_or(ViewProcessGen::INVALID),
2002 ),
2003 Some(nested.widget_count_offsets.clone()),
2004 );
2005
2006 self.hit_clips.parallel_fold(nested.hit_clips);
2008 self.display_list.parallel_fold(nested.display_list);
2009
2010 self.widget_count += nested.widget_count;
2014 self.debug_dot_overlays.extend(nested.debug_dot_overlays);
2015 }
2016
2017 pub fn render_widgets(&self) -> &Arc<RenderUpdates> {
2019 &self.render_widgets
2020 }
2021
2022 pub fn render_update_widgets(&self) -> &Arc<RenderUpdates> {
2024 &self.render_update_widgets
2025 }
2026
2027 pub fn finalize(self, info_tree: &WidgetInfoTree) -> BuiltFrame {
2029 info_tree.root().bounds_info().set_rendered(
2030 Some(WidgetRenderInfo {
2031 visible: self.visible,
2032 parent_perspective: self.perspective,
2033 seg_id: 0,
2034 back: 0,
2035 front: self.widget_count,
2036 }),
2037 info_tree,
2038 );
2039
2040 info_tree.after_render(
2041 self.frame_id,
2042 self.scale_factor,
2043 Some(
2044 self.renderer
2045 .as_ref()
2046 .and_then(|r| r.generation().ok())
2047 .unwrap_or(ViewProcessGen::INVALID),
2048 ),
2049 Some(self.widget_count_offsets),
2050 );
2051
2052 let display_list = self.display_list.finalize();
2053
2054 let clear_color = self.clear_color.unwrap_or_default();
2055
2056 BuiltFrame { display_list, clear_color }
2057 }
2058}
2059
2060pub struct ClipBuilder<'a> {
2064 builder: &'a mut FrameBuilder,
2065 render_count: usize,
2066 hit_test_count: usize,
2067}
2068impl ClipBuilder<'_> {
2069 pub fn push_clip_rect(&mut self, clip_rect: PxRect, clip_out: bool, hit_test: bool) {
2078 if self.builder.visible {
2079 self.builder.display_list.push_clip_rect(clip_rect, clip_out);
2080 self.render_count += 1;
2081 }
2082
2083 if hit_test || self.builder.auto_hit_test {
2084 self.builder.hit_clips.push_clip_rect(clip_rect.to_box2d(), clip_out);
2085 self.hit_test_count += 1;
2086 }
2087 }
2088
2089 pub fn push_clip_rounded_rect(&mut self, clip_rect: PxRect, corners: PxCornerRadius, clip_out: bool, hit_test: bool) {
2098 if self.builder.visible {
2099 self.builder.display_list.push_clip_rounded_rect(clip_rect, corners, clip_out);
2100 self.render_count += 1;
2101 }
2102
2103 if hit_test || self.builder.auto_hit_test {
2104 self.builder
2105 .hit_clips
2106 .push_clip_rounded_rect(clip_rect.to_box2d(), corners, clip_out);
2107 self.hit_test_count += 1;
2108 }
2109 }
2110}
2111
2112pub struct HitTestClipBuilder<'a> {
2116 hit_clips: &'a mut HitTestClips,
2117 count: usize,
2118}
2119impl HitTestClipBuilder<'_> {
2120 pub fn push_clip_rect(&mut self, rect: PxRect, clip_out: bool) {
2124 self.hit_clips.push_clip_rect(rect.to_box2d(), clip_out);
2125 self.count += 1;
2126 }
2127
2128 pub fn push_clip_rounded_rect(&mut self, rect: PxRect, corners: PxCornerRadius, clip_out: bool) {
2132 self.hit_clips.push_clip_rounded_rect(rect.to_box2d(), corners, clip_out);
2133 self.count += 1;
2134 }
2135
2136 pub fn push_clip_ellipse(&mut self, center: PxPoint, radii: PxSize, clip_out: bool) {
2140 self.hit_clips.push_clip_ellipse(center, radii, clip_out);
2141 self.count += 1;
2142 }
2143}
2144
2145pub struct HitTestBuilder<'a> {
2149 hit_clips: &'a mut HitTestClips,
2150 is_hit_testable: bool,
2151}
2152impl HitTestBuilder<'_> {
2153 pub fn is_hit_testable(&self) -> bool {
2155 self.is_hit_testable
2156 }
2157
2158 pub fn push_rect(&mut self, rect: PxRect) {
2160 if self.is_hit_testable && rect.size != PxSize::zero() {
2161 self.hit_clips.push_rect(rect.to_box2d());
2162 }
2163 }
2164
2165 pub fn push_rounded_rect(&mut self, rect: PxRect, corners: PxCornerRadius) {
2167 if self.is_hit_testable && rect.size != PxSize::zero() {
2168 self.hit_clips.push_rounded_rect(rect.to_box2d(), corners);
2169 }
2170 }
2171
2172 pub fn push_ellipse(&mut self, center: PxPoint, radii: PxSize) {
2174 if self.is_hit_testable && radii != PxSize::zero() {
2175 self.hit_clips.push_ellipse(center, radii);
2176 }
2177 }
2178
2179 pub fn push_clip_rect(&mut self, rect: PxRect, clip_out: bool, inner_hit_test: impl FnOnce(&mut Self)) {
2181 if !self.is_hit_testable {
2182 return;
2183 }
2184
2185 self.hit_clips.push_clip_rect(rect.to_box2d(), clip_out);
2186
2187 inner_hit_test(self);
2188
2189 self.hit_clips.pop_clip();
2190 }
2191
2192 pub fn push_clip_rounded_rect(
2194 &mut self,
2195 rect: PxRect,
2196 corners: PxCornerRadius,
2197 clip_out: bool,
2198 inner_hit_test: impl FnOnce(&mut Self),
2199 ) {
2200 self.push_clips(move |c| c.push_clip_rounded_rect(rect, corners, clip_out), inner_hit_test);
2201 }
2202
2203 pub fn push_clip_ellipse(&mut self, center: PxPoint, radii: PxSize, clip_out: bool, inner_hit_test: impl FnOnce(&mut Self)) {
2205 self.push_clips(move |c| c.push_clip_ellipse(center, radii, clip_out), inner_hit_test);
2206 }
2207
2208 pub fn push_clips(&mut self, clips: impl FnOnce(&mut HitTestClipBuilder), inner_hit_test: impl FnOnce(&mut Self)) {
2210 if !self.is_hit_testable {
2211 return;
2212 }
2213
2214 let mut count = {
2215 let mut builder = HitTestClipBuilder {
2216 hit_clips: &mut *self.hit_clips,
2217 count: 0,
2218 };
2219 clips(&mut builder);
2220 builder.count
2221 };
2222
2223 inner_hit_test(self);
2224
2225 while count > 0 {
2226 count -= 1;
2227 self.hit_clips.pop_clip();
2228 }
2229 }
2230
2231 pub fn push_transform(&mut self, transform: PxTransform, inner_hit_test: impl FnOnce(&mut Self)) {
2233 if !self.is_hit_testable {
2234 return;
2235 }
2236
2237 self.hit_clips.push_transform(FrameValue::Value(transform));
2238
2239 inner_hit_test(self);
2240
2241 self.hit_clips.pop_transform();
2242 }
2243
2244 pub fn push_border(&mut self, bounds: PxRect, widths: PxSideOffsets, corners: PxCornerRadius) {
2246 if !self.is_hit_testable {
2247 return;
2248 }
2249
2250 let bounds = bounds.to_box2d();
2251 let mut inner_bounds = bounds;
2252 inner_bounds.min.x += widths.left;
2253 inner_bounds.min.y += widths.top;
2254 inner_bounds.max.x -= widths.right;
2255 inner_bounds.max.y -= widths.bottom;
2256
2257 if inner_bounds.is_negative() {
2258 self.hit_clips.push_rounded_rect(bounds, corners);
2259 } else if corners == PxCornerRadius::zero() {
2260 self.hit_clips.push_clip_rect(inner_bounds, true);
2261 self.hit_clips.push_rect(bounds);
2262 self.hit_clips.pop_clip();
2263 } else {
2264 let inner_radii = corners.deflate(widths);
2265
2266 self.hit_clips.push_clip_rounded_rect(inner_bounds, inner_radii, true);
2267 self.hit_clips.push_rounded_rect(bounds, corners);
2268 self.hit_clips.pop_clip();
2269 }
2270 }
2271}
2272
2273#[non_exhaustive]
2275pub struct BuiltFrame {
2276 pub display_list: DisplayList,
2278 pub clear_color: Rgba,
2280}
2281
2282enum RenderLineCommand {
2283 Line(zng_view_api::LineStyle),
2284 Border(zng_view_api::BorderStyle),
2285}
2286impl border::LineStyle {
2287 fn render_command(self) -> RenderLineCommand {
2288 use RenderLineCommand::*;
2289 use border::LineStyle as LS;
2290 match self {
2291 LS::Solid => Line(zng_view_api::LineStyle::Solid),
2292 LS::Double => Border(zng_view_api::BorderStyle::Double),
2293 LS::Dotted => Line(zng_view_api::LineStyle::Dotted),
2294 LS::Dashed => Line(zng_view_api::LineStyle::Dashed),
2295 LS::Groove => Border(zng_view_api::BorderStyle::Groove),
2296 LS::Ridge => Border(zng_view_api::BorderStyle::Ridge),
2297 LS::Wavy(thickness) => Line(zng_view_api::LineStyle::Wavy(thickness)),
2298 LS::Hidden => Border(zng_view_api::BorderStyle::Hidden),
2299 }
2300 }
2301}
2302
2303pub struct FrameUpdate {
2310 render_update_widgets: Arc<RenderUpdates>,
2311
2312 transforms: Vec<FrameValueUpdate<PxTransform>>,
2313 floats: Vec<FrameValueUpdate<f32>>,
2314 colors: Vec<FrameValueUpdate<Rgba>>,
2315
2316 extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
2317
2318 current_clear_color: Rgba,
2319 clear_color: Option<Rgba>,
2320 frame_id: FrameId,
2321
2322 widget_id: WidgetId,
2323 transform: PxTransform,
2324 parent_child_offset: PxVector,
2325 perspective: Option<(f32, PxPoint)>,
2326 inner_transform: Option<PxTransform>,
2327 child_offset: PxVector,
2328 can_reuse_widget: bool,
2329 widget_bounds: WidgetBoundsInfo,
2330 parent_inner_bounds: Option<PxRect>,
2331
2332 auto_hit_test: bool,
2333 visible: bool,
2334}
2335impl FrameUpdate {
2336 pub fn new(
2344 render_update_widgets: Arc<RenderUpdates>,
2345 frame_id: FrameId,
2346 root_id: WidgetId,
2347 root_bounds: WidgetBoundsInfo,
2348 clear_color: Rgba,
2349 ) -> Self {
2350 FrameUpdate {
2351 render_update_widgets,
2352 widget_id: root_id,
2353 widget_bounds: root_bounds,
2354 transforms: vec![],
2355 floats: vec![],
2356 colors: vec![],
2357 extensions: vec![],
2358 clear_color: None,
2359 frame_id,
2360 current_clear_color: clear_color,
2361
2362 transform: PxTransform::identity(),
2363 perspective: None,
2364 parent_child_offset: PxVector::zero(),
2365 inner_transform: Some(PxTransform::identity()),
2366 child_offset: PxVector::zero(),
2367 can_reuse_widget: true,
2368
2369 auto_hit_test: false,
2370 parent_inner_bounds: None,
2371 visible: true,
2372 }
2373 }
2374
2375 pub fn frame_id(&self) -> FrameId {
2377 self.frame_id
2378 }
2379
2380 pub fn is_outer(&self) -> bool {
2387 self.inner_transform.is_some()
2388 }
2389
2390 pub fn transform(&self) -> &PxTransform {
2392 &self.transform
2393 }
2394
2395 pub fn set_clear_color(&mut self, color: Rgba) {
2397 if self.visible {
2398 self.clear_color = Some(color);
2399 }
2400 }
2401
2402 pub fn auto_hit_test(&self) -> bool {
2404 self.auto_hit_test
2405 }
2406 pub fn with_auto_hit_test(&mut self, auto_hit_test: bool, render_update: impl FnOnce(&mut Self)) {
2410 let prev = mem::replace(&mut self.auto_hit_test, auto_hit_test);
2411 render_update(self);
2412 self.auto_hit_test = prev;
2413 }
2414
2415 pub fn is_visible(&self) -> bool {
2417 self.visible
2418 }
2419
2420 pub fn hidden(&mut self, update: impl FnOnce(&mut Self)) {
2427 let parent_visible = mem::replace(&mut self.visible, false);
2428 update(self);
2429 self.visible = parent_visible;
2430 }
2431
2432 pub fn update_transform(&mut self, new_value: FrameValueUpdate<PxTransform>, hit_test: bool) {
2440 if self.visible {
2441 self.transforms.push(new_value);
2442 }
2443
2444 if hit_test || self.auto_hit_test {
2445 self.widget_bounds.update_hit_test_transform(new_value);
2446 }
2447 }
2448
2449 pub fn update_transform_opt(&mut self, new_value: Option<FrameValueUpdate<PxTransform>>, hit_test: bool) {
2451 if let Some(value) = new_value {
2452 self.update_transform(value, hit_test)
2453 }
2454 }
2455
2456 pub fn with_transform(&mut self, new_value: FrameValueUpdate<PxTransform>, hit_test: bool, render_update: impl FnOnce(&mut Self)) {
2465 self.with_transform_value(&new_value.value, render_update);
2466 self.update_transform(new_value, hit_test);
2467 }
2468
2469 pub fn with_transform_opt(
2473 &mut self,
2474 new_value: Option<FrameValueUpdate<PxTransform>>,
2475 hit_test: bool,
2476 render_update: impl FnOnce(&mut Self),
2477 ) {
2478 match new_value {
2479 Some(value) => self.with_transform(value, hit_test, render_update),
2480 None => render_update(self),
2481 }
2482 }
2483
2484 pub fn with_child(&mut self, offset: PxVector, render_update: impl FnOnce(&mut Self)) {
2488 self.child_offset = offset;
2489 render_update(self);
2490 self.child_offset = PxVector::zero();
2491 }
2492
2493 pub fn with_transform_value(&mut self, value: &PxTransform, render_update: impl FnOnce(&mut Self)) {
2499 let parent_transform = self.transform;
2500 self.transform = value.then(&parent_transform);
2501
2502 render_update(self);
2503 self.transform = parent_transform;
2504 }
2505
2506 pub fn with_inner_transform(&mut self, transform: &PxTransform, render_update: impl FnOnce(&mut Self)) {
2512 if let Some(inner_transform) = &mut self.inner_transform {
2513 let parent = *inner_transform;
2514 *inner_transform = inner_transform.then(transform);
2515
2516 render_update(self);
2517
2518 if let Some(inner_transform) = &mut self.inner_transform {
2519 *inner_transform = parent;
2520 }
2521 } else {
2522 tracing::error!("called `with_inner_transform` inside inner context of `{}`", self.widget_id);
2523 render_update(self);
2524 }
2525 }
2526
2527 pub fn can_reuse_widget(&self) -> bool {
2531 self.can_reuse_widget
2532 }
2533
2534 pub fn with_no_reuse(&mut self, render_update: impl FnOnce(&mut Self)) {
2538 let prev_can_reuse = self.can_reuse_widget;
2539 self.can_reuse_widget = false;
2540 render_update(self);
2541 self.can_reuse_widget = prev_can_reuse;
2542 }
2543
2544 pub fn update_widget(&mut self, render_update: impl FnOnce(&mut Self)) {
2551 let wgt_info = WIDGET.info();
2552 let id = wgt_info.id();
2553
2554 #[cfg(debug_assertions)]
2555 if self.inner_transform.is_some() && wgt_info.parent().is_some() {
2556 tracing::error!(
2557 "called `update_widget` for `{}` without calling `update_inner` for the parent `{}`",
2558 WIDGET.trace_id(),
2559 self.widget_id
2560 );
2561 }
2562
2563 let bounds = wgt_info.bounds_info();
2564 if bounds.is_collapsed() {
2565 let _ = WIDGET.take_update(UpdateFlags::LAYOUT | UpdateFlags::RENDER | UpdateFlags::RENDER_UPDATE);
2566 return;
2567 } else {
2568 #[cfg(debug_assertions)]
2569 if WIDGET.pending_update().contains(UpdateFlags::LAYOUT) {
2570 tracing::error!("called `update_widget` for `{}` with pending layout", WIDGET.trace_id());
2571 }
2572 }
2573
2574 let tree = wgt_info.tree();
2575
2576 let parent_can_reuse = self.can_reuse_widget;
2577 let parent_perspective = mem::replace(&mut self.perspective, wgt_info.perspective());
2578 let parent_bounds = mem::replace(&mut self.widget_bounds, bounds.clone());
2579
2580 if let Some((_, o)) = &mut self.perspective {
2581 *o -= self.child_offset;
2582 }
2583
2584 let render_info = bounds.render_info();
2585 if let Some(i) = &render_info
2586 && i.parent_perspective != self.perspective
2587 {
2588 self.can_reuse_widget = false;
2589 }
2590
2591 let outer_transform = PxTransform::from(self.child_offset).then(&self.transform);
2592
2593 if !WIDGET.take_update(UpdateFlags::RENDER_UPDATE)
2594 && self.can_reuse_widget
2595 && !self.render_update_widgets.delivery_list().enter_widget(id)
2596 && bounds.parent_child_offset() == self.child_offset
2597 {
2598 let _span = tracing::trace_span!("reuse-descendants", id=?self.widget_id).entered();
2599
2600 let prev_outer = bounds.outer_transform();
2601 if prev_outer != outer_transform {
2602 if let Some(undo_prev) = prev_outer.inverse() {
2603 let patch = undo_prev.then(&outer_transform);
2604
2605 let update = |info: WidgetInfo| {
2606 let bounds = info.bounds_info();
2607 bounds.set_outer_transform(bounds.outer_transform().then(&patch), tree);
2608 bounds.set_inner_transform(
2609 bounds.inner_transform().then(&patch),
2610 tree,
2611 info.id(),
2612 info.parent().map(|p| p.inner_bounds()),
2613 );
2614 };
2615 let targets = tree.get(id).unwrap().self_and_descendants();
2616 if PARALLEL_VAR.get().contains(Parallel::RENDER) {
2617 targets.par_bridge().for_each(update);
2618 } else {
2619 targets.for_each(update);
2620 }
2621
2622 return; }
2624 } else {
2625 return; }
2627
2628 self.can_reuse_widget = false;
2630 }
2631
2632 bounds.set_parent_child_offset(self.child_offset);
2633 bounds.set_outer_transform(outer_transform, tree);
2634 self.parent_child_offset = mem::take(&mut self.child_offset);
2635 self.inner_transform = Some(PxTransform::identity());
2636 let parent_id = self.widget_id;
2637 self.widget_id = id;
2638
2639 render_update(self);
2640
2641 if let Some(mut i) = render_info {
2642 i.parent_perspective = self.perspective;
2643 bounds.set_rendered(Some(i), tree);
2644 }
2645 self.parent_child_offset = PxVector::zero();
2646 self.inner_transform = None;
2647 self.widget_id = parent_id;
2648 self.can_reuse_widget = parent_can_reuse;
2649 self.perspective = parent_perspective;
2650 self.widget_bounds = parent_bounds;
2651 }
2652
2653 pub fn reuse_widget(&mut self) {
2658 if self.inner_transform.is_some() {
2659 tracing::error!(
2660 "called `reuse_widget` for `{}` without calling `update_inner` for the parent `{}`",
2661 WIDGET.trace_id(),
2662 self.widget_id
2663 );
2664 }
2665 }
2666
2667 pub fn update_inner(
2671 &mut self,
2672 layout_translation_key: FrameValueKey<PxTransform>,
2673 layout_translation_animating: bool,
2674 render_update: impl FnOnce(&mut Self),
2675 ) {
2676 let id = WIDGET.id();
2677 if let Some(mut inner_transform) = self.inner_transform.take() {
2678 let bounds = WIDGET.bounds();
2679 let tree = WINDOW.info();
2680
2681 let inner_offset = bounds.inner_offset();
2682 if let Some((p, mut o)) = self.perspective {
2683 o -= inner_offset;
2684 let x = o.x.0 as f32;
2685 let y = o.y.0 as f32;
2686 let p = PxTransform::translation(-x, -y)
2687 .then(&PxTransform::perspective(p))
2688 .then_translate(euclid::vec2(x, y));
2689 inner_transform = inner_transform.then(&p);
2690 }
2691 let inner_transform = inner_transform.then_translate((self.parent_child_offset + inner_offset).cast());
2692 self.update_transform(layout_translation_key.update(inner_transform, layout_translation_animating), false);
2693 let parent_transform = self.transform;
2694
2695 self.transform = inner_transform.then(&parent_transform);
2696
2697 bounds.set_inner_transform(self.transform, &tree, id, self.parent_inner_bounds);
2698 let parent_inner_bounds = self.parent_inner_bounds.replace(bounds.inner_bounds());
2699
2700 render_update(self);
2701
2702 self.transform = parent_transform;
2703 self.parent_inner_bounds = parent_inner_bounds;
2704 } else {
2705 tracing::error!("called `update_inner` more then once for `{}`", id);
2706 render_update(self)
2707 }
2708 }
2709
2710 pub fn update_f32(&mut self, new_value: FrameValueUpdate<f32>) {
2712 if self.visible {
2713 self.floats.push(new_value);
2714 }
2715 }
2716
2717 pub fn update_f32_opt(&mut self, new_value: Option<FrameValueUpdate<f32>>) {
2719 if let Some(value) = new_value {
2720 self.update_f32(value)
2721 }
2722 }
2723
2724 pub fn update_color(&mut self, new_value: FrameValueUpdate<Rgba>) {
2728 if self.visible {
2729 self.colors.push(new_value)
2730 }
2731 }
2732
2733 pub fn update_color_opt(&mut self, new_value: Option<FrameValueUpdate<Rgba>>) {
2735 if let Some(value) = new_value {
2736 self.update_color(value)
2737 }
2738 }
2739
2740 pub fn update_extension_raw(&mut self, extension_id: ApiExtensionId, extension_payload: ApiExtensionPayload) {
2742 self.extensions.push((extension_id, extension_payload))
2743 }
2744
2745 pub fn update_extension<T: serde::Serialize>(&mut self, extension_id: ApiExtensionId, payload: &T) {
2747 self.update_extension_raw(extension_id, ApiExtensionPayload::serialize(payload).unwrap())
2748 }
2749
2750 pub fn parallel_split(&self) -> ParallelBuilder<Self> {
2756 if self.inner_transform.is_some() && WIDGET.parent_id().is_some() {
2757 tracing::error!(
2758 "called `parallel_split` inside `{}` and before calling `update_inner`",
2759 self.widget_id
2760 );
2761 }
2762
2763 ParallelBuilder(Some(Self {
2764 render_update_widgets: self.render_update_widgets.clone(),
2765 current_clear_color: self.current_clear_color,
2766 frame_id: self.frame_id,
2767
2768 transforms: vec![],
2769 floats: vec![],
2770 colors: vec![],
2771 extensions: vec![],
2772 clear_color: None,
2773
2774 widget_id: self.widget_id,
2775 transform: self.transform,
2776 perspective: self.perspective,
2777 parent_child_offset: self.parent_child_offset,
2778 inner_transform: self.inner_transform,
2779 child_offset: self.child_offset,
2780 can_reuse_widget: self.can_reuse_widget,
2781 widget_bounds: self.widget_bounds.clone(),
2782 parent_inner_bounds: self.parent_inner_bounds,
2783 auto_hit_test: self.auto_hit_test,
2784 visible: self.visible,
2785 }))
2786 }
2787
2788 pub fn parallel_fold(&mut self, mut split: ParallelBuilder<Self>) {
2790 let mut split = split.take();
2791
2792 debug_assert_eq!(self.frame_id, split.frame_id);
2793 debug_assert_eq!(self.widget_id, split.widget_id);
2794
2795 fn take_or_append<T>(t: &mut Vec<T>, s: &mut Vec<T>) {
2796 if t.is_empty() {
2797 *t = mem::take(s);
2798 } else {
2799 t.append(s)
2800 }
2801 }
2802
2803 take_or_append(&mut self.transforms, &mut split.transforms);
2804 take_or_append(&mut self.floats, &mut split.floats);
2805 take_or_append(&mut self.colors, &mut split.colors);
2806 take_or_append(&mut self.extensions, &mut split.extensions);
2807
2808 if let Some(c) = self.clear_color.take() {
2809 self.clear_color = Some(c);
2810 }
2811 }
2812
2813 pub fn with_nested_window(
2815 &mut self,
2816 render_update_widgets: Arc<RenderUpdates>,
2817 root_id: WidgetId,
2818 root_bounds: WidgetBoundsInfo,
2819 update: impl FnOnce(&mut Self),
2820 ) {
2821 let mut nested = Self::new(render_update_widgets, self.frame_id, root_id, root_bounds, self.current_clear_color);
2822
2823 update(&mut nested);
2824
2825 fn take_or_append<T>(t: &mut Vec<T>, s: &mut Vec<T>) {
2827 if t.is_empty() {
2828 *t = mem::take(s);
2829 } else {
2830 t.append(s)
2831 }
2832 }
2833 take_or_append(&mut self.transforms, &mut nested.transforms);
2834 take_or_append(&mut self.floats, &mut nested.floats);
2835 take_or_append(&mut self.colors, &mut nested.colors);
2836 take_or_append(&mut self.extensions, &mut nested.extensions);
2837 }
2838
2839 pub fn render_update_widgets(&self) -> &Arc<RenderUpdates> {
2841 &self.render_update_widgets
2842 }
2843
2844 pub fn finalize(mut self, info_tree: &WidgetInfoTree) -> BuiltFrameUpdate {
2848 info_tree.after_render_update(self.frame_id);
2849
2850 if self.clear_color == Some(self.current_clear_color) {
2851 self.clear_color = None;
2852 }
2853
2854 BuiltFrameUpdate {
2855 clear_color: self.clear_color,
2856 transforms: self.transforms,
2857 floats: self.floats,
2858 colors: self.colors,
2859 extensions: self.extensions,
2860 }
2861 }
2862}
2863
2864#[non_exhaustive]
2866pub struct BuiltFrameUpdate {
2867 pub transforms: Vec<FrameValueUpdate<PxTransform>>,
2869 pub floats: Vec<FrameValueUpdate<f32>>,
2871 pub colors: Vec<FrameValueUpdate<Rgba>>,
2873 pub clear_color: Option<Rgba>,
2875 pub extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
2877}
2878
2879unique_id_32! {
2880 #[derive(Debug)]
2881 struct FrameBindingKeyId;
2882}
2883impl_unique_id_bytemuck!(FrameBindingKeyId);
2884
2885unique_id_32! {
2886 #[derive(Debug)]
2888 pub struct SpatialFrameId;
2889}
2890impl_unique_id_bytemuck!(SpatialFrameId);
2891
2892#[derive(Clone, Copy, PartialEq, Eq)]
2893enum ReferenceFrameIdInner {
2894 Unique(SpatialFrameId),
2895 UniqueIndex(SpatialFrameId, u32),
2896 Widget(WidgetId),
2897 WidgetIndex(WidgetId, u32),
2898 FrameValue(FrameValueKey<PxTransform>),
2899 FrameValueIndex(FrameValueKey<PxTransform>, u32),
2900}
2901impl ReferenceFrameIdInner {
2902 const _RESERVED: u64 = 1 << 63; const UNIQUE: u64 = 1 << 62;
2904 const WIDGET: u64 = 1 << 61;
2905 const FRAME_VALUE: u64 = 1 << 60;
2906}
2907impl From<ReferenceFrameIdInner> for RenderReferenceFrameId {
2908 fn from(value: ReferenceFrameIdInner) -> Self {
2909 match value {
2910 ReferenceFrameIdInner::UniqueIndex(id, index) => {
2911 RenderReferenceFrameId(id.get() as u64, index as u64 | ReferenceFrameIdInner::UNIQUE)
2912 }
2913 ReferenceFrameIdInner::WidgetIndex(id, index) => RenderReferenceFrameId(id.get(), index as u64 | ReferenceFrameIdInner::WIDGET),
2914 ReferenceFrameIdInner::FrameValue(key) => {
2915 RenderReferenceFrameId(((key.id.get() as u64) << 32) | u32::MAX as u64, ReferenceFrameIdInner::FRAME_VALUE)
2916 }
2917 ReferenceFrameIdInner::FrameValueIndex(key, index) => {
2918 RenderReferenceFrameId(((key.id.get() as u64) << 32) | index as u64, ReferenceFrameIdInner::FRAME_VALUE)
2919 }
2920 ReferenceFrameIdInner::Unique(id) => {
2921 RenderReferenceFrameId(id.get() as u64, (u32::MAX as u64 + 1) | ReferenceFrameIdInner::UNIQUE)
2922 }
2923 ReferenceFrameIdInner::Widget(id) => RenderReferenceFrameId(id.get(), (u32::MAX as u64 + 1) | ReferenceFrameIdInner::WIDGET),
2924 }
2925 }
2926}
2927
2928#[derive(Clone, Copy, PartialEq, Eq)]
2935pub struct ReferenceFrameId(ReferenceFrameIdInner);
2936impl ReferenceFrameId {
2937 fn from_widget(widget_id: WidgetId) -> Self {
2941 Self(ReferenceFrameIdInner::Widget(widget_id))
2942 }
2943
2944 pub fn from_widget_child(parent_id: WidgetId, child_index: u32) -> Self {
2948 Self(ReferenceFrameIdInner::WidgetIndex(parent_id, child_index))
2949 }
2950
2951 pub fn from_unique(id: SpatialFrameId) -> Self {
2953 Self(ReferenceFrameIdInner::Unique(id))
2954 }
2955
2956 pub fn from_unique_child(id: SpatialFrameId, child_index: u32) -> Self {
2958 Self(ReferenceFrameIdInner::UniqueIndex(id, child_index))
2959 }
2960
2961 pub fn from_frame_value(frame_value_key: FrameValueKey<PxTransform>) -> Self {
2963 Self(ReferenceFrameIdInner::FrameValue(frame_value_key))
2964 }
2965
2966 pub fn from_frame_value_child(frame_value_key: FrameValueKey<PxTransform>, child_index: u32) -> Self {
2968 Self(ReferenceFrameIdInner::FrameValueIndex(frame_value_key, child_index))
2969 }
2970}
2971impl From<ReferenceFrameId> for RenderReferenceFrameId {
2972 fn from(value: ReferenceFrameId) -> Self {
2973 value.0.into()
2974 }
2975}
2976impl From<FrameValueKey<PxTransform>> for ReferenceFrameId {
2977 fn from(value: FrameValueKey<PxTransform>) -> Self {
2978 Self::from_frame_value(value)
2979 }
2980}
2981impl From<SpatialFrameId> for ReferenceFrameId {
2982 fn from(id: SpatialFrameId) -> Self {
2983 Self::from_unique(id)
2984 }
2985}
2986impl From<(SpatialFrameId, u32)> for ReferenceFrameId {
2987 fn from((id, index): (SpatialFrameId, u32)) -> Self {
2988 Self::from_unique_child(id, index)
2989 }
2990}
2991impl From<(WidgetId, u32)> for ReferenceFrameId {
2992 fn from((id, index): (WidgetId, u32)) -> Self {
2993 Self::from_widget_child(id, index)
2994 }
2995}
2996impl From<(FrameValueKey<PxTransform>, u32)> for ReferenceFrameId {
2997 fn from((key, index): (FrameValueKey<PxTransform>, u32)) -> Self {
2998 Self::from_frame_value_child(key, index)
2999 }
3000}
3001
3002#[derive(Debug)]
3004pub struct FrameValueKey<T> {
3005 id: FrameBindingKeyId,
3006 _type: PhantomData<T>,
3007}
3008impl<T> PartialEq for FrameValueKey<T> {
3009 fn eq(&self, other: &Self) -> bool {
3010 self.id == other.id
3011 }
3012}
3013impl<T> Eq for FrameValueKey<T> {}
3014impl<T> Clone for FrameValueKey<T> {
3015 fn clone(&self) -> Self {
3016 *self
3017 }
3018}
3019impl<T> Copy for FrameValueKey<T> {}
3020impl<T> FrameValueKey<T> {
3021 pub fn new_unique() -> Self {
3023 FrameValueKey {
3024 id: FrameBindingKeyId::new_unique(),
3025 _type: PhantomData,
3026 }
3027 }
3028
3029 pub fn to_wr(self) -> zng_view_api::display_list::FrameValueId {
3031 Self::to_wr_child(self, u32::MAX)
3032 }
3033
3034 pub fn to_wr_child(self, child_index: u32) -> zng_view_api::display_list::FrameValueId {
3036 zng_view_api::display_list::FrameValueId::from_raw(((self.id.get() as u64) << 32) | child_index as u64)
3037 }
3038
3039 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: &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: &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: &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: &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: &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: &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: &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: &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 { FontSynthesis::ENABLED } else { FontSynthesis::DISABLED }
3181 }
3182}