use gpui::prelude::FluentBuilder;
use gpui::*;
use crate::theme::*;
#[derive(Clone, Debug)]
pub enum ButtonEvent {
Click,
}
impl EventEmitter<ButtonEvent> for Button {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ButtonVariant {
Primary,
Secondary,
Outline,
Text,
Danger,
}
pub struct Button {
label: String,
variant: ButtonVariant,
size: ComponentSize,
disabled: bool,
loading: bool,
full_width: bool,
}
impl Button {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
variant: ButtonVariant::Primary,
size: ComponentSize::Medium,
disabled: false,
loading: false,
full_width: false,
}
}
pub fn variant(mut self, variant: ButtonVariant) -> Self {
self.variant = variant;
self
}
pub fn size(mut self, size: ComponentSize) -> Self {
self.size = size;
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub fn loading(mut self, loading: bool) -> Self {
self.loading = loading;
self
}
pub fn full_width(mut self, full_width: bool) -> Self {
self.full_width = full_width;
self
}
fn background_color(&self, theme: &Theme, is_hovered: bool) -> Rgba {
if self.disabled {
return theme.colors.background_secondary;
}
match self.variant {
ButtonVariant::Primary => {
if is_hovered {
theme.colors.primary_hover
} else {
theme.colors.primary
}
}
ButtonVariant::Danger => {
if is_hovered {
crate::utils::lighten(theme.colors.error, 0.1)
} else {
theme.colors.error
}
}
ButtonVariant::Secondary | ButtonVariant::Outline => {
if is_hovered {
theme.colors.background_hover
} else {
rgb(0xFFFFFF)
}
}
ButtonVariant::Text => {
if is_hovered {
theme.colors.background_hover
} else {
rgb(0x00000000) }
}
}
}
fn text_color(&self, theme: &Theme) -> Rgba {
if self.disabled {
return theme.colors.text_disabled;
}
match self.variant {
ButtonVariant::Primary | ButtonVariant::Danger => rgb(0xFFFFFF),
ButtonVariant::Secondary | ButtonVariant::Outline | ButtonVariant::Text => {
theme.colors.text
}
}
}
fn border_color(&self, theme: &Theme) -> Option<Rgba> {
if self.disabled {
return Some(theme.colors.border);
}
match self.variant {
ButtonVariant::Primary | ButtonVariant::Text => None,
ButtonVariant::Secondary | ButtonVariant::Outline => Some(theme.colors.border),
ButtonVariant::Danger => None,
}
}
}
impl Render for Button {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = Theme::default();
let label = self.label.clone();
let disabled = self.disabled;
let loading = self.loading;
let size = self.size;
let (padding_y, padding_x) = size.padding();
div()
.flex()
.items_center()
.justify_center()
.py(padding_y)
.px(padding_x)
.rounded(px(BorderRadius::MD))
.text_size(size.font_size())
.font_weight(FontWeight::MEDIUM)
.when(self.full_width, |this| this.w_full())
.when(!self.full_width, |this| this.flex_shrink_0())
.when(!disabled && !loading, |this| {
this.cursor(CursorStyle::PointingHand)
})
.when(disabled || loading, |this| {
this.opacity(0.6)
})
.map(|this| {
let is_hovered = false; this.bg(self.background_color(&theme, is_hovered))
.text_color(self.text_color(&theme))
})
.when_some(self.border_color(&theme), |this, color| {
this.border_1().border_color(color)
})
.when(!disabled && !loading, |this| {
this.on_mouse_down(MouseButton::Left, cx.listener(|_this, _, _, cx| {
cx.emit(ButtonEvent::Click);
}))
})
.child(
div()
.flex()
.items_center()
.gap_2()
.when(loading, |this| {
this.child("⏳") })
.child(label)
)
}
}