From 8e525c6c990de870ea69415bc1cfdfcbcbb77228 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Tue, 19 Aug 2025 11:41:12 +0800 Subject: [PATCH] modal: Improve paddings and gap in Modal. (#1153) --- crates/story/src/modal_story.rs | 68 ++++++++++++++++---------------- crates/ui/src/modal.rs | 70 +++++++++++++++++++-------------- 2 files changed, 73 insertions(+), 65 deletions(-) diff --git a/crates/story/src/modal_story.rs b/crates/story/src/modal_story.rs index 5d9057ed..0dd53939 100644 --- a/crates/story/src/modal_story.rs +++ b/crates/story/src/modal_story.rs @@ -1,6 +1,6 @@ use gpui::{ - div, prelude::FluentBuilder as _, px, App, AppContext, Context, Entity, FocusHandle, Focusable, - InteractiveElement as _, IntoElement, ParentElement, Render, SharedString, Styled, Window, + div, px, App, AppContext, Context, Entity, FocusHandle, Focusable, InteractiveElement as _, + IntoElement, ParentElement, Render, SharedString, Styled, Window, }; use gpui_component::{ @@ -12,7 +12,7 @@ use gpui_component::{ input::{InputState, TextInput}, modal::ModalButtonProps, text::TextView, - v_flex, ActiveTheme, ContextModal as _, + v_flex, ActiveTheme, ContextModal as _, Icon, IconName, }; use crate::{section, TestAction}; @@ -26,7 +26,6 @@ pub struct ModalStory { dropdown: Entity>>, modal_overlay: bool, model_show_close: bool, - model_padding: bool, model_keyboard: bool, overlay_closable: bool, } @@ -78,7 +77,6 @@ impl ModalStory { dropdown, modal_overlay: true, model_show_close: true, - model_padding: true, model_keyboard: true, overlay_closable: true, } @@ -87,7 +85,6 @@ impl ModalStory { fn show_modal(&mut self, window: &mut Window, cx: &mut Context) { let overlay = self.modal_overlay; let modal_show_close = self.model_show_close; - let modal_padding = self.model_padding; let overlay_closable = self.overlay_closable; let input1 = self.input1.clone(); let date = self.date.clone(); @@ -102,7 +99,6 @@ impl ModalStory { .keyboard(keyboard) .show_close(modal_show_close) .overlay_closable(overlay_closable) - .when(!modal_padding, |this| this.p(px(0.))) .child( v_flex() .gap_3() @@ -148,7 +144,6 @@ impl ModalStory { .keyboard(keyboard) .show_close(modal_show_close) .overlay_closable(overlay_closable) - .when(!modal_padding, |this| this.p(px(0.))) }); }, ), @@ -225,15 +220,6 @@ impl Render for ModalStory { cx.notify(); })), ) - .child( - Checkbox::new("modal-padding") - .label("Model Padding") - .checked(self.model_padding) - .on_click(cx.listener(|view, _, _, cx| { - view.model_padding = !view.model_padding; - cx.notify(); - })), - ) .child( Checkbox::new("modal-keyboard") .label("Keyboard") @@ -307,31 +293,34 @@ impl Render for ModalStory { .outline() .label("Custom Buttons") .on_click(cx.listener(move |_, _, window, cx| { - window.open_modal(cx, move |modal, _, _| { + window.open_modal(cx, move |modal, _, cx| { modal .rounded_lg() - .p_3() .confirm() .overlay(modal_overlay) .overlay_closable(overlay_closable) - .child("Are you sure to delete this item?") + .child( + h_flex().gap_3() + .child(Icon::new(IconName::TriangleAlert).size_6().text_color(cx.theme().warning)) + .child("Update successful, we need to restart the application.") + ) .button_props( ModalButtonProps::default() - .cancel_text("Abort") + .cancel_text("Later") .cancel_variant(ButtonVariant::Secondary) - .ok_text("Sure") + .ok_text("Restart Now") .ok_variant(ButtonVariant::Danger), ) .on_ok(|_, window, cx| { window.push_notification( - "You have pressed sure.", + "You have pressed restart.", cx, ); true }) .on_cancel(|_, window, cx| { window.push_notification( - "You have pressed abort.", + "You have pressed later.", cx, ); true @@ -382,7 +371,22 @@ impl Render for ModalStory { ), ) .child( - section("Custom Modal style").child( + section("Custom Paddings").child( + Button::new("custom-modal-paddings") + .outline() + .label("Custom Paddings") + .on_click(cx.listener(move |_, _, window, cx| { + window.open_modal(cx, move |modal, _, _| { + modal + .p_3() + .title("Custom Modal Title") + .child("This is a custom modal content, we can use paddings to control the layout and spacing within the modal.") + }); + })), + ), + ) + .child( + section("Custom Style").child( Button::new("custom-modal-style") .outline() .label("Custom Modal Style") @@ -390,16 +394,10 @@ impl Render for ModalStory { window.open_modal(cx, move |modal, _, cx| { modal .rounded_lg() - .p_0() - .title(div().pt_4().px_4().child("Custom Modal Title")) - .child( - div() - .bg(cx.theme().info) - .text_color(cx.theme().info_foreground) - .p_4() - .rounded_b_lg() - .child("This is a custom modal content."), - ) + .bg(cx.theme().cyan) + .text_color(cx.theme().info_foreground) + .title("Custom Modal Title") + .child("This is a custom modal content.") }); })), ), diff --git a/crates/ui/src/modal.rs b/crates/ui/src/modal.rs index 31778645..b507597f 100644 --- a/crates/ui/src/modal.rs +++ b/crates/ui/src/modal.rs @@ -2,7 +2,7 @@ use std::{rc::Rc, time::Duration}; use gpui::{ anchored, div, hsla, point, prelude::FluentBuilder, px, relative, Animation, AnimationExt as _, - AnyElement, App, Axis, Bounds, BoxShadow, ClickEvent, Div, FocusHandle, Hsla, + AnyElement, App, Axis, Bounds, BoxShadow, ClickEvent, Div, Edges, FocusHandle, Hsla, InteractiveElement, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString, StyleRefinement, Styled, Window, }; @@ -354,13 +354,21 @@ impl RenderOnce for Modal { let y = self.margin_top.unwrap_or(view_size.height / 10.) + offset_top; let x = bounds.center().x - self.width / 2.; - let mut padding_right = px(24.); - let mut padding_left = px(24.); + let base_size = window.text_style().font_size; + let rem_size = window.rem_size(); + + let mut paddings = Edges::all(px(24.)); if let Some(pl) = self.style.padding.left { - padding_left = pl.to_pixels(self.width.into(), window.rem_size()); + paddings.left = pl.to_pixels(base_size, rem_size); } if let Some(pr) = self.style.padding.right { - padding_right = pr.to_pixels(self.width.into(), window.rem_size()); + paddings.right = pr.to_pixels(base_size, rem_size); + } + if let Some(pt) = self.style.padding.top { + paddings.top = pt.to_pixels(base_size, rem_size); + } + if let Some(pb) = self.style.padding.bottom { + paddings.bottom = pb.to_pixels(base_size, rem_size); } let animation = Animation::new(Duration::from_secs_f64(0.25)) @@ -401,8 +409,9 @@ impl RenderOnce for Modal { .border_color(cx.theme().border) .rounded(cx.theme().radius_lg) .min_h_24() - .py_6() - .gap_4() + .pt(paddings.top) + .pb(paddings.bottom) + .gap(paddings.top.min(px(16.))) .refine_style(&self.style) .px_0() .key_context(CONTEXT) @@ -448,46 +457,47 @@ impl RenderOnce for Modal { .when_some(self.title, |this, title| { this.child( div() - .font_semibold() - .pl(padding_left) - .pr(padding_right) + .pl(paddings.left) + .pr(paddings.right) .line_height(relative(1.)) + .font_semibold() .child(title), ) }) - .when(self.show_close, |this| { - this.child( - Button::new("close") - .absolute() - .top_4() - .right_4() - .small() - .ghost() - .icon(IconName::Close) - .on_click(move |_, window, cx| { + .children(self.show_close.then(|| { + Button::new("close") + .absolute() + .top(paddings.top - px(3.)) + .right(paddings.right - px(3.)) + .small() + .ghost() + .icon(IconName::Close) + .on_click({ + let on_cancel = self.on_cancel.clone(); + let on_close = self.on_close.clone(); + move |_, window, cx| { on_cancel(&ClickEvent::default(), window, cx); on_close(&ClickEvent::default(), window, cx); window.close_modal(cx); - }), - ) - }) + } + }) + })) .child( div().w_full().flex_1().overflow_hidden().child( v_flex() - .pl(padding_left) - .pr(padding_right) + .pl(paddings.left) + .pr(paddings.right) .scrollable(Axis::Vertical) .child(self.content), ), ) - .when(self.footer.is_some(), |this| { - let footer = self.footer.unwrap(); - + .when_some(self.footer, |this, footer| { this.child( h_flex() .gap_2() - .pl(padding_left) - .pr(padding_right) + .pl(paddings.left) + .pr(paddings.right) + .line_height(relative(1.)) .justify_end() .children(footer(render_ok, render_cancel, window, cx)), )