use teksilo_core::build_context::BuildContext;
use teksilo_core::signal::Signal;
use teksilo_core::styles::{SplitButtonStyle, SplitButtonStyleConfig};
use teksilo_core::widget_id::WidgetId;
use teksilo_tokens::{BorderRole, CornerRadius, SurfaceRole};
use crate::button::{ButtonVariant, InteractionState};
use crate::primitives::{MinSize, RectWidget, ZStack};
use crate::split_button::{
SPLIT_BUTTON_BORDER_WIDTH, SPLIT_BUTTON_CHEVRON_WIDTH, SPLIT_BUTTON_CORNER_RADIUS,
SPLIT_BUTTON_DIVIDER_WIDTH, SPLIT_BUTTON_HEIGHT, SPLIT_BUTTON_MIN_WIDTH, SplitButtonFamily,
classify,
};
#[derive(Debug, Default, Clone, Copy)]
pub struct RecipeSplitButtonStyle;
impl SplitButtonStyle for RecipeSplitButtonStyle {
fn make_body(&self, cfg: &SplitButtonStyleConfig, ctx: &mut BuildContext) -> WidgetId {
let variant = cfg.variant;
let state: Signal<InteractionState> = cfg
.is_pressed
.zip3(&cfg.is_focused, &cfg.is_hovered)
.map(|(pressed, focused, hovered)| {
if *pressed {
InteractionState::Pressed
} else if *focused {
InteractionState::Focused
} else if *hovered {
InteractionState::Hovered
} else {
InteractionState::Idle
}
});
let bg_role = state.map(move |s| resolve_bg_role(variant, *s));
let border_role = state.map(move |s| resolve_border_role(variant, *s));
let normal_bw = SPLIT_BUTTON_BORDER_WIDTH;
let focus_bw = ctx.theme().shape.focus_ring_width;
let border_width =
state.map(move |s| resolve_border_width(variant, *s, normal_bw, focus_bw));
let bg_id = ctx.add(
RectWidget::new()
.background(bg_role)
.border_color(border_role)
.border_width(border_width)
.corner_radius(CornerRadius::uniform(SPLIT_BUTTON_CORNER_RADIUS)),
);
let frame_id = ctx.add(ZStack::new().add_child(bg_id).add_child(cfg.content));
let total_min_width =
SPLIT_BUTTON_MIN_WIDTH + SPLIT_BUTTON_DIVIDER_WIDTH + SPLIT_BUTTON_CHEVRON_WIDTH;
ctx.add(MinSize::new(total_min_width, SPLIT_BUTTON_HEIGHT).child_id(frame_id))
}
}
fn resolve_bg_role(variant: ButtonVariant, state: InteractionState) -> SurfaceRole {
match (classify(variant), state) {
(SplitButtonFamily::FilledLike, InteractionState::Disabled) => SurfaceRole::AccentDisabled,
(SplitButtonFamily::FilledLike, InteractionState::Pressed) => SurfaceRole::AccentPressed,
(SplitButtonFamily::FilledLike, InteractionState::Hovered) => SurfaceRole::AccentHover,
(SplitButtonFamily::FilledLike, _) => SurfaceRole::Accent,
(SplitButtonFamily::PlainLike, InteractionState::Pressed) => SurfaceRole::Pressed,
(SplitButtonFamily::PlainLike, InteractionState::Hovered) => SurfaceRole::Hover,
(SplitButtonFamily::PlainLike, _) => SurfaceRole::Main,
(SplitButtonFamily::GhostLike, InteractionState::Pressed) => SurfaceRole::Pressed,
(SplitButtonFamily::GhostLike, InteractionState::Hovered) => SurfaceRole::Hover,
(SplitButtonFamily::GhostLike, _) => SurfaceRole::Transparent,
}
}
fn resolve_border_role(variant: ButtonVariant, state: InteractionState) -> BorderRole {
if state == InteractionState::Focused {
return BorderRole::Focused;
}
match classify(variant) {
SplitButtonFamily::FilledLike | SplitButtonFamily::GhostLike => BorderRole::Transparent,
SplitButtonFamily::PlainLike => match state {
InteractionState::Hovered | InteractionState::Pressed => BorderRole::Strong,
_ => BorderRole::Default,
},
}
}
fn resolve_border_width(
variant: ButtonVariant,
state: InteractionState,
normal_bw: f32,
focus_bw: f32,
) -> f32 {
if state == InteractionState::Focused {
return focus_bw;
}
match classify(variant) {
SplitButtonFamily::FilledLike | SplitButtonFamily::GhostLike => 0.0,
SplitButtonFamily::PlainLike => normal_bw,
}
}