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::{RoundedCorner, 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 #[builder(default = "Color::from_rgb(1.0, 1.0, 1.0)")]
26 pub ripple_color: Color,
27 #[builder(default = "Dp(12.0)")]
29 pub padding: Dp,
30 #[builder(default = "DimensionValue::WRAP", setter(into))]
32 pub width: DimensionValue,
33 #[builder(default = "DimensionValue::WRAP", setter(into))]
35 pub height: DimensionValue,
36 #[builder(default = "Color::new(0.5, 0.5, 0.5, 0.1)")]
38 pub tint_color: Color,
39 #[builder(default = "Shape::RoundedRectangle {
41 top_left: RoundedCorner::manual(Dp(25.0), 3.0),
42 top_right: RoundedCorner::manual(Dp(25.0), 3.0),
43 bottom_right: RoundedCorner::manual(Dp(25.0), 3.0),
44 bottom_left: RoundedCorner::manual(Dp(25.0), 3.0),
45 }")]
46 pub shape: Shape,
47 #[builder(default = "Dp(0.0)")]
49 pub blur_radius: Dp,
50 #[builder(default = "Dp(25.0)")]
52 pub dispersion_height: Dp,
53 #[builder(default = "1.1")]
55 pub chroma_multiplier: f32,
56 #[builder(default = "Dp(24.0)")]
58 pub refraction_height: Dp,
59 #[builder(default = "32.0")]
61 pub refraction_amount: f32,
62 #[builder(default = "0.0")]
64 pub noise_amount: f32,
65 #[builder(default = "1.0")]
67 pub noise_scale: f32,
68 #[builder(default = "0.0")]
70 pub time: f32,
71 #[builder(default, setter(strip_option))]
73 pub contrast: Option<f32>,
74 #[builder(default, setter(strip_option))]
76 pub border: Option<GlassBorder>,
77 #[builder(default, setter(strip_option, into))]
79 pub accessibility_label: Option<String>,
80 #[builder(default, setter(strip_option, into))]
82 pub accessibility_description: Option<String>,
83 #[builder(default)]
85 pub accessibility_focusable: bool,
86}
87
88impl GlassButtonArgs {
90 pub fn primary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
92 GlassButtonArgsBuilder::default()
93 .on_click(on_click)
94 .tint_color(Color::new(0.2, 0.5, 0.8, 0.2)) .border(GlassBorder::new(Dp(1.0).into()))
96 .build()
97 .expect("builder construction failed")
98 }
99
100 pub fn secondary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
102 GlassButtonArgsBuilder::default()
103 .on_click(on_click)
104 .tint_color(Color::new(0.6, 0.6, 0.6, 0.2)) .border(GlassBorder::new(Dp(1.0).into()))
106 .build()
107 .expect("builder construction failed")
108 }
109
110 pub fn success(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
112 GlassButtonArgsBuilder::default()
113 .on_click(on_click)
114 .tint_color(Color::new(0.1, 0.7, 0.3, 0.2)) .border(GlassBorder::new(Dp(1.0).into()))
116 .build()
117 .expect("builder construction failed")
118 }
119
120 pub fn danger(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
122 GlassButtonArgsBuilder::default()
123 .on_click(on_click)
124 .tint_color(Color::new(0.8, 0.2, 0.2, 0.2)) .border(GlassBorder::new(Dp(1.0).into()))
126 .build()
127 .expect("builder construction failed")
128 }
129}
130
131#[tessera]
169pub fn glass_button(
170 args: impl Into<GlassButtonArgs>,
171 ripple_state: RippleState,
172 child: impl FnOnce() + Send + Sync + 'static,
173) {
174 let args: GlassButtonArgs = args.into();
175
176 let mut glass_args_builder = FluidGlassArgsBuilder::default();
177 if let Some((progress, center)) = ripple_state.get_animation_progress() {
178 let ripple_alpha = (1.0 - progress) * 0.3; glass_args_builder = glass_args_builder
180 .ripple_center(center)
181 .ripple_radius(progress)
182 .ripple_alpha(ripple_alpha)
183 .ripple_strength(progress);
184 }
185
186 if let Some(contrast) = args.contrast {
187 glass_args_builder = glass_args_builder.contrast(contrast);
188 }
189
190 let mut glass_args = glass_args_builder
191 .tint_color(args.tint_color)
192 .shape(args.shape)
193 .blur_radius(args.blur_radius)
194 .dispersion_height(args.dispersion_height)
195 .chroma_multiplier(args.chroma_multiplier)
196 .refraction_height(args.refraction_height)
197 .refraction_amount(args.refraction_amount)
198 .noise_amount(args.noise_amount)
199 .noise_scale(args.noise_scale)
200 .width(args.width)
201 .height(args.height)
202 .time(args.time)
203 .padding(args.padding);
204
205 if let Some(on_click) = args.on_click {
206 glass_args = glass_args.on_click(on_click);
207 }
208
209 if let Some(border) = args.border {
210 glass_args = glass_args.border(border);
211 }
212
213 if let Some(label) = args.accessibility_label {
214 glass_args = glass_args.accessibility_label(label);
215 }
216
217 if let Some(description) = args.accessibility_description {
218 glass_args = glass_args.accessibility_description(description);
219 }
220
221 if args.accessibility_focusable {
222 glass_args = glass_args.accessibility_focusable(true);
223 }
224
225 let glass_args = glass_args.build().expect("builder construction failed");
226
227 fluid_glass(glass_args, Some(ripple_state), child);
228}