1use std::collections::HashMap;
7use std::sync::Arc;
8
9use blinc_core::{
10 BlurQuality, Brush, ClipPath, Color, CornerRadius, CornerShape, DynFloat, DynValue, FlowGraph,
11 LayerEffect, OverflowFade, Rect, Shadow, Transform, ValueContext,
12};
13use taffy::Layout;
14
15use crate::tree::LayoutNodeId;
16
17#[derive(Clone, Debug)]
24pub enum FlowRef {
25 Name(String),
27 Graph(Arc<FlowGraph>),
29}
30
31impl From<&str> for FlowRef {
32 fn from(s: &str) -> Self {
33 FlowRef::Name(s.to_string())
34 }
35}
36
37impl From<String> for FlowRef {
38 fn from(s: String) -> Self {
39 FlowRef::Name(s)
40 }
41}
42
43impl From<FlowGraph> for FlowRef {
44 fn from(g: FlowGraph) -> Self {
45 FlowRef::Graph(Arc::new(g))
46 }
47}
48
49#[derive(Clone, Debug, Default, PartialEq)]
51pub struct SvgTagStyle {
52 pub fill: Option<[f32; 4]>,
53 pub stroke: Option<[f32; 4]>,
54 pub stroke_width: Option<f32>,
55 pub stroke_dasharray: Option<Vec<f32>>,
56 pub stroke_dashoffset: Option<f32>,
57 pub opacity: Option<f32>,
58}
59
60#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
69pub enum CursorStyle {
70 #[default]
72 Default,
73 Pointer,
75 Text,
77 Crosshair,
79 Move,
81 NotAllowed,
83 ResizeNS,
85 ResizeEW,
87 ResizeNESW,
89 ResizeNWSE,
91 Grab,
93 Grabbing,
95 Wait,
97 Progress,
99 None,
101}
102
103#[derive(Clone, Debug)]
113pub enum Material {
114 Glass(GlassMaterial),
116 Metallic(MetallicMaterial),
118 Wood(WoodMaterial),
120 Solid(SolidMaterial),
122}
123
124impl Default for Material {
125 fn default() -> Self {
126 Material::Solid(SolidMaterial::default())
127 }
128}
129
130impl From<GlassMaterial> for Material {
135 fn from(glass: GlassMaterial) -> Self {
136 Material::Glass(glass)
137 }
138}
139
140impl From<MetallicMaterial> for Material {
141 fn from(metal: MetallicMaterial) -> Self {
142 Material::Metallic(metal)
143 }
144}
145
146impl From<WoodMaterial> for Material {
147 fn from(wood: WoodMaterial) -> Self {
148 Material::Wood(wood)
149 }
150}
151
152impl From<SolidMaterial> for Material {
153 fn from(solid: SolidMaterial) -> Self {
154 Material::Solid(solid)
155 }
156}
157
158#[derive(Clone, Debug)]
166pub struct GlassMaterial {
167 pub blur: f32,
169 pub tint: Color,
171 pub saturation: f32,
173 pub brightness: f32,
175 pub noise: f32,
177 pub border_thickness: f32,
179 pub shadow: Option<MaterialShadow>,
181 pub simple: bool,
183}
184
185impl Default for GlassMaterial {
186 fn default() -> Self {
187 Self {
188 blur: 20.0,
189 tint: Color::rgba(1.0, 1.0, 1.0, 0.1),
190 saturation: 1.0,
191 brightness: 1.0,
192 noise: 0.0,
193 border_thickness: 0.8,
194 shadow: None,
195 simple: false,
196 }
197 }
198}
199
200impl GlassMaterial {
201 pub fn new() -> Self {
203 Self::default()
204 }
205
206 pub fn blur(mut self, blur: f32) -> Self {
208 self.blur = blur;
209 self
210 }
211
212 pub fn tint(mut self, color: Color) -> Self {
214 self.tint = color;
215 self
216 }
217
218 pub fn tint_rgba(mut self, r: f32, g: f32, b: f32, a: f32) -> Self {
220 self.tint = Color::rgba(r, g, b, a);
221 self
222 }
223
224 pub fn saturation(mut self, saturation: f32) -> Self {
226 self.saturation = saturation;
227 self
228 }
229
230 pub fn brightness(mut self, brightness: f32) -> Self {
232 self.brightness = brightness;
233 self
234 }
235
236 pub fn noise(mut self, amount: f32) -> Self {
238 self.noise = amount;
239 self
240 }
241
242 pub fn border(mut self, thickness: f32) -> Self {
244 self.border_thickness = thickness;
245 self
246 }
247
248 pub fn shadow(mut self, shadow: MaterialShadow) -> Self {
250 self.shadow = Some(shadow);
251 self
252 }
253
254 pub fn ultra_thin() -> Self {
258 Self::new().blur(10.0)
259 }
260
261 pub fn thin() -> Self {
263 Self::new().blur(15.0)
264 }
265
266 pub fn regular() -> Self {
268 Self::new()
269 }
270
271 pub fn thick() -> Self {
273 Self::new().blur(30.0)
274 }
275
276 pub fn frosted() -> Self {
278 Self::new().noise(0.03)
279 }
280
281 pub fn card() -> Self {
283 Self::new().border(1.0).shadow(MaterialShadow::md())
284 }
285
286 pub fn simple() -> Self {
292 Self {
293 blur: 15.0,
294 tint: Color::rgba(1.0, 1.0, 1.0, 0.15),
295 saturation: 1.1,
296 brightness: 1.0,
297 noise: 0.0,
298 border_thickness: 0.0,
299 shadow: None,
300 simple: true,
301 }
302 }
303
304 pub fn with_simple(mut self, simple: bool) -> Self {
306 self.simple = simple;
307 self
308 }
309}
310
311#[derive(Clone, Debug)]
319pub struct MetallicMaterial {
320 pub color: Color,
322 pub roughness: f32,
324 pub metallic: f32,
326 pub reflection: f32,
328 pub shadow: Option<MaterialShadow>,
330}
331
332impl Default for MetallicMaterial {
333 fn default() -> Self {
334 Self {
335 color: Color::rgba(0.8, 0.8, 0.85, 1.0),
336 roughness: 0.3,
337 metallic: 1.0,
338 reflection: 0.5,
339 shadow: None,
340 }
341 }
342}
343
344impl MetallicMaterial {
345 pub fn new() -> Self {
347 Self::default()
348 }
349
350 pub fn color(mut self, color: Color) -> Self {
352 self.color = color;
353 self
354 }
355
356 pub fn roughness(mut self, roughness: f32) -> Self {
358 self.roughness = roughness;
359 self
360 }
361
362 pub fn metallic(mut self, metallic: f32) -> Self {
364 self.metallic = metallic;
365 self
366 }
367
368 pub fn reflection(mut self, reflection: f32) -> Self {
370 self.reflection = reflection;
371 self
372 }
373
374 pub fn shadow(mut self, shadow: MaterialShadow) -> Self {
376 self.shadow = Some(shadow);
377 self
378 }
379
380 pub fn chrome() -> Self {
384 Self::new().roughness(0.1).reflection(0.8)
385 }
386
387 pub fn brushed() -> Self {
389 Self::new().roughness(0.5).reflection(0.3)
390 }
391
392 pub fn gold() -> Self {
394 Self::new()
395 .color(Color::rgba(1.0, 0.84, 0.0, 1.0))
396 .roughness(0.2)
397 }
398
399 pub fn silver() -> Self {
401 Self::new()
402 .color(Color::rgba(0.75, 0.75, 0.75, 1.0))
403 .roughness(0.2)
404 }
405
406 pub fn copper() -> Self {
408 Self::new()
409 .color(Color::rgba(0.72, 0.45, 0.2, 1.0))
410 .roughness(0.3)
411 }
412}
413
414#[derive(Clone, Debug)]
420pub struct WoodMaterial {
421 pub color: Color,
423 pub grain: f32,
425 pub gloss: f32,
427 pub shadow: Option<MaterialShadow>,
429}
430
431impl Default for WoodMaterial {
432 fn default() -> Self {
433 Self {
434 color: Color::rgba(0.55, 0.35, 0.2, 1.0),
435 grain: 0.5,
436 gloss: 0.2,
437 shadow: None,
438 }
439 }
440}
441
442impl WoodMaterial {
443 pub fn new() -> Self {
445 Self::default()
446 }
447
448 pub fn color(mut self, color: Color) -> Self {
450 self.color = color;
451 self
452 }
453
454 pub fn grain(mut self, grain: f32) -> Self {
456 self.grain = grain;
457 self
458 }
459
460 pub fn gloss(mut self, gloss: f32) -> Self {
462 self.gloss = gloss;
463 self
464 }
465
466 pub fn shadow(mut self, shadow: MaterialShadow) -> Self {
468 self.shadow = Some(shadow);
469 self
470 }
471
472 pub fn oak() -> Self {
476 Self::new().color(Color::rgba(0.6, 0.45, 0.25, 1.0))
477 }
478
479 pub fn walnut() -> Self {
481 Self::new().color(Color::rgba(0.4, 0.25, 0.15, 1.0))
482 }
483
484 pub fn cherry() -> Self {
486 Self::new().color(Color::rgba(0.6, 0.3, 0.2, 1.0))
487 }
488
489 pub fn pine() -> Self {
491 Self::new().color(Color::rgba(0.8, 0.65, 0.45, 1.0))
492 }
493}
494
495#[derive(Clone, Debug, Default)]
501pub struct SolidMaterial {
502 pub shadow: Option<MaterialShadow>,
504}
505
506impl SolidMaterial {
507 pub fn new() -> Self {
509 Self::default()
510 }
511
512 pub fn shadow(mut self, shadow: MaterialShadow) -> Self {
514 self.shadow = Some(shadow);
515 self
516 }
517}
518
519#[derive(Clone, Debug)]
525pub struct MaterialShadow {
526 pub color: Color,
528 pub blur: f32,
530 pub offset: (f32, f32),
532 pub opacity: f32,
534}
535
536impl Default for MaterialShadow {
537 fn default() -> Self {
538 Self {
539 color: Color::rgba(0.0, 0.0, 0.0, 1.0),
540 blur: 10.0,
541 offset: (0.0, 4.0),
542 opacity: 0.25,
543 }
544 }
545}
546
547impl From<Shadow> for MaterialShadow {
549 fn from(shadow: Shadow) -> Self {
550 Self {
551 color: shadow.color,
552 blur: shadow.blur,
553 offset: (shadow.offset_x, shadow.offset_y),
554 opacity: shadow.color.a,
556 }
557 }
558}
559
560impl From<&Shadow> for MaterialShadow {
562 fn from(shadow: &Shadow) -> Self {
563 Self {
564 color: shadow.color,
565 blur: shadow.blur,
566 offset: (shadow.offset_x, shadow.offset_y),
567 opacity: shadow.color.a,
568 }
569 }
570}
571
572impl MaterialShadow {
573 pub fn new() -> Self {
575 Self::default()
576 }
577
578 pub fn color(mut self, color: Color) -> Self {
580 self.color = color;
581 self
582 }
583
584 pub fn blur(mut self, blur: f32) -> Self {
586 self.blur = blur;
587 self
588 }
589
590 pub fn offset(mut self, x: f32, y: f32) -> Self {
592 self.offset = (x, y);
593 self
594 }
595
596 pub fn opacity(mut self, opacity: f32) -> Self {
598 self.opacity = opacity;
599 self
600 }
601
602 pub fn sm() -> Self {
606 Self::new().blur(4.0).offset(0.0, 2.0).opacity(0.2)
607 }
608
609 pub fn md() -> Self {
611 Self::new().blur(10.0).offset(0.0, 4.0).opacity(0.25)
612 }
613
614 pub fn lg() -> Self {
616 Self::new().blur(20.0).offset(0.0, 8.0).opacity(0.3)
617 }
618
619 pub fn xl() -> Self {
621 Self::new().blur(30.0).offset(0.0, 12.0).opacity(0.35)
622 }
623}
624
625#[derive(Clone, Copy, Debug, Default)]
627pub struct ElementBounds {
628 pub x: f32,
630 pub y: f32,
632 pub width: f32,
634 pub height: f32,
636}
637
638impl ElementBounds {
639 pub fn from_layout(layout: &Layout, parent_offset: (f32, f32)) -> Self {
641 Self {
642 x: parent_offset.0 + layout.location.x,
643 y: parent_offset.1 + layout.location.y,
644 width: layout.size.width,
645 height: layout.size.height,
646 }
647 }
648
649 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
651 Self {
652 x,
653 y,
654 width,
655 height,
656 }
657 }
658
659 pub fn to_rect(&self) -> Rect {
661 Rect::new(self.x, self.y, self.width, self.height)
662 }
663
664 pub fn local(&self) -> Self {
666 Self {
667 x: 0.0,
668 y: 0.0,
669 width: self.width,
670 height: self.height,
671 }
672 }
673}
674
675#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
683pub enum RenderLayer {
684 #[default]
686 Background,
687 Glass,
689 Foreground,
691}
692
693#[derive(Clone, Debug, Default)]
695pub struct MotionAnimation {
696 pub enter_from: Option<MotionKeyframe>,
698 pub enter_duration_ms: u32,
700 pub enter_delay_ms: u32,
702 pub exit_to: Option<MotionKeyframe>,
704 pub exit_duration_ms: u32,
706}
707
708#[derive(Clone, Debug, Default)]
710pub struct MotionKeyframe {
711 pub opacity: Option<f32>,
713 pub scale_x: Option<f32>,
715 pub scale_y: Option<f32>,
717 pub translate_x: Option<f32>,
719 pub translate_y: Option<f32>,
721 pub rotate: Option<f32>,
723}
724
725impl MotionKeyframe {
726 pub fn new() -> Self {
728 Self::default()
729 }
730
731 pub fn opacity(mut self, opacity: f32) -> Self {
733 self.opacity = Some(opacity);
734 self
735 }
736
737 pub fn scale(mut self, scale: f32) -> Self {
739 self.scale_x = Some(scale);
740 self.scale_y = Some(scale);
741 self
742 }
743
744 pub fn translate(mut self, x: f32, y: f32) -> Self {
746 self.translate_x = Some(x);
747 self.translate_y = Some(y);
748 self
749 }
750
751 pub fn rotate(mut self, degrees: f32) -> Self {
753 self.rotate = Some(degrees);
754 self
755 }
756
757 pub fn from_keyframe_properties(props: &blinc_animation::KeyframeProperties) -> Self {
759 Self {
760 opacity: props.opacity,
761 scale_x: props.scale_x,
762 scale_y: props.scale_y,
763 translate_x: props.translate_x,
764 translate_y: props.translate_y,
765 rotate: props.rotate,
766 }
767 }
768
769 pub fn lerp(&self, other: &Self, t: f32) -> Self {
774 Self {
775 opacity: lerp_opt_with_default(self.opacity, other.opacity, t, 1.0),
777 scale_x: lerp_opt_with_default(self.scale_x, other.scale_x, t, 1.0),
779 scale_y: lerp_opt_with_default(self.scale_y, other.scale_y, t, 1.0),
780 translate_x: lerp_opt_with_default(self.translate_x, other.translate_x, t, 0.0),
782 translate_y: lerp_opt_with_default(self.translate_y, other.translate_y, t, 0.0),
783 rotate: lerp_opt_with_default(self.rotate, other.rotate, t, 0.0),
785 }
786 }
787
788 pub fn resolved_opacity(&self) -> f32 {
790 self.opacity.unwrap_or(1.0)
791 }
792
793 pub fn resolved_scale(&self) -> (f32, f32) {
795 (self.scale_x.unwrap_or(1.0), self.scale_y.unwrap_or(1.0))
796 }
797
798 pub fn resolved_translate(&self) -> (f32, f32) {
800 (
801 self.translate_x.unwrap_or(0.0),
802 self.translate_y.unwrap_or(0.0),
803 )
804 }
805
806 pub fn resolved_rotate(&self) -> f32 {
808 self.rotate.unwrap_or(0.0)
809 }
810}
811
812fn lerp_opt_with_default(a: Option<f32>, b: Option<f32>, t: f32, default: f32) -> Option<f32> {
814 match (a, b) {
815 (Some(a), Some(b)) => Some(a + (b - a) * t),
816 (Some(a), None) => Some(a + (default - a) * t), (None, Some(b)) => Some(default + (b - default) * t), (None, None) => None,
819 }
820}
821
822impl MotionAnimation {
823 pub fn from_enter_animation(
825 anim: &blinc_animation::MultiKeyframeAnimation,
826 delay_ms: u32,
827 ) -> Self {
828 let enter_from = anim
830 .first_keyframe()
831 .map(|kf| MotionKeyframe::from_keyframe_properties(&kf.properties));
832
833 Self {
834 enter_from,
835 enter_duration_ms: anim.duration_ms(),
836 enter_delay_ms: delay_ms,
837 exit_to: None,
838 exit_duration_ms: 0,
839 }
840 }
841
842 pub fn with_exit_animation(mut self, anim: &blinc_animation::MultiKeyframeAnimation) -> Self {
844 self.exit_to = anim
846 .last_keyframe()
847 .map(|kf| MotionKeyframe::from_keyframe_properties(&kf.properties));
848 self.exit_duration_ms = anim.duration_ms();
849 self
850 }
851}
852
853#[derive(Clone, Copy, Debug, Default)]
855pub struct BorderSide {
856 pub width: f32,
858 pub color: Color,
860}
861
862impl BorderSide {
863 pub fn new(width: f32, color: Color) -> Self {
865 Self { width, color }
866 }
867
868 pub fn is_visible(&self) -> bool {
870 self.width > 0.0 && self.color.a > 0.0
871 }
872}
873
874#[derive(Clone, Copy, Debug, Default)]
879pub struct BorderSides {
880 pub top: Option<BorderSide>,
882 pub right: Option<BorderSide>,
884 pub bottom: Option<BorderSide>,
886 pub left: Option<BorderSide>,
888}
889
890impl BorderSides {
891 pub fn none() -> Self {
893 Self::default()
894 }
895
896 pub fn has_any(&self) -> bool {
898 self.top.as_ref().is_some_and(|b| b.is_visible())
899 || self.right.as_ref().is_some_and(|b| b.is_visible())
900 || self.bottom.as_ref().is_some_and(|b| b.is_visible())
901 || self.left.as_ref().is_some_and(|b| b.is_visible())
902 }
903}
904
905#[derive(Clone, Copy, Debug, Default)]
923pub struct BorderBuilder {
924 sides: BorderSides,
925}
926
927impl BorderBuilder {
928 pub fn new() -> Self {
930 Self::default()
931 }
932
933 pub fn left(mut self, width: f32, color: Color) -> Self {
935 self.sides.left = Some(BorderSide::new(width, color));
936 self
937 }
938
939 pub fn right(mut self, width: f32, color: Color) -> Self {
941 self.sides.right = Some(BorderSide::new(width, color));
942 self
943 }
944
945 pub fn top(mut self, width: f32, color: Color) -> Self {
947 self.sides.top = Some(BorderSide::new(width, color));
948 self
949 }
950
951 pub fn bottom(mut self, width: f32, color: Color) -> Self {
953 self.sides.bottom = Some(BorderSide::new(width, color));
954 self
955 }
956
957 pub fn x(mut self, width: f32, color: Color) -> Self {
959 let side = BorderSide::new(width, color);
960 self.sides.left = Some(side);
961 self.sides.right = Some(side);
962 self
963 }
964
965 pub fn y(mut self, width: f32, color: Color) -> Self {
967 let side = BorderSide::new(width, color);
968 self.sides.top = Some(side);
969 self.sides.bottom = Some(side);
970 self
971 }
972
973 pub fn all(mut self, width: f32, color: Color) -> Self {
975 let side = BorderSide::new(width, color);
976 self.sides.top = Some(side);
977 self.sides.right = Some(side);
978 self.sides.bottom = Some(side);
979 self.sides.left = Some(side);
980 self
981 }
982
983 pub fn build(self) -> BorderSides {
985 self.sides
986 }
987}
988
989#[derive(Clone)]
991pub struct RenderProps {
992 pub background: Option<Brush>,
994 pub border_radius: CornerRadius,
996 pub border_radius_explicit: bool,
998 pub corner_shape: CornerShape,
1000 pub border_color: Option<Color>,
1002 pub border_width: f32,
1004 pub border_sides: BorderSides,
1006 pub outline_color: Option<Color>,
1008 pub outline_width: f32,
1010 pub outline_offset: f32,
1012 pub layer: RenderLayer,
1014 pub material: Option<Material>,
1016 pub node_id: Option<LayoutNodeId>,
1018 pub shadow: Option<Shadow>,
1020 pub transform: Option<Transform>,
1022 pub opacity: f32,
1024 pub clips_content: bool,
1026 pub overflow_fade: OverflowFade,
1028 pub motion: Option<MotionAnimation>,
1030 pub motion_stable_id: Option<String>,
1033 pub motion_should_replay: bool,
1036 pub motion_is_suspended: bool,
1039 pub motion_on_ready_callback:
1042 Option<std::sync::Arc<dyn Fn(ElementBounds) + Send + Sync + 'static>>,
1043 pub is_stack_layer: bool,
1046 pub cursor: Option<CursorStyle>,
1048 pub pointer_events_none: bool,
1052 pub is_fixed: bool,
1055 pub is_sticky: bool,
1058 pub sticky_top: Option<f32>,
1060 pub sticky_bottom: Option<f32>,
1062 pub z_index: i32,
1064 pub text_color: Option<[f32; 4]>,
1066 pub font_size: Option<f32>,
1068 pub text_shadow: Option<Shadow>,
1070 pub font_weight: Option<crate::div::FontWeight>,
1072 pub text_decoration: Option<crate::element_style::TextDecoration>,
1074 pub line_height: Option<f32>,
1076 pub text_align: Option<crate::div::TextAlign>,
1078 pub letter_spacing: Option<f32>,
1080 pub fill: Option<[f32; 4]>,
1082 pub stroke: Option<[f32; 4]>,
1084 pub stroke_width: Option<f32>,
1086 pub stroke_dasharray: Option<Vec<f32>>,
1088 pub stroke_dashoffset: Option<f32>,
1090 pub svg_path_data: Option<String>,
1092 pub transform_origin: Option<[f32; 2]>,
1094 pub layer_effects: Vec<LayerEffect>,
1097 pub rotate_x: Option<f32>,
1100 pub rotate_y: Option<f32>,
1102 pub perspective: Option<f32>,
1104 pub shape_3d: Option<f32>,
1106 pub depth: Option<f32>,
1108 pub light_direction: Option<[f32; 3]>,
1110 pub light_intensity: Option<f32>,
1112 pub ambient: Option<f32>,
1114 pub specular: Option<f32>,
1116 pub translate_z: Option<f32>,
1118 pub op_3d: Option<f32>,
1120 pub blend_3d: Option<f32>,
1122 pub clip_path: Option<ClipPath>,
1124 pub filter: Option<crate::element_style::CssFilter>,
1126 #[deprecated(
1135 since = "0.1.0",
1136 note = "Use query_motion(key).exit() to explicitly trigger motion exit"
1137 )]
1138 pub motion_is_exiting: bool,
1139 pub visible: bool,
1141 pub object_fit: Option<u8>,
1143 pub object_position: Option<[f32; 2]>,
1145 pub loading_strategy: Option<u8>,
1147 pub placeholder_type: Option<u8>,
1149 pub placeholder_color: Option<[f32; 4]>,
1151 pub placeholder_image: Option<String>,
1153 pub fade_duration_ms: Option<u32>,
1155 pub svg_tag_styles: HashMap<String, SvgTagStyle>,
1157 pub mix_blend_mode: Option<blinc_core::BlendMode>,
1159 pub text_decoration_color: Option<[f32; 4]>,
1161 pub text_decoration_thickness: Option<f32>,
1163 pub text_overflow: Option<crate::element_style::TextOverflow>,
1165 pub white_space: Option<crate::element_style::WhiteSpace>,
1167 pub mask_image: Option<blinc_core::MaskImage>,
1169 pub mask_mode: Option<blinc_core::MaskMode>,
1171 pub flow: Option<String>,
1173 pub flow_graph: Option<Arc<FlowGraph>>,
1175}
1176
1177impl Default for RenderProps {
1178 #[allow(deprecated)]
1179 fn default() -> Self {
1180 Self {
1181 background: None,
1182 border_radius: CornerRadius::default(),
1183 border_radius_explicit: false,
1184 corner_shape: CornerShape::default(),
1185 border_color: None,
1186 border_width: 0.0,
1187 border_sides: BorderSides::default(),
1188 outline_color: None,
1189 outline_width: 0.0,
1190 outline_offset: 0.0,
1191 layer: RenderLayer::default(),
1192 material: None,
1193 node_id: None,
1194 shadow: None,
1195 transform: None,
1196 opacity: 1.0,
1197 clips_content: false,
1198 overflow_fade: OverflowFade::default(),
1199 motion: None,
1200 motion_stable_id: None,
1201 motion_should_replay: false,
1202 motion_is_suspended: false,
1203 motion_on_ready_callback: None,
1204 is_stack_layer: false,
1205 cursor: None,
1206 pointer_events_none: false,
1207 is_fixed: false,
1208 is_sticky: false,
1209 sticky_top: None,
1210 sticky_bottom: None,
1211 layer_effects: Vec::new(),
1212 rotate_x: None,
1213 rotate_y: None,
1214 perspective: None,
1215 shape_3d: None,
1216 depth: None,
1217 light_direction: None,
1218 light_intensity: None,
1219 ambient: None,
1220 specular: None,
1221 translate_z: None,
1222 op_3d: None,
1223 blend_3d: None,
1224 clip_path: None,
1225 filter: None,
1226 motion_is_exiting: false,
1227 z_index: 0,
1228 text_color: None,
1229 font_size: None,
1230 text_shadow: None,
1231 font_weight: None,
1232 text_decoration: None,
1233 line_height: None,
1234 text_align: None,
1235 letter_spacing: None,
1236 fill: None,
1237 stroke: None,
1238 stroke_width: None,
1239 stroke_dasharray: None,
1240 stroke_dashoffset: None,
1241 svg_path_data: None,
1242 transform_origin: None,
1243 visible: true,
1244 object_fit: None,
1245 object_position: None,
1246 loading_strategy: None,
1247 placeholder_type: None,
1248 placeholder_color: None,
1249 placeholder_image: None,
1250 fade_duration_ms: None,
1251 svg_tag_styles: HashMap::new(),
1252 mix_blend_mode: None,
1253 text_decoration_color: None,
1254 text_decoration_thickness: None,
1255 text_overflow: None,
1256 white_space: None,
1257 mask_image: None,
1258 mask_mode: None,
1259 flow: None,
1260 flow_graph: None,
1261 }
1262 }
1263}
1264
1265impl RenderProps {
1266 pub fn new() -> Self {
1268 Self::default()
1269 }
1270
1271 pub fn with_background(mut self, brush: impl Into<Brush>) -> Self {
1273 self.background = Some(brush.into());
1274 self
1275 }
1276
1277 pub fn with_bg_color(mut self, color: Color) -> Self {
1279 self.background = Some(Brush::Solid(color));
1280 self
1281 }
1282
1283 pub fn with_border_radius(mut self, radius: CornerRadius) -> Self {
1285 self.border_radius = radius;
1286 self
1287 }
1288
1289 pub fn with_rounded(mut self, radius: f32) -> Self {
1291 self.border_radius = CornerRadius::uniform(radius);
1292 self
1293 }
1294
1295 pub fn with_layer(mut self, layer: RenderLayer) -> Self {
1297 self.layer = layer;
1298 self
1299 }
1300
1301 pub fn with_material(mut self, material: Material) -> Self {
1303 self.material = Some(material);
1304 self
1305 }
1306
1307 pub fn with_node_id(mut self, id: LayoutNodeId) -> Self {
1309 self.node_id = Some(id);
1310 self
1311 }
1312
1313 pub fn is_glass(&self) -> bool {
1315 matches!(self.material, Some(Material::Glass(_)))
1316 }
1317
1318 pub fn glass_material(&self) -> Option<&GlassMaterial> {
1320 match &self.material {
1321 Some(Material::Glass(glass)) => Some(glass),
1322 _ => None,
1323 }
1324 }
1325
1326 pub fn with_shadow(mut self, shadow: Shadow) -> Self {
1328 self.shadow = Some(shadow);
1329 self
1330 }
1331
1332 pub fn with_transform(mut self, transform: Transform) -> Self {
1334 self.transform = Some(transform);
1335 self
1336 }
1337
1338 pub fn with_opacity(mut self, opacity: f32) -> Self {
1340 self.opacity = opacity.clamp(0.0, 1.0);
1341 self
1342 }
1343
1344 pub fn with_clips_content(mut self, clips: bool) -> Self {
1346 self.clips_content = clips;
1347 self
1348 }
1349
1350 pub fn with_cursor(mut self, cursor: CursorStyle) -> Self {
1352 self.cursor = Some(cursor);
1353 self
1354 }
1355
1356 pub fn merge_from(&mut self, other: &RenderProps) {
1362 if other.background.is_some() {
1364 self.background = other.background.clone();
1365 }
1366 if other.border_radius_explicit || other.border_radius != CornerRadius::default() {
1368 self.border_radius = other.border_radius;
1369 self.border_radius_explicit = other.border_radius_explicit;
1370 }
1371 if other.border_color.is_some() {
1373 self.border_color = other.border_color;
1374 }
1375 if other.border_width > 0.0 {
1377 self.border_width = other.border_width;
1378 }
1379 if other.layer != RenderLayer::default() {
1381 self.layer = other.layer;
1382 }
1383 if other.material.is_some() {
1385 self.material = other.material.clone();
1386 }
1387 if other.shadow.is_some() {
1390 self.shadow = other.shadow;
1391 }
1392 if other.transform.is_some() {
1394 self.transform = other.transform.clone();
1395 }
1396 if (other.opacity - 1.0).abs() > f32::EPSILON {
1398 self.opacity = other.opacity;
1399 }
1400 if other.clips_content {
1402 self.clips_content = true;
1403 }
1404 if other.motion.is_some() {
1406 self.motion = other.motion.clone();
1407 }
1408 }
1409}
1410
1411#[derive(Clone)]
1439pub struct DynRenderProps {
1440 pub background: Option<DynValue<Brush>>,
1442 pub border_radius: CornerRadius,
1444 pub corner_shape: CornerShape,
1446 pub layer: RenderLayer,
1448 pub material: Option<Material>,
1450 pub node_id: Option<LayoutNodeId>,
1452 pub shadow: Option<Shadow>,
1454 pub transform: Option<Transform>,
1456 pub opacity: DynFloat,
1458 pub clips_content: bool,
1460 pub overflow_fade: OverflowFade,
1462}
1463
1464impl Default for DynRenderProps {
1465 fn default() -> Self {
1466 Self {
1467 background: None,
1468 border_radius: CornerRadius::default(),
1469 corner_shape: CornerShape::default(),
1470 layer: RenderLayer::default(),
1471 material: None,
1472 node_id: None,
1473 shadow: None,
1474 transform: None,
1475 opacity: DynFloat::Static(1.0),
1476 clips_content: false,
1477 overflow_fade: OverflowFade::default(),
1478 }
1479 }
1480}
1481
1482impl DynRenderProps {
1483 pub fn new() -> Self {
1485 Self::default()
1486 }
1487
1488 pub fn resolve(&self, ctx: &ValueContext) -> ResolvedRenderProps {
1492 ResolvedRenderProps {
1493 background: self.background.as_ref().map(|v| v.get(ctx)),
1494 border_radius: self.border_radius,
1495 corner_shape: self.corner_shape,
1496 layer: self.layer,
1497 material: self.material.clone(),
1498 node_id: self.node_id,
1499 shadow: self.shadow,
1500 transform: self.transform.clone(),
1501 opacity: self.opacity.get(ctx),
1502 clips_content: self.clips_content,
1503 overflow_fade: self.overflow_fade,
1504 }
1505 }
1506
1507 pub fn from_static(props: RenderProps) -> Self {
1509 Self {
1510 background: props.background.map(DynValue::Static),
1511 border_radius: props.border_radius,
1512 corner_shape: props.corner_shape,
1513 layer: props.layer,
1514 material: props.material,
1515 node_id: props.node_id,
1516 shadow: props.shadow,
1517 transform: props.transform,
1518 opacity: DynFloat::Static(props.opacity),
1519 clips_content: props.clips_content,
1520 overflow_fade: props.overflow_fade,
1521 }
1522 }
1523
1524 pub fn with_background(mut self, brush: impl Into<Brush>) -> Self {
1526 self.background = Some(DynValue::Static(brush.into()));
1527 self
1528 }
1529
1530 pub fn with_background_signal(mut self, signal_id: u64, default: Brush) -> Self {
1532 self.background = Some(DynValue::Signal {
1533 id: signal_id,
1534 default,
1535 });
1536 self
1537 }
1538
1539 pub fn with_opacity(mut self, opacity: f32) -> Self {
1541 self.opacity = DynFloat::Static(opacity.clamp(0.0, 1.0));
1542 self
1543 }
1544
1545 pub fn with_opacity_signal(mut self, signal_id: u64, default: f32) -> Self {
1547 self.opacity = DynFloat::Signal {
1548 id: signal_id,
1549 default,
1550 };
1551 self
1552 }
1553
1554 pub fn with_opacity_spring(mut self, spring_id: u64, generation: u32, default: f32) -> Self {
1556 self.opacity = DynFloat::Spring {
1557 id: spring_id,
1558 generation,
1559 default,
1560 };
1561 self
1562 }
1563}
1564
1565#[derive(Clone)]
1570pub struct ResolvedRenderProps {
1571 pub background: Option<Brush>,
1573 pub border_radius: CornerRadius,
1575 pub corner_shape: CornerShape,
1577 pub layer: RenderLayer,
1579 pub material: Option<Material>,
1581 pub node_id: Option<LayoutNodeId>,
1583 pub shadow: Option<Shadow>,
1585 pub transform: Option<Transform>,
1587 pub opacity: f32,
1589 pub clips_content: bool,
1591 pub overflow_fade: OverflowFade,
1593}
1594
1595impl Default for ResolvedRenderProps {
1596 fn default() -> Self {
1597 Self {
1598 background: None,
1599 border_radius: CornerRadius::default(),
1600 corner_shape: CornerShape::default(),
1601 layer: RenderLayer::default(),
1602 material: None,
1603 node_id: None,
1604 shadow: None,
1605 transform: None,
1606 opacity: 1.0,
1607 clips_content: false,
1608 overflow_fade: OverflowFade::default(),
1609 }
1610 }
1611}