modal: Fix modal, drawer overlay position on Linux. (#515)

- Update `overlay` for Modal, still handle click out to dismiss, even it
no overlay.
This commit is contained in:
Jason Lee 2024-12-24 14:43:37 +08:00 committed by GitHub
parent 6acbfb5298
commit 9eba97ddf6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 80 additions and 67 deletions

View file

@ -15,7 +15,9 @@ use crate::{
scroll::ScrollbarAxis, scroll::ScrollbarAxis,
theme::ActiveTheme, theme::ActiveTheme,
title_bar::TITLE_BAR_HEIGHT, title_bar::TITLE_BAR_HEIGHT,
v_flex, IconName, Placement, Sizable, StyledExt as _, v_flex,
window_border::SHADOW_SIZE,
IconName, Placement, Sizable, StyledExt as _,
}; };
actions!(drawer, [Escape]); actions!(drawer, [Escape]);
@ -119,11 +121,11 @@ impl RenderOnce for Drawer {
fn render(self, cx: &mut WindowContext) -> impl IntoElement { fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let placement = self.placement; let placement = self.placement;
let titlebar_height = self.margin_top; let titlebar_height = self.margin_top;
let size = cx.viewport_size(); let size = cx.viewport_size() - gpui::size(SHADOW_SIZE * 2, SHADOW_SIZE * 2);
let on_close = self.on_close.clone(); let on_close = self.on_close.clone();
anchored() anchored()
.position(point(px(0.), titlebar_height)) .position(point(px(0.) + SHADOW_SIZE, titlebar_height + SHADOW_SIZE))
.snap_to_window() .snap_to_window()
.child( .child(
div() div()

View file

@ -1,7 +1,7 @@
use std::{rc::Rc, time::Duration}; use std::{rc::Rc, time::Duration};
use gpui::{ use gpui::{
actions, anchored, div, hsla, prelude::FluentBuilder, px, relative, Animation, actions, anchored, div, hsla, point, prelude::FluentBuilder, px, relative, Animation,
AnimationExt as _, AnyElement, AppContext, Bounds, ClickEvent, Div, FocusHandle, Hsla, AnimationExt as _, AnyElement, AppContext, Bounds, ClickEvent, Div, FocusHandle, Hsla,
InteractiveElement, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point, InteractiveElement, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point,
RenderOnce, SharedString, Styled, WindowContext, RenderOnce, SharedString, Styled, WindowContext,
@ -11,7 +11,9 @@ use crate::{
animation::cubic_bezier, animation::cubic_bezier,
button::{Button, ButtonVariants as _}, button::{Button, ButtonVariants as _},
theme::ActiveTheme as _, theme::ActiveTheme as _,
v_flex, ContextModal, IconName, Sizable as _, v_flex,
window_border::SHADOW_SIZE,
ContextModal, IconName, Sizable as _,
}; };
actions!(modal, [Escape]); actions!(modal, [Escape]);
@ -162,7 +164,7 @@ impl RenderOnce for Modal {
fn render(self, cx: &mut WindowContext) -> impl gpui::IntoElement { fn render(self, cx: &mut WindowContext) -> impl gpui::IntoElement {
let layer_ix = self.layer_ix; let layer_ix = self.layer_ix;
let on_close = self.on_close.clone(); let on_close = self.on_close.clone();
let view_size = cx.viewport_size(); let view_size = cx.viewport_size() - gpui::size(SHADOW_SIZE * 2, SHADOW_SIZE * 2);
let bounds = Bounds { let bounds = Bounds {
origin: Point::default(), origin: Point::default(),
size: view_size, size: view_size,
@ -171,78 +173,83 @@ impl RenderOnce for Modal {
let y = self.margin_top.unwrap_or(view_size.height / 10.) + offset_top; let y = self.margin_top.unwrap_or(view_size.height / 10.) + offset_top;
let x = bounds.center().x - self.width / 2.; let x = bounds.center().x - self.width / 2.;
anchored().snap_to_window().child( anchored()
div() .position(point(SHADOW_SIZE, SHADOW_SIZE))
.occlude() .snap_to_window()
.w(view_size.width) .child(
.h(view_size.height) div()
.when(self.overlay_visible, |this| { .occlude()
this.bg(overlay_color(self.overlay, cx)) .w(view_size.width)
}) .h(view_size.height)
.when(self.overlay, |this| { .when(self.overlay_visible, |this| {
this.on_mouse_down(MouseButton::Left, { this.bg(overlay_color(self.overlay, cx))
})
.on_mouse_down(MouseButton::Left, {
let on_close = self.on_close.clone(); let on_close = self.on_close.clone();
move |_, cx| { move |_, cx| {
on_close(&ClickEvent::default(), cx); on_close(&ClickEvent::default(), cx);
cx.close_modal(); cx.close_modal();
} }
}) })
}) .child(
.child( self.base
self.base .id(SharedString::from(format!("modal-{layer_ix}")))
.id(SharedString::from(format!("modal-{layer_ix}"))) .key_context(CONTEXT)
.key_context(CONTEXT) .track_focus(&self.focus_handle)
.track_focus(&self.focus_handle) .when(self.keyboard, |this| {
.when(self.keyboard, |this| { this.on_action({
this.on_action({ let on_close = self.on_close.clone();
let on_close = self.on_close.clone(); move |_: &Escape, cx| {
move |_: &Escape, cx| { // FIXME:
// FIXME: //
// // Here some Modal have no focus_handle, so it will not work will Escape key.
// Here some Modal have no focus_handle, so it will not work will Escape key. // But by now, we `cx.close_modal()` going to close the last active model, so the Escape is unexpected to work.
// But by now, we `cx.close_modal()` going to close the last active model, so the Escape is unexpected to work. on_close(&ClickEvent::default(), cx);
on_close(&ClickEvent::default(), cx); cx.close_modal();
cx.close_modal(); }
} })
}) })
}) .absolute()
.absolute() .occlude()
.occlude() .relative()
.relative() .left(x)
.left(x) .top(y)
.top(y) .w(self.width)
.w(self.width) .when_some(self.max_width, |this, w| this.max_w(w))
.when_some(self.max_width, |this, w| this.max_w(w)) .when_some(self.title, |this, title| {
.when_some(self.title, |this, title| { this.child(div().line_height(relative(1.)).child(title))
this.child(div().line_height(relative(1.)).child(title)) })
}) .when(self.show_close, |this| {
.when(self.show_close, |this| { this.child(
this.child( Button::new(SharedString::from(format!(
Button::new(SharedString::from(format!("modal-close-{layer_ix}"))) "modal-close-{layer_ix}"
)))
.absolute() .absolute()
.top_2() .top_2()
.right_2() .right_2()
.small() .small()
.ghost() .ghost()
.icon(IconName::Close) .icon(IconName::Close)
.on_click(move |_, cx| { .on_click(
on_close(&ClickEvent::default(), cx); move |_, cx| {
cx.close_modal(); on_close(&ClickEvent::default(), cx);
}), cx.close_modal();
) },
}) ),
.child(self.content) )
.children(self.footer) })
.with_animation( .child(self.content)
"slide-down", .children(self.footer)
Animation::new(Duration::from_secs_f64(0.25)) .with_animation(
.with_easing(cubic_bezier(0.32, 0.72, 0., 1.)), "slide-down",
move |this, delta| { Animation::new(Duration::from_secs_f64(0.25))
let y_offset = px(0.) + delta * px(30.); .with_easing(cubic_bezier(0.32, 0.72, 0., 1.)),
this.top(y + y_offset).opacity(delta) move |this, delta| {
}, let y_offset = px(0.) + delta * px(30.);
), this.top(y + y_offset).opacity(delta)
), },
) ),
),
)
} }
} }

View file

@ -405,6 +405,7 @@ impl Render for Root {
window_border().child( window_border().child(
div() div()
.id("root") .id("root")
.relative()
.size_full() .size_full()
.font_family(".SystemUIFont") .font_family(".SystemUIFont")
.bg(cx.theme().background) .bg(cx.theme().background)

View file

@ -8,7 +8,10 @@ use gpui::{
use crate::theme::ActiveTheme; use crate::theme::ActiveTheme;
const SHADOW_SIZE: Pixels = Pixels(12.0); #[cfg(not(target_os = "linux"))]
pub(crate) const SHADOW_SIZE: Pixels = Pixels(0.0);
#[cfg(target_os = "linux")]
pub(crate) const SHADOW_SIZE: Pixels = Pixels(12.0);
const BORDER_SIZE: Pixels = Pixels(1.0); const BORDER_SIZE: Pixels = Pixels(1.0);
pub(crate) const BORDER_RADIUS: Pixels = Pixels(0.0); pub(crate) const BORDER_RADIUS: Pixels = Pixels(0.0);