1use std::rc::Rc;
2
3use cranpose_ui_graphics::{
4 BlendMode, Color, ColorFilter, CompositingStrategy, Density, Dp, GradientBlurDirection,
5 GradientCutMaskSpec, GradientFadeMaskSpec, LayerShape, Px, RenderEffect, RoundedCornerShape,
6 RuntimeShader, TransformOrigin, gradient_blur_effect, gradient_cut_mask_effect,
7 gradient_fade_dst_out_effect, rounded_alpha_mask_effect,
8};
9
10use super::{GraphicsLayer, Modifier, inspector_metadata};
11use crate::modifier_nodes::{GraphicsLayerElement, LazyGraphicsLayerElement};
12
13fn backdrop_blur_layer(radius: Dp, shape: LayerShape) -> GraphicsLayer {
14 let density = Density::from_scale(crate::render_state::current_density());
15 let radius_px = radius.to_px(density).max(Px::ZERO);
16 GraphicsLayer {
17 backdrop_effect: (radius_px.0 > 0.0).then(|| RenderEffect::blur(radius_px.0)),
18 shape,
19 clip: true,
20 ..Default::default()
21 }
22}
23
24fn backdrop_gradient_blur_layer(
25 start_radius: Dp,
26 end_radius: Dp,
27 direction: GradientBlurDirection,
28) -> GraphicsLayer {
29 let density = Density::from_scale(crate::render_state::current_density());
30 let start_px = start_radius.to_px(density).max(Px::ZERO);
31 let end_px = end_radius.to_px(density).max(Px::ZERO);
32 GraphicsLayer {
33 backdrop_effect: (start_px.0 > 0.0 || end_px.0 > 0.0)
34 .then(|| gradient_blur_effect(start_px.0, end_px.0, direction)),
35 shape: LayerShape::Rectangle,
36 clip: true,
37 ..Default::default()
38 }
39}
40
41impl Modifier {
42 pub fn graphics_layer(self, layer: impl Fn() -> GraphicsLayer + 'static) -> Self {
78 let modifier = Self::with_element(LazyGraphicsLayerElement::new(Rc::new(layer)))
79 .with_inspector_metadata(inspector_metadata("graphicsLayer", |info| {
80 info.add_property("lazy", "true");
81 }));
82 self.then(modifier)
83 }
84
85 pub fn graphics_layer_value(self, layer: GraphicsLayer) -> Self {
90 let inspector_values = layer.clone();
91 let modifier = Self::with_element(GraphicsLayerElement::new(layer))
92 .with_inspector_metadata(inspector_metadata("graphicsLayer", move |info| {
93 info.add_property("alpha", inspector_values.alpha.to_string());
94 info.add_property("scale", inspector_values.scale.to_string());
95 info.add_property("scaleX", inspector_values.scale_x.to_string());
96 info.add_property("scaleY", inspector_values.scale_y.to_string());
97 info.add_property("rotationX", inspector_values.rotation_x.to_string());
98 info.add_property("rotationY", inspector_values.rotation_y.to_string());
99 info.add_property("rotationZ", inspector_values.rotation_z.to_string());
100 info.add_property(
101 "cameraDistance",
102 inspector_values.camera_distance.to_string(),
103 );
104 info.add_property(
105 "transformOrigin",
106 format!(
107 "{},{}",
108 inspector_values.transform_origin.pivot_fraction_x,
109 inspector_values.transform_origin.pivot_fraction_y
110 ),
111 );
112 info.add_property("translationX", inspector_values.translation_x.to_string());
113 info.add_property("translationY", inspector_values.translation_y.to_string());
114 info.add_property(
115 "shadowElevation",
116 inspector_values.shadow_elevation.to_string(),
117 );
118 info.add_property("shape", format!("{:?}", inspector_values.shape));
119 info.add_property("clip", inspector_values.clip.to_string());
120 info.add_property(
121 "ambientShadowColor",
122 format!("{:?}", inspector_values.ambient_shadow_color),
123 );
124 info.add_property(
125 "spotShadowColor",
126 format!("{:?}", inspector_values.spot_shadow_color),
127 );
128 info.add_property(
129 "compositingStrategy",
130 format!("{:?}", inspector_values.compositing_strategy),
131 );
132 info.add_property("blendMode", format!("{:?}", inspector_values.blend_mode));
133 if let Some(filter) = inspector_values.color_filter {
134 info.add_property("colorFilter", format!("{filter:?}"));
135 }
136 }));
137 self.then(modifier)
138 }
139
140 #[allow(clippy::too_many_arguments)]
145 pub fn graphics_layer_params(
146 self,
147 scale_x: f32,
148 scale_y: f32,
149 alpha: f32,
150 translation_x: f32,
151 translation_y: f32,
152 shadow_elevation: f32,
153 rotation_x: f32,
154 rotation_y: f32,
155 rotation_z: f32,
156 camera_distance: f32,
157 transform_origin: TransformOrigin,
158 shape: LayerShape,
159 clip: bool,
160 render_effect: Option<RenderEffect>,
161 ambient_shadow_color: Color,
162 spot_shadow_color: Color,
163 compositing_strategy: CompositingStrategy,
164 blend_mode: BlendMode,
165 color_filter: Option<ColorFilter>,
166 ) -> Self {
167 self.graphics_layer_value(GraphicsLayer {
168 alpha,
169 scale: 1.0,
170 scale_x,
171 scale_y,
172 rotation_x,
173 rotation_y,
174 rotation_z,
175 camera_distance,
176 transform_origin,
177 translation_x,
178 translation_y,
179 shadow_elevation,
180 ambient_shadow_color,
181 spot_shadow_color,
182 shape,
183 clip,
184 compositing_strategy,
185 blend_mode,
186 color_filter,
187 render_effect,
188 backdrop_effect: None,
189 })
190 }
191
192 pub fn graphics_layer_block(self, configure: impl Fn(&mut GraphicsLayer) + 'static) -> Self {
197 self.graphics_layer(move || {
198 let mut layer = GraphicsLayer::default();
199 configure(&mut layer);
200 layer
201 })
202 }
203
204 pub fn rotate(self, degrees: f32) -> Self {
211 self.graphics_layer_value(GraphicsLayer {
212 rotation_z: degrees,
213 ..Default::default()
214 })
215 }
216
217 pub fn scale(self, scale: f32) -> Self {
222 self.scale_xy(scale, scale)
223 }
224
225 pub fn scale_xy(self, scale_x: f32, scale_y: f32) -> Self {
229 self.graphics_layer_value(GraphicsLayer {
230 scale_x,
231 scale_y,
232 ..Default::default()
233 })
234 }
235
236 pub fn shadow(self, elevation: f32) -> Self {
242 self.shadow_with(
243 elevation,
244 LayerShape::Rectangle,
245 elevation > 0.0,
246 Color::BLACK,
247 Color::BLACK,
248 )
249 }
250
251 pub fn shadow_with(
253 self,
254 elevation: f32,
255 shape: LayerShape,
256 clip: bool,
257 ambient_color: Color,
258 spot_color: Color,
259 ) -> Self {
260 let clamped_elevation = elevation.max(0.0);
261 if clamped_elevation == 0.0 && !clip {
262 return self;
263 }
264
265 self.graphics_layer_value(GraphicsLayer {
266 shadow_elevation: clamped_elevation,
267 ambient_shadow_color: ambient_color,
268 spot_shadow_color: spot_color,
269 shape,
270 clip,
271 ..Default::default()
272 })
273 }
274
275 pub fn backdrop_effect(self, effect: RenderEffect) -> Self {
277 let layer = GraphicsLayer {
278 backdrop_effect: Some(effect),
279 ..Default::default()
280 };
281 let modifier = Self::with_element(GraphicsLayerElement::new(layer))
282 .with_inspector_metadata(inspector_metadata("backdropEffect", |info| {
283 info.add_property("enabled", "true");
284 }));
285 self.then(modifier)
286 }
287
288 pub fn backdrop_blur(self, radius: Dp) -> Self {
293 if radius.0 <= 0.0 {
294 return self.clip_to_bounds();
295 }
296
297 let modifier = Self::with_element(LazyGraphicsLayerElement::new(Rc::new(move || {
298 backdrop_blur_layer(radius, LayerShape::Rectangle)
299 })))
300 .with_inspector_metadata(inspector_metadata("backdropBlur", move |info| {
301 info.add_property("radius", radius.0.to_string());
302 }));
303 self.then(modifier)
304 }
305
306 pub fn backdrop_gradient_blur(
311 self,
312 start_radius: Dp,
313 end_radius: Dp,
314 direction: GradientBlurDirection,
315 ) -> Self {
316 if start_radius.0 <= 0.0 && end_radius.0 <= 0.0 {
317 return self.clip_to_bounds();
318 }
319 let modifier = Self::with_element(LazyGraphicsLayerElement::new(Rc::new(move || {
320 backdrop_gradient_blur_layer(start_radius, end_radius, direction)
321 })))
322 .with_inspector_metadata(inspector_metadata(
323 "backdropGradientBlur",
324 move |info| {
325 info.add_property("startRadius", start_radius.0.to_string());
326 info.add_property("endRadius", end_radius.0.to_string());
327 info.add_property("direction", format!("{direction:?}"));
328 },
329 ));
330 self.then(modifier)
331 }
332
333 pub fn glass_material(self, material: GlassMaterial) -> Self {
335 let blur_radius = material.blur_radius;
336 let shape = material.shape;
337 let modifier = Self::with_element(LazyGraphicsLayerElement::new(Rc::new(move || {
338 backdrop_blur_layer(blur_radius, LayerShape::Rounded(shape))
339 })))
340 .with_inspector_metadata(inspector_metadata("glassMaterial", move |info| {
341 info.add_property("blurRadius", blur_radius.0.to_string());
342 info.add_property("shape", format!("{shape:?}"));
343 }));
344
345 self.then(modifier)
346 .rounded_corner_shape(material.shape)
347 .background(material.tint)
348 }
349
350 pub fn shader_background(self, shader: RuntimeShader) -> Self {
352 self.backdrop_effect(RenderEffect::runtime_shader(shader))
353 }
354
355 pub fn color_filter(self, filter: ColorFilter) -> Self {
359 let layer = GraphicsLayer {
360 color_filter: Some(filter),
361 ..Default::default()
362 };
363 let modifier = Self::with_element(GraphicsLayerElement::new(layer))
364 .with_inspector_metadata(inspector_metadata("colorFilter", |info| {
365 info.add_property("enabled", "true");
366 }));
367 self.then(modifier)
368 }
369
370 pub fn tint(self, tint: Color) -> Self {
372 self.color_filter(ColorFilter::tint(tint))
373 }
374
375 pub fn compositing_strategy(self, strategy: CompositingStrategy) -> Self {
377 self.graphics_layer_value(GraphicsLayer {
378 compositing_strategy: strategy,
379 ..Default::default()
380 })
381 }
382
383 pub fn layer_blend_mode(self, blend_mode: BlendMode) -> Self {
388 self.graphics_layer_value(GraphicsLayer {
389 blend_mode,
390 ..Default::default()
391 })
392 }
393
394 pub fn gradient_cut_mask(
398 self,
399 area_width: f32,
400 area_height: f32,
401 spec: GradientCutMaskSpec,
402 ) -> Self {
403 let layer = GraphicsLayer {
404 render_effect: Some(gradient_cut_mask_effect(&spec, area_width, area_height)),
405 ..Default::default()
406 };
407 self.graphics_layer_value(layer)
408 }
409
410 pub fn rounded_alpha_mask(
415 self,
416 area_width: f32,
417 area_height: f32,
418 corner_radius: f32,
419 edge_feather: f32,
420 ) -> Self {
421 let has_radius = corner_radius.is_finite() && corner_radius > 0.0;
422 let has_feather = edge_feather.is_finite() && edge_feather > 0.0;
423 if !has_radius && !has_feather {
424 return self;
425 }
426 let layer = GraphicsLayer {
427 render_effect: Some(rounded_alpha_mask_effect(
428 area_width,
429 area_height,
430 corner_radius,
431 edge_feather,
432 )),
433 ..Default::default()
434 };
435 self.graphics_layer_value(layer)
436 }
437
438 pub fn gradient_fade_dst_out(
443 self,
444 area_width: f32,
445 area_height: f32,
446 spec: GradientFadeMaskSpec,
447 ) -> Self {
448 let layer = GraphicsLayer {
449 render_effect: Some(gradient_fade_dst_out_effect(&spec, area_width, area_height)),
450 ..Default::default()
451 };
452 self.graphics_layer_value(layer)
453 }
454}
455
456#[derive(Clone, Copy, Debug, PartialEq)]
458pub struct GlassMaterial {
459 pub blur_radius: Dp,
460 pub tint: Color,
461 pub shape: RoundedCornerShape,
462}
463
464impl GlassMaterial {
465 pub fn new(blur_radius: Dp, tint: Color, shape: RoundedCornerShape) -> Self {
466 Self {
467 blur_radius,
468 tint,
469 shape,
470 }
471 }
472}