Components / overlay
Glass builder
A glass surface: optionally scattered and bent backdrop, optional fill, and caller-owned content.
A builder is RenderOnce. Construct and mount it in one expression.
use gpui_kit::overlay::Glass;
Reviewed in glass glass-optics glass-materials.
Construct
new(ident: impl Into<Ident>) -> Self
Options
Chain onto the value.
surface(surface: Surface) -> Self
focused(focused: bool) -> Self
elevation(elevation: Elevation) -> Self
radius(radius: Radius) -> Self
radius_px(radius: f32) -> Self
blur(blur: f32) -> Self
preset(preset: GlassPreset) -> Self
thickness(thickness: f32) -> Self
refractive_index(refractive_index: f32) -> Self
backdrop_depth(backdrop_depth: f32) -> Self
refraction(refraction: f32) -> Self
dispersion(dispersion: f32) -> Self
specular(specular: f32) -> Self
light_angle(radians: f32) -> Self
track_pointer(track_pointer: bool) -> Self
pressable(pressable: bool) -> Self
adaptive(adaptive: bool) -> Self
adaptive_appearance(adaptive: bool) -> Self
dimmed(dimmed: bool) -> Self
protect_text_contrast(protect: bool) -> Self
tint(tint: impl Into<Hsla>) -> Self
edge_mask(edge: GlassEdge, band: f32) -> Self
child(child: impl IntoElement) -> Self
Example ยท glass
fn glass(_window: &mut Window, cx: &mut App) -> AnyElement {
let theme = cx.theme().clone();
let label = |title: &'static str, body: &'static str| {
div()
.column()
.gap(px(theme.space(Space::Xs)))
.p(px(theme.space(Space::Md)))
.child(div().type_scale(&theme, TypeScale::Label).child(title))
.child(div().type_scale(&theme, TypeScale::Caption).child(body))
};
// A ruled checkerboard makes the material contract visible: Regular's
// rim refracts the scattered source, bending colour and luminance bands
// without restoring recognizable fine lines. Clear stays sharp because
// its blur is zero. The board is neutral, and every plate is an exact
// number of squares across and down: a board cut through the middle of a
// square at the plate edge reads as a broken pattern rather than as the
// ruled backdrop the optics are being measured against.
let checker_light = theme.colors.text_faint;
let checker_dark = theme.colors.canvas;
let checker_rule = theme.colors.divider.opacity(0.55);
let checkerboard =
|width: f32, height: f32| {
let columns = (width / TILE).ceil() as usize;
let rows = (height / TILE).ceil() as usize;
div()
.absolute()
.top(px(0.0))
.left(px(0.0))
.w(px(width))
.h(px(height))
.column()
.overflow_hidden()
.children((0..rows).map(|row| {
div()
.flex()
.flex_none()
.h(px(TILE))
.children((0..columns).map(move |column| {
div().flex_none().w(px(TILE)).h(px(TILE)).bg(
if (row + column) % 2 == 0 {
checker_light
} else {
checker_dark
},
)
}))
}))
.children((0..=(width / 12.0) as usize).map(|column| {
div()
.absolute()
.top(px(0.0))
.left(px(column as f32 * 12.0))
.w(px(1.0))
.h(px(height))
.bg(checker_rule)
}))
.children((0..=(height / 12.0) as usize).map(|row| {
div()
.absolute()
.top(px(row as f32 * 12.0))
.left(px(0.0))
.w(px(width))
.h(px(1.0))
.bg(checker_rule)
}))
};
let plate =
|ident: &'static str, preset: GlassPreset, title: &'static str, body: &'static str| {
div()
.relative()
.h(px(PLATE_HEIGHT))
.w(px(PLATE_WIDTH))
.surface(&theme, Surface::Panel)
.radius(&theme, Radius::Card)
.overflow_hidden()
.child(
if preset == GlassPreset::Clear || ident == "scene.glass.media" {
gpui::img(glass_media())
.object_fit(gpui::ObjectFit::Cover)
.w(px(PLATE_WIDTH))
.h(px(PLATE_HEIGHT))
.into_any_element()
} else {
checkerboard(PLATE_WIDTH, PLATE_HEIGHT).into_any_element()
},
)
.when(ident == "scene.glass.liquid", |element| {
element.child(div().absolute().inset_0().child(filler(
&theme,
"Document text behind Regular glass",
6,
)))
})
.child(
div()
.absolute()
.top(px(28.0))
.left(px(52.0))
.w(px(280.0))
.child(
Glass::new(ident)
.preset(preset)
.dimmed(preset == GlassPreset::Clear)
.adaptive(true)
.radius(Radius::Dialog)
.child(label(title, body)),
),
)
};
stack(&theme)
.w(px(900.0))
.child(caption(
&theme,
"Clear: dark Frosted / reduced transparency Clear + 35% dimming / media only",
))
.child(
row(&theme)
.child(crate::foundation::ThemeOverlay::theme(
theme.clone().with_reduce_transparency(true),
plate(
"scene.glass.frosted",
GlassPreset::Clear,
"Clear: reduced transparency",
"Dark Frosted and light content in every theme",
),
))
.child(plate(
"scene.glass.clear",
GlassPreset::Clear,
"Clear + dimmed",
"A sharp media fixture, never default chrome",
)),
)
.child(caption(
&theme,
"Regular over text and media: blur, achromatic wash, lensing and hairline",
))
.child(
row(&theme)
.child(plate(
"scene.glass.liquid",
GlassPreset::Liquid,
"Regular Liquid",
"Large reading surfaces keep the window appearance",
))
.child(plate(
"scene.glass.media",
GlassPreset::Liquid,
"Regular on media",
"The same material, not the Clear variant",
)),
)
.child(caption(
&theme,
"Compact Regular controls: appearance may flip; no opaque face",
))
.child(
div()
.relative()
.h(px(TILE * 2.0))
.w(px(PLATE_WIDTH))
.surface(&theme, Surface::Panel)
.radius(&theme, Radius::Card)
.overflow_hidden()
.child(div().absolute().inset_0().bg(gpui::white()))
.child(
div()
.absolute()
.right_0()
.top_0()
.w(px(PLATE_WIDTH / 2.0))
.h_full()
.bg(gpui::black()),
)
.child(
div()
.absolute()
.inset_0()
.flex()
.items_center()
.justify_center()
.gap(px(theme.space(Space::Sm)))
.child(
Glass::new("scene.glass.regular.one")
.preset(GlassPreset::Liquid)
.surface(Surface::Raised)
.radius(Radius::Pill)
.adaptive(true)
.child(
div()
.w(px(110.0))
.h(px(32.0))
.flex()
.items_center()
.justify_center()
.child("Portal"),
),
)
.child(
Glass::new("scene.glass.regular.two")
.preset(GlassPreset::Liquid)
.radius(Radius::Pill)
.adaptive(true)
.child(
div()
.w(px(110.0))
.h(px(32.0))
.rounded(px(theme.radius(Radius::Pill)))
.bg(theme.colors.selected)
.flex()
.items_center()
.justify_center()
.child("Harbour"),
),
),
),
)
// The last two demonstrations sit side by side so the whole scene
// stays inside the window a real display can give the gallery, which
// is where the DirectX renderer gets looked at.
.child(
div()
.flex()
.flex_row()
.gap(px(theme.space(Space::Md)))
.child(
div()
.column()
.gap(px(theme.space(Space::Sm)))
.child(caption(&theme, "Fused: two panes joined into one body"))
.child(
div()
.relative()
.h(px(PLATE_HEIGHT))
.w(px(JOIN_WIDTH))
.surface(&theme, Surface::Panel)
.radius(&theme, Radius::Card)
.overflow_hidden()
.child(checkerboard(JOIN_WIDTH, PLATE_HEIGHT))
.child(
div().absolute().top(px(40.0)).left(px(40.0)).child(
GlassGroup::new("scene.glass.fused")
.preset(GlassPreset::Liquid)
.radius(Radius::Dialog)
.gap(12.0)
.pane(
"scene.glass.fused.left",
label("Left", "One lobe of the body"),
)
.pane(
"scene.glass.fused.right",
label("Right", "Joined across the gap"),
),
),
),
),
)
.child(
div()
.column()
.gap(px(theme.space(Space::Sm)))
.child(caption(&theme, "Large adaptive surfaces never flip"))
.child(
div()
.relative()
.h(px(PLATE_HEIGHT))
.w(px(JOIN_WIDTH))
.surface(&theme, Surface::Panel)
.radius(&theme, Radius::Card)
.overflow_hidden()
.child(
div()
.absolute()
.top(px(0.0))
.left(px(0.0))
.w(px(JOIN_WIDTH / 2.0))
.h(px(PLATE_HEIGHT))
.bg(gpui::white()),
)
.child(
div()
.absolute()
.top(px(0.0))
.left(px(JOIN_WIDTH / 2.0))
.w(px(JOIN_WIDTH / 2.0))
.h(px(PLATE_HEIGHT))
.bg(gpui::black()),
)
.child(
div()
.absolute()
.top(px(28.0))
.left(px(15.0))
.w(px(160.0))
.child(
Glass::new("scene.glass.adaptive.bright")
.preset(GlassPreset::Liquid)
.radius(Radius::Dialog)
.adaptive(true)
.adaptive_appearance(true)
.child(label(
"Bright",
"The reading lands next frame",
)),
),
)
.child(
div()
.absolute()
.top(px(28.0))
.left(px(JOIN_WIDTH / 2.0 + 15.0))
.w(px(160.0))
.child(
Glass::new("scene.glass.adaptive.dark")
.preset(GlassPreset::Liquid)
.radius(Radius::Dialog)
.adaptive(true)
.child(label("Dark", "The same glass, other side")),
),
),
),
),
)
.child(caption(
&theme,
"Focused: one inward report edge โ Regular, Clear + dimmed, Frosted, reduced",
))
.child(
row(&theme).children(
[
("regular", GlassPreset::Liquid, false, "Regular"),
("clear", GlassPreset::Clear, false, "Clear + dimmed"),
("frosted", GlassPreset::Frosted, false, "Frosted"),
("reduced", GlassPreset::Clear, true, "Reduced"),
]
.into_iter()
.map(|(name, preset, reduced, title)| {
div()
.relative()
.w(px(210.0))
.h(px(96.0))
.radius(&theme, Radius::Card)
.overflow_hidden()
.child(
gpui::img(glass_media())
.size_full()
.object_fit(gpui::ObjectFit::Cover),
)
.child(
div()
.absolute()
.left(px(12.0))
.right(px(12.0))
.top(px(24.0))
.child(crate::foundation::ThemeOverlay::theme(
theme.clone().with_reduce_transparency(reduced),
Glass::new(format!("scene.glass.focused.{name}"))
.preset(preset)
.dimmed(preset == GlassPreset::Clear)
.focused(true)
.radius(Radius::Pill)
.child(
div()
.w_full()
.h(px(40.0))
.flex()
.items_center()
.justify_center()
.type_scale(&theme, TypeScale::Label)
.child(title),
),
)),
)
}),
),
)
.child(caption(
&theme,
"Budget fallback: opaque cards, never holes โ R keeps its focus report",
))
.child(div().flex().gap(px(4.0)).children(('A'..='R').map(|name| {
Glass::new(format!("scene.glass.budget.{name}"))
.focused(name == 'R')
.child(
div()
.w(px(38.0))
.h(px(30.0))
.flex()
.items_center()
.justify_center()
.child(name.to_string()),
)
})))
.child(caption(
&theme,
"Nine lobes exceed the fused budget: every pane retains an opaque face",
))
.child(
('A'..='I').fold(GlassGroup::new("scene.glass.lobe-budget"), |group, name| {
group.pane(
format!("scene.glass.lobe-budget.{name}"),
div()
.w(px(58.0))
.h(px(30.0))
.flex()
.items_center()
.justify_center()
.child(name.to_string()),
)
}),
)
.into_any_element()
}