use std::sync::Arc;
use derive_setters::Setters;
use tessera_ui::{Color, Dp, Modifier, tessera, use_context};
use crate::{
button::{ButtonArgs, ButtonDefaults, button},
glass_button::{GlassButtonArgs, glass_button},
icon::{IconArgs, icon},
modifier::ModifierExt,
shape_def::Shape,
theme::MaterialTheme,
};
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum IconButtonVariant {
#[default]
Standard,
Filled,
FilledTonal,
Outlined,
}
#[derive(Clone, Setters)]
pub struct IconButtonArgs {
pub variant: IconButtonVariant,
#[setters(into)]
pub icon: IconArgs,
#[setters(skip)]
pub on_click: Option<Arc<dyn Fn() + Send + Sync>>,
pub enabled: bool,
#[setters(strip_option)]
pub color: Option<Color>,
#[setters(strip_option)]
pub content_color: Option<Color>,
}
impl IconButtonArgs {
pub fn new(icon: impl Into<IconArgs>) -> Self {
Self {
variant: IconButtonVariant::default(),
icon: icon.into(),
on_click: None,
enabled: true,
color: None,
content_color: None,
}
}
pub fn on_click(mut self, on_click: impl Fn() + Send + Sync + 'static) -> Self {
self.on_click = Some(Arc::new(on_click));
self
}
pub fn on_click_shared(mut self, on_click: Arc<dyn Fn() + Send + Sync>) -> Self {
self.on_click = Some(on_click);
self
}
}
#[derive(Clone, Setters)]
pub struct GlassIconButtonArgs {
#[setters(into)]
pub button: GlassButtonArgs,
#[setters(into)]
pub icon: IconArgs,
}
impl GlassIconButtonArgs {
pub fn new(icon: impl Into<IconArgs>) -> Self {
Self {
button: GlassButtonArgs::default(),
icon: icon.into(),
}
}
}
#[tessera]
pub fn icon_button(args: impl Into<IconButtonArgs>) {
let args: IconButtonArgs = args.into();
let scheme = use_context::<MaterialTheme>()
.expect("MaterialTheme must be provided")
.get()
.color_scheme;
let (default_container_color, default_content_color, border_width, border_color) =
match args.variant {
IconButtonVariant::Filled => (scheme.primary, scheme.on_primary, Dp(0.0), None),
IconButtonVariant::FilledTonal => (
scheme.secondary_container,
scheme.on_secondary_container,
Dp(0.0),
None,
),
IconButtonVariant::Outlined => (
Color::TRANSPARENT,
scheme.on_surface_variant,
Dp(1.0),
Some(scheme.outline),
),
IconButtonVariant::Standard => {
(Color::TRANSPARENT, scheme.on_surface_variant, Dp(0.0), None)
}
};
let container_color = args.color.unwrap_or(default_container_color);
let content_color = args.content_color.unwrap_or(default_content_color);
let ripple_color = content_color;
let mut button_args = ButtonArgs::default()
.modifier(Modifier::new().size(Dp(40.0), Dp(40.0)))
.padding(Dp(8.0))
.shape(Shape::rounded_rectangle(Dp(20.0)))
.color(container_color)
.content_color(content_color)
.enabled(args.enabled)
.disabled_container_color(match args.variant {
IconButtonVariant::Standard | IconButtonVariant::Outlined => Color::TRANSPARENT,
IconButtonVariant::Filled | IconButtonVariant::FilledTonal => {
ButtonDefaults::disabled_container_color(&scheme)
}
})
.disabled_content_color(ButtonDefaults::disabled_content_color(&scheme))
.disabled_border_color(ButtonDefaults::disabled_border_color(&scheme))
.ripple_color(ripple_color)
.border_width(border_width);
if let Some(bc) = border_color {
button_args = button_args.border_color(Some(bc));
}
if let Some(on_click) = args.on_click {
button_args = button_args.on_click_shared(on_click);
}
let mut icon_args = args.icon;
icon_args.size = Dp(24.0);
icon_args.tint = content_color;
button(button_args, move || {
icon(icon_args);
});
}
#[tessera]
pub fn glass_icon_button(args: impl Into<GlassIconButtonArgs>) {
let args: GlassIconButtonArgs = args.into();
let icon_args = args.icon.clone();
glass_button(args.button, move || {
icon(icon_args.clone());
});
}