1use vizia_style::{ColorStop, CornerRadius, Rect};
2
3use super::internal;
4use crate::prelude::*;
5
6pub trait StyleModifiers: internal::Modifiable {
8 fn id(mut self, id: impl Into<String>) -> Self {
27 let id = id.into();
29 let entity = self.entity();
30 self.context().style.ids.insert(entity, id.clone());
31 self.context().needs_restyle(entity);
32
33 self.context().entity_identifiers.insert(id, entity);
34
35 self
36 }
37
38 fn class(mut self, name: &str) -> Self {
55 let entity = self.entity();
56 if let Some(class_list) = self.context().style.classes.get_mut(entity) {
57 class_list.insert(name.to_string());
58 }
59
60 self.context().needs_restyle(entity);
61
62 self
63 }
64
65 fn toggle_class(mut self, name: &str, applied: impl Res<bool>) -> Self {
67 let name = name.to_owned();
68 let entity = self.entity();
69 let current = self.current();
70 self.context().with_current(current, |cx| {
71 applied.set_or_bind(cx, move |cx, applied| {
72 let applied = applied.get_value(cx);
73 if let Some(class_list) = cx.style.classes.get_mut(entity) {
74 if applied {
75 class_list.insert(name.clone());
76 } else {
77 class_list.remove(&name);
78 }
79 }
80
81 cx.needs_restyle(entity);
82 });
83 });
84
85 self
86 }
87
88 fn checked<U: Into<bool>>(mut self, state: impl Res<U>) -> Self {
93 let entity = self.entity();
94 let current = self.current();
95 if let Some(abilities) = self.context().style.abilities.get_mut(entity) {
97 abilities.set(Abilities::CHECKABLE, true);
98 }
99
100 self.context().with_current(current, move |cx| {
101 state.set_or_bind(cx, move |cx, val| {
102 let val = val.get_value(cx).into();
103 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(entity) {
104 pseudo_classes.set(PseudoClassFlags::CHECKED, val);
105 }
106 cx.needs_restyle(entity);
107 cx.style.needs_access_update(entity);
108 });
109 });
110
111 self
112 }
113
114 fn focused<U: Into<bool>>(mut self, state: impl Res<U>) -> Self {
119 let entity = self.entity();
120 let current = self.current();
121
122 self.context().with_current(current, |cx| {
123 state.set_or_bind(cx, move |cx, val| {
124 let val = val.get_value(cx).into();
125
126 if val {
127 cx.focus();
128 }
130
131 cx.needs_restyle(entity);
132 });
133 });
134
135 self
136 }
137
138 fn focused_with_visibility<U: Into<bool>>(
140 mut self,
141 focus: impl Res<U> + Copy + 'static,
142 visibility: impl Res<U> + Copy + 'static,
143 ) -> Self {
144 let entity = self.entity();
145 let current = self.current();
146
147 self.context().with_current(current, move |cx| {
148 focus.set_or_bind(cx, move |cx, f| {
149 visibility.set_or_bind(cx, move |cx, v| {
150 let focus = f.get_value(cx).into();
151 let visibility = v.get_value(cx).into();
152 if focus {
153 cx.focus_with_visibility(visibility);
155 cx.needs_restyle(entity);
156 }
157 });
158 });
159 });
160
161 self
162 }
163
164 fn read_only<U: Into<bool>>(mut self, state: impl Res<U>) -> Self {
166 let entity = self.entity();
167 let current = self.current();
168 self.context().with_current(current, |cx| {
169 state.set_or_bind(cx, move |cx, val| {
170 let val = val.get_value(cx).into();
171 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(entity) {
172 pseudo_classes.set(PseudoClassFlags::READ_ONLY, val);
173 }
174
175 cx.needs_restyle(entity);
176 });
177 });
178
179 self
180 }
181
182 fn read_write<U: Into<bool>>(mut self, state: impl Res<U>) -> Self {
184 let entity = self.entity();
185 let current = self.current();
186 self.context().with_current(current, |cx| {
187 state.set_or_bind(cx, move |cx, val| {
188 let val = val.get_value(cx).into();
189 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(entity) {
190 pseudo_classes.set(PseudoClassFlags::READ_WRITE, val);
191 }
192
193 cx.needs_restyle(entity);
194 });
195 });
196
197 self
198 }
199
200 fn focus_within<U: Into<bool>>(mut self, state: impl Res<U>) -> Self {
202 let entity = self.entity();
203 let current = self.current();
204 self.context().with_current(current, |cx| {
205 state.set_or_bind(cx, move |cx, val| {
206 let val = val.get_value(cx).into();
207 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(entity) {
208 pseudo_classes.set(PseudoClassFlags::FOCUS_WITHIN, val);
209 }
210
211 cx.needs_restyle(entity);
212 });
213 });
214
215 self
216 }
217
218 fn placeholder_shown<U: Into<bool>>(mut self, state: impl Res<U>) -> Self {
220 let entity = self.entity();
221 let current = self.current();
222 self.context().with_current(current, |cx| {
223 state.set_or_bind(cx, move |cx, val| {
224 let val = val.get_value(cx).into();
225 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(entity) {
226 pseudo_classes.set(PseudoClassFlags::PLACEHOLDER_SHOWN, val);
227 }
228
229 cx.needs_restyle(entity);
230 });
231 });
232
233 self
234 }
235
236 modifier!(
237 disabled,
241 bool,
242 SystemFlags::RESTYLE | SystemFlags::REACCESS
243 );
244
245 modifier!(
246 display,
250 Display,
251 SystemFlags::RELAYOUT | SystemFlags::REDRAW | SystemFlags::REFLOW
252 );
253
254 modifier!(
255 visibility,
259 Visibility,
260 SystemFlags::REDRAW
261 );
262
263 modifier!(
264 opacity,
268 Opacity,
269 SystemFlags::REDRAW
270 );
271
272 fn z_index<U: Into<i32>>(mut self, value: impl Res<U>) -> Self {
277 let entity = self.entity();
278 let cx = self.context();
280 let value = value.get_value(cx).into();
281 cx.style.z_index.insert(entity, value);
282 cx.needs_redraw(entity);
283 self
286 }
287
288 modifier!(
289 ignore_clipping,
293 bool,
294 SystemFlags::RECLIP | SystemFlags::REDRAW
295 );
296
297 fn clip_path<U: Into<ClipPath>>(mut self, value: impl Res<U>) -> Self {
299 let entity = self.entity();
300 let current = self.current();
301 self.context().with_current(current, |cx| {
302 value.set_or_bind(cx, move |cx, v| {
303 let value = v.get_value(cx).into();
304 cx.style.clip_path.insert(entity, value);
305 cx.needs_reclip(entity);
306 cx.needs_redraw(entity);
307 });
308 });
309
310 self
311 }
312
313 fn overflow<U: Into<Overflow>>(mut self, value: impl Res<U>) -> Self {
315 let entity = self.entity();
316 let current = self.current();
317 self.context().with_current(current, |cx| {
318 value.set_or_bind(cx, move |cx, v| {
319 let value = v.get_value(cx).into();
320 cx.style.overflowx.insert(entity, value);
321 cx.style.overflowy.insert(entity, value);
322 cx.needs_reclip(entity);
323 cx.needs_redraw(entity);
324 });
325 });
326
327 self
328 }
329
330 modifier!(
331 overflowx,
335 Overflow,
336 SystemFlags::RECLIP | SystemFlags::REDRAW
337 );
338
339 modifier!(
340 overflowy,
344 Overflow,
345 SystemFlags::RECLIP | SystemFlags::REDRAW
346 );
347
348 fn filter<U: Into<Filter>>(mut self, value: impl Res<U>) -> Self {
350 let entity = self.entity();
351 let current = self.current();
352 self.context().with_current(current, |cx| {
353 value.set_or_bind(cx, move |cx, v| {
354 let value = v.get_value(cx).into();
355 cx.style.filter.insert(entity, value);
356
357 cx.needs_redraw(entity);
358 });
359 });
360
361 self
362 }
363
364 fn backdrop_filter<U: Into<Filter>>(mut self, value: impl Res<U>) -> Self {
366 let entity = self.entity();
367 let current = self.current();
368 self.context().with_current(current, |cx| {
369 value.set_or_bind(cx, move |cx, v| {
370 let value = v.get_value(cx).into();
371 cx.style.backdrop_filter.insert(entity, value);
372
373 cx.needs_redraw(entity);
374 });
375 });
376
377 self
378 }
379
380 fn shadow<U: Into<Shadow>>(mut self, value: impl Res<U>) -> Self {
382 let entity = self.entity();
383 let current = self.current();
384 self.context().with_current(current, |cx| {
385 value.set_or_bind(cx, move |cx, v| {
386 let value = v.get_value(cx).into();
387 if let Some(shadows) = cx.style.shadow.get_inline_mut(entity) {
388 shadows.push(value);
389 } else {
390 cx.style.shadow.insert(entity, vec![value]);
391 }
392
393 cx.needs_redraw(entity);
394 });
395 });
396
397 self
398 }
399
400 fn shadows<U: Into<Vec<Shadow>>>(mut self, value: impl Res<U>) -> Self {
402 let entity = self.entity();
403 let current = self.current();
404 self.context().with_current(current, |cx| {
405 value.set_or_bind(cx, move |cx, v| {
406 let value = v.get_value(cx).into();
407
408 cx.style.shadow.insert(entity, value);
409
410 cx.needs_redraw(entity);
411 });
412 });
413
414 self
415 }
416
417 fn background_gradient<U: Into<Gradient>>(mut self, value: impl Res<U>) -> Self {
419 let entity = self.entity();
420 let current = self.current();
421 self.context().with_current(current, |cx| {
422 value.set_or_bind(cx, move |cx, v| {
423 let value = v.get_value(cx).into();
424 if let Some(background_images) = cx.style.background_image.get_inline_mut(entity) {
425 background_images.push(ImageOrGradient::Gradient(value));
426 } else {
427 cx.style
428 .background_image
429 .insert(entity, vec![ImageOrGradient::Gradient(value)]);
430 }
431
432 cx.needs_redraw(entity);
433 });
434 });
435
436 self
437 }
438
439 modifier!(
441 background_color,
443 Color,
444 SystemFlags::REDRAW
445 );
446
447 modifier!(
448 background_size,
450 Vec<BackgroundSize>,
451 SystemFlags::REDRAW
452 );
453
454 fn background_image<'i, U: Into<BackgroundImage<'i>>>(mut self, value: impl Res<U>) -> Self {
456 let entity = self.entity();
457 let current = self.current();
458 self.context().with_current(current, |cx| {
459 value.set_or_bind(cx, move |cx, val| {
460 let image = val.get_value(cx).into();
461 let image = match image {
462 BackgroundImage::Gradient(gradient) => {
463 Some(ImageOrGradient::Gradient(*gradient))
464 }
465 BackgroundImage::Url(url) => Some(ImageOrGradient::Image(url.url.to_string())),
466
467 _ => None,
468 };
469
470 if let Some(image) = image {
471 cx.style.background_image.insert(entity, vec![image]);
472 }
473
474 cx.needs_redraw(entity);
475 });
476 });
477
478 self
479 }
480
481 fn border_width<U: Into<LengthOrPercentage>>(mut self, value: impl Res<U>) -> Self {
483 let entity = self.entity();
484 let _current = self.current();
485 value.set_or_bind(self.context(), move |cx, v| {
486 cx.style.border_width.insert(entity, v.get_value(cx).into());
487 cx.cache.path.remove(entity);
488 cx.style.system_flags |= SystemFlags::RELAYOUT | SystemFlags::REDRAW;
489 cx.set_system_flags(entity, SystemFlags::RELAYOUT | SystemFlags::REDRAW);
490 });
491
492 self
493 }
494
495 modifier!(
496 border_color,
498 Color,
499 SystemFlags::REDRAW
500 );
501
502 modifier!(
503 border_style,
505 BorderStyleKeyword,
506 SystemFlags::REDRAW
507 );
508
509 modifier!(
510 corner_top_left_radius,
512 LengthOrPercentage,
513 SystemFlags::REDRAW
514 );
515
516 modifier!(
517 corner_top_right_radius,
519 LengthOrPercentage,
520 SystemFlags::REDRAW
521 );
522
523 modifier!(
524 corner_bottom_left_radius,
526 LengthOrPercentage,
527 SystemFlags::REDRAW
528 );
529
530 modifier!(
531 corner_bottom_right_radius,
533 LengthOrPercentage,
534 SystemFlags::REDRAW
535 );
536
537 fn corner_radius<U: std::fmt::Debug + Into<CornerRadius>>(
539 mut self,
540 value: impl Res<U>,
541 ) -> Self {
542 let entity = self.entity();
543 let current = self.current();
544 self.context().with_current(current, |cx| {
545 value.set_or_bind(cx, move |cx, v| {
546 let value = v.get_value(cx).into();
547 cx.style.corner_top_left_radius.insert(entity, value.top_left);
548 cx.style.corner_top_right_radius.insert(entity, value.top_right);
549 cx.style.corner_bottom_left_radius.insert(entity, value.bottom_left);
550 cx.style.corner_bottom_right_radius.insert(entity, value.bottom_right);
551
552 cx.needs_redraw(entity);
553 });
554 });
555
556 self
557 }
558
559 modifier!(
560 corner_top_left_shape,
562 CornerShape,
563 SystemFlags::REDRAW
564 );
565
566 modifier!(
567 corner_top_right_shape,
569 CornerShape,
570 SystemFlags::REDRAW
571 );
572
573 modifier!(
574 corner_bottom_left_shape,
576 CornerShape,
577 SystemFlags::REDRAW
578 );
579
580 modifier!(
581 corner_bottom_right_shape,
583 CornerShape,
584 SystemFlags::REDRAW
585 );
586
587 fn corner_shape<U: std::fmt::Debug + Into<Rect<CornerShape>>>(
589 mut self,
590 value: impl Res<U>,
591 ) -> Self {
592 let entity = self.entity();
593 let current = self.current();
594 self.context().with_current(current, |cx| {
595 value.set_or_bind(cx, move |cx, v| {
596 let value = v.get_value(cx).into();
597 cx.style.corner_top_left_shape.insert(entity, value.0);
598 cx.style.corner_top_right_shape.insert(entity, value.1);
599 cx.style.corner_bottom_right_shape.insert(entity, value.2);
600 cx.style.corner_bottom_left_shape.insert(entity, value.3);
601
602 cx.needs_redraw(entity);
603 });
604 });
605
606 self
607 }
608
609 modifier!(
610 corner_top_left_smoothing,
612 f32,
613 SystemFlags::REDRAW
614 );
615
616 modifier!(
617 corner_top_right_smoothing,
619 f32,
620 SystemFlags::REDRAW
621 );
622
623 modifier!(
624 corner_bottom_left_smoothing,
626 f32,
627 SystemFlags::REDRAW
628 );
629
630 modifier!(
631 corner_bottom_right_smoothing,
633 f32,
634 SystemFlags::REDRAW
635 );
636
637 fn corner_smoothing<U: std::fmt::Debug + Into<Rect<f32>>>(
639 mut self,
640 value: impl Res<U>,
641 ) -> Self {
642 let entity = self.entity();
643 let current = self.current();
644 self.context().with_current(current, |cx| {
645 value.set_or_bind(cx, move |cx, v| {
646 let value = v.get_value(cx).into();
647 cx.style.corner_top_left_smoothing.insert(entity, value.0);
648 cx.style.corner_top_right_smoothing.insert(entity, value.1);
649 cx.style.corner_bottom_left_smoothing.insert(entity, value.2);
650 cx.style.corner_bottom_right_smoothing.insert(entity, value.3);
651
652 cx.needs_redraw(entity);
653 });
654 });
655
656 self
657 }
658
659 modifier!(
661 outline_width,
663 LengthOrPercentage,
664 SystemFlags::REDRAW
665 );
666
667 modifier!(
668 outline_color,
670 Color,
671 SystemFlags::REDRAW
672 );
673
674 modifier!(
675 outline_offset,
677 LengthOrPercentage,
678 SystemFlags::REDRAW
679 );
680
681 modifier!(
682 fill,
684 Color,
685 SystemFlags::REDRAW
686 );
687
688 modifier!(
690 cursor,
692 CursorIcon,
693 SystemFlags::empty()
694 );
695
696 fn pointer_events<U: Into<PointerEvents>>(mut self, value: impl Res<U>) -> Self {
698 let entity = self.entity();
699 let current = self.current();
700 self.context().with_current(current, |cx| {
701 value.set_or_bind(cx, move |cx, v| {
702 let value = v.get_value(cx).into();
703 cx.style.pointer_events.insert(entity, value);
704 });
705 });
706
707 self
708 }
709
710 fn transform<U: Into<Vec<Transform>>>(mut self, value: impl Res<U>) -> Self {
712 let entity = self.entity();
713 let current = self.current();
714 self.context().with_current(current, |cx| {
715 value.set_or_bind(cx, move |cx, v| {
716 let value = v.get_value(cx).into();
717 cx.style.transform.insert(entity, value);
718 cx.needs_retransform(entity);
719 cx.needs_redraw(entity);
720 });
721 });
722
723 self
724 }
725
726 fn transform_origin<U: Into<Position>>(mut self, value: impl Res<U>) -> Self {
728 let entity = self.entity();
729 let current = self.current();
730 self.context().with_current(current, |cx| {
731 value.set_or_bind(cx, move |cx, v| {
732 let value = v.get_value(cx).into();
733 let x = value.x.to_length_or_percentage();
734 let y = value.y.to_length_or_percentage();
735 cx.style.transform_origin.insert(entity, Translate { x, y });
736 cx.needs_retransform(entity);
737 cx.needs_redraw(entity);
738 });
739 });
740
741 self
742 }
743
744 modifier!(
746 translate,
750 Translate,
751 SystemFlags::RETRANSFORM | SystemFlags::REDRAW
752 );
753
754 modifier!(
756 rotate,
760 Angle,
761 SystemFlags::RETRANSFORM | SystemFlags::REDRAW
762 );
763
764 modifier!(
766 scale,
770 Scale,
771 SystemFlags::RETRANSFORM | SystemFlags::REDRAW
772 );
773}
774
775impl<V: View> StyleModifiers for Handle<'_, V> {}
776
777#[derive(Debug, Clone)]
779pub struct LinearGradientBuilder {
780 direction: LineDirection,
781 stops: Vec<ColorStop<LengthOrPercentage>>,
782}
783
784impl Default for LinearGradientBuilder {
785 fn default() -> Self {
786 Self::new()
787 }
788}
789
790impl LinearGradientBuilder {
791 pub fn new() -> Self {
793 LinearGradientBuilder { direction: LineDirection::default(), stops: Vec::new() }
794 }
795
796 pub fn with_direction(direction: impl Into<LineDirection>) -> Self {
798 LinearGradientBuilder { direction: direction.into(), stops: Vec::new() }
799 }
800
801 fn build(self) -> Gradient {
802 Gradient::Linear(LinearGradient { direction: self.direction, stops: self.stops })
803 }
804
805 pub fn add_stop(mut self, stop: impl Into<ColorStop<LengthOrPercentage>>) -> Self {
807 self.stops.push(stop.into());
808
809 self
810 }
811}
812
813impl From<LinearGradientBuilder> for Gradient {
814 fn from(value: LinearGradientBuilder) -> Self {
815 value.build()
816 }
817}
818
819#[derive(Debug, Clone)]
821pub struct ShadowBuilder {
822 shadow: Shadow,
823}
824
825impl Default for ShadowBuilder {
826 fn default() -> Self {
827 Self::new()
828 }
829}
830
831impl ShadowBuilder {
832 pub fn new() -> Self {
834 Self { shadow: Shadow::default() }
835 }
836
837 fn build(self) -> Shadow {
838 self.shadow
839 }
840
841 pub fn x_offset(mut self, offset: impl Into<Length>) -> Self {
843 self.shadow.x_offset = offset.into();
844
845 self
846 }
847
848 pub fn y_offset(mut self, offset: impl Into<Length>) -> Self {
850 self.shadow.y_offset = offset.into();
851
852 self
853 }
854
855 pub fn blur(mut self, radius: Length) -> Self {
857 self.shadow.blur_radius = Some(radius);
858
859 self
860 }
861
862 pub fn spread(mut self, radius: Length) -> Self {
864 self.shadow.spread_radius = Some(radius);
865
866 self
867 }
868
869 pub fn color(mut self, color: Color) -> Self {
871 self.shadow.color = Some(color);
872
873 self
874 }
875
876 pub fn inset(mut self) -> Self {
878 self.shadow.inset = true;
879
880 self
881 }
882}
883
884impl From<ShadowBuilder> for Shadow {
885 fn from(value: ShadowBuilder) -> Self {
886 value.build()
887 }
888}