use std::collections::HashMap;
use std::sync::Arc;
use blinc_core::{
BlurQuality, Brush, ClipPath, Color, CornerRadius, CornerShape, DynFloat, DynValue, FlowGraph,
LayerEffect, OverflowFade, Rect, Shadow, Transform, ValueContext,
};
use taffy::Layout;
use crate::tree::LayoutNodeId;
#[derive(Clone, Debug)]
pub enum FlowRef {
Name(String),
Graph(Arc<FlowGraph>),
}
impl From<&str> for FlowRef {
fn from(s: &str) -> Self {
FlowRef::Name(s.to_string())
}
}
impl From<String> for FlowRef {
fn from(s: String) -> Self {
FlowRef::Name(s)
}
}
impl From<FlowGraph> for FlowRef {
fn from(g: FlowGraph) -> Self {
FlowRef::Graph(Arc::new(g))
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SvgTagStyle {
pub fill: Option<[f32; 4]>,
pub stroke: Option<[f32; 4]>,
pub stroke_width: Option<f32>,
pub stroke_dasharray: Option<Vec<f32>>,
pub stroke_dashoffset: Option<f32>,
pub opacity: Option<f32>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum CursorStyle {
#[default]
Default,
Pointer,
Text,
Crosshair,
Move,
NotAllowed,
ResizeNS,
ResizeEW,
ResizeNESW,
ResizeNWSE,
Grab,
Grabbing,
Wait,
Progress,
None,
}
#[derive(Clone, Debug)]
pub enum Material {
Glass(GlassMaterial),
Metallic(MetallicMaterial),
Wood(WoodMaterial),
Solid(SolidMaterial),
}
impl Default for Material {
fn default() -> Self {
Material::Solid(SolidMaterial::default())
}
}
impl From<GlassMaterial> for Material {
fn from(glass: GlassMaterial) -> Self {
Material::Glass(glass)
}
}
impl From<MetallicMaterial> for Material {
fn from(metal: MetallicMaterial) -> Self {
Material::Metallic(metal)
}
}
impl From<WoodMaterial> for Material {
fn from(wood: WoodMaterial) -> Self {
Material::Wood(wood)
}
}
impl From<SolidMaterial> for Material {
fn from(solid: SolidMaterial) -> Self {
Material::Solid(solid)
}
}
#[derive(Clone, Debug)]
pub struct GlassMaterial {
pub blur: f32,
pub tint: Color,
pub saturation: f32,
pub brightness: f32,
pub noise: f32,
pub border_thickness: f32,
pub shadow: Option<MaterialShadow>,
pub simple: bool,
}
impl Default for GlassMaterial {
fn default() -> Self {
Self {
blur: 20.0,
tint: Color::rgba(1.0, 1.0, 1.0, 0.1),
saturation: 1.0,
brightness: 1.0,
noise: 0.0,
border_thickness: 0.8,
shadow: None,
simple: false,
}
}
}
impl GlassMaterial {
pub fn new() -> Self {
Self::default()
}
pub fn blur(mut self, blur: f32) -> Self {
self.blur = blur;
self
}
pub fn tint(mut self, color: Color) -> Self {
self.tint = color;
self
}
pub fn tint_rgba(mut self, r: f32, g: f32, b: f32, a: f32) -> Self {
self.tint = Color::rgba(r, g, b, a);
self
}
pub fn saturation(mut self, saturation: f32) -> Self {
self.saturation = saturation;
self
}
pub fn brightness(mut self, brightness: f32) -> Self {
self.brightness = brightness;
self
}
pub fn noise(mut self, amount: f32) -> Self {
self.noise = amount;
self
}
pub fn border(mut self, thickness: f32) -> Self {
self.border_thickness = thickness;
self
}
pub fn shadow(mut self, shadow: MaterialShadow) -> Self {
self.shadow = Some(shadow);
self
}
pub fn ultra_thin() -> Self {
Self::new().blur(10.0)
}
pub fn thin() -> Self {
Self::new().blur(15.0)
}
pub fn regular() -> Self {
Self::new()
}
pub fn thick() -> Self {
Self::new().blur(30.0)
}
pub fn frosted() -> Self {
Self::new().noise(0.03)
}
pub fn card() -> Self {
Self::new().border(1.0).shadow(MaterialShadow::md())
}
pub fn simple() -> Self {
Self {
blur: 15.0,
tint: Color::rgba(1.0, 1.0, 1.0, 0.15),
saturation: 1.1,
brightness: 1.0,
noise: 0.0,
border_thickness: 0.0,
shadow: None,
simple: true,
}
}
pub fn with_simple(mut self, simple: bool) -> Self {
self.simple = simple;
self
}
}
#[derive(Clone, Debug)]
pub struct MetallicMaterial {
pub color: Color,
pub roughness: f32,
pub metallic: f32,
pub reflection: f32,
pub shadow: Option<MaterialShadow>,
}
impl Default for MetallicMaterial {
fn default() -> Self {
Self {
color: Color::rgba(0.8, 0.8, 0.85, 1.0),
roughness: 0.3,
metallic: 1.0,
reflection: 0.5,
shadow: None,
}
}
}
impl MetallicMaterial {
pub fn new() -> Self {
Self::default()
}
pub fn color(mut self, color: Color) -> Self {
self.color = color;
self
}
pub fn roughness(mut self, roughness: f32) -> Self {
self.roughness = roughness;
self
}
pub fn metallic(mut self, metallic: f32) -> Self {
self.metallic = metallic;
self
}
pub fn reflection(mut self, reflection: f32) -> Self {
self.reflection = reflection;
self
}
pub fn shadow(mut self, shadow: MaterialShadow) -> Self {
self.shadow = Some(shadow);
self
}
pub fn chrome() -> Self {
Self::new().roughness(0.1).reflection(0.8)
}
pub fn brushed() -> Self {
Self::new().roughness(0.5).reflection(0.3)
}
pub fn gold() -> Self {
Self::new()
.color(Color::rgba(1.0, 0.84, 0.0, 1.0))
.roughness(0.2)
}
pub fn silver() -> Self {
Self::new()
.color(Color::rgba(0.75, 0.75, 0.75, 1.0))
.roughness(0.2)
}
pub fn copper() -> Self {
Self::new()
.color(Color::rgba(0.72, 0.45, 0.2, 1.0))
.roughness(0.3)
}
}
#[derive(Clone, Debug)]
pub struct WoodMaterial {
pub color: Color,
pub grain: f32,
pub gloss: f32,
pub shadow: Option<MaterialShadow>,
}
impl Default for WoodMaterial {
fn default() -> Self {
Self {
color: Color::rgba(0.55, 0.35, 0.2, 1.0),
grain: 0.5,
gloss: 0.2,
shadow: None,
}
}
}
impl WoodMaterial {
pub fn new() -> Self {
Self::default()
}
pub fn color(mut self, color: Color) -> Self {
self.color = color;
self
}
pub fn grain(mut self, grain: f32) -> Self {
self.grain = grain;
self
}
pub fn gloss(mut self, gloss: f32) -> Self {
self.gloss = gloss;
self
}
pub fn shadow(mut self, shadow: MaterialShadow) -> Self {
self.shadow = Some(shadow);
self
}
pub fn oak() -> Self {
Self::new().color(Color::rgba(0.6, 0.45, 0.25, 1.0))
}
pub fn walnut() -> Self {
Self::new().color(Color::rgba(0.4, 0.25, 0.15, 1.0))
}
pub fn cherry() -> Self {
Self::new().color(Color::rgba(0.6, 0.3, 0.2, 1.0))
}
pub fn pine() -> Self {
Self::new().color(Color::rgba(0.8, 0.65, 0.45, 1.0))
}
}
#[derive(Clone, Debug, Default)]
pub struct SolidMaterial {
pub shadow: Option<MaterialShadow>,
}
impl SolidMaterial {
pub fn new() -> Self {
Self::default()
}
pub fn shadow(mut self, shadow: MaterialShadow) -> Self {
self.shadow = Some(shadow);
self
}
}
#[derive(Clone, Debug)]
pub struct MaterialShadow {
pub color: Color,
pub blur: f32,
pub offset: (f32, f32),
pub opacity: f32,
}
impl Default for MaterialShadow {
fn default() -> Self {
Self {
color: Color::rgba(0.0, 0.0, 0.0, 1.0),
blur: 10.0,
offset: (0.0, 4.0),
opacity: 0.25,
}
}
}
impl From<Shadow> for MaterialShadow {
fn from(shadow: Shadow) -> Self {
Self {
color: shadow.color,
blur: shadow.blur,
offset: (shadow.offset_x, shadow.offset_y),
opacity: shadow.color.a,
}
}
}
impl From<&Shadow> for MaterialShadow {
fn from(shadow: &Shadow) -> Self {
Self {
color: shadow.color,
blur: shadow.blur,
offset: (shadow.offset_x, shadow.offset_y),
opacity: shadow.color.a,
}
}
}
impl MaterialShadow {
pub fn new() -> Self {
Self::default()
}
pub fn color(mut self, color: Color) -> Self {
self.color = color;
self
}
pub fn blur(mut self, blur: f32) -> Self {
self.blur = blur;
self
}
pub fn offset(mut self, x: f32, y: f32) -> Self {
self.offset = (x, y);
self
}
pub fn opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity;
self
}
pub fn sm() -> Self {
Self::new().blur(4.0).offset(0.0, 2.0).opacity(0.2)
}
pub fn md() -> Self {
Self::new().blur(10.0).offset(0.0, 4.0).opacity(0.25)
}
pub fn lg() -> Self {
Self::new().blur(20.0).offset(0.0, 8.0).opacity(0.3)
}
pub fn xl() -> Self {
Self::new().blur(30.0).offset(0.0, 12.0).opacity(0.35)
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ElementBounds {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl ElementBounds {
pub fn from_layout(layout: &Layout, parent_offset: (f32, f32)) -> Self {
Self {
x: parent_offset.0 + layout.location.x,
y: parent_offset.1 + layout.location.y,
width: layout.size.width,
height: layout.size.height,
}
}
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
Self {
x,
y,
width,
height,
}
}
pub fn to_rect(&self) -> Rect {
Rect::new(self.x, self.y, self.width, self.height)
}
pub fn local(&self) -> Self {
Self {
x: 0.0,
y: 0.0,
width: self.width,
height: self.height,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum RenderLayer {
#[default]
Background,
Glass,
Foreground,
}
#[derive(Clone, Debug, Default)]
pub struct MotionAnimation {
pub enter_from: Option<MotionKeyframe>,
pub enter_duration_ms: u32,
pub enter_delay_ms: u32,
pub exit_to: Option<MotionKeyframe>,
pub exit_duration_ms: u32,
}
#[derive(Clone, Debug, Default)]
pub struct MotionKeyframe {
pub opacity: Option<f32>,
pub scale_x: Option<f32>,
pub scale_y: Option<f32>,
pub translate_x: Option<f32>,
pub translate_y: Option<f32>,
pub rotate: Option<f32>,
}
impl MotionKeyframe {
pub fn new() -> Self {
Self::default()
}
pub fn opacity(mut self, opacity: f32) -> Self {
self.opacity = Some(opacity);
self
}
pub fn scale(mut self, scale: f32) -> Self {
self.scale_x = Some(scale);
self.scale_y = Some(scale);
self
}
pub fn translate(mut self, x: f32, y: f32) -> Self {
self.translate_x = Some(x);
self.translate_y = Some(y);
self
}
pub fn rotate(mut self, degrees: f32) -> Self {
self.rotate = Some(degrees);
self
}
pub fn from_keyframe_properties(props: &blinc_animation::KeyframeProperties) -> Self {
Self {
opacity: props.opacity,
scale_x: props.scale_x,
scale_y: props.scale_y,
translate_x: props.translate_x,
translate_y: props.translate_y,
rotate: props.rotate,
}
}
pub fn lerp(&self, other: &Self, t: f32) -> Self {
Self {
opacity: lerp_opt_with_default(self.opacity, other.opacity, t, 1.0),
scale_x: lerp_opt_with_default(self.scale_x, other.scale_x, t, 1.0),
scale_y: lerp_opt_with_default(self.scale_y, other.scale_y, t, 1.0),
translate_x: lerp_opt_with_default(self.translate_x, other.translate_x, t, 0.0),
translate_y: lerp_opt_with_default(self.translate_y, other.translate_y, t, 0.0),
rotate: lerp_opt_with_default(self.rotate, other.rotate, t, 0.0),
}
}
pub fn resolved_opacity(&self) -> f32 {
self.opacity.unwrap_or(1.0)
}
pub fn resolved_scale(&self) -> (f32, f32) {
(self.scale_x.unwrap_or(1.0), self.scale_y.unwrap_or(1.0))
}
pub fn resolved_translate(&self) -> (f32, f32) {
(
self.translate_x.unwrap_or(0.0),
self.translate_y.unwrap_or(0.0),
)
}
pub fn resolved_rotate(&self) -> f32 {
self.rotate.unwrap_or(0.0)
}
}
fn lerp_opt_with_default(a: Option<f32>, b: Option<f32>, t: f32, default: f32) -> Option<f32> {
match (a, b) {
(Some(a), Some(b)) => Some(a + (b - a) * t),
(Some(a), None) => Some(a + (default - a) * t), (None, Some(b)) => Some(default + (b - default) * t), (None, None) => None,
}
}
impl MotionAnimation {
pub fn from_enter_animation(
anim: &blinc_animation::MultiKeyframeAnimation,
delay_ms: u32,
) -> Self {
let enter_from = anim
.first_keyframe()
.map(|kf| MotionKeyframe::from_keyframe_properties(&kf.properties));
Self {
enter_from,
enter_duration_ms: anim.duration_ms(),
enter_delay_ms: delay_ms,
exit_to: None,
exit_duration_ms: 0,
}
}
pub fn with_exit_animation(mut self, anim: &blinc_animation::MultiKeyframeAnimation) -> Self {
self.exit_to = anim
.last_keyframe()
.map(|kf| MotionKeyframe::from_keyframe_properties(&kf.properties));
self.exit_duration_ms = anim.duration_ms();
self
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct BorderSide {
pub width: f32,
pub color: Color,
}
impl BorderSide {
pub fn new(width: f32, color: Color) -> Self {
Self { width, color }
}
pub fn is_visible(&self) -> bool {
self.width > 0.0 && self.color.a > 0.0
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct BorderSides {
pub top: Option<BorderSide>,
pub right: Option<BorderSide>,
pub bottom: Option<BorderSide>,
pub left: Option<BorderSide>,
}
impl BorderSides {
pub fn none() -> Self {
Self::default()
}
pub fn has_any(&self) -> bool {
self.top.as_ref().is_some_and(|b| b.is_visible())
|| self.right.as_ref().is_some_and(|b| b.is_visible())
|| self.bottom.as_ref().is_some_and(|b| b.is_visible())
|| self.left.as_ref().is_some_and(|b| b.is_visible())
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct BorderBuilder {
sides: BorderSides,
}
impl BorderBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn left(mut self, width: f32, color: Color) -> Self {
self.sides.left = Some(BorderSide::new(width, color));
self
}
pub fn right(mut self, width: f32, color: Color) -> Self {
self.sides.right = Some(BorderSide::new(width, color));
self
}
pub fn top(mut self, width: f32, color: Color) -> Self {
self.sides.top = Some(BorderSide::new(width, color));
self
}
pub fn bottom(mut self, width: f32, color: Color) -> Self {
self.sides.bottom = Some(BorderSide::new(width, color));
self
}
pub fn x(mut self, width: f32, color: Color) -> Self {
let side = BorderSide::new(width, color);
self.sides.left = Some(side);
self.sides.right = Some(side);
self
}
pub fn y(mut self, width: f32, color: Color) -> Self {
let side = BorderSide::new(width, color);
self.sides.top = Some(side);
self.sides.bottom = Some(side);
self
}
pub fn all(mut self, width: f32, color: Color) -> Self {
let side = BorderSide::new(width, color);
self.sides.top = Some(side);
self.sides.right = Some(side);
self.sides.bottom = Some(side);
self.sides.left = Some(side);
self
}
pub fn build(self) -> BorderSides {
self.sides
}
}
#[derive(Clone)]
pub struct RenderProps {
pub background: Option<Brush>,
pub border_radius: CornerRadius,
pub border_radius_explicit: bool,
pub corner_shape: CornerShape,
pub border_color: Option<Color>,
pub border_width: f32,
pub border_sides: BorderSides,
pub outline_color: Option<Color>,
pub outline_width: f32,
pub outline_offset: f32,
pub layer: RenderLayer,
pub material: Option<Material>,
pub node_id: Option<LayoutNodeId>,
pub shadow: Option<Shadow>,
pub transform: Option<Transform>,
pub opacity: f32,
pub clips_content: bool,
pub overflow_fade: OverflowFade,
pub motion: Option<MotionAnimation>,
pub motion_stable_id: Option<String>,
pub motion_should_replay: bool,
pub motion_is_suspended: bool,
pub motion_on_ready_callback:
Option<std::sync::Arc<dyn Fn(ElementBounds) + Send + Sync + 'static>>,
pub is_stack_layer: bool,
pub cursor: Option<CursorStyle>,
pub pointer_events_none: bool,
pub is_fixed: bool,
pub is_sticky: bool,
pub sticky_top: Option<f32>,
pub sticky_bottom: Option<f32>,
pub z_index: i32,
pub text_color: Option<[f32; 4]>,
pub font_size: Option<f32>,
pub text_shadow: Option<Shadow>,
pub font_weight: Option<crate::div::FontWeight>,
pub text_decoration: Option<crate::element_style::TextDecoration>,
pub line_height: Option<f32>,
pub text_align: Option<crate::div::TextAlign>,
pub letter_spacing: Option<f32>,
pub fill: Option<[f32; 4]>,
pub stroke: Option<[f32; 4]>,
pub stroke_width: Option<f32>,
pub stroke_dasharray: Option<Vec<f32>>,
pub stroke_dashoffset: Option<f32>,
pub svg_path_data: Option<String>,
pub transform_origin: Option<[f32; 2]>,
pub layer_effects: Vec<LayerEffect>,
pub rotate_x: Option<f32>,
pub rotate_y: Option<f32>,
pub perspective: Option<f32>,
pub shape_3d: Option<f32>,
pub depth: Option<f32>,
pub light_direction: Option<[f32; 3]>,
pub light_intensity: Option<f32>,
pub ambient: Option<f32>,
pub specular: Option<f32>,
pub translate_z: Option<f32>,
pub op_3d: Option<f32>,
pub blend_3d: Option<f32>,
pub clip_path: Option<ClipPath>,
pub filter: Option<crate::element_style::CssFilter>,
#[deprecated(
since = "0.1.0",
note = "Use query_motion(key).exit() to explicitly trigger motion exit"
)]
pub motion_is_exiting: bool,
pub visible: bool,
pub object_fit: Option<u8>,
pub object_position: Option<[f32; 2]>,
pub loading_strategy: Option<u8>,
pub placeholder_type: Option<u8>,
pub placeholder_color: Option<[f32; 4]>,
pub placeholder_image: Option<String>,
pub fade_duration_ms: Option<u32>,
pub svg_tag_styles: HashMap<String, SvgTagStyle>,
pub mix_blend_mode: Option<blinc_core::BlendMode>,
pub text_decoration_color: Option<[f32; 4]>,
pub text_decoration_thickness: Option<f32>,
pub text_overflow: Option<crate::element_style::TextOverflow>,
pub white_space: Option<crate::element_style::WhiteSpace>,
pub mask_image: Option<blinc_core::MaskImage>,
pub mask_mode: Option<blinc_core::MaskMode>,
pub flow: Option<String>,
pub flow_graph: Option<Arc<FlowGraph>>,
}
impl Default for RenderProps {
#[allow(deprecated)]
fn default() -> Self {
Self {
background: None,
border_radius: CornerRadius::default(),
border_radius_explicit: false,
corner_shape: CornerShape::default(),
border_color: None,
border_width: 0.0,
border_sides: BorderSides::default(),
outline_color: None,
outline_width: 0.0,
outline_offset: 0.0,
layer: RenderLayer::default(),
material: None,
node_id: None,
shadow: None,
transform: None,
opacity: 1.0,
clips_content: false,
overflow_fade: OverflowFade::default(),
motion: None,
motion_stable_id: None,
motion_should_replay: false,
motion_is_suspended: false,
motion_on_ready_callback: None,
is_stack_layer: false,
cursor: None,
pointer_events_none: false,
is_fixed: false,
is_sticky: false,
sticky_top: None,
sticky_bottom: None,
layer_effects: Vec::new(),
rotate_x: None,
rotate_y: None,
perspective: None,
shape_3d: None,
depth: None,
light_direction: None,
light_intensity: None,
ambient: None,
specular: None,
translate_z: None,
op_3d: None,
blend_3d: None,
clip_path: None,
filter: None,
motion_is_exiting: false,
z_index: 0,
text_color: None,
font_size: None,
text_shadow: None,
font_weight: None,
text_decoration: None,
line_height: None,
text_align: None,
letter_spacing: None,
fill: None,
stroke: None,
stroke_width: None,
stroke_dasharray: None,
stroke_dashoffset: None,
svg_path_data: None,
transform_origin: None,
visible: true,
object_fit: None,
object_position: None,
loading_strategy: None,
placeholder_type: None,
placeholder_color: None,
placeholder_image: None,
fade_duration_ms: None,
svg_tag_styles: HashMap::new(),
mix_blend_mode: None,
text_decoration_color: None,
text_decoration_thickness: None,
text_overflow: None,
white_space: None,
mask_image: None,
mask_mode: None,
flow: None,
flow_graph: None,
}
}
}
impl RenderProps {
pub fn new() -> Self {
Self::default()
}
pub fn with_background(mut self, brush: impl Into<Brush>) -> Self {
self.background = Some(brush.into());
self
}
pub fn with_bg_color(mut self, color: Color) -> Self {
self.background = Some(Brush::Solid(color));
self
}
pub fn with_border_radius(mut self, radius: CornerRadius) -> Self {
self.border_radius = radius;
self
}
pub fn with_rounded(mut self, radius: f32) -> Self {
self.border_radius = CornerRadius::uniform(radius);
self
}
pub fn with_layer(mut self, layer: RenderLayer) -> Self {
self.layer = layer;
self
}
pub fn with_material(mut self, material: Material) -> Self {
self.material = Some(material);
self
}
pub fn with_node_id(mut self, id: LayoutNodeId) -> Self {
self.node_id = Some(id);
self
}
pub fn is_glass(&self) -> bool {
matches!(self.material, Some(Material::Glass(_)))
}
pub fn glass_material(&self) -> Option<&GlassMaterial> {
match &self.material {
Some(Material::Glass(glass)) => Some(glass),
_ => None,
}
}
pub fn with_shadow(mut self, shadow: Shadow) -> Self {
self.shadow = Some(shadow);
self
}
pub fn with_transform(mut self, transform: Transform) -> Self {
self.transform = Some(transform);
self
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity.clamp(0.0, 1.0);
self
}
pub fn with_clips_content(mut self, clips: bool) -> Self {
self.clips_content = clips;
self
}
pub fn with_cursor(mut self, cursor: CursorStyle) -> Self {
self.cursor = Some(cursor);
self
}
pub fn merge_from(&mut self, other: &RenderProps) {
if other.background.is_some() {
self.background = other.background.clone();
}
if other.border_radius_explicit || other.border_radius != CornerRadius::default() {
self.border_radius = other.border_radius;
self.border_radius_explicit = other.border_radius_explicit;
}
if other.border_color.is_some() {
self.border_color = other.border_color;
}
if other.border_width > 0.0 {
self.border_width = other.border_width;
}
if other.layer != RenderLayer::default() {
self.layer = other.layer;
}
if other.material.is_some() {
self.material = other.material.clone();
}
if other.shadow.is_some() {
self.shadow = other.shadow;
}
if other.transform.is_some() {
self.transform = other.transform.clone();
}
if (other.opacity - 1.0).abs() > f32::EPSILON {
self.opacity = other.opacity;
}
if other.clips_content {
self.clips_content = true;
}
if other.motion.is_some() {
self.motion = other.motion.clone();
}
}
}
#[derive(Clone)]
pub struct DynRenderProps {
pub background: Option<DynValue<Brush>>,
pub border_radius: CornerRadius,
pub corner_shape: CornerShape,
pub layer: RenderLayer,
pub material: Option<Material>,
pub node_id: Option<LayoutNodeId>,
pub shadow: Option<Shadow>,
pub transform: Option<Transform>,
pub opacity: DynFloat,
pub clips_content: bool,
pub overflow_fade: OverflowFade,
}
impl Default for DynRenderProps {
fn default() -> Self {
Self {
background: None,
border_radius: CornerRadius::default(),
corner_shape: CornerShape::default(),
layer: RenderLayer::default(),
material: None,
node_id: None,
shadow: None,
transform: None,
opacity: DynFloat::Static(1.0),
clips_content: false,
overflow_fade: OverflowFade::default(),
}
}
}
impl DynRenderProps {
pub fn new() -> Self {
Self::default()
}
pub fn resolve(&self, ctx: &ValueContext) -> ResolvedRenderProps {
ResolvedRenderProps {
background: self.background.as_ref().map(|v| v.get(ctx)),
border_radius: self.border_radius,
corner_shape: self.corner_shape,
layer: self.layer,
material: self.material.clone(),
node_id: self.node_id,
shadow: self.shadow,
transform: self.transform.clone(),
opacity: self.opacity.get(ctx),
clips_content: self.clips_content,
overflow_fade: self.overflow_fade,
}
}
pub fn from_static(props: RenderProps) -> Self {
Self {
background: props.background.map(DynValue::Static),
border_radius: props.border_radius,
corner_shape: props.corner_shape,
layer: props.layer,
material: props.material,
node_id: props.node_id,
shadow: props.shadow,
transform: props.transform,
opacity: DynFloat::Static(props.opacity),
clips_content: props.clips_content,
overflow_fade: props.overflow_fade,
}
}
pub fn with_background(mut self, brush: impl Into<Brush>) -> Self {
self.background = Some(DynValue::Static(brush.into()));
self
}
pub fn with_background_signal(mut self, signal_id: u64, default: Brush) -> Self {
self.background = Some(DynValue::Signal {
id: signal_id,
default,
});
self
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.opacity = DynFloat::Static(opacity.clamp(0.0, 1.0));
self
}
pub fn with_opacity_signal(mut self, signal_id: u64, default: f32) -> Self {
self.opacity = DynFloat::Signal {
id: signal_id,
default,
};
self
}
pub fn with_opacity_spring(mut self, spring_id: u64, generation: u32, default: f32) -> Self {
self.opacity = DynFloat::Spring {
id: spring_id,
generation,
default,
};
self
}
}
#[derive(Clone)]
pub struct ResolvedRenderProps {
pub background: Option<Brush>,
pub border_radius: CornerRadius,
pub corner_shape: CornerShape,
pub layer: RenderLayer,
pub material: Option<Material>,
pub node_id: Option<LayoutNodeId>,
pub shadow: Option<Shadow>,
pub transform: Option<Transform>,
pub opacity: f32,
pub clips_content: bool,
pub overflow_fade: OverflowFade,
}
impl Default for ResolvedRenderProps {
fn default() -> Self {
Self {
background: None,
border_radius: CornerRadius::default(),
corner_shape: CornerShape::default(),
layer: RenderLayer::default(),
material: None,
node_id: None,
shadow: None,
transform: None,
opacity: 1.0,
clips_content: false,
overflow_fade: OverflowFade::default(),
}
}
}