theme: derive elevations from the platform's native shadows

This commit is contained in:
2026-11-01 09:54:35 +07:00
parent 5a32401e25
commit d107f35084
4 changed files with 127 additions and 37 deletions
+90 -24
View File
@@ -84,10 +84,10 @@ impl Default for Radius {
}
}
/// One drop shadow of a floating layer,
/// transcribed from the platform's native overlay shadows.
/// One layer of a floating panel's drop shadow.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Elevation {
pub struct Shadow {
pub offset_x: Pixels,
pub offset_y: Pixels,
pub blur_radius: Pixels,
pub spread_radius: Pixels,
@@ -95,43 +95,109 @@ pub struct Elevation {
pub color: Hsla,
}
impl Shadow {
fn none() -> Self {
Self {
offset_x: px(0.),
offset_y: px(0.),
blur_radius: px(0.),
spread_radius: px(0.),
color: colors::scrim(0.),
}
}
fn black(offset_x: f32, offset_y: f32, blur_radius: f32, alpha: f32) -> Self {
Self {
offset_x: px(offset_x),
offset_y: px(offset_y),
blur_radius: px(blur_radius),
spread_radius: px(0.),
color: colors::scrim(alpha),
}
}
}
/// The drop shadow stack of a floating layer.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Elevation {
/// Key shadow first, ambient grounding second.
pub layers: [Shadow; 2],
}
impl Elevation {
fn single(key: Shadow) -> Self {
Self {
layers: [key, Shadow::none()],
}
}
fn layered(key: Shadow, ambient: Shadow) -> Self {
Self {
layers: [key, ambient],
}
}
/// The gpui shadow list an overlay panel paints with.
pub fn shadows(self) -> Vec<gpui::BoxShadow> {
vec![gpui::BoxShadow {
color: self.color,
offset: gpui::point(px(0.), self.offset_y),
blur_radius: self.blur_radius,
spread_radius: self.spread_radius,
inset: false,
}]
self.layers
.iter()
.filter(|layer| layer.color.a > 0.)
.map(|layer| gpui::BoxShadow {
color: layer.color,
offset: gpui::point(layer.offset_x, layer.offset_y),
blur_radius: layer.blur_radius,
spread_radius: layer.spread_radius,
inset: false,
})
.collect()
}
}
/// The drop shadows of each floating layer class.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Elevations {
/// Popovers and menus ride above content on a tight shadow.
/// Popovers, menus, and pushed alerts ride above content.
pub popover: Elevation,
/// Modal dialogs sit higher with a wider one.
pub dialog: Elevation,
}
impl Default for Elevations {
fn default() -> Self {
Self {
popover: Elevation {
offset_y: px(4.),
blur_radius: px(24.),
spread_radius: px(0.),
color: colors::scrim(0.3),
impl Elevations {
pub(crate) fn for_platform(source: SourceKind, variant: ThemeVariant) -> Self {
if variant == ThemeVariant::Dark {
return Self {
popover: Elevation::single(Shadow::black(0., 4., 24., 0.3)),
dialog: Elevation::single(Shadow::black(0., 8., 40., 0.35)),
};
}
match source {
SourceKind::Macos => Self {
popover: Elevation::layered(
Shadow::black(0., 4., 24., 0.12),
Shadow::black(0., 1., 1., 0.05),
),
dialog: Elevation::layered(
Shadow::black(0., 8., 40., 0.14),
Shadow::black(0., 1., 2., 0.06),
),
},
dialog: Elevation {
offset_y: px(8.),
blur_radius: px(40.),
spread_radius: px(0.),
color: colors::scrim(0.35),
SourceKind::Windows => Self {
popover: Elevation::layered(
Shadow::black(0., 8., 16., 0.14),
Shadow::black(0., 2., 4., 0.1),
),
dialog: Elevation::layered(
Shadow::black(0., 16., 32., 0.16),
Shadow::black(0., 4., 8., 0.1),
),
},
SourceKind::Omarchy | SourceKind::Kde | SourceKind::Gnome | SourceKind::Fallback => {
Self {
popover: Elevation::single(Shadow::black(0., 3., 6., 0.2)),
dialog: Elevation::single(Shadow::black(0., 9., 18., 0.24)),
}
}
}
}
}