use std::sync::Arc;
use derive_builder::Builder;
use tessera_ui::{Color, DimensionValue, Dp, accesskit::Role, tessera};
use crate::{
pipelines::ShadowProps,
ripple_state::RippleState,
shape_def::Shape,
surface::{SurfaceArgsBuilder, surface},
};
#[derive(Builder, Clone)]
#[builder(pattern = "owned")]
pub struct ButtonArgs {
#[builder(default = "Color::new(0.2, 0.5, 0.8, 1.0)")]
pub color: Color,
#[builder(default)]
pub hover_color: Option<Color>,
#[builder(
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 }"
)]
pub shape: Shape,
#[builder(default = "Dp(12.0)")]
pub padding: Dp,
#[builder(default = "DimensionValue::WRAP", setter(into))]
pub width: DimensionValue,
#[builder(default = "DimensionValue::WRAP", setter(into))]
pub height: DimensionValue,
#[builder(default, setter(strip_option))]
pub on_click: Option<Arc<dyn Fn() + Send + Sync>>,
#[builder(default = "Color::from_rgb(1.0, 1.0, 1.0)")]
pub ripple_color: Color,
#[builder(default = "Dp(0.0)")]
pub border_width: Dp,
#[builder(default)]
pub border_color: Option<Color>,
#[builder(default, setter(strip_option))]
pub shadow: Option<ShadowProps>,
#[builder(default, setter(strip_option, into))]
pub accessibility_label: Option<String>,
#[builder(default, setter(strip_option, into))]
pub accessibility_description: Option<String>,
}
impl Default for ButtonArgs {
fn default() -> Self {
ButtonArgsBuilder::default()
.on_click(Arc::new(|| {}))
.build()
.unwrap()
}
}
#[tessera]
pub fn button(args: impl Into<ButtonArgs>, ripple_state: RippleState, child: impl FnOnce()) {
let button_args: ButtonArgs = args.into();
surface(create_surface_args(&button_args), Some(ripple_state), child);
}
fn create_surface_args(args: &ButtonArgs) -> crate::surface::SurfaceArgs {
let style = if args.border_width.to_pixels_f32() > 0.0 {
crate::surface::SurfaceStyle::FilledOutlined {
fill_color: args.color,
border_color: args.border_color.unwrap_or(args.color),
border_width: args.border_width,
}
} else {
crate::surface::SurfaceStyle::Filled { color: args.color }
};
let hover_style = if let Some(hover_color) = args.hover_color {
let style = if args.border_width.to_pixels_f32() > 0.0 {
crate::surface::SurfaceStyle::FilledOutlined {
fill_color: hover_color,
border_color: args.border_color.unwrap_or(hover_color),
border_width: args.border_width,
}
} else {
crate::surface::SurfaceStyle::Filled { color: hover_color }
};
Some(style)
} else {
None
};
let mut builder = SurfaceArgsBuilder::default();
if let Some(shadow) = args.shadow {
builder = builder.shadow(shadow);
}
if let Some(on_click) = args.on_click.clone() {
builder = builder.on_click(on_click);
}
if let Some(label) = args.accessibility_label.clone() {
builder = builder.accessibility_label(label);
}
if let Some(description) = args.accessibility_description.clone() {
builder = builder.accessibility_description(description);
}
builder
.style(style)
.hover_style(hover_style)
.shape(args.shape)
.padding(args.padding)
.ripple_color(args.ripple_color)
.width(args.width)
.height(args.height)
.accessibility_role(Role::Button)
.accessibility_focusable(true)
.build()
.unwrap()
}
impl ButtonArgs {
pub fn primary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
ButtonArgsBuilder::default()
.color(Color::new(0.2, 0.5, 0.8, 1.0)) .on_click(on_click)
.build()
.unwrap()
}
pub fn secondary(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
ButtonArgsBuilder::default()
.color(Color::new(0.6, 0.6, 0.6, 1.0)) .on_click(on_click)
.build()
.unwrap()
}
pub fn success(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
ButtonArgsBuilder::default()
.color(Color::new(0.1, 0.7, 0.3, 1.0)) .on_click(on_click)
.build()
.unwrap()
}
pub fn danger(on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
ButtonArgsBuilder::default()
.color(Color::new(0.8, 0.2, 0.2, 1.0)) .on_click(on_click)
.build()
.unwrap()
}
}
impl ButtonArgs {
pub fn with_color(mut self, color: Color) -> Self {
self.color = color;
self
}
pub fn with_hover_color(mut self, hover_color: Color) -> Self {
self.hover_color = Some(hover_color);
self
}
pub fn with_padding(mut self, padding: Dp) -> Self {
self.padding = padding;
self
}
pub fn with_shape(mut self, shape: Shape) -> Self {
self.shape = shape;
self
}
pub fn with_width(mut self, width: DimensionValue) -> Self {
self.width = width;
self
}
pub fn with_height(mut self, height: DimensionValue) -> Self {
self.height = height;
self
}
pub fn with_ripple_color(mut self, ripple_color: Color) -> Self {
self.ripple_color = ripple_color;
self
}
pub fn with_border(mut self, width: Dp, color: Option<Color>) -> Self {
self.border_width = width;
self.border_color = color;
self
}
}