audit popover and dialog
This commit is contained in:
@@ -130,7 +130,11 @@ impl RenderOnce for Dialog {
|
||||
let theme = cx.theme();
|
||||
(
|
||||
theme.colors.overlay,
|
||||
theme.colors.border,
|
||||
if theme.borders.overlays {
|
||||
theme.colors.border
|
||||
} else {
|
||||
theme.colors.surface
|
||||
},
|
||||
theme.colors.foreground,
|
||||
theme.colors.muted_foreground,
|
||||
theme.colors.surface,
|
||||
|
||||
@@ -7,7 +7,7 @@ use gpui::{
|
||||
Entity, InteractiveElement as _, IntoElement, KeyDownEvent, MouseButton, MouseDownEvent,
|
||||
ParentElement as _, Pixels, Point, RenderOnce, Role, ScrollHandle, SharedString,
|
||||
StatefulInteractiveElement as _, Styled as _, Window, anchored, canvas, deferred, div,
|
||||
ease_out_quint, px, relative,
|
||||
ease_out_quint, point, px, relative,
|
||||
};
|
||||
use gpui_native_icons::{IconRole, icon as icon_glyph};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
@@ -374,11 +374,11 @@ impl RenderOnce for Menu {
|
||||
|
||||
let panel = v_flex()
|
||||
.id("menu-panel")
|
||||
.debug_selector(|| "menu-panel".to_string())
|
||||
.occlude()
|
||||
.track_focus(&focus)
|
||||
.role(Role::Menu)
|
||||
.aria_label(label)
|
||||
.mt_1()
|
||||
.min_w_56()
|
||||
.p(chrome.spacing_unit)
|
||||
.bg(chrome.surface)
|
||||
@@ -435,7 +435,8 @@ impl RenderOnce for Menu {
|
||||
let anchored = match self.open_at {
|
||||
Some(position) => anchored().position(position),
|
||||
None => anchored(),
|
||||
};
|
||||
}
|
||||
.offset(point(px(0.), chrome.spacing_unit));
|
||||
|
||||
wrapper = wrapper.child(deferred(anchored.child(panel.with_animation(
|
||||
"menu-enter",
|
||||
|
||||
@@ -6,7 +6,7 @@ use gpui::{
|
||||
Animation, AnimationExt as _, AnyElement, App, Bounds, DispatchPhase, ElementId,
|
||||
InteractiveElement as _, IntoElement, KeyDownEvent, MouseButton, MouseDownEvent,
|
||||
ParentElement as _, Pixels, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
||||
Styled as _, Window, anchored, canvas, deferred, div, ease_out_quint,
|
||||
Styled as _, Window, anchored, canvas, deferred, div, ease_out_quint, point, px,
|
||||
};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
|
||||
@@ -91,13 +91,19 @@ impl Popover {
|
||||
|
||||
impl RenderOnce for Popover {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let (colors, radius, shadows, enter_duration) = {
|
||||
let (colors, radius, shadows, enter_duration, panel_border, anchor_gap) = {
|
||||
let theme = cx.theme();
|
||||
(
|
||||
theme.colors,
|
||||
theme.radius,
|
||||
theme.elevations.popover.shadows(),
|
||||
theme.motion.overlay,
|
||||
if theme.borders.overlays {
|
||||
theme.colors.border
|
||||
} else {
|
||||
theme.colors.surface
|
||||
},
|
||||
theme.spacing.unit,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -165,17 +171,17 @@ impl RenderOnce for Popover {
|
||||
|
||||
let panel = v_flex()
|
||||
.id("popover-panel")
|
||||
.debug_selector(|| "popover-panel".to_string())
|
||||
.occlude()
|
||||
.track_focus(&focus)
|
||||
.role(Role::Dialog)
|
||||
.aria_label(label)
|
||||
.mt_1()
|
||||
.min_w_32()
|
||||
.bg(colors.surface)
|
||||
.text_color(colors.surface_foreground)
|
||||
.rounded(radius.overlay)
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
.border_color(panel_border)
|
||||
.shadow(shadows)
|
||||
.overflow_hidden()
|
||||
.on_mouse_down_out({
|
||||
@@ -218,11 +224,15 @@ impl RenderOnce for Popover {
|
||||
.inset_0(),
|
||||
);
|
||||
|
||||
wrapper = wrapper.child(deferred(anchored().child(panel.with_animation(
|
||||
"popover-enter",
|
||||
Animation::new(enter_duration).with_easing(ease_out_quint()),
|
||||
|panel, delta| panel.opacity(delta),
|
||||
))));
|
||||
wrapper = wrapper.child(deferred(
|
||||
anchored()
|
||||
.offset(point(px(0.), anchor_gap))
|
||||
.child(panel.with_animation(
|
||||
"popover-enter",
|
||||
Animation::new(enter_duration).with_easing(ease_out_quint()),
|
||||
|panel, delta| panel.opacity(delta),
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
wrapper
|
||||
|
||||
@@ -45,6 +45,7 @@ impl Render for Harness {
|
||||
})
|
||||
.trigger(|_, _| {
|
||||
div()
|
||||
.debug_selector(|| "popover-trigger".to_string())
|
||||
.w(px(100.))
|
||||
.h(px(32.))
|
||||
.bg(rgba(0x000000ff))
|
||||
@@ -188,3 +189,26 @@ fn clicking_another_control_dismisses_and_reaches_it() {
|
||||
assert_eq!(*changes.borrow(), vec![false]);
|
||||
assert_eq!(*clicks.borrow(), 1);
|
||||
}
|
||||
|
||||
/// The panel hangs one spacing unit below the trigger: the anchor gap is
|
||||
/// theme-owned, and rides the anchored element so the window-edge clamp
|
||||
/// stays intact.
|
||||
#[test]
|
||||
fn the_panel_hangs_one_spacing_unit_below_the_trigger() {
|
||||
let (cx, mut visual, _window, _changes, _clicks) = popover_window(true);
|
||||
|
||||
let trigger = visual
|
||||
.debug_bounds("popover-trigger")
|
||||
.expect("trigger rendered");
|
||||
let panel = visual
|
||||
.debug_bounds("popover-panel")
|
||||
.expect("panel rendered");
|
||||
|
||||
let expected = cx.update(|cx| Theme::global(cx).spacing.unit);
|
||||
let gap = panel.top() - trigger.bottom();
|
||||
// Painted bounds are snapped to device pixels; allow half a pixel.
|
||||
assert!(
|
||||
(gap - expected).abs() <= px(0.5),
|
||||
"the anchor gap is {gap:?}, expected {expected:?}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -189,6 +189,13 @@ pub struct Elevation {
|
||||
}
|
||||
|
||||
impl Elevation {
|
||||
/// No shadow: dark floating layers separate by border.
|
||||
fn none() -> Self {
|
||||
Self {
|
||||
layers: [Shadow::none(), Shadow::none()],
|
||||
}
|
||||
}
|
||||
|
||||
fn single(key: Shadow) -> Self {
|
||||
Self {
|
||||
layers: [key, Shadow::none()],
|
||||
@@ -232,8 +239,8 @@ 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)),
|
||||
popover: Elevation::none(),
|
||||
dialog: Elevation::none(),
|
||||
control: Elevation::single(Shadow::black(0., 1., 2., 0.25)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -545,6 +545,8 @@ mod tests {
|
||||
resolved.borders.overlays,
|
||||
"{source:?} frames floating layers in dark"
|
||||
);
|
||||
assert!(resolved.elevations.popover.shadows().is_empty());
|
||||
assert!(resolved.elevations.dialog.shadows().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,7 +580,7 @@ mod tests {
|
||||
#[test]
|
||||
fn elevations_and_motion_resolve_with_defaults() {
|
||||
let resolved = resolve(
|
||||
&system(ThemeVariant::Dark, PartialPalette::default(), None),
|
||||
&system(ThemeVariant::Light, PartialPalette::default(), None),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
@@ -590,7 +592,7 @@ mod tests {
|
||||
);
|
||||
assert!(resolved.motion.overlay > std::time::Duration::ZERO);
|
||||
let shadows = resolved.elevations.popover.shadows();
|
||||
assert_eq!(shadows.len(), 1);
|
||||
assert_eq!(shadows.len(), 2);
|
||||
assert!(!shadows[0].inset);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user