use gpui::{Div, SharedString, Svg, div, prelude::*, px};
use icons::Icon;
use theme::{TextStyle, Theme, ThemeExt, Typeset};
const DECK_DEPTH: usize = 2;
const DECK_PEEK: f32 = 4.0;
const DECK_GAP: f32 = 1.5;
const DECK_HEIGHT: [f32; DECK_DEPTH] = [0.8, 0.6];
const DECK_FRONT_INK: f32 = 0.12;
const DECK_INK: [f32; DECK_DEPTH] = [0.18, 0.09];
const DECK_PAD: f32 = 3.0;
const DECK_RADIUS: f32 = 4.0;
pub trait Content: ThemeExt {
fn badge(&self, label: impl Into<SharedString>) -> Div {
let theme = self.theme();
div()
.flex_none()
.px(px(8.0))
.py(px(2.0))
.rounded_full()
.border_1()
.border_color(theme.border)
.text_style(TextStyle::Caption)
.text_color(theme.text_muted)
.child(label.into())
}
fn badge_active(&self, label: impl Into<SharedString>) -> Div {
let theme = self.theme();
let emerald = theme.success;
let emerald_text = theme.success_muted; div()
.flex_none()
.px(px(8.0))
.py(px(2.0))
.rounded_full()
.bg(emerald.opacity(0.12))
.text_style(TextStyle::Caption)
.text_color(emerald_text.opacity(0.9))
.child(label.into())
}
fn avatar(&self, initials: impl Into<SharedString>) -> Div {
let theme = self.theme();
div()
.flex_none()
.size(px(28.0))
.rounded_full()
.bg(theme.ink(0.12))
.flex()
.items_center()
.justify_center()
.text_style(TextStyle::Subheadline)
.font_weight(gpui::FontWeight::MEDIUM)
.text_color(theme.text_muted)
.child(initials.into())
}
fn icon_deck(&self, front: impl Into<Icon>, behind: usize) -> Div {
let theme = self.theme();
let glyph = TextStyle::Caption.painted();
let edge = glyph + DECK_PAD * 2.;
div()
.flex_none()
.relative()
.w(px(edge + DECK_PEEK * DECK_DEPTH as f32))
.h(px(edge))
.children((1..=behind.min(DECK_DEPTH)).map(|at| {
let height = edge * DECK_HEIGHT[at - 1];
let shown = DECK_PEEK - DECK_GAP;
div()
.absolute()
.top_0()
.left(px(edge + DECK_PEEK * (at - 1) as f32 + DECK_GAP))
.w(px(shown))
.h(px(edge))
.overflow_hidden()
.child(
div()
.absolute()
.left(px(shown - edge))
.top(px((edge - height) / 2.))
.w(px(edge))
.h(px(height))
.rounded(px(DECK_RADIUS))
.bg(theme.ink(DECK_INK[at - 1])),
)
}))
.child(
div()
.absolute()
.top_0()
.left_0()
.size(px(edge))
.flex()
.items_center()
.justify_center()
.rounded(px(DECK_RADIUS))
.bg(theme.ink(DECK_FRONT_INK))
.child(
crate::icons::icon(front)
.size(px(glyph))
.text_color(theme.text),
),
)
}
fn tag(&self, label: impl Into<SharedString>) -> Div {
let theme = self.theme();
div()
.self_start()
.flex()
.flex_row()
.items_center()
.gap(px(5.0))
.pl(px(8.0))
.pr(px(5.0))
.py(px(3.0))
.rounded(px(Theme::control_radius()))
.bg(theme.ink(0.07))
.border_1()
.border_color(theme.border)
.text_style(TextStyle::Callout)
.text_color(theme.text)
.child(label.into())
.child(
crate::icons::icon(crate::icons::glyph::X)
.size(px(10.0))
.text_color(theme.text_faint),
)
}
fn chip(&self, label: impl Into<SharedString>, tint: gpui::Hsla) -> Div {
let theme = self.theme();
div()
.flex_none()
.flex()
.flex_row()
.items_center()
.gap(px(2.0))
.px(px(6.0))
.rounded(px(Theme::control_radius()))
.bg(tint.opacity(0.22))
.text_style(TextStyle::Callout)
.text_color(theme.text)
.child(div().min_w_0().truncate().child(label.into()))
}
fn breadcrumb(&self) -> Div {
div()
.self_start()
.flex()
.flex_row()
.items_center()
.gap(px(4.0))
.min_w_0()
}
fn breadcrumb_item(&self, label: impl Into<SharedString>, current: bool) -> Div {
let theme = self.theme();
let mut crumb = div()
.min_w_0()
.truncate()
.text_style(TextStyle::Callout)
.child(label.into());
crumb = if current {
crumb.text_color(theme.text)
} else {
crumb.text_color(theme.text_muted).cursor_pointer()
};
crumb
}
fn breadcrumb_separator(&self) -> Svg {
let theme = self.theme();
crate::icons::icon(crate::icons::glyph::ChevronRight)
.size(px(12.0))
.text_color(theme.text_faint)
}
fn empty_state(
&self,
icon: impl Into<Icon>,
title: impl Into<SharedString>,
hint: impl Into<SharedString>,
) -> Div {
let theme = self.theme();
div()
.w_full()
.flex()
.flex_col()
.items_center()
.justify_center()
.gap(px(6.0))
.py(px(40.0))
.child(
crate::icons::icon(icon)
.size(px(24.0))
.text_color(theme.text_faint),
)
.child(
div()
.text_style(TextStyle::Headline)
.text_color(theme.text)
.child(title.into()),
)
.child(
div()
.text_style(TextStyle::Callout)
.text_color(theme.text_muted)
.child(hint.into()),
)
}
}
impl Content for Theme {}