tessera_ui_basic_components/
glass_button.rs1use std::sync::Arc;
7
8use derive_builder::Builder;
9use tessera_ui::{Color, DimensionValue, Dp, tessera};
10
11use crate::{
12 fluid_glass::{FluidGlassArgsBuilder, GlassBorder, fluid_glass},
13 ripple_state::RippleState,
14 shape_def::Shape,
15};
16
17#[derive(Builder, Clone, Default)]
19#[builder(pattern = "owned", setter(into, strip_option), default)]
20pub struct GlassButtonArgs {
21 #[builder(setter(strip_option, into = false))]
23 pub on_click: Option<Arc<dyn Fn() + Send + Sync>>,
24
25 #[builder(default = "Color::from_rgb(1.0, 1.0, 1.0)")]
28 pub ripple_color: Color,
29
30 #[builder(default = "Dp(12.0)")]
33 pub padding: Dp,
34 #[builder(default = "DimensionValue::WRAP", setter(into))]
36 pub width: DimensionValue,
37 #[builder(default = "DimensionValue::WRAP", setter(into))]
39 pub height: DimensionValue,
40
41 #[builder(default = "Color::new(0.5, 0.5, 0.5, 0.1)")]
43 pub tint_color: Color,
44 #[builder(
45 default = "Shape::RoundedRectangle { top_left: Dp(25.0), top_right: Dp(25.0), bottom_right: Dp(25.0), bottom_left: Dp(25.0), g2_k_value: 3.0 }"
46 )]
47 pub shape: Shape,
48 #[builder(default = "Dp(0.0)")]
49 pub blur_radius: Dp,
50 #[builder(default = "Dp(25.0)")]
51 pub dispersion_height: Dp,
52 #[builder(default = "1.1")]
53 pub chroma_multiplier: f32,
54 #[builder(default = "Dp(24.0)")]
55 pub refraction_height: Dp,
56 #[builder(default = "32.0")]
57 pub refraction_amount: f32,
58 #[builder(default = "0.0")]
59 pub noise_amount: f32,
60 #[builder(default = "1.0")]
61 pub noise_scale: f32,
62 #[builder(default = "0.0")]
63 pub time: f32,
64 #[builder(default, setter(strip_option))]
65 pub contrast: Option<f32>,
66 #[builder(default, setter(strip_option))]
67 pub border: Option<GlassBorder>,
68 #[builder(default, setter(strip_option, into))]
70 pub accessibility_label: Option<String>,
71 #[builder(default, setter(strip_option, into))]
73 pub accessibility_description: Option<String>,
74 #[builder(default)]
76 pub accessibility_focusable: bool,
77}
78
79impl GlassButtonArgs {
81 pub fn primary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
83 GlassButtonArgsBuilder::default()
84 .on_click(on_click)
85 .tint_color(Color::new(0.2, 0.5, 0.8, 0.2)) .border(GlassBorder::new(Dp(1.0).into()))
87 .build()
88 .unwrap()
89 }
90
91 pub fn secondary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
93 GlassButtonArgsBuilder::default()
94 .on_click(on_click)
95 .tint_color(Color::new(0.6, 0.6, 0.6, 0.2)) .border(GlassBorder::new(Dp(1.0).into()))
97 .build()
98 .unwrap()
99 }
100
101 pub fn success(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
103 GlassButtonArgsBuilder::default()
104 .on_click(on_click)
105 .tint_color(Color::new(0.1, 0.7, 0.3, 0.2)) .border(GlassBorder::new(Dp(1.0).into()))
107 .build()
108 .unwrap()
109 }
110
111 pub fn danger(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
113 GlassButtonArgsBuilder::default()
114 .on_click(on_click)
115 .tint_color(Color::new(0.8, 0.2, 0.2, 0.2)) .border(GlassBorder::new(Dp(1.0).into()))
117 .build()
118 .unwrap()
119 }
120}
121
122#[tessera]
160pub fn glass_button(
161 args: impl Into<GlassButtonArgs>,
162 ripple_state: RippleState,
163 child: impl FnOnce() + Send + Sync + 'static,
164) {
165 let args: GlassButtonArgs = args.into();
166
167 let mut glass_args_builder = FluidGlassArgsBuilder::default();
168 if let Some((progress, center)) = ripple_state.get_animation_progress() {
169 let ripple_alpha = (1.0 - progress) * 0.3; glass_args_builder = glass_args_builder
171 .ripple_center(center)
172 .ripple_radius(progress)
173 .ripple_alpha(ripple_alpha)
174 .ripple_strength(progress);
175 }
176
177 if let Some(contrast) = args.contrast {
178 glass_args_builder = glass_args_builder.contrast(contrast);
179 }
180
181 let mut glass_args = glass_args_builder
182 .tint_color(args.tint_color)
183 .shape(args.shape)
184 .blur_radius(args.blur_radius)
185 .dispersion_height(args.dispersion_height)
186 .chroma_multiplier(args.chroma_multiplier)
187 .refraction_height(args.refraction_height)
188 .refraction_amount(args.refraction_amount)
189 .noise_amount(args.noise_amount)
190 .noise_scale(args.noise_scale)
191 .width(args.width)
192 .height(args.height)
193 .time(args.time)
194 .padding(args.padding);
195
196 if let Some(on_click) = args.on_click {
197 glass_args = glass_args.on_click(on_click);
198 }
199
200 if let Some(border) = args.border {
201 glass_args = glass_args.border(border);
202 }
203
204 if let Some(label) = args.accessibility_label {
205 glass_args = glass_args.accessibility_label(label);
206 }
207
208 if let Some(description) = args.accessibility_description {
209 glass_args = glass_args.accessibility_description(description);
210 }
211
212 if args.accessibility_focusable {
213 glass_args = glass_args.accessibility_focusable(true);
214 }
215
216 let glass_args = glass_args.build().unwrap();
217
218 fluid_glass(glass_args, Some(ripple_state), child);
219}