1use zng_wgt::prelude::{gradient::*, *};
4
5pub struct GradientBuilder<S> {
11 stops: S,
12}
13
14pub fn gradient<S>(stops: S) -> GradientBuilder<S::Var>
16where
17 S: IntoVar<GradientStops>,
18{
19 GradientBuilder { stops: stops.into_var() }
20}
21
22pub fn linear_gradient<A: IntoVar<LinearGradientAxis>, S: IntoVar<GradientStops>>(
26 axis: A,
27 stops: S,
28) -> LinearGradient<S::Var, A::Var, LocalVar<ExtendMode>> {
29 gradient(stops).linear(axis)
30}
31
32pub fn radial_gradient<C, R, S>(center: C, radius: R, stops: S) -> RadialGradient<S::Var, C::Var, R::Var, LocalVar<ExtendMode>>
36where
37 C: IntoVar<Point>,
38 R: IntoVar<GradientRadius>,
39 S: IntoVar<GradientStops>,
40{
41 gradient(stops).radial(center, radius)
42}
43
44pub fn conic_gradient<C, A, S>(center: C, angle: A, stops: S) -> ConicGradient<S::Var, C::Var, A::Var, LocalVar<ExtendMode>>
48where
49 C: IntoVar<Point>,
50 A: IntoVar<AngleRadian>,
51 S: IntoVar<GradientStops>,
52{
53 gradient(stops).conic(center, angle)
54}
55
56impl<S> GradientBuilder<S>
57where
58 S: Var<GradientStops>,
59{
60 pub fn linear<A>(self, axis: A) -> LinearGradient<S, A::Var, LocalVar<ExtendMode>>
65 where
66 A: IntoVar<LinearGradientAxis>,
67 {
68 LinearGradient {
69 stops: self.stops,
70 axis: axis.into_var(),
71 extend_mode: ExtendMode::Clamp.into_var(),
72
73 data: LinearNodeData::default(),
74 }
75 }
76
77 pub fn radial<C, R>(self, center: C, radius: R) -> RadialGradient<S, C::Var, R::Var, LocalVar<ExtendMode>>
82 where
83 C: IntoVar<Point>,
84 R: IntoVar<GradientRadius>,
85 {
86 RadialGradient {
87 stops: self.stops,
88 center: center.into_var(),
89 radius: radius.into_var(),
90 extend_mode: ExtendMode::Clamp.into_var(),
91 data: RadialNodeData::default(),
92 }
93 }
94
95 pub fn conic<C: IntoVar<Point>, A: IntoVar<AngleRadian>>(
100 self,
101 center: C,
102 angle: A,
103 ) -> ConicGradient<S, C::Var, A::Var, LocalVar<ExtendMode>> {
104 ConicGradient {
105 stops: self.stops,
106 center: center.into_var(),
107 angle: angle.into_var(),
108 extend_mode: ExtendMode::Clamp.into_var(),
109 data: ConicNodeData::default(),
110 }
111 }
112}
113
114pub struct LinearGradient<S, A, E> {
123 stops: S,
124 axis: A,
125 extend_mode: E,
126
127 data: LinearNodeData,
128}
129impl<S, A, E> LinearGradient<S, A, E>
130where
131 S: Var<GradientStops>,
132 A: Var<LinearGradientAxis>,
133 E: Var<ExtendMode>,
134{
135 pub fn extend_mode<E2: IntoVar<ExtendMode>>(self, mode: E2) -> LinearGradient<S, A, E2::Var> {
141 LinearGradient {
142 stops: self.stops,
143 axis: self.axis,
144 extend_mode: mode.into_var(),
145 data: self.data,
146 }
147 }
148
149 pub fn repeat(self) -> LinearGradient<S, A, LocalVar<ExtendMode>> {
153 self.extend_mode(ExtendMode::Repeat)
154 }
155
156 pub fn reflect(self) -> LinearGradient<S, A, LocalVar<ExtendMode>> {
160 self.extend_mode(ExtendMode::Reflect)
161 }
162
163 pub fn tile<T, TS>(self, tile_size: T, tile_spacing: TS) -> TiledLinearGradient<S, A, E, LocalVar<Point>, T::Var, TS::Var>
165 where
166 T: IntoVar<Size>,
167 TS: IntoVar<Size>,
168 {
169 TiledLinearGradient {
170 stops: self.stops,
171 axis: self.axis,
172 extend_mode: self.extend_mode,
173 tile_origin: LocalVar(Point::zero()),
174 tile_size: tile_size.into_var(),
175 tile_spacing: tile_spacing.into_var(),
176 data: self.data,
177 tile_data: TiledNodeData::default(),
178 }
179 }
180
181 pub fn tile_size<T>(self, size: T) -> TiledLinearGradient<S, A, E, LocalVar<Point>, T::Var, LocalVar<Size>>
186 where
187 T: IntoVar<Size>,
188 {
189 self.tile(size, Size::zero())
190 }
191}
192
193pub struct TiledLinearGradient<S, A, E, O, T, TS> {
200 stops: S,
201 axis: A,
202 extend_mode: E,
203 tile_origin: O,
204 tile_size: T,
205 tile_spacing: TS,
206 data: LinearNodeData,
207 tile_data: TiledNodeData,
208}
209impl<S, A, E, O, T, TS> TiledLinearGradient<S, A, E, O, T, TS>
210where
211 S: Var<GradientStops>,
212 A: Var<LinearGradientAxis>,
213 E: Var<ExtendMode>,
214 O: Var<Point>,
215 T: Var<Size>,
216 TS: Var<Size>,
217{
218 pub fn tile_spacing<TS2>(self, spacing: TS2) -> TiledLinearGradient<S, A, E, O, T, TS2::Var>
228 where
229 TS2: IntoVar<Size>,
230 {
231 TiledLinearGradient {
232 stops: self.stops,
233 axis: self.axis,
234 extend_mode: self.extend_mode,
235 tile_origin: self.tile_origin,
236 tile_size: self.tile_size,
237 tile_spacing: spacing.into_var(),
238 data: self.data,
239 tile_data: self.tile_data,
240 }
241 }
242
243 pub fn tile_origin<O2>(self, origin: O2) -> TiledLinearGradient<S, A, E, O2::Var, T, TS>
248 where
249 O2: IntoVar<Point>,
250 {
251 TiledLinearGradient {
252 stops: self.stops,
253 axis: self.axis,
254 extend_mode: self.extend_mode,
255 tile_origin: origin.into_var(),
256 tile_size: self.tile_size,
257 tile_spacing: self.tile_spacing,
258 data: self.data,
259 tile_data: self.tile_data,
260 }
261 }
262}
263
264pub struct RadialGradient<S, C, R, E> {
273 stops: S,
274 center: C,
275 radius: R,
276 extend_mode: E,
277
278 data: RadialNodeData,
279}
280impl<S, C, R, E> RadialGradient<S, C, R, E>
281where
282 S: Var<GradientStops>,
283 C: Var<Point>,
284 R: Var<GradientRadius>,
285 E: Var<ExtendMode>,
286{
287 pub fn extend_mode<E2: IntoVar<ExtendMode>>(self, mode: E2) -> RadialGradient<S, C, R, E2::Var> {
293 RadialGradient {
294 stops: self.stops,
295 center: self.center,
296 radius: self.radius,
297 extend_mode: mode.into_var(),
298 data: self.data,
299 }
300 }
301
302 pub fn repeat(self) -> RadialGradient<S, C, R, LocalVar<ExtendMode>> {
306 self.extend_mode(ExtendMode::Repeat)
307 }
308
309 pub fn reflect(self) -> RadialGradient<S, C, R, LocalVar<ExtendMode>> {
313 self.extend_mode(ExtendMode::Reflect)
314 }
315
316 pub fn tile<T, TS>(self, tile_size: T, tile_spacing: TS) -> TiledRadialGradient<S, C, R, E, LocalVar<Point>, T::Var, TS::Var>
318 where
319 T: IntoVar<Size>,
320 TS: IntoVar<Size>,
321 {
322 TiledRadialGradient {
323 stops: self.stops,
324 center: self.center,
325 radius: self.radius,
326 extend_mode: self.extend_mode,
327 tile_origin: LocalVar(Point::zero()),
328 tile_size: tile_size.into_var(),
329 tile_spacing: tile_spacing.into_var(),
330 data: self.data,
331 tile_data: TiledNodeData::default(),
332 }
333 }
334
335 pub fn tile_size<T>(self, size: T) -> TiledRadialGradient<S, C, R, E, LocalVar<Point>, T::Var, LocalVar<Size>>
337 where
338 T: IntoVar<Size>,
339 {
340 self.tile(size, Size::zero())
341 }
342}
343
344pub struct TiledRadialGradient<S, C, R, E, O, T, TS> {
352 stops: S,
353 center: C,
354 radius: R,
355 extend_mode: E,
356 tile_origin: O,
357 tile_size: T,
358 tile_spacing: TS,
359 data: RadialNodeData,
360 tile_data: TiledNodeData,
361}
362impl<S, C, R, E, O, T, TS> TiledRadialGradient<S, C, R, E, O, T, TS>
363where
364 S: Var<GradientStops>,
365 C: Var<Point>,
366 R: Var<GradientRadius>,
367 E: Var<ExtendMode>,
368 O: Var<Point>,
369 T: Var<Size>,
370 TS: Var<Size>,
371{
372 pub fn tile_spacing<TS2>(self, spacing: TS2) -> TiledRadialGradient<S, C, R, E, O, T, TS2::Var>
382 where
383 TS2: IntoVar<Size>,
384 {
385 TiledRadialGradient {
386 stops: self.stops,
387 center: self.center,
388 radius: self.radius,
389 extend_mode: self.extend_mode,
390 tile_origin: self.tile_origin,
391 tile_size: self.tile_size,
392 tile_spacing: spacing.into_var(),
393 data: self.data,
394 tile_data: self.tile_data,
395 }
396 }
397
398 pub fn tile_origin<O2>(self, origin: O2) -> TiledRadialGradient<S, C, R, E, O2::Var, T, TS>
403 where
404 O2: IntoVar<Point>,
405 {
406 TiledRadialGradient {
407 stops: self.stops,
408 center: self.center,
409 radius: self.radius,
410 extend_mode: self.extend_mode,
411 tile_origin: origin.into_var(),
412 tile_size: self.tile_size,
413 tile_spacing: self.tile_spacing,
414 data: self.data,
415 tile_data: self.tile_data,
416 }
417 }
418}
419
420pub struct ConicGradient<S, C, A, E> {
429 stops: S,
430 center: C,
431 angle: A,
432 extend_mode: E,
433
434 data: ConicNodeData,
435}
436impl<S, C, A, E> ConicGradient<S, C, A, E>
437where
438 S: Var<GradientStops>,
439 C: Var<Point>,
440 A: Var<AngleRadian>,
441 E: Var<ExtendMode>,
442{
443 pub fn extend_mode<E2: IntoVar<ExtendMode>>(self, mode: E2) -> ConicGradient<S, C, A, E2::Var> {
449 ConicGradient {
450 stops: self.stops,
451 center: self.center,
452 angle: self.angle,
453 extend_mode: mode.into_var(),
454 data: self.data,
455 }
456 }
457
458 pub fn repeat(self) -> ConicGradient<S, C, A, LocalVar<ExtendMode>> {
462 self.extend_mode(ExtendMode::Repeat)
463 }
464
465 pub fn reflect(self) -> ConicGradient<S, C, A, LocalVar<ExtendMode>> {
469 self.extend_mode(ExtendMode::Reflect)
470 }
471
472 pub fn tile<T, TS>(self, tile_size: T, tile_spacing: TS) -> TiledConicGradient<S, C, A, E, LocalVar<Point>, T::Var, TS::Var>
474 where
475 T: IntoVar<Size>,
476 TS: IntoVar<Size>,
477 {
478 TiledConicGradient {
479 stops: self.stops,
480 center: self.center,
481 angle: self.angle,
482 extend_mode: self.extend_mode,
483 tile_origin: LocalVar(Point::zero()),
484 tile_size: tile_size.into_var(),
485 tile_spacing: tile_spacing.into_var(),
486 data: self.data,
487 tile_data: TiledNodeData::default(),
488 }
489 }
490
491 pub fn tile_size<T>(self, size: T) -> TiledConicGradient<S, C, A, E, LocalVar<Point>, T::Var, LocalVar<Size>>
493 where
494 T: IntoVar<Size>,
495 {
496 self.tile(size, Size::zero())
497 }
498}
499
500pub struct TiledConicGradient<S, C, A, E, O, T, TS> {
508 stops: S,
509 center: C,
510 angle: A,
511 extend_mode: E,
512 tile_origin: O,
513 tile_size: T,
514 tile_spacing: TS,
515 data: ConicNodeData,
516 tile_data: TiledNodeData,
517}
518impl<S, C, A, E, O, T, TS> TiledConicGradient<S, C, A, E, O, T, TS>
519where
520 S: Var<GradientStops>,
521 C: Var<Point>,
522 A: Var<AngleRadian>,
523 E: Var<ExtendMode>,
524 O: Var<Point>,
525 T: Var<Size>,
526 TS: Var<Size>,
527{
528 pub fn tile_spacing<TS2>(self, spacing: TS2) -> TiledConicGradient<S, C, A, E, O, T, TS2::Var>
538 where
539 TS2: IntoVar<Size>,
540 {
541 TiledConicGradient {
542 stops: self.stops,
543 center: self.center,
544 angle: self.angle,
545 extend_mode: self.extend_mode,
546 tile_origin: self.tile_origin,
547 tile_size: self.tile_size,
548 tile_spacing: spacing.into_var(),
549 data: self.data,
550 tile_data: self.tile_data,
551 }
552 }
553
554 pub fn tile_origin<O2>(self, origin: O2) -> TiledConicGradient<S, C, A, E, O2::Var, T, TS>
559 where
560 O2: IntoVar<Point>,
561 {
562 TiledConicGradient {
563 stops: self.stops,
564 center: self.center,
565 angle: self.angle,
566 extend_mode: self.extend_mode,
567 tile_origin: origin.into_var(),
568 tile_size: self.tile_size,
569 tile_spacing: self.tile_spacing,
570 data: self.data,
571 tile_data: self.tile_data,
572 }
573 }
574}
575
576#[derive(Default)]
577struct LinearNodeData {
578 line: PxLine,
579 stops: Vec<RenderGradientStop>,
580 size: PxSize,
581}
582#[ui_node(none)]
583impl<S, A, E> UiNode for LinearGradient<S, A, E>
584where
585 S: Var<GradientStops>,
586 A: Var<LinearGradientAxis>,
587 E: Var<ExtendMode>,
588{
589 fn init(&mut self) {
590 WIDGET.sub_var_layout(&self.axis).sub_var(&self.stops).sub_var(&self.extend_mode);
591 }
592
593 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
594 LAYOUT.constraints().fill_size()
595 }
596
597 fn update(&mut self, _: &WidgetUpdates) {
598 if self.stops.is_new() || self.extend_mode.is_new() {
599 WIDGET.layout().render();
600 }
601 }
602
603 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
604 let size = LAYOUT.constraints().fill_size();
605 let axis = self.axis.layout();
606 if self.data.size != size || self.data.line != axis {
607 self.data.size = size;
608 self.data.line = axis;
609 WIDGET.render();
610 }
611
612 let length = self.data.line.length();
613 LAYOUT.with_constraints(LAYOUT.constraints().with_new_exact_x(length), || {
614 self.stops
615 .with(|s| s.layout_linear(LayoutAxis::X, self.extend_mode.get(), &mut self.data.line, &mut self.data.stops))
616 });
617
618 size
619 }
620
621 fn render(&mut self, frame: &mut FrameBuilder) {
622 frame.push_linear_gradient(
623 PxRect::from_size(self.data.size),
624 self.data.line,
625 &self.data.stops,
626 self.extend_mode.get().into(),
627 PxPoint::zero(),
628 self.data.size,
629 PxSize::zero(),
630 );
631 }
632}
633
634#[derive(Default)]
635struct TiledNodeData {
636 origin: PxPoint,
637 size: PxSize,
638 spacing: PxSize,
639}
640#[ui_node(none)]
641impl<S, A, E, O, T, TS> UiNode for TiledLinearGradient<S, A, E, O, T, TS>
642where
643 S: Var<GradientStops>,
644 A: Var<LinearGradientAxis>,
645 E: Var<ExtendMode>,
646 O: Var<Point>,
647 T: Var<Size>,
648 TS: Var<Size>,
649{
650 fn init(&mut self) {
651 WIDGET
652 .sub_var_layout(&self.axis)
653 .sub_var(&self.stops)
654 .sub_var(&self.extend_mode)
655 .sub_var_layout(&self.tile_origin)
656 .sub_var_layout(&self.tile_size)
657 .sub_var_layout(&self.tile_spacing);
658 }
659
660 fn update(&mut self, _: &WidgetUpdates) {
661 if self.stops.is_new() || self.extend_mode.is_new() {
662 WIDGET.layout().render();
663 }
664 }
665
666 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
667 LAYOUT.constraints().fill_size()
668 }
669
670 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
671 let constraints = LAYOUT.constraints();
672 let size = constraints.fill_size();
673 let axis = self.axis.layout();
674 let tile_size = self.tile_size.layout_dft(size);
675
676 let mut request_render = false;
677
678 if self.data.size != size || self.data.line != axis || self.tile_data.size != tile_size {
679 self.data.size = size;
680 self.data.line = self.axis.layout();
681 self.tile_data.size = tile_size;
682 request_render = true;
683 }
684
685 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(self.tile_data.size), || {
686 let leftover = tile_leftover(self.tile_data.size, size);
687 LAYOUT.with_leftover(Some(leftover.width), Some(leftover.height), || {
688 let spacing = self.tile_spacing.layout();
689 request_render |= self.tile_data.spacing != spacing;
690 self.tile_data.spacing = spacing;
691 });
692 let origin = self.tile_origin.layout();
693 request_render |= self.tile_data.origin != origin;
694 self.tile_data.origin = origin;
695 });
696
697 let length = self.data.line.length();
698 LAYOUT.with_constraints(constraints.with_new_exact_x(length), || {
699 self.stops
700 .with(|s| s.layout_linear(LayoutAxis::X, self.extend_mode.get(), &mut self.data.line, &mut self.data.stops))
701 });
702
703 if request_render {
704 WIDGET.render();
705 }
706
707 size
708 }
709
710 fn render(&mut self, frame: &mut FrameBuilder) {
711 frame.push_linear_gradient(
712 PxRect::from_size(self.data.size),
713 self.data.line,
714 &self.data.stops,
715 self.extend_mode.get().into(),
716 self.tile_data.origin,
717 self.tile_data.size,
718 self.tile_data.spacing,
719 );
720 }
721}
722
723#[derive(Default)]
724struct RadialNodeData {
725 size: PxSize,
726 center: PxPoint,
727 radius: PxSize,
728 stops: Vec<RenderGradientStop>,
729}
730
731#[ui_node(none)]
732impl<S, C, R, E> UiNode for RadialGradient<S, C, R, E>
733where
734 S: Var<GradientStops>,
735 C: Var<Point>,
736 R: Var<GradientRadius>,
737 E: Var<ExtendMode>,
738{
739 fn init(&mut self) {
740 WIDGET
741 .sub_var_layout(&self.center)
742 .sub_var_layout(&self.radius)
743 .sub_var(&self.stops)
744 .sub_var(&self.extend_mode);
745 }
746
747 fn update(&mut self, _: &WidgetUpdates) {
748 if self.stops.is_new() || self.extend_mode.is_new() {
749 WIDGET.layout().render();
750 }
751 }
752
753 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
754 LAYOUT.constraints().fill_size()
755 }
756
757 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
758 let size = LAYOUT.constraints().fill_size();
759
760 let mut request_render = size != self.data.size;
761
762 self.data.size = size;
763 LAYOUT.with_constraints(PxConstraints2d::new_fill_size(size), || {
764 let center = self.center.layout_dft(size.to_vector().to_point() * 0.5.fct());
765 let radius = self.radius.get().layout(center);
766 request_render |= center != self.data.center || radius != self.data.radius;
767 self.data.center = center;
768 self.data.radius = radius;
769 });
770
771 LAYOUT.with_constraints(
772 LAYOUT
773 .constraints()
774 .with_exact_x(self.data.radius.width.max(self.data.radius.height)),
775 || {
776 self.stops
777 .with(|s| s.layout_radial(LayoutAxis::X, self.extend_mode.get(), &mut self.data.stops))
778 },
779 );
780
781 if request_render {
782 WIDGET.render();
783 }
784
785 size
786 }
787
788 fn render(&mut self, frame: &mut FrameBuilder) {
789 frame.push_radial_gradient(
790 PxRect::from_size(self.data.size),
791 self.data.center,
792 self.data.radius,
793 &self.data.stops,
794 self.extend_mode.get().into(),
795 PxPoint::zero(),
796 self.data.size,
797 PxSize::zero(),
798 );
799 }
800}
801
802#[ui_node(none)]
803impl<S, C, R, E, O, T, TS> UiNode for TiledRadialGradient<S, C, R, E, O, T, TS>
804where
805 S: Var<GradientStops>,
806 C: Var<Point>,
807 R: Var<GradientRadius>,
808 E: Var<ExtendMode>,
809 O: Var<Point>,
810 T: Var<Size>,
811 TS: Var<Size>,
812{
813 fn init(&mut self) {
814 WIDGET
815 .sub_var_layout(&self.center)
816 .sub_var_layout(&self.radius)
817 .sub_var(&self.stops)
818 .sub_var(&self.extend_mode)
819 .sub_var_layout(&self.tile_origin)
820 .sub_var_layout(&self.tile_size)
821 .sub_var_layout(&self.tile_spacing);
822 }
823
824 fn update(&mut self, _: &WidgetUpdates) {
825 if self.stops.is_new() || self.extend_mode.is_new() {
826 WIDGET.layout().render();
827 }
828 }
829
830 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
831 LAYOUT.constraints().fill_size()
832 }
833
834 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
835 let size = LAYOUT.constraints().fill_size();
836 let tile_size = self.tile_size.layout_dft(size);
837
838 let mut request_render = size != self.data.size || self.tile_data.size != tile_size;
839
840 self.data.size = size;
841 self.tile_data.size = tile_size;
842
843 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(self.tile_data.size), || {
844 let leftover = tile_leftover(self.tile_data.size, size);
845 LAYOUT.with_leftover(Some(leftover.width), Some(leftover.height), || {
846 let spacing = self.tile_spacing.layout();
847 request_render |= self.tile_data.spacing != spacing;
848 self.tile_data.spacing = spacing;
849 });
850
851 let center = self.center.layout_dft(tile_size.to_vector().to_point() * 0.5.fct());
852 let radius = self.radius.get().layout(center);
853 let origin = self.tile_origin.layout();
854
855 request_render |= self.data.center != center || self.data.radius != radius || self.tile_data.origin != origin;
856
857 self.data.center = center;
858 self.data.radius = radius;
859 self.tile_data.origin = origin;
860 });
861
862 LAYOUT.with_constraints(
863 LAYOUT
864 .constraints()
865 .with_exact_x(self.data.radius.width.max(self.data.radius.height)),
866 || {
867 self.stops
868 .with(|s| s.layout_radial(LayoutAxis::X, self.extend_mode.get(), &mut self.data.stops))
869 },
870 );
871
872 if request_render {
873 WIDGET.render();
874 }
875
876 size
877 }
878
879 fn render(&mut self, frame: &mut FrameBuilder) {
880 frame.push_radial_gradient(
881 PxRect::from_size(self.data.size),
882 self.data.center,
883 self.data.radius,
884 &self.data.stops,
885 self.extend_mode.get().into(),
886 self.tile_data.origin,
887 self.tile_data.size,
888 self.tile_data.spacing,
889 );
890 }
891}
892
893#[derive(Default)]
894struct ConicNodeData {
895 size: PxSize,
896 center: PxPoint,
897 stops: Vec<RenderGradientStop>,
898}
899
900#[ui_node(none)]
901impl<S, C, A, E> UiNode for ConicGradient<S, C, A, E>
902where
903 S: Var<GradientStops>,
904 C: Var<Point>,
905 A: Var<AngleRadian>,
906 E: Var<ExtendMode>,
907{
908 fn init(&mut self) {
909 WIDGET
910 .sub_var_layout(&self.center)
911 .sub_var_layout(&self.angle)
912 .sub_var(&self.stops)
913 .sub_var(&self.extend_mode);
914 }
915
916 fn update(&mut self, _: &WidgetUpdates) {
917 if self.stops.is_new() || self.extend_mode.is_new() {
918 WIDGET.layout().render();
919 }
920 }
921
922 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
923 LAYOUT.constraints().fill_size()
924 }
925
926 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
927 let size = LAYOUT.constraints().fill_size();
928
929 let mut request_render = size != self.data.size;
930
931 self.data.size = size;
932 LAYOUT.with_constraints(PxConstraints2d::new_fill_size(size), || {
933 let center = self.center.layout_dft(size.to_vector().to_point() * 0.5.fct());
934 request_render |= self.data.center != center;
935 self.data.center = center;
936 });
937
938 let perimeter = Px({
939 let a = size.width.0 as f32;
940 let b = size.height.0 as f32;
941 std::f32::consts::PI * 2.0 * ((a * a + b * b) / 2.0).sqrt()
942 } as _);
943 LAYOUT.with_constraints(LAYOUT.constraints().with_exact_x(perimeter), || {
944 self.stops
945 .with(|s| s.layout_radial(LayoutAxis::X, self.extend_mode.get(), &mut self.data.stops))
946 });
947
948 if request_render {
949 WIDGET.render();
950 }
951
952 size
953 }
954
955 fn render(&mut self, frame: &mut FrameBuilder) {
956 frame.push_conic_gradient(
957 PxRect::from_size(self.data.size),
958 self.data.center,
959 self.angle.get(),
960 &self.data.stops,
961 self.extend_mode.get().into(),
962 PxPoint::zero(),
963 self.data.size,
964 PxSize::zero(),
965 );
966 }
967}
968
969#[ui_node(none)]
970impl<S, C, A, E, O, T, TS> UiNode for TiledConicGradient<S, C, A, E, O, T, TS>
971where
972 S: Var<GradientStops>,
973 C: Var<Point>,
974 A: Var<AngleRadian>,
975 E: Var<ExtendMode>,
976 O: Var<Point>,
977 T: Var<Size>,
978 TS: Var<Size>,
979{
980 fn init(&mut self) {
981 WIDGET
982 .sub_var_layout(&self.center)
983 .sub_var_layout(&self.angle)
984 .sub_var(&self.stops)
985 .sub_var(&self.extend_mode)
986 .sub_var_layout(&self.tile_origin)
987 .sub_var_layout(&self.tile_size)
988 .sub_var_layout(&self.tile_spacing);
989 }
990
991 fn update(&mut self, _: &WidgetUpdates) {
992 if self.stops.is_new() || self.extend_mode.is_new() {
993 WIDGET.layout().render();
994 }
995 }
996
997 fn measure(&mut self, _: &mut WidgetMeasure) -> PxSize {
998 LAYOUT.constraints().fill_size()
999 }
1000
1001 fn layout(&mut self, _: &mut WidgetLayout) -> PxSize {
1002 let size = LAYOUT.constraints().fill_size();
1003 let tile_size = self.tile_size.layout_dft(size);
1004
1005 let mut request_render = size != self.data.size || tile_size != self.tile_data.size;
1006
1007 self.data.size = size;
1008 self.tile_data.size = tile_size;
1009
1010 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(tile_size), || {
1011 let leftover = tile_leftover(tile_size, size);
1012 LAYOUT.with_leftover(Some(leftover.width), Some(leftover.height), || {
1013 let spacing = self.tile_spacing.layout();
1014 request_render |= self.tile_data.spacing != spacing;
1015 self.tile_data.spacing = spacing;
1016 });
1017 let center = self.center.get().layout_dft(tile_size.to_vector().to_point() * 0.5.fct());
1018 let origin = self.tile_origin.layout();
1019 request_render |= self.data.center != center || self.tile_data.origin != origin;
1020 self.data.center = center;
1021 self.tile_data.origin = origin;
1022 });
1023
1024 let perimeter = Px({
1025 let a = self.tile_data.size.width.0 as f32;
1026 let b = self.tile_data.size.height.0 as f32;
1027 std::f32::consts::PI * 2.0 * ((a * a + b * b) / 2.0).sqrt()
1028 } as _);
1029 LAYOUT.with_constraints(LAYOUT.constraints().with_exact_x(perimeter), || {
1030 self.stops
1031 .with(|s| s.layout_radial(LayoutAxis::X, self.extend_mode.get(), &mut self.data.stops))
1032 });
1033
1034 if request_render {
1035 WIDGET.render();
1036 }
1037
1038 size
1039 }
1040
1041 fn render(&mut self, frame: &mut FrameBuilder) {
1042 frame.push_conic_gradient(
1043 PxRect::from_size(self.data.size),
1044 self.data.center,
1045 self.angle.get(),
1046 &self.data.stops,
1047 self.extend_mode.get().into(),
1048 self.tile_data.origin,
1049 self.tile_data.size,
1050 self.tile_data.spacing,
1051 );
1052 }
1053}
1054
1055pub fn flood(color: impl IntoVar<Rgba>) -> impl UiNode {
1059 let color = color.into_var();
1060 let mut render_size = PxSize::zero();
1061 let frame_key = FrameValueKey::new_unique();
1062
1063 match_node_leaf(move |op| match op {
1064 UiNodeOp::Init => {
1065 WIDGET.sub_var_render_update(&color);
1066 }
1067 UiNodeOp::Measure { desired_size, .. } => {
1068 *desired_size = LAYOUT.constraints().fill_size();
1069 }
1070 UiNodeOp::Layout { final_size, .. } => {
1071 *final_size = LAYOUT.constraints().fill_size();
1072 if *final_size != render_size {
1073 render_size = *final_size;
1074 WIDGET.render();
1075 }
1076 }
1077 UiNodeOp::Render { frame } => {
1078 if !render_size.is_empty() {
1079 frame.push_color(PxRect::from_size(render_size), frame_key.bind_var(&color, |&c| c));
1080 }
1081 }
1082 UiNodeOp::RenderUpdate { update } => {
1083 if !render_size.is_empty() {
1084 update.update_color_opt(frame_key.update_var(&color, |&c| c));
1085 }
1086 }
1087 _ => {}
1088 })
1089}
1090
1091fn tile_leftover(tile_size: PxSize, wgt_size: PxSize) -> PxSize {
1092 if tile_size.is_empty() || wgt_size.is_empty() {
1093 return PxSize::zero();
1094 }
1095
1096 let full_leftover_x = wgt_size.width % tile_size.width;
1097 let full_leftover_y = wgt_size.height % tile_size.height;
1098 let full_tiles_x = wgt_size.width / tile_size.width;
1099 let full_tiles_y = wgt_size.height / tile_size.height;
1100 let spaces_x = full_tiles_x - Px(1);
1101 let spaces_y = full_tiles_y - Px(1);
1102 PxSize::new(
1103 if spaces_x > Px(0) { full_leftover_x / spaces_x } else { Px(0) },
1104 if spaces_y > Px(0) { full_leftover_y / spaces_y } else { Px(0) },
1105 )
1106}