1use zng_wgt::prelude::{gradient::*, *};
4
5pub struct GradientBuilder {
11 stops: Var<GradientStops>,
12}
13
14pub fn gradient(stops: impl IntoVar<GradientStops>) -> GradientBuilder {
16 GradientBuilder { stops: stops.into_var() }
17}
18
19pub fn linear_gradient(axis: impl IntoVar<LinearGradientAxis>, stops: impl IntoVar<GradientStops>) -> LinearGradient {
23 gradient(stops).linear(axis)
24}
25
26pub fn radial_gradient(
30 center: impl IntoVar<Point>,
31 radius: impl IntoVar<GradientRadius>,
32 stops: impl IntoVar<GradientStops>,
33) -> RadialGradient {
34 gradient(stops).radial(center, radius)
35}
36
37pub fn conic_gradient(center: impl IntoVar<Point>, angle: impl IntoVar<AngleRadian>, stops: impl IntoVar<GradientStops>) -> ConicGradient {
41 gradient(stops).conic(center, angle)
42}
43
44impl GradientBuilder {
45 pub fn linear(self, axis: impl IntoVar<LinearGradientAxis>) -> LinearGradient {
50 LinearGradient {
51 stops: self.stops,
52 axis: axis.into_var(),
53 extend_mode: ExtendMode::Clamp.into_var(),
54
55 data: LinearNodeData::default(),
56 }
57 }
58
59 pub fn radial(self, center: impl IntoVar<Point>, radius: impl IntoVar<GradientRadius>) -> RadialGradient {
64 RadialGradient {
65 stops: self.stops,
66 center: center.into_var(),
67 radius: radius.into_var(),
68 extend_mode: ExtendMode::Clamp.into_var(),
69 data: RadialNodeData::default(),
70 }
71 }
72
73 pub fn conic(self, center: impl IntoVar<Point>, angle: impl IntoVar<AngleRadian>) -> ConicGradient {
78 ConicGradient {
79 stops: self.stops,
80 center: center.into_var(),
81 angle: angle.into_var(),
82 extend_mode: ExtendMode::Clamp.into_var(),
83 data: ConicNodeData::default(),
84 }
85 }
86}
87
88pub struct LinearGradient {
97 stops: Var<GradientStops>,
98 axis: Var<LinearGradientAxis>,
99 extend_mode: Var<ExtendMode>,
100
101 data: LinearNodeData,
102}
103impl LinearGradient {
104 pub fn extend_mode(self, mode: impl IntoVar<ExtendMode>) -> LinearGradient {
110 LinearGradient {
111 stops: self.stops,
112 axis: self.axis,
113 extend_mode: mode.into_var(),
114 data: self.data,
115 }
116 }
117
118 pub fn repeat(self) -> LinearGradient {
122 self.extend_mode(ExtendMode::Repeat)
123 }
124
125 pub fn reflect(self) -> LinearGradient {
129 self.extend_mode(ExtendMode::Reflect)
130 }
131
132 pub fn tile(self, tile_size: impl IntoVar<Size>, tile_spacing: impl IntoVar<Size>) -> TiledLinearGradient {
134 TiledLinearGradient {
135 stops: self.stops,
136 axis: self.axis,
137 extend_mode: self.extend_mode,
138 tile_origin: const_var(Point::zero()),
139 tile_size: tile_size.into_var(),
140 tile_spacing: tile_spacing.into_var(),
141 data: self.data,
142 tile_data: TiledNodeData::default(),
143 }
144 }
145
146 pub fn tile_size(self, size: impl IntoVar<Size>) -> TiledLinearGradient {
151 self.tile(size, Size::zero())
152 }
153}
154
155pub struct TiledLinearGradient {
162 stops: Var<GradientStops>,
163 axis: Var<LinearGradientAxis>,
164 extend_mode: Var<ExtendMode>,
165 tile_origin: Var<Point>,
166 tile_size: Var<Size>,
167 tile_spacing: Var<Size>,
168 data: LinearNodeData,
169 tile_data: TiledNodeData,
170}
171impl TiledLinearGradient {
172 pub fn tile_spacing(self, spacing: impl IntoVar<Size>) -> TiledLinearGradient {
182 TiledLinearGradient {
183 stops: self.stops,
184 axis: self.axis,
185 extend_mode: self.extend_mode,
186 tile_origin: self.tile_origin,
187 tile_size: self.tile_size,
188 tile_spacing: spacing.into_var(),
189 data: self.data,
190 tile_data: self.tile_data,
191 }
192 }
193
194 pub fn tile_origin(self, origin: impl IntoVar<Point>) -> TiledLinearGradient {
199 TiledLinearGradient {
200 stops: self.stops,
201 axis: self.axis,
202 extend_mode: self.extend_mode,
203 tile_origin: origin.into_var(),
204 tile_size: self.tile_size,
205 tile_spacing: self.tile_spacing,
206 data: self.data,
207 tile_data: self.tile_data,
208 }
209 }
210}
211
212pub struct RadialGradient {
221 stops: Var<GradientStops>,
222 center: Var<Point>,
223 radius: Var<GradientRadius>,
224 extend_mode: Var<ExtendMode>,
225
226 data: RadialNodeData,
227}
228impl RadialGradient {
229 pub fn extend_mode(self, mode: impl IntoVar<ExtendMode>) -> RadialGradient {
235 RadialGradient {
236 stops: self.stops,
237 center: self.center,
238 radius: self.radius,
239 extend_mode: mode.into_var(),
240 data: self.data,
241 }
242 }
243
244 pub fn repeat(self) -> RadialGradient {
248 self.extend_mode(ExtendMode::Repeat)
249 }
250
251 pub fn reflect(self) -> RadialGradient {
255 self.extend_mode(ExtendMode::Reflect)
256 }
257
258 pub fn tile(self, tile_size: impl IntoVar<Size>, tile_spacing: impl IntoVar<Size>) -> TiledRadialGradient {
260 TiledRadialGradient {
261 stops: self.stops,
262 center: self.center,
263 radius: self.radius,
264 extend_mode: self.extend_mode,
265 tile_origin: const_var(Point::zero()),
266 tile_size: tile_size.into_var(),
267 tile_spacing: tile_spacing.into_var(),
268 data: self.data,
269 tile_data: TiledNodeData::default(),
270 }
271 }
272
273 pub fn tile_size(self, size: impl IntoVar<Size>) -> TiledRadialGradient {
275 self.tile(size, Size::zero())
276 }
277}
278
279pub struct TiledRadialGradient {
287 stops: Var<GradientStops>,
288 center: Var<Point>,
289 radius: Var<GradientRadius>,
290 extend_mode: Var<ExtendMode>,
291 tile_origin: Var<Point>,
292 tile_size: Var<Size>,
293 tile_spacing: Var<Size>,
294 data: RadialNodeData,
295 tile_data: TiledNodeData,
296}
297impl TiledRadialGradient {
298 pub fn tile_spacing(self, spacing: impl IntoVar<Size>) -> TiledRadialGradient {
308 TiledRadialGradient {
309 stops: self.stops,
310 center: self.center,
311 radius: self.radius,
312 extend_mode: self.extend_mode,
313 tile_origin: self.tile_origin,
314 tile_size: self.tile_size,
315 tile_spacing: spacing.into_var(),
316 data: self.data,
317 tile_data: self.tile_data,
318 }
319 }
320
321 pub fn tile_origin(self, origin: impl IntoVar<Point>) -> TiledRadialGradient {
326 TiledRadialGradient {
327 stops: self.stops,
328 center: self.center,
329 radius: self.radius,
330 extend_mode: self.extend_mode,
331 tile_origin: origin.into_var(),
332 tile_size: self.tile_size,
333 tile_spacing: self.tile_spacing,
334 data: self.data,
335 tile_data: self.tile_data,
336 }
337 }
338}
339
340pub struct ConicGradient {
349 stops: Var<GradientStops>,
350 center: Var<Point>,
351 angle: Var<AngleRadian>,
352 extend_mode: Var<ExtendMode>,
353
354 data: ConicNodeData,
355}
356impl ConicGradient {
357 pub fn extend_mode(self, mode: impl IntoVar<ExtendMode>) -> ConicGradient {
363 ConicGradient {
364 stops: self.stops,
365 center: self.center,
366 angle: self.angle,
367 extend_mode: mode.into_var(),
368 data: self.data,
369 }
370 }
371
372 pub fn repeat(self) -> ConicGradient {
376 self.extend_mode(ExtendMode::Repeat)
377 }
378
379 pub fn reflect(self) -> ConicGradient {
383 self.extend_mode(ExtendMode::Reflect)
384 }
385
386 pub fn tile(self, tile_size: impl IntoVar<Size>, tile_spacing: impl IntoVar<Size>) -> TiledConicGradient {
388 TiledConicGradient {
389 stops: self.stops,
390 center: self.center,
391 angle: self.angle,
392 extend_mode: self.extend_mode,
393 tile_origin: const_var(Point::zero()),
394 tile_size: tile_size.into_var(),
395 tile_spacing: tile_spacing.into_var(),
396 data: self.data,
397 tile_data: TiledNodeData::default(),
398 }
399 }
400
401 pub fn tile_size(self, size: impl IntoVar<Size>) -> TiledConicGradient {
403 self.tile(size, Size::zero())
404 }
405}
406
407pub struct TiledConicGradient {
415 stops: Var<GradientStops>,
416 center: Var<Point>,
417 angle: Var<AngleRadian>,
418 extend_mode: Var<ExtendMode>,
419 tile_origin: Var<Point>,
420 tile_size: Var<Size>,
421 tile_spacing: Var<Size>,
422 data: ConicNodeData,
423 tile_data: TiledNodeData,
424}
425impl TiledConicGradient {
426 pub fn tile_spacing<TS2>(self, spacing: impl IntoVar<Size>) -> TiledConicGradient {
436 TiledConicGradient {
437 stops: self.stops,
438 center: self.center,
439 angle: self.angle,
440 extend_mode: self.extend_mode,
441 tile_origin: self.tile_origin,
442 tile_size: self.tile_size,
443 tile_spacing: spacing.into_var(),
444 data: self.data,
445 tile_data: self.tile_data,
446 }
447 }
448
449 pub fn tile_origin(self, origin: impl IntoVar<Point>) -> TiledConicGradient {
454 TiledConicGradient {
455 stops: self.stops,
456 center: self.center,
457 angle: self.angle,
458 extend_mode: self.extend_mode,
459 tile_origin: origin.into_var(),
460 tile_size: self.tile_size,
461 tile_spacing: self.tile_spacing,
462 data: self.data,
463 tile_data: self.tile_data,
464 }
465 }
466}
467
468#[derive(Default)]
469struct LinearNodeData {
470 line: PxLine,
471 stops: Vec<RenderGradientStop>,
472 size: PxSize,
473}
474impl UiNodeImpl for LinearGradient {
475 fn children_len(&self) -> usize {
476 0
477 }
478
479 fn with_child(&mut self, _: usize, _: &mut dyn FnMut(&mut UiNode)) {}
480
481 fn init(&mut self) {
482 WIDGET.sub_var_layout(&self.axis).sub_var(&self.stops).sub_var(&self.extend_mode);
483 }
484
485 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
486 LAYOUT.constraints().fill_size()
487 }
488
489 fn update(&mut self, _: &WidgetUpdates) {
490 if self.stops.is_new() || self.extend_mode.is_new() {
491 WIDGET.layout().render();
492 }
493 }
494
495 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
496 let size = LAYOUT.constraints().fill_size();
497 let axis = self.axis.layout();
498 if self.data.size != size || self.data.line != axis {
499 self.data.size = size;
500 self.data.line = axis;
501 WIDGET.render();
502 }
503
504 let length = self.data.line.length();
505 LAYOUT.with_constraints(LAYOUT.constraints().with_new_exact_x(length), || {
506 self.stops
507 .with(|s| s.layout_linear(LayoutAxis::X, self.extend_mode.get(), &mut self.data.line, &mut self.data.stops))
508 });
509
510 size
511 }
512
513 fn render(&mut self, frame: &mut FrameBuilder) {
514 frame.push_linear_gradient(
515 PxRect::from_size(self.data.size),
516 self.data.line,
517 &self.data.stops,
518 self.extend_mode.get().into(),
519 PxPoint::zero(),
520 self.data.size,
521 PxSize::zero(),
522 );
523 }
524}
525
526#[derive(Default)]
527struct TiledNodeData {
528 origin: PxPoint,
529 size: PxSize,
530 spacing: PxSize,
531}
532impl UiNodeImpl for TiledLinearGradient {
533 fn children_len(&self) -> usize {
534 0
535 }
536
537 fn with_child(&mut self, _: usize, _: &mut dyn FnMut(&mut UiNode)) {}
538
539 fn init(&mut self) {
540 WIDGET
541 .sub_var_layout(&self.axis)
542 .sub_var(&self.stops)
543 .sub_var(&self.extend_mode)
544 .sub_var_layout(&self.tile_origin)
545 .sub_var_layout(&self.tile_size)
546 .sub_var_layout(&self.tile_spacing);
547 }
548
549 fn update(&mut self, _: &WidgetUpdates) {
550 if self.stops.is_new() || self.extend_mode.is_new() {
551 WIDGET.layout().render();
552 }
553 }
554
555 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
556 LAYOUT.constraints().fill_size()
557 }
558
559 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
560 let constraints = LAYOUT.constraints();
561 let size = constraints.fill_size();
562 let axis = self.axis.layout();
563 let tile_size = self.tile_size.layout_dft(size);
564
565 let mut request_render = false;
566
567 if self.data.size != size || self.data.line != axis || self.tile_data.size != tile_size {
568 self.data.size = size;
569 self.data.line = self.axis.layout();
570 self.tile_data.size = tile_size;
571 request_render = true;
572 }
573
574 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(self.tile_data.size), || {
575 let leftover = tile_leftover(self.tile_data.size, size);
576 LAYOUT.with_leftover(Some(leftover.width), Some(leftover.height), || {
577 let spacing = self.tile_spacing.layout();
578 request_render |= self.tile_data.spacing != spacing;
579 self.tile_data.spacing = spacing;
580 });
581 let origin = self.tile_origin.layout();
582 request_render |= self.tile_data.origin != origin;
583 self.tile_data.origin = origin;
584 });
585
586 let length = self.data.line.length();
587 LAYOUT.with_constraints(constraints.with_new_exact_x(length), || {
588 self.stops
589 .with(|s| s.layout_linear(LayoutAxis::X, self.extend_mode.get(), &mut self.data.line, &mut self.data.stops))
590 });
591
592 if request_render {
593 WIDGET.render();
594 }
595
596 size
597 }
598
599 fn render(&mut self, frame: &mut FrameBuilder) {
600 frame.push_linear_gradient(
601 PxRect::from_size(self.data.size),
602 self.data.line,
603 &self.data.stops,
604 self.extend_mode.get().into(),
605 self.tile_data.origin,
606 self.tile_data.size,
607 self.tile_data.spacing,
608 );
609 }
610}
611
612#[derive(Default)]
613struct RadialNodeData {
614 size: PxSize,
615 center: PxPoint,
616 radius: PxSize,
617 stops: Vec<RenderGradientStop>,
618}
619impl UiNodeImpl for RadialGradient {
620 fn children_len(&self) -> usize {
621 0
622 }
623
624 fn with_child(&mut self, _: usize, _: &mut dyn FnMut(&mut UiNode)) {}
625
626 fn init(&mut self) {
627 WIDGET
628 .sub_var_layout(&self.center)
629 .sub_var_layout(&self.radius)
630 .sub_var(&self.stops)
631 .sub_var(&self.extend_mode);
632 }
633
634 fn update(&mut self, _: &WidgetUpdates) {
635 if self.stops.is_new() || self.extend_mode.is_new() {
636 WIDGET.layout().render();
637 }
638 }
639
640 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
641 LAYOUT.constraints().fill_size()
642 }
643
644 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
645 let size = LAYOUT.constraints().fill_size();
646
647 let mut request_render = size != self.data.size;
648
649 self.data.size = size;
650 LAYOUT.with_constraints(PxConstraints2d::new_fill_size(size), || {
651 let center = self.center.layout_dft(size.to_vector().to_point() * 0.5.fct());
652 let radius = self.radius.get().layout(center);
653 request_render |= center != self.data.center || radius != self.data.radius;
654 self.data.center = center;
655 self.data.radius = radius;
656 });
657
658 LAYOUT.with_constraints(
659 LAYOUT
660 .constraints()
661 .with_exact_x(self.data.radius.width.max(self.data.radius.height)),
662 || {
663 self.stops
664 .with(|s| s.layout_radial(LayoutAxis::X, self.extend_mode.get(), &mut self.data.stops))
665 },
666 );
667
668 if request_render {
669 WIDGET.render();
670 }
671
672 size
673 }
674
675 fn render(&mut self, frame: &mut FrameBuilder) {
676 frame.push_radial_gradient(
677 PxRect::from_size(self.data.size),
678 self.data.center,
679 self.data.radius,
680 &self.data.stops,
681 self.extend_mode.get().into(),
682 PxPoint::zero(),
683 self.data.size,
684 PxSize::zero(),
685 );
686 }
687}
688impl UiNodeImpl for TiledRadialGradient {
689 fn children_len(&self) -> usize {
690 0
691 }
692
693 fn with_child(&mut self, _: usize, _: &mut dyn FnMut(&mut UiNode)) {}
694
695 fn init(&mut self) {
696 WIDGET
697 .sub_var_layout(&self.center)
698 .sub_var_layout(&self.radius)
699 .sub_var(&self.stops)
700 .sub_var(&self.extend_mode)
701 .sub_var_layout(&self.tile_origin)
702 .sub_var_layout(&self.tile_size)
703 .sub_var_layout(&self.tile_spacing);
704 }
705
706 fn update(&mut self, _: &WidgetUpdates) {
707 if self.stops.is_new() || self.extend_mode.is_new() {
708 WIDGET.layout().render();
709 }
710 }
711
712 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
713 LAYOUT.constraints().fill_size()
714 }
715
716 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
717 let size = LAYOUT.constraints().fill_size();
718 let tile_size = self.tile_size.layout_dft(size);
719
720 let mut request_render = size != self.data.size || self.tile_data.size != tile_size;
721
722 self.data.size = size;
723 self.tile_data.size = tile_size;
724
725 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(self.tile_data.size), || {
726 let leftover = tile_leftover(self.tile_data.size, size);
727 LAYOUT.with_leftover(Some(leftover.width), Some(leftover.height), || {
728 let spacing = self.tile_spacing.layout();
729 request_render |= self.tile_data.spacing != spacing;
730 self.tile_data.spacing = spacing;
731 });
732
733 let center = self.center.layout_dft(tile_size.to_vector().to_point() * 0.5.fct());
734 let radius = self.radius.get().layout(center);
735 let origin = self.tile_origin.layout();
736
737 request_render |= self.data.center != center || self.data.radius != radius || self.tile_data.origin != origin;
738
739 self.data.center = center;
740 self.data.radius = radius;
741 self.tile_data.origin = origin;
742 });
743
744 LAYOUT.with_constraints(
745 LAYOUT
746 .constraints()
747 .with_exact_x(self.data.radius.width.max(self.data.radius.height)),
748 || {
749 self.stops
750 .with(|s| s.layout_radial(LayoutAxis::X, self.extend_mode.get(), &mut self.data.stops))
751 },
752 );
753
754 if request_render {
755 WIDGET.render();
756 }
757
758 size
759 }
760
761 fn render(&mut self, frame: &mut FrameBuilder) {
762 frame.push_radial_gradient(
763 PxRect::from_size(self.data.size),
764 self.data.center,
765 self.data.radius,
766 &self.data.stops,
767 self.extend_mode.get().into(),
768 self.tile_data.origin,
769 self.tile_data.size,
770 self.tile_data.spacing,
771 );
772 }
773}
774
775#[derive(Default)]
776struct ConicNodeData {
777 size: PxSize,
778 center: PxPoint,
779 stops: Vec<RenderGradientStop>,
780}
781impl UiNodeImpl for ConicGradient {
782 fn children_len(&self) -> usize {
783 0
784 }
785 fn with_child(&mut self, _: usize, _: &mut dyn FnMut(&mut UiNode)) {}
786
787 fn init(&mut self) {
788 WIDGET
789 .sub_var_layout(&self.center)
790 .sub_var_layout(&self.angle)
791 .sub_var(&self.stops)
792 .sub_var(&self.extend_mode);
793 }
794
795 fn update(&mut self, _: &WidgetUpdates) {
796 if self.stops.is_new() || self.extend_mode.is_new() {
797 WIDGET.layout().render();
798 }
799 }
800
801 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
802 LAYOUT.constraints().fill_size()
803 }
804
805 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
806 let size = LAYOUT.constraints().fill_size();
807
808 let mut request_render = size != self.data.size;
809
810 self.data.size = size;
811 LAYOUT.with_constraints(PxConstraints2d::new_fill_size(size), || {
812 let center = self.center.layout_dft(size.to_vector().to_point() * 0.5.fct());
813 request_render |= self.data.center != center;
814 self.data.center = center;
815 });
816
817 let perimeter = Px({
818 let a = size.width.0 as f32;
819 let b = size.height.0 as f32;
820 std::f32::consts::PI * 2.0 * ((a * a + b * b) / 2.0).sqrt()
821 } as _);
822 LAYOUT.with_constraints(LAYOUT.constraints().with_exact_x(perimeter), || {
823 self.stops
824 .with(|s| s.layout_radial(LayoutAxis::X, self.extend_mode.get(), &mut self.data.stops))
825 });
826
827 if request_render {
828 WIDGET.render();
829 }
830
831 size
832 }
833
834 fn render(&mut self, frame: &mut FrameBuilder) {
835 frame.push_conic_gradient(
836 PxRect::from_size(self.data.size),
837 self.data.center,
838 self.angle.get(),
839 &self.data.stops,
840 self.extend_mode.get().into(),
841 PxPoint::zero(),
842 self.data.size,
843 PxSize::zero(),
844 );
845 }
846}
847impl UiNodeImpl for TiledConicGradient {
848 fn children_len(&self) -> usize {
849 0
850 }
851 fn with_child(&mut self, _: usize, _: &mut dyn FnMut(&mut UiNode)) {}
852
853 fn init(&mut self) {
854 WIDGET
855 .sub_var_layout(&self.center)
856 .sub_var_layout(&self.angle)
857 .sub_var(&self.stops)
858 .sub_var(&self.extend_mode)
859 .sub_var_layout(&self.tile_origin)
860 .sub_var_layout(&self.tile_size)
861 .sub_var_layout(&self.tile_spacing);
862 }
863
864 fn update(&mut self, _: &WidgetUpdates) {
865 if self.stops.is_new() || self.extend_mode.is_new() {
866 WIDGET.layout().render();
867 }
868 }
869
870 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
871 LAYOUT.constraints().fill_size()
872 }
873
874 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
875 let size = LAYOUT.constraints().fill_size();
876 let tile_size = self.tile_size.layout_dft(size);
877
878 let mut request_render = size != self.data.size || tile_size != self.tile_data.size;
879
880 self.data.size = size;
881 self.tile_data.size = tile_size;
882
883 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(tile_size), || {
884 let leftover = tile_leftover(tile_size, size);
885 LAYOUT.with_leftover(Some(leftover.width), Some(leftover.height), || {
886 let spacing = self.tile_spacing.layout();
887 request_render |= self.tile_data.spacing != spacing;
888 self.tile_data.spacing = spacing;
889 });
890 let center = self.center.get().layout_dft(tile_size.to_vector().to_point() * 0.5.fct());
891 let origin = self.tile_origin.layout();
892 request_render |= self.data.center != center || self.tile_data.origin != origin;
893 self.data.center = center;
894 self.tile_data.origin = origin;
895 });
896
897 let perimeter = Px({
898 let a = self.tile_data.size.width.0 as f32;
899 let b = self.tile_data.size.height.0 as f32;
900 std::f32::consts::PI * 2.0 * ((a * a + b * b) / 2.0).sqrt()
901 } as _);
902 LAYOUT.with_constraints(LAYOUT.constraints().with_exact_x(perimeter), || {
903 self.stops
904 .with(|s| s.layout_radial(LayoutAxis::X, self.extend_mode.get(), &mut self.data.stops))
905 });
906
907 if request_render {
908 WIDGET.render();
909 }
910
911 size
912 }
913
914 fn render(&mut self, frame: &mut FrameBuilder) {
915 frame.push_conic_gradient(
916 PxRect::from_size(self.data.size),
917 self.data.center,
918 self.angle.get(),
919 &self.data.stops,
920 self.extend_mode.get().into(),
921 self.tile_data.origin,
922 self.tile_data.size,
923 self.tile_data.spacing,
924 );
925 }
926}
927
928pub fn flood(color: impl IntoVar<Rgba>) -> UiNode {
932 let color = color.into_var();
933 let mut render_size = PxSize::zero();
934 let frame_key = FrameValueKey::new_unique();
935
936 match_node_leaf(move |op| match op {
937 UiNodeOp::Init => {
938 WIDGET.sub_var_render_update(&color);
939 }
940 UiNodeOp::Measure { desired_size, .. } => {
941 *desired_size = LAYOUT.constraints().fill_size();
942 }
943 UiNodeOp::Layout { final_size, .. } => {
944 *final_size = LAYOUT.constraints().fill_size();
945 if *final_size != render_size {
946 render_size = *final_size;
947 WIDGET.render();
948 }
949 }
950 UiNodeOp::Render { frame } => {
951 if !render_size.is_empty() {
952 frame.push_color(PxRect::from_size(render_size), frame_key.bind_var(&color, |&c| c));
953 }
954 }
955 UiNodeOp::RenderUpdate { update } => {
956 if !render_size.is_empty() {
957 update.update_color_opt(frame_key.update_var(&color, |&c| c));
958 }
959 }
960 _ => {}
961 })
962}
963
964fn tile_leftover(tile_size: PxSize, wgt_size: PxSize) -> PxSize {
965 if tile_size.is_empty() || wgt_size.is_empty() {
966 return PxSize::zero();
967 }
968
969 let full_leftover_x = wgt_size.width % tile_size.width;
970 let full_leftover_y = wgt_size.height % tile_size.height;
971 let full_tiles_x = wgt_size.width / tile_size.width;
972 let full_tiles_y = wgt_size.height / tile_size.height;
973 let spaces_x = full_tiles_x - Px(1);
974 let spaces_y = full_tiles_y - Px(1);
975 PxSize::new(
976 if spaces_x > Px(0) { full_leftover_x / spaces_x } else { Px(0) },
977 if spaces_y > Px(0) { full_leftover_y / spaces_y } else { Px(0) },
978 )
979}