use gpui::{Div, ElementId, SharedString, Stateful, div, prelude::*, px};
use icons::Icon;
use motion::{self, Fade};
use theme::{ControlSize, Sizing, Theme, ThemeExt, ink};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ButtonStyle {
Ghost,
Prominent,
Destructive,
}
const GLYPH: f32 = 14.0;
const GROUP_PAD: f32 = 2.0;
fn frame() -> Div {
div()
.control_size(ControlSize::Regular)
.flex()
.items_center()
.cursor_pointer()
}
pub trait Buttons: ThemeExt {
fn button(
&self,
label: impl Into<SharedString>,
style: ButtonStyle,
fade: Option<Fade>,
) -> Div {
let theme = self.theme();
let label = label.into();
match style {
ButtonStyle::Ghost => match fade {
Some(fade) => {
let mut btn = frame()
.text_color(motion::hover_blend(&fade, theme.text_muted, theme.text))
.bg(motion::hover_blend(&fade, ink(0.0), theme.element_hover))
.child(label);
btn.interactivity().on_hover(motion::hover_listener(fade));
btn
}
None => frame().text_color(theme.text_muted).child(label),
},
ButtonStyle::Prominent => frame()
.bg(theme.text)
.font_weight(gpui::FontWeight::MEDIUM)
.text_color(theme.on_solid)
.hover(|s| s.opacity(0.9))
.child(label),
ButtonStyle::Destructive => frame()
.bg(theme.danger_strong)
.font_weight(gpui::FontWeight::MEDIUM)
.text_color(gpui::white())
.hover(|s| s.opacity(0.9))
.child(label),
}
}
fn icon_button(&self, icon: impl Into<Icon>, style: ButtonStyle, fade: Option<Fade>) -> Div {
let theme = self.theme();
let square = frame()
.px(px(0.0))
.w(px(Theme::BUTTON_HEIGHT))
.justify_center();
let icon = icon.into();
let glyph = |tint| {
crate::icons::icon(icon.clone())
.size(px(GLYPH))
.text_color(tint)
};
match style {
ButtonStyle::Ghost => match fade {
Some(fade) => {
let mut btn = square
.bg(motion::hover_blend(&fade, ink(0.0), theme.element_hover))
.child(glyph(motion::hover_blend(
&fade,
theme.text_muted,
theme.text,
)));
btn.interactivity().on_hover(motion::hover_listener(fade));
btn
}
None => square.child(glyph(theme.text_muted)),
},
ButtonStyle::Prominent => square
.bg(theme.text)
.hover(|s| s.opacity(0.9))
.child(glyph(theme.on_solid)),
ButtonStyle::Destructive => square
.bg(theme.danger_strong)
.hover(|s| s.opacity(0.9))
.child(glyph(gpui::white())),
}
}
fn control_group(&self) -> Div {
let theme = self.theme();
div()
.self_start()
.flex()
.flex_row()
.gap(px(GROUP_PAD))
.p(px(GROUP_PAD))
.rounded(px(Theme::button_radius() + GROUP_PAD))
.bg(theme.surface_raised)
.border_1()
.border_color(theme.border)
}
fn ghost(&self, id: impl Into<ElementId>) -> Stateful<Div> {
let tint = self.theme().element_hover;
div()
.id(id)
.flex()
.flex_row()
.items_center()
.rounded(px(Theme::control_radius()))
.cursor_pointer()
.hover(move |el| el.bg(tint))
}
}
impl Buttons for Theme {}