use super::*;
pub fn menu_row(theme: &Theme, active: bool, fade: Option<Fade>) -> gpui::Div {
let row = div()
.flex()
.flex_row()
.items_center()
.gap(px(10.0))
.px(px(MENU_ROW_INSET))
.py(px(MENU_ROW_PAD_Y))
.rounded(px(Theme::inset_radius(Theme::surface_radius(), MENU_PAD)))
.text_style(TextStyle::Body)
.cursor_pointer();
match (active, fade) {
(true, _) => row
.bg(theme.card_selected_bg())
.shadow(theme::glass_selected_shadows())
.text_color(theme.text),
(false, None) => row.text_color(theme.text.opacity(0.9)),
(false, Some(fade)) => {
let mut row = row
.text_color(motion::hover_blend(
&fade,
theme.text.opacity(0.9),
theme.text,
))
.bg(motion::hover_blend(
&fade,
theme::ink(0.0),
theme.element_hover,
));
row.interactivity().on_hover(motion::hover_listener(fade));
row
}
}
}
pub fn menu_heading(theme: &Theme, label: impl Into<SharedString>) -> gpui::Div {
let label = label.into();
div()
.px(px(8.0))
.pb(px(4.0))
.pt(px(6.0))
.text_style(TextStyle::Caption2)
.text_color(theme.text_muted.opacity(0.6))
.child(tracked_upper(&label))
}
pub fn menu_note(theme: &Theme, text: impl Into<SharedString>) -> gpui::Div {
div()
.px(px(8.0))
.py(px(2.0))
.text_style(TextStyle::Caption)
.text_color(theme.text_muted)
.child(text.into())
}
pub fn tracked_upper(label: &str) -> String {
let upper = label.to_uppercase();
let mut out = String::with_capacity(upper.len() * 2);
let mut first = true;
for ch in upper.chars() {
if !first {
out.push('\u{200A}'); }
out.push(ch);
first = false;
}
out
}
pub fn divider(theme: &Theme) -> gpui::Div {
div()
.h(px(1.0))
.mx(px(-4.0))
.my(px(4.0))
.bg(theme.border_faint)
}
pub fn band() -> gpui::Hsla {
theme::band()
}
pub fn key_cap(_theme: &Theme) -> gpui::Div {
div()
.h(px(22.0))
.px(px(5.0))
.rounded(px(5.0))
.flex()
.flex_row()
.items_center()
.justify_center()
.gap(px(4.0))
.bg(ink(0.05))
}
pub(super) fn key_hint_label(theme: &Theme, label: &'static str) -> gpui::Div {
div()
.text_style(TextStyle::Caption)
.text_color(theme.text_muted.opacity(0.45))
.child(SharedString::from(label))
}
pub fn key_hint(theme: &Theme, icon: impl Into<Icon>, label: &'static str) -> gpui::Div {
div()
.flex()
.flex_row()
.items_center()
.gap(px(5.0))
.child(
key_cap(theme).child(
crate::icons::icon(icon)
.size(px(12.5))
.text_color(theme.text_muted.opacity(0.7)),
),
)
.child(key_hint_label(theme, label))
}
pub fn key_hint_text(
theme: &Theme,
cap: impl Into<SharedString>,
label: &'static str,
) -> gpui::Div {
div()
.flex()
.flex_row()
.items_center()
.gap(px(5.0))
.child(
key_cap(theme)
.text_style(TextStyle::Subheadline)
.font_family(theme.font_mono.clone())
.text_color(theme.text_muted.opacity(0.7))
.child(cap.into()),
)
.child(key_hint_label(theme, label))
}
pub fn key_hint_pair(
theme: &Theme,
first: impl Into<Icon>,
second: impl Into<Icon>,
label: &'static str,
) -> gpui::Div {
div()
.flex()
.flex_row()
.items_center()
.gap(px(5.0))
.child(
key_cap(theme)
.child(
crate::icons::icon(first)
.size(px(12.5))
.text_color(theme.text_muted.opacity(0.7)),
)
.child(div().w(px(1.0)).h(px(11.0)).bg(theme.border))
.child(
crate::icons::icon(second)
.size(px(12.5))
.text_color(theme.text_muted.opacity(0.7)),
),
)
.child(key_hint_label(theme, label))
}
pub fn kbd_hint(theme: &Theme, label: impl Into<SharedString>) -> gpui::Div {
div()
.flex_none()
.px(px(5.0))
.py(px(1.0))
.rounded(px(5.0))
.bg(ink(0.05))
.text_style(TextStyle::Caption)
.font_family(theme.font_mono.clone())
.text_color(theme.text_muted.opacity(0.6))
.child(label.into())
}
pub fn search_line(theme: &Theme, input: AnyElement) -> gpui::Div {
stack::row()
.mx(px(-MENU_PAD))
.px(px(MENU_PAD + MENU_ROW_INSET))
.py(px(7.0))
.mb(px(MENU_PAD))
.border_b_1()
.border_color(theme.border_faint)
.text_style(TextStyle::Body)
.child(
icons::icon(icons::glyph::Search)
.size(px(13.0))
.text_color(theme.text_faint),
)
.child(div().flex_1().child(input))
}
pub fn menu_section(theme: &Theme) -> gpui::Div {
div()
.mt(px(4.0))
.pt(px(4.0))
.border_t_1()
.border_color(theme.border_faint)
.flex()
.flex_col()
.gap(px(2.0))
}
pub fn dialog_card(theme: &Theme) -> gpui::Div {
div()
.w(px(360.0))
.p(px(20.0))
.rounded(px(DIALOG_RADIUS))
.bg(theme.surface_dialog)
.border_1()
.border_color(theme.border)
.shadow_lg()
.flex()
.flex_col()
.text_color(theme.text)
}
pub fn dialog_title(theme: &Theme, title: impl Into<SharedString>) -> gpui::Div {
div()
.text_style(TextStyle::Headline)
.text_color(theme.text)
.child(title.into())
}
pub fn dialog_body(theme: &Theme, copy: impl Into<SharedString>) -> gpui::Div {
div()
.text_style(TextStyle::Body)
.line_height(px(19.0))
.text_color(theme.text_muted)
.child(copy.into())
}
pub fn dialog_field(theme: &Theme, input: AnyElement) -> gpui::Div {
div()
.w_full()
.px(px(12.0))
.py(px(8.0))
.rounded(px(Theme::button_radius()))
.border_1()
.border_color(theme.border)
.bg(ink(0.04))
.text_style(TextStyle::Body)
.child(input)
}
pub fn redacted_rows(
_id: &'static str,
_theme: &Theme,
count: usize,
painter: Painter,
cx: &mut gpui::App,
) -> AnyElement {
let wash = ink(0.04);
let delta = motion::pulse_delta(&PULSE, painter, cx);
div()
.flex()
.flex_col()
.gap(px(6.0))
.py(px(4.0))
.children((0..count).map(move |i| {
let phase = motion::staggered_phase(delta, i, 0.08);
div()
.h(px(28.0))
.rounded(px(Theme::control_radius()))
.bg(wash)
.opacity(0.35 + 0.4 * motion::pulse_wave(phase))
}))
.into_any_element()
}
pub fn error_row(theme: &Theme, message: impl Into<SharedString>) -> gpui::Div {
div()
.flex()
.flex_col()
.gap(px(6.0))
.p(px(Theme::SPACE))
.text_style(TextStyle::Callout)
.text_color(theme.danger)
.child(message.into())
}