tessera_ui_basic_components/
button.rs1use std::sync::Arc;
7
8use derive_builder::Builder;
9use tessera_ui::{Color, DimensionValue, Dp, accesskit::Role, tessera};
10
11use crate::{
12 pipelines::ShadowProps,
13 ripple_state::RippleState,
14 shape_def::Shape,
15 surface::{SurfaceArgsBuilder, surface},
16};
17
18#[derive(Builder, Clone)]
20#[builder(pattern = "owned")]
21pub struct ButtonArgs {
22 #[builder(default = "Color::new(0.2, 0.5, 0.8, 1.0)")]
24 pub color: Color,
25 #[builder(default)]
27 pub hover_color: Option<Color>,
28 #[builder(
30 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 }"
31 )]
32 pub shape: Shape,
33 #[builder(default = "Dp(12.0)")]
35 pub padding: Dp,
36 #[builder(default = "DimensionValue::WRAP", setter(into))]
38 pub width: DimensionValue,
39 #[builder(default = "DimensionValue::WRAP", setter(into))]
41 pub height: DimensionValue,
42 #[builder(default, setter(strip_option))]
44 pub on_click: Option<Arc<dyn Fn() + Send + Sync>>,
45 #[builder(default = "Color::from_rgb(1.0, 1.0, 1.0)")]
47 pub ripple_color: Color,
48 #[builder(default = "Dp(0.0)")]
50 pub border_width: Dp,
51 #[builder(default)]
53 pub border_color: Option<Color>,
54 #[builder(default, setter(strip_option))]
56 pub shadow: Option<ShadowProps>,
57 #[builder(default, setter(strip_option, into))]
59 pub accessibility_label: Option<String>,
60 #[builder(default, setter(strip_option, into))]
62 pub accessibility_description: Option<String>,
63}
64
65impl Default for ButtonArgs {
66 fn default() -> Self {
67 ButtonArgsBuilder::default()
68 .on_click(Arc::new(|| {}))
69 .build()
70 .unwrap()
71 }
72}
73
74#[tessera]
110pub fn button(args: impl Into<ButtonArgs>, ripple_state: RippleState, child: impl FnOnce()) {
111 let button_args: ButtonArgs = args.into();
112
113 surface(create_surface_args(&button_args), Some(ripple_state), child);
115}
116
117fn create_surface_args(args: &ButtonArgs) -> crate::surface::SurfaceArgs {
119 let style = if args.border_width.to_pixels_f32() > 0.0 {
120 crate::surface::SurfaceStyle::FilledOutlined {
121 fill_color: args.color,
122 border_color: args.border_color.unwrap_or(args.color),
123 border_width: args.border_width,
124 }
125 } else {
126 crate::surface::SurfaceStyle::Filled { color: args.color }
127 };
128
129 let hover_style = if let Some(hover_color) = args.hover_color {
130 let style = if args.border_width.to_pixels_f32() > 0.0 {
131 crate::surface::SurfaceStyle::FilledOutlined {
132 fill_color: hover_color,
133 border_color: args.border_color.unwrap_or(hover_color),
134 border_width: args.border_width,
135 }
136 } else {
137 crate::surface::SurfaceStyle::Filled { color: hover_color }
138 };
139 Some(style)
140 } else {
141 None
142 };
143
144 let mut builder = SurfaceArgsBuilder::default();
145
146 if let Some(shadow) = args.shadow {
148 builder = builder.shadow(shadow);
149 }
150
151 if let Some(on_click) = args.on_click.clone() {
153 builder = builder.on_click(on_click);
154 }
155
156 if let Some(label) = args.accessibility_label.clone() {
157 builder = builder.accessibility_label(label);
158 }
159
160 if let Some(description) = args.accessibility_description.clone() {
161 builder = builder.accessibility_description(description);
162 }
163
164 builder
165 .style(style)
166 .hover_style(hover_style)
167 .shape(args.shape)
168 .padding(args.padding)
169 .ripple_color(args.ripple_color)
170 .width(args.width)
171 .height(args.height)
172 .accessibility_role(Role::Button)
173 .accessibility_focusable(true)
174 .build()
175 .unwrap()
176}
177
178impl ButtonArgs {
180 pub fn primary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
182 ButtonArgsBuilder::default()
183 .color(Color::new(0.2, 0.5, 0.8, 1.0)) .on_click(on_click)
185 .build()
186 .unwrap()
187 }
188
189 pub fn secondary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
191 ButtonArgsBuilder::default()
192 .color(Color::new(0.6, 0.6, 0.6, 1.0)) .on_click(on_click)
194 .build()
195 .unwrap()
196 }
197
198 pub fn success(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
200 ButtonArgsBuilder::default()
201 .color(Color::new(0.1, 0.7, 0.3, 1.0)) .on_click(on_click)
203 .build()
204 .unwrap()
205 }
206
207 pub fn danger(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
209 ButtonArgsBuilder::default()
210 .color(Color::new(0.8, 0.2, 0.2, 1.0)) .on_click(on_click)
212 .build()
213 .unwrap()
214 }
215}
216
217impl ButtonArgs {
219 pub fn with_color(mut self, color: Color) -> Self {
220 self.color = color;
221 self
222 }
223
224 pub fn with_hover_color(mut self, hover_color: Color) -> Self {
225 self.hover_color = Some(hover_color);
226 self
227 }
228
229 pub fn with_padding(mut self, padding: Dp) -> Self {
230 self.padding = padding;
231 self
232 }
233
234 pub fn with_shape(mut self, shape: Shape) -> Self {
235 self.shape = shape;
236 self
237 }
238
239 pub fn with_width(mut self, width: DimensionValue) -> Self {
240 self.width = width;
241 self
242 }
243
244 pub fn with_height(mut self, height: DimensionValue) -> Self {
245 self.height = height;
246 self
247 }
248
249 pub fn with_ripple_color(mut self, ripple_color: Color) -> Self {
250 self.ripple_color = ripple_color;
251 self
252 }
253
254 pub fn with_border(mut self, width: Dp, color: Option<Color>) -> Self {
255 self.border_width = width;
256 self.border_color = color;
257 self
258 }
259}