use crate::stack;
use gpui::{App, Axis, Div, DragMoveEvent, ElementId, SharedString, div, prelude::*, px};
use theme::{TextStyle, Theme, ThemeExt, Typeset, ink};
pub struct SliderDrag(pub ElementId);
pub fn slider_fraction(
event: &DragMoveEvent<SliderDrag>,
id: impl Into<ElementId>,
cx: &App,
) -> Option<f32> {
(event.drag(cx).0 == id.into()).then(|| {
crate::widgets::axis_fraction(event.event.position, event.bounds, Axis::Horizontal, 0.0)
})
}
pub trait Controls: ThemeExt {
fn toggle(&self, on: bool) -> Div {
let theme = self.theme();
div()
.flex_none()
.w(px(32.0))
.h(px(18.0))
.rounded_full()
.bg(if on { theme.text } else { ink(0.15) })
.border_1()
.border_color(crate::widgets::RING_SLOT)
.relative()
.child(
div()
.absolute()
.top(px(1.0))
.left(px(if on { 15.0 } else { 1.0 }))
.size(px(14.0))
.rounded_full()
.bg(if on { theme.on_solid } else { ink(0.7) }),
)
}
fn checkbox(&self, checked: bool) -> Div {
let theme = self.theme();
let mut box_ = div()
.flex_none()
.size(px(16.0))
.rounded(px(4.0))
.flex()
.items_center()
.justify_center();
box_ = if checked {
box_.border_1()
.border_color(crate::widgets::RING_SLOT)
.bg(theme.text)
} else {
box_.border_1().border_color(ink(0.25)).bg(ink(0.03))
};
if checked {
box_.child(
crate::icons::icon(crate::icons::glyph::Check)
.size(px(11.0))
.text_color(theme.on_solid),
)
} else {
box_
}
}
fn radio_button(&self, selected: bool) -> Div {
let theme = self.theme();
div()
.flex_none()
.size(px(16.0))
.rounded_full()
.border_1()
.border_color(if selected { theme.text } else { ink(0.25) })
.bg(ink(0.03))
.flex()
.items_center()
.justify_center()
.when(selected, |ring| {
ring.child(div().size(px(8.0)).rounded_full().bg(theme.text))
})
}
fn progress_bar(&self, fraction: f32) -> Div {
let theme = self.theme();
let fraction = fraction.clamp(0.0, 1.0);
div()
.w_full()
.h(px(4.0))
.rounded_full()
.bg(ink(0.12))
.child(
div()
.h_full()
.w(gpui::relative(fraction))
.rounded_full()
.bg(theme.text),
)
}
fn slider(&self, fraction: f32) -> Div {
let theme = self.theme();
let fraction = fraction.clamp(0.0, 1.0);
div()
.w_full()
.h(px(16.0))
.border_1()
.border_color(crate::widgets::RING_SLOT)
.rounded(px(4.0))
.flex()
.items_center()
.relative()
.cursor_pointer()
.child(
div()
.w_full()
.h(px(4.0))
.rounded_full()
.bg(ink(0.12))
.child(
div()
.h_full()
.w(gpui::relative(fraction))
.rounded_full()
.bg(theme.text),
),
)
.child(
div().absolute().left(gpui::relative(fraction)).child(
div()
.size(px(14.0))
.ml(px(-7.0))
.rounded_full()
.bg(theme.text),
),
)
}
fn select_trigger(&self, label: impl Into<SharedString>) -> Div {
let theme = self.theme();
stack::row()
.justify_between()
.px(px(10.0))
.py(px(7.0))
.rounded(px(Theme::button_radius()))
.bg(theme.input_bg)
.border_1()
.border_color(theme.border)
.text_style(TextStyle::Body)
.text_color(theme.text)
.cursor_pointer()
.child(div().min_w_0().truncate().child(label.into()))
.child(
crate::icons::icon(crate::icons::glyph::ChevronDown)
.size(px(14.0))
.text_color(theme.text_muted),
)
}
fn toggle_group(&self) -> Div {
let theme = self.theme();
div()
.self_start()
.flex()
.flex_row()
.items_center()
.gap(px(TOGGLE_GROUP_PAD))
.p(px(TOGGLE_GROUP_PAD))
.rounded(px(TOGGLE_GROUP_RADIUS))
.bg(ink(0.06))
.border_1()
.border_color(theme.border)
}
fn toggle_group_item(&self, label: impl Into<SharedString>, selected: bool) -> Div {
let theme = self.theme();
let mut item = div()
.px(px(10.0))
.py(px(4.0))
.rounded(px(Theme::inset_radius(
TOGGLE_GROUP_RADIUS,
TOGGLE_GROUP_PAD,
)))
.border_1()
.border_color(crate::widgets::RING_SLOT)
.text_style(TextStyle::Callout)
.cursor_pointer()
.child(label.into());
item = if selected {
item.bg(theme.element_active)
.font_weight(gpui::FontWeight::MEDIUM)
.text_color(theme.text)
} else {
item.text_color(theme.text_muted)
};
item
}
}
impl Controls for Theme {}
const TOGGLE_GROUP_RADIUS: f32 = 9.0;
const TOGGLE_GROUP_PAD: f32 = 2.0;