tessera_ui_basic_components/
glass_button.rs1use std::sync::Arc;
2
3use derive_builder::Builder;
4use tessera_ui::{Color, DimensionValue, Dp};
5use tessera_ui_macros::tessera;
6
7use crate::{
8 fluid_glass::{FluidGlassArgsBuilder, GlassBorder, fluid_glass},
9 ripple_state::RippleState,
10 shape_def::Shape,
11};
12
13#[derive(Builder, Clone, Default)]
15#[builder(pattern = "owned", setter(into, strip_option), default)]
16pub struct GlassButtonArgs {
17 #[builder(setter(strip_option, into = false))]
19 pub on_click: Option<Arc<dyn Fn() + Send + Sync>>,
20
21 #[builder(default = "Color::from_rgb(1.0, 1.0, 1.0)")]
24 pub ripple_color: Color,
25
26 #[builder(default = "Dp(12.0)")]
29 pub padding: Dp,
30 #[builder(default, setter(strip_option))]
32 pub width: Option<DimensionValue>,
33 #[builder(default, setter(strip_option))]
35 pub height: Option<DimensionValue>,
36
37 #[builder(default = "Color::new(0.5, 0.5, 0.5, 0.1)")]
39 pub tint_color: Color,
40 #[builder(default = "Shape::RoundedRectangle { corner_radius: 25.0, g2_k_value: 3.0 }")]
41 pub shape: Shape,
42 #[builder(default = "0.0")]
43 pub blur_radius: f32,
44 #[builder(default = "25.0")]
45 pub dispersion_height: f32,
46 #[builder(default = "1.2")]
47 pub chroma_multiplier: f32,
48 #[builder(default = "24.0")]
49 pub refraction_height: f32,
50 #[builder(default = "32.0")]
51 pub refraction_amount: f32,
52 #[builder(default = "0.02")]
53 pub noise_amount: f32,
54 #[builder(default = "1.0")]
55 pub noise_scale: f32,
56 #[builder(default = "0.0")]
57 pub time: f32,
58 #[builder(default, setter(strip_option))]
59 pub contrast: Option<f32>,
60 #[builder(default, setter(strip_option))]
61 pub border: Option<GlassBorder>,
62}
63
64impl GlassButtonArgs {
66 pub fn primary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
68 GlassButtonArgsBuilder::default()
69 .on_click(on_click)
70 .tint_color(Color::new(0.2, 0.5, 0.8, 0.2)) .border(GlassBorder::new(Dp(2.0), Color::new(0.2, 0.5, 0.8, 0.5)))
72 .build()
73 .unwrap()
74 }
75
76 pub fn secondary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
78 GlassButtonArgsBuilder::default()
79 .on_click(on_click)
80 .tint_color(Color::new(0.6, 0.6, 0.6, 0.2)) .border(GlassBorder::new(Dp(2.0), Color::new(0.6, 0.6, 0.6, 0.5)))
82 .build()
83 .unwrap()
84 }
85
86 pub fn success(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
88 GlassButtonArgsBuilder::default()
89 .on_click(on_click)
90 .tint_color(Color::new(0.1, 0.7, 0.3, 0.2)) .border(GlassBorder::new(Dp(2.0), Color::new(0.1, 0.7, 0.3, 0.5)))
92 .build()
93 .unwrap()
94 }
95
96 pub fn danger(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
98 GlassButtonArgsBuilder::default()
99 .on_click(on_click)
100 .tint_color(Color::new(0.8, 0.2, 0.2, 0.2)) .border(GlassBorder::new(Dp(2.0), Color::new(0.8, 0.2, 0.2, 0.5)))
102 .build()
103 .unwrap()
104 }
105}
106
107#[tessera]
112pub fn glass_button(
113 args: impl Into<GlassButtonArgs>,
114 ripple_state: Arc<RippleState>,
115 child: impl FnOnce() + Send + Sync + 'static,
116) {
117 let args: GlassButtonArgs = args.into();
118
119 let mut glass_args_builder = FluidGlassArgsBuilder::default();
120 if let Some((progress, center)) = ripple_state.get_animation_progress() {
121 let ripple_alpha = (1.0 - progress) * 0.3; glass_args_builder = glass_args_builder
123 .ripple_center(center)
124 .ripple_radius(progress)
125 .ripple_alpha(ripple_alpha)
126 .ripple_strength(progress);
127 }
128
129 if let Some(width) = args.width {
130 glass_args_builder = glass_args_builder.width(width);
131 }
132 if let Some(height) = args.height {
133 glass_args_builder = glass_args_builder.height(height);
134 }
135 if let Some(contrast) = args.contrast {
136 glass_args_builder = glass_args_builder.contrast(contrast);
137 }
138
139 let mut glass_args = glass_args_builder
140 .tint_color(args.tint_color)
141 .shape(args.shape)
142 .blur_radius(args.blur_radius)
143 .dispersion_height(args.dispersion_height)
144 .chroma_multiplier(args.chroma_multiplier)
145 .refraction_height(args.refraction_height)
146 .refraction_amount(args.refraction_amount)
147 .noise_amount(args.noise_amount)
148 .noise_scale(args.noise_scale)
149 .time(args.time)
150 .padding(args.padding);
151
152 if let Some(on_click) = args.on_click {
153 glass_args = glass_args.on_click(on_click);
154 }
155
156 if let Some(border) = args.border {
157 glass_args = glass_args.border(border);
158 }
159
160 let glass_args = glass_args.build().unwrap();
161
162 fluid_glass(glass_args, Some(ripple_state), child);
163}