use gpui::prelude::*;
use gpui::{div, px, App, ClickEvent, ElementId, IntoElement, SharedString, Window};
use super::ClickHandler;
use crate::theme::{theme, ColorName, Size};
#[derive(IntoElement)]
pub struct Switch {
id: ElementId,
checked: bool,
label: Option<SharedString>,
size: Size,
color: ColorName,
disabled: bool,
on_change: Option<ClickHandler>,
}
impl Switch {
pub fn new(id: impl Into<ElementId>) -> Self {
Switch {
id: id.into(),
checked: false,
label: None,
size: Size::Md,
color: ColorName::Blue,
disabled: false,
on_change: None,
}
}
pub fn checked(mut self, checked: bool) -> Self {
self.checked = checked;
self
}
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
self.label = Some(label.into());
self
}
pub fn size(mut self, size: Size) -> Self {
self.size = size;
self
}
pub fn color(mut self, color: ColorName) -> Self {
self.color = color;
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub fn on_change(
mut self,
handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
) -> Self {
self.on_change = Some(Box::new(handler));
self
}
fn track_height(&self) -> f32 {
match self.size {
Size::Xs => 16.0,
Size::Sm => 20.0,
Size::Md => 24.0,
Size::Lg => 30.0,
Size::Xl => 36.0,
}
}
}
impl RenderOnce for Switch {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let t = theme(cx);
let height = self.track_height();
let width = (height * 1.85).round();
let knob = height - 4.0;
let accent = t.color(self.color, t.primary_shade());
let track_bg = if self.checked {
accent.hsla()
} else {
t.color(ColorName::Gray, if t.scheme.is_dark() { 6 } else { 4 }).hsla()
};
let knob_x = if self.checked { width - knob - 2.0 } else { 2.0 };
let track = div()
.w(px(width))
.h(px(height))
.rounded(px(height))
.bg(track_bg)
.relative()
.child(
div()
.absolute()
.top(px(2.0))
.left(px(knob_x))
.w(px(knob))
.h(px(knob))
.rounded(px(knob))
.bg(t.white.hsla()),
);
let mut row = div().id(self.id).flex().items_center().gap(px(8.0)).child(track);
if let Some(label) = self.label {
row = row.child(
div()
.text_size(px(t.font_size(self.size)))
.text_color(t.text().hsla())
.child(label),
);
}
if self.disabled {
row.opacity(0.5)
} else {
if let Some(handler) = self.on_change {
row = row.on_click(handler);
}
row
}
}
}