pub use crate::component_traits::{Collapsible, Disableable, Selectable};
pub use crate::sizing::{Sizable, Size, StyleSized};
use gpui::{
App, BoxShadow, Corners, Edges, Hsla, ParentElement, Pixels, StyleRefinement, Styled, Window,
div, hsla, px,
};
pub use gpui_base::{FocusableExt, RoleOverride, StyledExt, box_shadow, h_flex, v_flex};
use crate::ActiveTheme as _;
const FOCUS_RING_WIDTH: Pixels = px(3.);
const FOCUS_RING_OPACITY: f32 = 0.5;
const SURFACE_SHADOW_INK: f32 = 0.1;
const POPOVER_RING_INK: f32 = 0.1;
pub(crate) fn popover_ring(cx: &App) -> Hsla {
cx.theme().foreground.alpha(POPOVER_RING_INK)
}
pub(crate) fn popover_shadow(ring: Hsla, strength: f32) -> Vec<BoxShadow> {
let strength = strength.clamp(0., 1.);
let ink = hsla(0., 0., 0., SURFACE_SHADOW_INK * strength);
vec![
BoxShadow::new(px(0.), px(0.), ring.alpha(ring.a * strength))
.blur_radius(px(0.))
.spread_radius(px(1.)),
BoxShadow::new(px(0.), px(4.), ink)
.blur_radius(px(3.))
.spread_radius(px(-1.)),
BoxShadow::new(px(0.), px(2.), ink)
.blur_radius(px(2.))
.spread_radius(px(-2.)),
]
}
pub(crate) fn toast_shadow(strength: f32) -> Vec<BoxShadow> {
let ink = hsla(0., 0., 0., SURFACE_SHADOW_INK * strength.clamp(0., 1.));
vec![
BoxShadow::new(px(0.), px(10.), ink)
.blur_radius(px(7.5))
.spread_radius(px(-3.)),
BoxShadow::new(px(0.), px(4.), ink)
.blur_radius(px(3.))
.spread_radius(px(-4.)),
]
}
pub(crate) fn raised_shadow() -> Vec<BoxShadow> {
let ink = hsla(0., 0., 0., SURFACE_SHADOW_INK);
vec![
BoxShadow::new(px(0.), px(1.), ink).blur_radius(px(1.5)),
BoxShadow::new(px(0.), px(1.), ink)
.blur_radius(px(1.))
.spread_radius(px(-1.)),
]
}
pub trait ThemeStyled: Styled + Sized {
fn focus_ring_style(self, window: &Window, cx: &App) -> Self
where
Self: ParentElement;
fn popover_style(self, cx: &App) -> Self;
fn rounded_full_style(self, cx: &App) -> Self {
self.rounded(cx.theme().radius_full())
}
}
impl<T: Styled + Sized> ThemeStyled for T {
fn focus_ring_style(mut self, window: &Window, cx: &App) -> Self
where
Self: ParentElement,
{
if !cx.theme().focus_ring {
return self.border_color(cx.theme().ring);
}
let rem_size = window.rem_size();
let style = self.style();
let border_widths = Edges::<Pixels> {
top: style
.border_widths
.top
.map(|v| v.to_pixels(rem_size))
.unwrap_or_default(),
bottom: style
.border_widths
.bottom
.map(|v| v.to_pixels(rem_size))
.unwrap_or_default(),
left: style
.border_widths
.left
.map(|v| v.to_pixels(rem_size))
.unwrap_or_default(),
right: style
.border_widths
.right
.map(|v| v.to_pixels(rem_size))
.unwrap_or_default(),
};
let radius = Corners::<Pixels> {
top_left: style
.corner_radii
.top_left
.map(|v| v.to_pixels(rem_size))
.unwrap_or_default(),
top_right: style
.corner_radii
.top_right
.map(|v| v.to_pixels(rem_size))
.unwrap_or_default(),
bottom_left: style
.corner_radii
.bottom_left
.map(|v| v.to_pixels(rem_size))
.unwrap_or_default(),
bottom_right: style
.corner_radii
.bottom_right
.map(|v| v.to_pixels(rem_size))
.unwrap_or_default(),
}
.map(|value| *value + FOCUS_RING_WIDTH);
let mut ring_style = StyleRefinement::default();
ring_style.corner_radii.top_left = Some(radius.top_left.into());
ring_style.corner_radii.top_right = Some(radius.top_right.into());
ring_style.corner_radii.bottom_left = Some(radius.bottom_left.into());
ring_style.corner_radii.bottom_right = Some(radius.bottom_right.into());
let inset = FOCUS_RING_WIDTH;
self.border_color(cx.theme().ring).child(
div()
.flex_none()
.absolute()
.top(-(inset + border_widths.top))
.left(-(inset + border_widths.left))
.right(-(inset + border_widths.right))
.bottom(-(inset + border_widths.bottom))
.border(FOCUS_RING_WIDTH)
.border_color(cx.theme().ring.alpha(FOCUS_RING_OPACITY))
.refine_style(&ring_style),
)
}
fn popover_style(self, cx: &App) -> Self {
let theme = cx.theme();
self.bg(theme.popover)
.text_color(theme.popover_foreground)
.shadow(popover_shadow(popover_ring(cx), 1.))
.rounded(theme.radius)
}
}