1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 13:20:05 -04:00

CSS-like shadows with offset, spread, and blur (#4232)

This makes `epaint::Shadow` more like CSS's box-shadow, adding `offset`
and replacing `extrusion` with `blur` and `spread`.

* Closes https://github.com/emilk/egui/pull/3047

The offsets make for nice drop-shadow effects.

Old shadows:
<img width="1447" alt="old-shadows"
src="https://github.com/emilk/egui/assets/1148717/8a30f7b9-fb9d-49ea-9a2f-9367a60c448a">

New shadows:
<img width="1447" alt="new-shadows-full"
src="https://github.com/emilk/egui/assets/1148717/28cc9c1e-b0de-4c5b-a705-22e52c556584">
This commit is contained in:
Emil Ernerfeldt
2024-03-26 10:37:12 +01:00
committed by GitHub
parent 1634554032
commit c530504a04
4 changed files with 120 additions and 69 deletions

View File

@@ -1,84 +1,65 @@
use super::*;
/// The color and fuzziness of a fuzzy shape.
///
/// Can be used for a rectangular shadow with a soft penumbra.
///
/// Very similar to a box-shadow in CSS.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Shadow {
/// The shadow extends this much outside the rect.
/// The size of the fuzzy penumbra.
pub extrusion: f32,
/// Move the shadow by this much.
///
/// For instance, a value of `[1.0, 2.0]` will move the shadow 1 point to the right and 2 points down,
/// causing a drop-shadow effet.
pub offset: Vec2,
/// The width of the blur, i.e. the width of the fuzzy penumbra.
///
/// A value of 0.0 means no blur.
pub blur: f32,
/// Expand the shadow in all directions by this much.
pub spread: f32,
/// Color of the opaque center of the shadow.
pub color: Color32,
}
impl Shadow {
/// No shadow at all.
pub const NONE: Self = Self {
extrusion: 0.0,
offset: Vec2::ZERO,
blur: 0.0,
spread: 0.0,
color: Color32::TRANSPARENT,
};
pub const fn new(extrusion: f32, color: Color32) -> Self {
Self { extrusion, color }
}
/// Tooltips, menus, …, for dark mode.
pub const fn small_dark() -> Self {
Self {
extrusion: 16.0,
color: Color32::from_black_alpha(96),
}
}
/// Tooltips, menus, …, for light mode.
pub const fn small_light() -> Self {
Self {
extrusion: 16.0,
color: Color32::from_black_alpha(20),
}
}
/// Used for egui windows in dark mode.
pub const fn big_dark() -> Self {
Self {
extrusion: 32.0,
color: Color32::from_black_alpha(96),
}
}
/// Used for egui windows in light mode.
pub const fn big_light() -> Self {
Self {
extrusion: 32.0,
color: Color32::from_black_alpha(16),
}
}
pub fn tessellate(&self, rect: Rect, rounding: impl Into<Rounding>) -> Mesh {
// tessellator.clip_rect = clip_rect; // TODO(emilk): culling
let Self { extrusion, color } = *self;
let rounding: Rounding = rounding.into();
let half_ext = 0.5 * extrusion;
let ext_rounding = Rounding {
nw: rounding.nw + half_ext,
ne: rounding.ne + half_ext,
sw: rounding.sw + half_ext,
se: rounding.se + half_ext,
};
use crate::tessellator::*;
let rect = RectShape::filled(rect.expand(half_ext), ext_rounding, color);
let Self {
offset,
blur,
spread,
color,
} = *self;
let rect = rect.translate(offset);
let rounding_expansion = spread.abs() + 0.5 * blur;
let rounding = rounding.into() + Rounding::from(rounding_expansion);
let rect = RectShape::filled(rect.expand(spread), rounding, color);
let pixels_per_point = 1.0; // doesn't matter here
let font_tex_size = [1; 2]; // unused size we are not tessellating text.
let font_tex_size = [1; 2]; // unused since we are not tessellating text.
let mut tessellator = Tessellator::new(
pixels_per_point,
TessellationOptions {
feathering: true,
feathering_size_in_pixels: extrusion * pixels_per_point,
feathering_size_in_pixels: blur * pixels_per_point,
..Default::default()
},
font_tex_size,
@@ -88,4 +69,20 @@ impl Shadow {
tessellator.tessellate_rect(&rect, &mut mesh);
mesh
}
/// How much larger than the parent rect are we in each direction?
pub fn margin(&self) -> Margin {
let Self {
offset,
blur,
spread,
color: _,
} = *self;
Margin {
left: spread + 0.5 * blur - offset.x,
right: spread + 0.5 * blur + offset.x,
top: spread + 0.5 * blur - offset.y,
bottom: spread + 0.5 * blur + offset.y,
}
}
}