dialog: Rename Modal to Dialog. (#1538)
## Break Change - Renamed `Modal` to `Dialog`. ```diff - window.open_modal(...) + window.open_dialog(...) - window.close_modal(...); + window.close_dialog(...); ``` - Renamed `show_close` method to `close_button` in Dialog. ```diff - .show_close(false) + .close_button(true) ```
This commit is contained in:
parent
c78811c4b9
commit
945db0be56
30 changed files with 540 additions and 580 deletions
|
|
@ -36,7 +36,7 @@
|
|||
"default": null
|
||||
},
|
||||
"radius.lg": {
|
||||
"description": "The border radius for large elements like Modals and Notifications, default is 8.",
|
||||
"description": "The border radius for large elements like Dialogs and Notifications, default is 8.",
|
||||
"type": ["number", "null"],
|
||||
"default": null
|
||||
},
|
||||
|
|
|
|||
18
Cargo.lock
generated
18
Cargo.lock
generated
|
|
@ -1603,6 +1603,15 @@ version = "1.6.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04"
|
||||
|
||||
[[package]]
|
||||
name = "dialog_overlay"
|
||||
version = "0.4.0-preview1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gpui",
|
||||
"gpui-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
|
|
@ -4367,15 +4376,6 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "modal_overlay"
|
||||
version = "0.4.0-preview1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gpui",
|
||||
"gpui-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "25.0.1"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ members = [
|
|||
"examples/hello_world",
|
||||
"examples/input",
|
||||
"examples/window_title",
|
||||
"examples/modal_overlay",
|
||||
"examples/dialog_overlay",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use gpui_component::{
|
|||
use serde::Deserialize;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use story::{
|
||||
AccordionStory, AppState, AppTitleBar, Assets, ButtonStory, CalendarStory, FormStory,
|
||||
IconStory, ImageStory, InputStory, LabelStory, ListStory, ModalStory, NotificationStory, Open,
|
||||
AccordionStory, AppState, AppTitleBar, Assets, ButtonStory, CalendarStory, DialogStory,
|
||||
FormStory, IconStory, ImageStory, InputStory, LabelStory, ListStory, NotificationStory, Open,
|
||||
PopoverStory, ProgressStory, ResizableStory, ScrollableStory, SelectStory, SidebarStory,
|
||||
StoryContainer, SwitchStory, TableStory, TooltipStory, WebViewStory,
|
||||
};
|
||||
|
|
@ -138,11 +138,11 @@ impl StoryWorkspace {
|
|||
Box::new(TogglePanelVisible(SharedString::from("Sidebar"))),
|
||||
)
|
||||
.menu_with_check(
|
||||
"Modal",
|
||||
"Dialog",
|
||||
!invisible_panels
|
||||
.read(cx)
|
||||
.contains(&SharedString::from("Modal")),
|
||||
Box::new(TogglePanelVisible(SharedString::from("Modal"))),
|
||||
.contains(&SharedString::from("Dialog")),
|
||||
Box::new(TogglePanelVisible(SharedString::from("Dialog"))),
|
||||
)
|
||||
.menu_with_check(
|
||||
"Accordion",
|
||||
|
|
@ -347,7 +347,7 @@ impl StoryWorkspace {
|
|||
Arc::new(StoryContainer::panel::<InputStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<SelectStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<LabelStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<ModalStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<DialogStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<PopoverStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<SwitchStory>(window, cx)),
|
||||
Arc::new(StoryContainer::panel::<ProgressStory>(window, cx)),
|
||||
|
|
@ -435,7 +435,7 @@ impl StoryWorkspace {
|
|||
1 => Arc::new(StoryContainer::panel::<InputStory>(window, cx)),
|
||||
2 => Arc::new(StoryContainer::panel::<SelectStory>(window, cx)),
|
||||
3 => Arc::new(StoryContainer::panel::<LabelStory>(window, cx)),
|
||||
4 => Arc::new(StoryContainer::panel::<ModalStory>(window, cx)),
|
||||
4 => Arc::new(StoryContainer::panel::<DialogStory>(window, cx)),
|
||||
5 => Arc::new(StoryContainer::panel::<PopoverStory>(window, cx)),
|
||||
6 => Arc::new(StoryContainer::panel::<SwitchStory>(window, cx)),
|
||||
7 => Arc::new(StoryContainer::panel::<ProgressStory>(window, cx)),
|
||||
|
|
@ -507,7 +507,7 @@ pub fn open_new(
|
|||
impl Render for StoryWorkspace {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let sheet_layer = Root::render_sheet_layer(window, cx);
|
||||
let modal_layer = Root::render_modal_layer(window, cx);
|
||||
let dialog_layer = Root::render_dialog_layer(window, cx);
|
||||
let notification_layer = Root::render_notification_layer(window, cx);
|
||||
|
||||
div()
|
||||
|
|
@ -522,7 +522,7 @@ impl Render for StoryWorkspace {
|
|||
.child(self.title_bar.clone())
|
||||
.child(self.dock_area.clone())
|
||||
.children(sheet_layer)
|
||||
.children(modal_layer)
|
||||
.children(dialog_layer)
|
||||
.children(notification_layer)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -692,7 +692,7 @@ impl Example {
|
|||
let editor = self.editor.clone();
|
||||
let input_state = self.go_to_line_state.clone();
|
||||
|
||||
window.open_modal(cx, move |modal, window, cx| {
|
||||
window.open_dialog(cx, move |dialog, window, cx| {
|
||||
input_state.update(cx, |state, cx| {
|
||||
let cursor_pos = editor.read(cx).cursor_position();
|
||||
state.set_placeholder(
|
||||
|
|
@ -703,7 +703,7 @@ impl Example {
|
|||
state.focus(window, cx);
|
||||
});
|
||||
|
||||
modal
|
||||
dialog
|
||||
.title("Go to line")
|
||||
.child(Input::new(&input_state))
|
||||
.confirm()
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ impl Example {
|
|||
let editor = self.editor.clone();
|
||||
let input_state = self.go_to_line_state.clone();
|
||||
|
||||
window.open_modal(cx, move |modal, window, cx| {
|
||||
window.open_dialog(cx, move |dialog, window, cx| {
|
||||
input_state.update(cx, |state, cx| {
|
||||
let position = editor.read(cx).cursor_position();
|
||||
state.set_placeholder(
|
||||
|
|
@ -64,7 +64,7 @@ impl Example {
|
|||
state.focus(window, cx);
|
||||
});
|
||||
|
||||
modal
|
||||
dialog
|
||||
.title("Go to line")
|
||||
.child(Input::new(&input_state))
|
||||
.confirm()
|
||||
|
|
|
|||
|
|
@ -406,7 +406,7 @@ pub fn open_new(
|
|||
impl Render for StoryTiles {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let sheet_layer = Root::render_sheet_layer(window, cx);
|
||||
let modal_layer = Root::render_modal_layer(window, cx);
|
||||
let dialog_layer = Root::render_dialog_layer(window, cx);
|
||||
let notification_layer = Root::render_notification_layer(window, cx);
|
||||
|
||||
div()
|
||||
|
|
@ -420,7 +420,7 @@ impl Render for StoryTiles {
|
|||
.child(TitleBar::new().child(div().flex().items_center().child("Story Tiles")))
|
||||
.child(self.dock_area.clone())
|
||||
.children(sheet_layer)
|
||||
.children(modal_layer)
|
||||
.children(dialog_layer)
|
||||
.children(notification_layer)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ use gpui_component::{
|
|||
button::{Button, ButtonVariant, ButtonVariants as _},
|
||||
checkbox::Checkbox,
|
||||
date_picker::{DatePicker, DatePickerState},
|
||||
dialog::DialogButtonProps,
|
||||
h_flex,
|
||||
input::{Input, InputState},
|
||||
modal::ModalButtonProps,
|
||||
select::{Select, SelectState},
|
||||
text::TextView,
|
||||
v_flex,
|
||||
|
|
@ -18,26 +18,26 @@ use gpui_component::{
|
|||
|
||||
use crate::{TestAction, section};
|
||||
|
||||
pub struct ModalStory {
|
||||
pub struct DialogStory {
|
||||
focus_handle: FocusHandle,
|
||||
selected_value: Option<SharedString>,
|
||||
input1: Entity<InputState>,
|
||||
input2: Entity<InputState>,
|
||||
date: Entity<DatePickerState>,
|
||||
select: Entity<SelectState<Vec<String>>>,
|
||||
modal_overlay: bool,
|
||||
model_show_close: bool,
|
||||
model_keyboard: bool,
|
||||
dialog_overlay: bool,
|
||||
close_button: bool,
|
||||
keyboard: bool,
|
||||
overlay_closable: bool,
|
||||
}
|
||||
|
||||
impl super::Story for ModalStory {
|
||||
impl super::Story for DialogStory {
|
||||
fn title() -> &'static str {
|
||||
"Modal"
|
||||
"Dialog"
|
||||
}
|
||||
|
||||
fn description() -> &'static str {
|
||||
"A modal dialog"
|
||||
"A dialog dialog"
|
||||
}
|
||||
|
||||
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render> {
|
||||
|
|
@ -45,7 +45,7 @@ impl super::Story for ModalStory {
|
|||
}
|
||||
}
|
||||
|
||||
impl ModalStory {
|
||||
impl DialogStory {
|
||||
pub fn view(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||
cx.new(|cx| Self::new(window, cx))
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ impl ModalStory {
|
|||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let input1 = cx.new(|cx| InputState::new(window, cx).placeholder("Your Name"));
|
||||
let input2 = cx.new(|cx| {
|
||||
InputState::new(window, cx).placeholder("For test focus back on modal close.")
|
||||
InputState::new(window, cx).placeholder("For test focus back on dialog close.")
|
||||
});
|
||||
let date = cx.new(|cx| DatePickerState::new(window, cx));
|
||||
let select = cx.new(|cx| {
|
||||
|
|
@ -76,34 +76,34 @@ impl ModalStory {
|
|||
input2,
|
||||
date,
|
||||
select,
|
||||
modal_overlay: true,
|
||||
model_show_close: true,
|
||||
model_keyboard: true,
|
||||
dialog_overlay: true,
|
||||
close_button: true,
|
||||
keyboard: true,
|
||||
overlay_closable: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn show_modal(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let overlay = self.modal_overlay;
|
||||
let modal_show_close = self.model_show_close;
|
||||
fn show_dialog(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let overlay = self.dialog_overlay;
|
||||
let dialog_close_button = self.close_button;
|
||||
let overlay_closable = self.overlay_closable;
|
||||
let input1 = self.input1.clone();
|
||||
let date = self.date.clone();
|
||||
let select = self.select.clone();
|
||||
let view = cx.entity().clone();
|
||||
let keyboard = self.model_keyboard;
|
||||
let keyboard = self.keyboard;
|
||||
|
||||
window.open_modal(cx, move |modal, _, _| {
|
||||
modal
|
||||
.title("Form Modal")
|
||||
window.open_dialog(cx, move |dialog, _, _| {
|
||||
dialog
|
||||
.title("Form Dialog")
|
||||
.overlay(overlay)
|
||||
.keyboard(keyboard)
|
||||
.show_close(modal_show_close)
|
||||
.close_button(dialog_close_button)
|
||||
.overlay_closable(overlay_closable)
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_3()
|
||||
.child("This is a modal dialog.")
|
||||
.child("This is a dialog dialog.")
|
||||
.child("You can put anything here.")
|
||||
.child(Input::new(&input1))
|
||||
.child(Select::new(&select))
|
||||
|
|
@ -120,7 +120,7 @@ impl ModalStory {
|
|||
let input1 = input1.clone();
|
||||
let date = date.clone();
|
||||
move |_, window, cx| {
|
||||
window.close_modal(cx);
|
||||
window.close_dialog(cx);
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
view.selected_value = Some(
|
||||
|
|
@ -134,24 +134,24 @@ impl ModalStory {
|
|||
});
|
||||
}
|
||||
}),
|
||||
Button::new("new-modal").label("Open Other Modal").on_click(
|
||||
move |_, window, cx| {
|
||||
window.open_modal(cx, move |modal, _, _| {
|
||||
modal
|
||||
.title("Other Modal")
|
||||
.child("This is another modal.")
|
||||
Button::new("new-dialog")
|
||||
.label("Open Other Dialog")
|
||||
.on_click(move |_, window, cx| {
|
||||
window.open_dialog(cx, move |dialog, _, _| {
|
||||
dialog
|
||||
.title("Other Dialog")
|
||||
.child("This is another dialog.")
|
||||
.min_h(px(100.))
|
||||
.overlay(overlay)
|
||||
.keyboard(keyboard)
|
||||
.show_close(modal_show_close)
|
||||
.close_button(dialog_close_button)
|
||||
.overlay_closable(overlay_closable)
|
||||
});
|
||||
},
|
||||
),
|
||||
}),
|
||||
Button::new("cancel")
|
||||
.label("Cancel")
|
||||
.on_click(move |_, window, cx| {
|
||||
window.close_modal(cx);
|
||||
window.close_dialog(cx);
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
|
@ -171,19 +171,19 @@ impl ModalStory {
|
|||
}
|
||||
}
|
||||
|
||||
impl Focusable for ModalStory {
|
||||
impl Focusable for DialogStory {
|
||||
fn focus_handle(&self, _cx: &gpui::App) -> FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ModalStory {
|
||||
impl Render for DialogStory {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let modal_overlay = self.modal_overlay;
|
||||
let dialog_overlay = self.dialog_overlay;
|
||||
let overlay_closable = self.overlay_closable;
|
||||
|
||||
div()
|
||||
.id("modal-story")
|
||||
.id("dialog-story")
|
||||
.track_focus(&self.focus_handle)
|
||||
.on_action(cx.listener(Self::on_action_test_action))
|
||||
.size_full()
|
||||
|
|
@ -195,11 +195,11 @@ impl Render for ModalStory {
|
|||
.items_center()
|
||||
.gap_3()
|
||||
.child(
|
||||
Checkbox::new("modal-overlay")
|
||||
.label("Modal Overlay")
|
||||
.checked(self.modal_overlay)
|
||||
Checkbox::new("dialog-overlay")
|
||||
.label("Dialog Overlay")
|
||||
.checked(self.dialog_overlay)
|
||||
.on_click(cx.listener(|view, _, _, cx| {
|
||||
view.modal_overlay = !view.modal_overlay;
|
||||
view.dialog_overlay = !view.dialog_overlay;
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
|
|
@ -213,31 +213,31 @@ impl Render for ModalStory {
|
|||
})),
|
||||
)
|
||||
.child(
|
||||
Checkbox::new("modal-show-close")
|
||||
Checkbox::new("dialog-show-close")
|
||||
.label("Model Close Button")
|
||||
.checked(self.model_show_close)
|
||||
.checked(self.close_button)
|
||||
.on_click(cx.listener(|view, _, _, cx| {
|
||||
view.model_show_close = !view.model_show_close;
|
||||
view.close_button = !view.close_button;
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Checkbox::new("modal-keyboard")
|
||||
Checkbox::new("dialog-keyboard")
|
||||
.label("Keyboard")
|
||||
.checked(self.model_keyboard)
|
||||
.checked(self.keyboard)
|
||||
.on_click(cx.listener(|view, _, _, cx| {
|
||||
view.model_keyboard = !view.model_keyboard;
|
||||
view.keyboard = !view.keyboard;
|
||||
cx.notify();
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
section("Normal Modal").child(
|
||||
Button::new("show-modal")
|
||||
section("Normal Dialog").child(
|
||||
Button::new("show-dialog")
|
||||
.outline()
|
||||
.label("Open Modal")
|
||||
.label("Open Dialog")
|
||||
.on_click(
|
||||
cx.listener(|this, _, window, cx| this.show_modal(window, cx)),
|
||||
cx.listener(|this, _, window, cx| this.show_dialog(window, cx)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -255,21 +255,21 @@ impl Render for ModalStory {
|
|||
})
|
||||
.tooltip(
|
||||
"This button for test dispatch action, \
|
||||
to make sure when Modal close,\
|
||||
to make sure when Dialog close,\
|
||||
\nthis still can handle the action.",
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
section("Confirm Modal").child(
|
||||
Button::new("confirm-modal0")
|
||||
section("Confirm Dialog").child(
|
||||
Button::new("confirm-dialog0")
|
||||
.outline()
|
||||
.label("Open Confirm Modal")
|
||||
.label("Open Confirm Dialog")
|
||||
.on_click(cx.listener(move |_, _, window, cx| {
|
||||
window.open_modal(cx, move |modal, _, _| {
|
||||
modal
|
||||
window.open_dialog(cx, move |dialog, _, _| {
|
||||
dialog
|
||||
.confirm()
|
||||
.overlay(modal_overlay)
|
||||
.overlay(dialog_overlay)
|
||||
.overlay_closable(overlay_closable)
|
||||
.child("Are you sure to submit?")
|
||||
.on_ok(|_, window, cx| {
|
||||
|
|
@ -289,16 +289,16 @@ impl Render for ModalStory {
|
|||
),
|
||||
)
|
||||
.child(
|
||||
section("Confirm Modal with custom buttons").child(
|
||||
Button::new("confirm-modal1")
|
||||
section("Confirm Dialog with custom buttons").child(
|
||||
Button::new("confirm-dialog1")
|
||||
.outline()
|
||||
.label("Custom Buttons")
|
||||
.on_click(cx.listener(move |_, _, window, cx| {
|
||||
window.open_modal(cx, move |modal, _, cx| {
|
||||
modal
|
||||
window.open_dialog(cx, move |dialog, _, cx| {
|
||||
dialog
|
||||
.rounded_lg()
|
||||
.confirm()
|
||||
.overlay(modal_overlay)
|
||||
.overlay(dialog_overlay)
|
||||
.overlay_closable(overlay_closable)
|
||||
.child(
|
||||
h_flex().gap_3()
|
||||
|
|
@ -306,7 +306,7 @@ impl Render for ModalStory {
|
|||
.child("Update successful, we need to restart the application.")
|
||||
)
|
||||
.button_props(
|
||||
ModalButtonProps::default()
|
||||
DialogButtonProps::default()
|
||||
.cancel_text("Later")
|
||||
.cancel_variant(ButtonVariant::Secondary)
|
||||
.ok_text("Restart Now")
|
||||
|
|
@ -331,15 +331,15 @@ impl Render for ModalStory {
|
|||
),
|
||||
)
|
||||
.child(
|
||||
section("Alert Modal").child(
|
||||
Button::new("alert-modal")
|
||||
section("Alert Dialog").child(
|
||||
Button::new("alert-dialog")
|
||||
.outline()
|
||||
.label("Alert")
|
||||
.on_click(cx.listener(move |_, _, window, cx| {
|
||||
window.open_modal(cx, move |modal, _, _| {
|
||||
modal
|
||||
window.open_dialog(cx, move |dialog, _, _| {
|
||||
dialog
|
||||
.confirm()
|
||||
.overlay(modal_overlay)
|
||||
.overlay(dialog_overlay)
|
||||
.overlay_closable(overlay_closable)
|
||||
.child("You are successfully logged in.")
|
||||
.alert()
|
||||
|
|
@ -352,17 +352,17 @@ impl Render for ModalStory {
|
|||
),
|
||||
)
|
||||
.child(
|
||||
section("Scrollable Modal").child(
|
||||
Button::new("scrollable-modal")
|
||||
section("Scrollable Dialog").child(
|
||||
Button::new("scrollable-dialog")
|
||||
.outline()
|
||||
.label("Scrollable Modal")
|
||||
.label("Scrollable Dialog")
|
||||
.on_click(cx.listener(move |_, _, window, cx| {
|
||||
window.open_modal(cx, move |modal, window, cx| {
|
||||
modal
|
||||
window.open_dialog(cx, move |dialog, window, cx| {
|
||||
dialog
|
||||
.h(px(450.))
|
||||
.overlay(modal_overlay)
|
||||
.overlay(dialog_overlay)
|
||||
.overlay_closable(overlay_closable)
|
||||
.title("Modal with scrollbar")
|
||||
.title("Dialog with scrollbar")
|
||||
.child(TextView::markdown(
|
||||
"markdown1",
|
||||
include_str!("../../../README.md"),
|
||||
|
|
@ -375,32 +375,32 @@ impl Render for ModalStory {
|
|||
)
|
||||
.child(
|
||||
section("Custom Paddings").child(
|
||||
Button::new("custom-modal-paddings")
|
||||
Button::new("custom-dialog-paddings")
|
||||
.outline()
|
||||
.label("Custom Paddings")
|
||||
.on_click(cx.listener(move |_, _, window, cx| {
|
||||
window.open_modal(cx, move |modal, _, _| {
|
||||
modal
|
||||
window.open_dialog(cx, move |dialog, _, _| {
|
||||
dialog
|
||||
.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.")
|
||||
.title("Custom Dialog Title")
|
||||
.child("This is a custom dialog content, we can use paddings to control the layout and spacing within the dialog.")
|
||||
});
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
section("Custom Style").child(
|
||||
Button::new("custom-modal-style")
|
||||
Button::new("custom-dialog-style")
|
||||
.outline()
|
||||
.label("Custom Modal Style")
|
||||
.label("Custom Dialog Style")
|
||||
.on_click(cx.listener(move |_, _, window, cx| {
|
||||
window.open_modal(cx, move |modal, _, cx| {
|
||||
modal
|
||||
window.open_dialog(cx, move |dialog, _, cx| {
|
||||
dialog
|
||||
.rounded_lg()
|
||||
.bg(cx.theme().cyan)
|
||||
.text_color(cx.theme().info_foreground)
|
||||
.title("Custom Modal Title")
|
||||
.child("This is a custom modal content.")
|
||||
.title("Custom Dialog Title")
|
||||
.child("This is a custom dialog content.")
|
||||
});
|
||||
})),
|
||||
),
|
||||
|
|
@ -13,6 +13,7 @@ mod collapsible_story;
|
|||
mod color_picker_story;
|
||||
mod date_picker_story;
|
||||
mod description_list_story;
|
||||
mod dialog_story;
|
||||
mod form_story;
|
||||
mod group_box_story;
|
||||
mod icon_story;
|
||||
|
|
@ -22,7 +23,6 @@ mod kbd_story;
|
|||
mod label_story;
|
||||
mod list_story;
|
||||
mod menu_story;
|
||||
mod modal_story;
|
||||
mod notification_story;
|
||||
mod number_input_story;
|
||||
mod otp_input_story;
|
||||
|
|
@ -73,6 +73,7 @@ pub use collapsible_story::CollapsibleStory;
|
|||
pub use color_picker_story::ColorPickerStory;
|
||||
pub use date_picker_story::DatePickerStory;
|
||||
pub use description_list_story::DescriptionListStory;
|
||||
pub use dialog_story::DialogStory;
|
||||
pub use form_story::FormStory;
|
||||
pub use group_box_story::GroupBoxStory;
|
||||
pub use icon_story::IconStory;
|
||||
|
|
@ -82,7 +83,6 @@ pub use kbd_story::KbdStory;
|
|||
pub use label_story::LabelStory;
|
||||
pub use list_story::ListStory;
|
||||
pub use menu_story::MenuStory;
|
||||
pub use modal_story::ModalStory;
|
||||
pub use notification_story::NotificationStory;
|
||||
pub use number_input_story::NumberInputStory;
|
||||
pub use otp_input_story::OtpInputStory;
|
||||
|
|
@ -263,7 +263,7 @@ impl StoryRoot {
|
|||
impl Render for StoryRoot {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let sheet_layer = Root::render_sheet_layer(window, cx);
|
||||
let modal_layer = Root::render_modal_layer(window, cx);
|
||||
let dialog_layer = Root::render_dialog_layer(window, cx);
|
||||
let notification_layer = Root::render_notification_layer(window, cx);
|
||||
|
||||
div()
|
||||
|
|
@ -275,7 +275,7 @@ impl Render for StoryRoot {
|
|||
.child(div().flex_1().overflow_hidden().child(self.view.clone())),
|
||||
)
|
||||
.children(sheet_layer)
|
||||
.children(modal_layer)
|
||||
.children(dialog_layer)
|
||||
.children(notification_layer)
|
||||
}
|
||||
}
|
||||
|
|
@ -666,7 +666,7 @@ impl StoryState {
|
|||
"ImageStory" => story!(ImageStory),
|
||||
"InputStory" => story!(InputStory),
|
||||
"ListStory" => story!(ListStory),
|
||||
"ModalStory" => story!(ModalStory),
|
||||
"DialogStory" => story!(DialogStory),
|
||||
"PopoverStory" => story!(PopoverStory),
|
||||
"ProgressStory" => story!(ProgressStory),
|
||||
"ResizableStory" => story!(ResizableStory),
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ impl Gallery {
|
|||
StoryContainer::panel::<ColorPickerStory>(window, cx),
|
||||
StoryContainer::panel::<DatePickerStory>(window, cx),
|
||||
StoryContainer::panel::<DescriptionListStory>(window, cx),
|
||||
StoryContainer::panel::<DialogStory>(window, cx),
|
||||
StoryContainer::panel::<FormStory>(window, cx),
|
||||
StoryContainer::panel::<GroupBoxStory>(window, cx),
|
||||
StoryContainer::panel::<IconStory>(window, cx),
|
||||
|
|
@ -58,7 +59,6 @@ impl Gallery {
|
|||
StoryContainer::panel::<LabelStory>(window, cx),
|
||||
StoryContainer::panel::<ListStory>(window, cx),
|
||||
StoryContainer::panel::<MenuStory>(window, cx),
|
||||
StoryContainer::panel::<ModalStory>(window, cx),
|
||||
StoryContainer::panel::<NotificationStory>(window, cx),
|
||||
StoryContainer::panel::<NumberInputStory>(window, cx),
|
||||
StoryContainer::panel::<OtpInputStory>(window, cx),
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ impl SheetStory {
|
|||
|
||||
let input1 = cx.new(|cx| InputState::new(window, cx).placeholder("Your Name"));
|
||||
let input2 = cx.new(|cx| {
|
||||
InputState::new(window, cx).placeholder("For test focus back on modal close.")
|
||||
InputState::new(window, cx).placeholder("For test focus back on dialog close.")
|
||||
});
|
||||
let date = cx.new(|cx| DatePickerState::new(window, cx));
|
||||
|
||||
|
|
@ -424,7 +424,7 @@ impl Render for SheetStory {
|
|||
})
|
||||
.tooltip(
|
||||
"This button for test dispatch action, \
|
||||
to make sure when Modal close,\
|
||||
to make sure when Dialog close,\
|
||||
\nthis still can handle the action.",
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ Dock:
|
|||
zh-CN: 展开
|
||||
zh-HK: 展開
|
||||
it: Espandi
|
||||
Modal:
|
||||
Dialog:
|
||||
ok:
|
||||
en: OK
|
||||
zh-CN: 确定
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
h_flex, v_flex, ActiveTheme as _, IconName, Root, Sizable as _, StyledExt, WindowExt as _,
|
||||
};
|
||||
|
||||
const CONTEXT: &str = "Modal";
|
||||
const CONTEXT: &str = "Dialog";
|
||||
pub(crate) fn init(cx: &mut App) {
|
||||
cx.bind_keys([
|
||||
KeyBinding::new("escape", Cancel, Some(CONTEXT)),
|
||||
|
|
@ -27,15 +27,15 @@ type RenderButtonFn = Box<dyn FnOnce(&mut Window, &mut App) -> AnyElement>;
|
|||
type FooterFn =
|
||||
Box<dyn Fn(RenderButtonFn, RenderButtonFn, &mut Window, &mut App) -> Vec<AnyElement>>;
|
||||
|
||||
/// Modal button props.
|
||||
pub struct ModalButtonProps {
|
||||
/// Dialog button props.
|
||||
pub struct DialogButtonProps {
|
||||
ok_text: Option<SharedString>,
|
||||
ok_variant: ButtonVariant,
|
||||
cancel_text: Option<SharedString>,
|
||||
cancel_variant: ButtonVariant,
|
||||
}
|
||||
|
||||
impl Default for ModalButtonProps {
|
||||
impl Default for DialogButtonProps {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ok_text: None,
|
||||
|
|
@ -46,7 +46,7 @@ impl Default for ModalButtonProps {
|
|||
}
|
||||
}
|
||||
|
||||
impl ModalButtonProps {
|
||||
impl DialogButtonProps {
|
||||
/// Sets the text of the OK button. Default is `OK`.
|
||||
pub fn ok_text(mut self, ok_text: impl Into<SharedString>) -> Self {
|
||||
self.ok_text = Some(ok_text.into());
|
||||
|
|
@ -74,7 +74,7 @@ impl ModalButtonProps {
|
|||
|
||||
/// A modal to display content in a dialog box.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Modal {
|
||||
pub struct Dialog {
|
||||
style: StyleRefinement,
|
||||
title: Option<AnyElement>,
|
||||
footer: Option<FooterFn>,
|
||||
|
|
@ -86,13 +86,13 @@ pub struct Modal {
|
|||
on_close: Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
|
||||
on_ok: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static>>,
|
||||
on_cancel: Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static>,
|
||||
button_props: ModalButtonProps,
|
||||
show_close: bool,
|
||||
button_props: DialogButtonProps,
|
||||
close_button: bool,
|
||||
overlay: bool,
|
||||
overlay_closable: bool,
|
||||
keyboard: bool,
|
||||
|
||||
/// This will be change when open the modal, the focus handle is create when open the modal.
|
||||
/// This will be change when open the dialog, the focus handle is create when open the dialog.
|
||||
pub(crate) focus_handle: FocusHandle,
|
||||
pub(crate) layer_ix: usize,
|
||||
pub(crate) overlay_visible: bool,
|
||||
|
|
@ -106,8 +106,8 @@ pub(crate) fn overlay_color(overlay: bool, cx: &App) -> Hsla {
|
|||
cx.theme().overlay
|
||||
}
|
||||
|
||||
impl Modal {
|
||||
/// Create a new modal.
|
||||
impl Dialog {
|
||||
/// Create a new dialog.
|
||||
pub fn new(_: &mut Window, cx: &mut App) -> Self {
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
|
|
@ -125,19 +125,19 @@ impl Modal {
|
|||
on_close: Rc::new(|_, _, _| {}),
|
||||
on_ok: None,
|
||||
on_cancel: Rc::new(|_, _, _| true),
|
||||
button_props: ModalButtonProps::default(),
|
||||
show_close: true,
|
||||
button_props: DialogButtonProps::default(),
|
||||
close_button: true,
|
||||
overlay_closable: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the title of the modal.
|
||||
/// Sets the title of the dialog.
|
||||
pub fn title(mut self, title: impl IntoElement) -> Self {
|
||||
self.title = Some(title.into_any_element());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the footer of the modal.
|
||||
/// Set the footer of the dialog.
|
||||
///
|
||||
/// The `footer` is a function that takes two `RenderButtonFn` and a `WindowContext` and returns a list of `AnyElement`.
|
||||
///
|
||||
|
|
@ -159,31 +159,31 @@ impl Modal {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set to use confirm modal, with OK and Cancel buttons.
|
||||
/// Set to use confirm dialog, with OK and Cancel buttons.
|
||||
///
|
||||
/// See also [`Self::alert`]
|
||||
pub fn confirm(self) -> Self {
|
||||
self.footer(|ok, cancel, window, cx| vec![cancel(window, cx), ok(window, cx)])
|
||||
.overlay_closable(false)
|
||||
.show_close(false)
|
||||
.close_button(false)
|
||||
}
|
||||
|
||||
/// Set to as a alter modal, with OK button.
|
||||
/// Set to as a alter dialog, with OK button.
|
||||
///
|
||||
/// See also [`Self::confirm`]
|
||||
pub fn alert(self) -> Self {
|
||||
self.footer(|ok, _, window, cx| vec![ok(window, cx)])
|
||||
.overlay_closable(false)
|
||||
.show_close(false)
|
||||
.close_button(false)
|
||||
}
|
||||
|
||||
/// Set the button props of the modal.
|
||||
pub fn button_props(mut self, button_props: ModalButtonProps) -> Self {
|
||||
/// Set the button props of the dialog.
|
||||
pub fn button_props(mut self, button_props: DialogButtonProps) -> Self {
|
||||
self.button_props = button_props;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the callback for when the modal is closed.
|
||||
/// Sets the callback for when the dialog is closed.
|
||||
///
|
||||
/// Called after [`Self::on_ok`] or [`Self::on_cancel`] callback.
|
||||
pub fn on_close(
|
||||
|
|
@ -194,9 +194,9 @@ impl Modal {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the callback for when the modal is has been confirmed.
|
||||
/// Sets the callback for when the dialog is has been confirmed.
|
||||
///
|
||||
/// The callback should return `true` to close the modal, if return `false` the modal will not be closed.
|
||||
/// The callback should return `true` to close the dialog, if return `false` the dialog will not be closed.
|
||||
pub fn on_ok(
|
||||
mut self,
|
||||
on_ok: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static,
|
||||
|
|
@ -205,9 +205,9 @@ impl Modal {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the callback for when the modal is has been canceled.
|
||||
/// Sets the callback for when the dialog is has been canceled.
|
||||
///
|
||||
/// The callback should return `true` to close the modal, if return `false` the modal will not be closed.
|
||||
/// The callback should return `true` to close the dialog, if return `false` the dialog will not be closed.
|
||||
pub fn on_cancel(
|
||||
mut self,
|
||||
on_cancel: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static,
|
||||
|
|
@ -217,44 +217,44 @@ impl Modal {
|
|||
}
|
||||
|
||||
/// Sets the false to hide close icon, default: true
|
||||
pub fn show_close(mut self, show_close: bool) -> Self {
|
||||
self.show_close = show_close;
|
||||
pub fn close_button(mut self, close_button: bool) -> Self {
|
||||
self.close_button = close_button;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the top offset of the modal, defaults to None, will use the 1/10 of the viewport height.
|
||||
/// Set the top offset of the dialog, defaults to None, will use the 1/10 of the viewport height.
|
||||
pub fn margin_top(mut self, margin_top: Pixels) -> Self {
|
||||
self.margin_top = Some(margin_top);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the width of the modal, defaults to 480px.
|
||||
/// Sets the width of the dialog, defaults to 480px.
|
||||
pub fn width(mut self, width: Pixels) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the maximum width of the modal, defaults to `None`.
|
||||
/// Set the maximum width of the dialog, defaults to `None`.
|
||||
pub fn max_w(mut self, max_width: Pixels) -> Self {
|
||||
self.max_width = Some(max_width);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the overlay of the modal, defaults to `true`.
|
||||
/// Set the overlay of the dialog, defaults to `true`.
|
||||
pub fn overlay(mut self, overlay: bool) -> Self {
|
||||
self.overlay = overlay;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the overlay closable of the modal, defaults to `true`.
|
||||
/// Set the overlay closable of the dialog, defaults to `true`.
|
||||
///
|
||||
/// When the overlay is clicked, the modal will be closed.
|
||||
/// When the overlay is clicked, the dialog will be closed.
|
||||
pub fn overlay_closable(mut self, overlay_closable: bool) -> Self {
|
||||
self.overlay_closable = overlay_closable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether to support keyboard esc to close the modal, defaults to `true`.
|
||||
/// Set whether to support keyboard esc to close the dialog, defaults to `true`.
|
||||
pub fn keyboard(mut self, keyboard: bool) -> Self {
|
||||
self.keyboard = keyboard;
|
||||
self
|
||||
|
|
@ -265,19 +265,19 @@ impl Modal {
|
|||
}
|
||||
}
|
||||
|
||||
impl ParentElement for Modal {
|
||||
impl ParentElement for Dialog {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||
self.content.extend(elements);
|
||||
}
|
||||
}
|
||||
|
||||
impl Styled for Modal {
|
||||
impl Styled for Dialog {
|
||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||
&mut self.style
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Modal {
|
||||
impl RenderOnce for Dialog {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl gpui::IntoElement {
|
||||
let layer_ix = self.layer_ix;
|
||||
let on_close = self.on_close.clone();
|
||||
|
|
@ -290,7 +290,7 @@ impl RenderOnce for Modal {
|
|||
let ok_text = self
|
||||
.button_props
|
||||
.ok_text
|
||||
.unwrap_or_else(|| t!("Modal.ok").into());
|
||||
.unwrap_or_else(|| t!("Dialog.ok").into());
|
||||
let ok_variant = self.button_props.ok_variant;
|
||||
move |_, _| {
|
||||
Button::new("ok")
|
||||
|
|
@ -308,7 +308,7 @@ impl RenderOnce for Modal {
|
|||
}
|
||||
|
||||
on_close(&ClickEvent::default(), window, cx);
|
||||
window.close_modal(cx);
|
||||
window.close_dialog(cx);
|
||||
}
|
||||
})
|
||||
.into_any_element()
|
||||
|
|
@ -320,7 +320,7 @@ impl RenderOnce for Modal {
|
|||
let cancel_text = self
|
||||
.button_props
|
||||
.cancel_text
|
||||
.unwrap_or_else(|| t!("Modal.cancel").into());
|
||||
.unwrap_or_else(|| t!("Dialog.cancel").into());
|
||||
let cancel_variant = self.button_props.cancel_variant;
|
||||
move |_, _| {
|
||||
Button::new("cancel")
|
||||
|
|
@ -335,7 +335,7 @@ impl RenderOnce for Modal {
|
|||
}
|
||||
|
||||
on_close(&ClickEvent::default(), window, cx);
|
||||
window.close_modal(cx);
|
||||
window.close_dialog(cx);
|
||||
}
|
||||
})
|
||||
.into_any_element()
|
||||
|
|
@ -381,7 +381,7 @@ impl RenderOnce for Modal {
|
|||
.snap_to_window()
|
||||
.child(
|
||||
div()
|
||||
.id("modal")
|
||||
.id("dialog")
|
||||
.occlude()
|
||||
.w(view_size.width)
|
||||
.h(view_size.height)
|
||||
|
|
@ -389,8 +389,8 @@ impl RenderOnce for Modal {
|
|||
this.bg(overlay_color(self.overlay, cx))
|
||||
})
|
||||
.when(self.overlay, |this| {
|
||||
// Only the last modal owns the `mouse down - close modal` event.
|
||||
if (self.layer_ix + 1) != Root::read(window, cx).active_modals.len() {
|
||||
// Only the last dialog owns the `mouse down - close dialog` event.
|
||||
if (self.layer_ix + 1) != Root::read(window, cx).active_dialogs.len() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +403,7 @@ impl RenderOnce for Modal {
|
|||
if self.overlay_closable && event.button == MouseButton::Left {
|
||||
on_cancel(&ClickEvent::default(), window, cx);
|
||||
on_close(&ClickEvent::default(), window, cx);
|
||||
window.close_modal(cx);
|
||||
window.close_dialog(cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -431,11 +431,11 @@ impl RenderOnce for Modal {
|
|||
move |_: &Cancel, window, cx| {
|
||||
// FIXME:
|
||||
//
|
||||
// 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.
|
||||
// Here some Dialog have no focus_handle, so it will not work will Escape key.
|
||||
// But by now, we `cx.close_dialog()` going to close the last active model, so the Escape is unexpected to work.
|
||||
on_cancel(&ClickEvent::default(), window, cx);
|
||||
on_close(&ClickEvent::default(), window, cx);
|
||||
window.close_modal(cx);
|
||||
window.close_dialog(cx);
|
||||
}
|
||||
})
|
||||
.on_action({
|
||||
|
|
@ -446,10 +446,10 @@ impl RenderOnce for Modal {
|
|||
if let Some(on_ok) = &on_ok {
|
||||
if on_ok(&ClickEvent::default(), window, cx) {
|
||||
on_close(&ClickEvent::default(), window, cx);
|
||||
window.close_modal(cx);
|
||||
window.close_dialog(cx);
|
||||
}
|
||||
} else if has_footer {
|
||||
window.close_modal(cx);
|
||||
window.close_dialog(cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -472,7 +472,7 @@ impl RenderOnce for Modal {
|
|||
.child(title),
|
||||
)
|
||||
})
|
||||
.children(self.show_close.then(|| {
|
||||
.children(self.close_button.then(|| {
|
||||
let top = (paddings.top - px(10.)).max(px(8.));
|
||||
let right = (paddings.right - px(10.)).max(px(8.));
|
||||
|
||||
|
|
@ -489,7 +489,7 @@ impl RenderOnce for Modal {
|
|||
move |_, window, cx| {
|
||||
on_cancel(&ClickEvent::default(), window, cx);
|
||||
on_close(&ClickEvent::default(), window, cx);
|
||||
window.close_modal(cx);
|
||||
window.close_dialog(cx);
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
|
@ -1166,7 +1166,7 @@ impl InputState {
|
|||
self.replace_text_in_range_silent(None, &new_line_text, window, cx);
|
||||
self.pause_blink_cursor(cx);
|
||||
} else {
|
||||
// Single line input, just emit the event (e.g.: In a modal dialog to confirm).
|
||||
// Single line input, just emit the event (e.g.: In a dialog to confirm).
|
||||
cx.propagate();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ pub mod clipboard;
|
|||
pub mod collapsible;
|
||||
pub mod color_picker;
|
||||
pub mod description_list;
|
||||
pub mod dialog;
|
||||
pub mod divider;
|
||||
pub mod dock;
|
||||
pub mod form;
|
||||
|
|
@ -41,7 +42,6 @@ pub mod label;
|
|||
pub mod link;
|
||||
pub mod list;
|
||||
pub mod menu;
|
||||
pub mod modal;
|
||||
pub mod notification;
|
||||
pub mod plot;
|
||||
pub mod popover;
|
||||
|
|
@ -104,7 +104,7 @@ pub fn init(cx: &mut App) {
|
|||
select::init(cx);
|
||||
input::init(cx);
|
||||
list::init(cx);
|
||||
modal::init(cx);
|
||||
dialog::init(cx);
|
||||
popover::init(cx);
|
||||
menu::init(cx);
|
||||
table::init(cx);
|
||||
|
|
|
|||
|
|
@ -418,8 +418,8 @@ impl<M: ManagedView> Element for Popover<M> {
|
|||
.subscribe(
|
||||
&new_content_view,
|
||||
cx,
|
||||
move |modal, _: &DismissEvent, window, cx| {
|
||||
if modal.focus_handle(cx).contains_focused(window, cx) {
|
||||
move |dialog, _: &DismissEvent, window, cx| {
|
||||
if dialog.focus_handle(cx).contains_focused(window, cx) {
|
||||
if let Some(previous_focus_handle) =
|
||||
previous_focus_handle.as_ref()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
dialog::Dialog,
|
||||
input::InputState,
|
||||
modal::Modal,
|
||||
notification::{Notification, NotificationList},
|
||||
sheet::Sheet,
|
||||
window_border, ActiveTheme, Placement,
|
||||
|
|
@ -22,7 +22,7 @@ pub(crate) fn init(cx: &mut App) {
|
|||
]);
|
||||
}
|
||||
|
||||
/// Extension trait for [`Window`] to add modal, sheet .. functionality.
|
||||
/// Extension trait for [`Window`] to add dialog, sheet .. functionality.
|
||||
pub trait WindowExt: Sized {
|
||||
/// Opens a Sheet at right placement.
|
||||
fn open_sheet<F>(&mut self, cx: &mut App, build: F)
|
||||
|
|
@ -40,19 +40,19 @@ pub trait WindowExt: Sized {
|
|||
/// Closes the active Sheet.
|
||||
fn close_sheet(&mut self, cx: &mut App);
|
||||
|
||||
/// Opens a Modal.
|
||||
fn open_modal<F>(&mut self, cx: &mut App, build: F)
|
||||
/// Opens a Dialog.
|
||||
fn open_dialog<F>(&mut self, cx: &mut App, build: F)
|
||||
where
|
||||
F: Fn(Modal, &mut Window, &mut App) -> Modal + 'static;
|
||||
F: Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static;
|
||||
|
||||
/// Return true, if there is an active Modal.
|
||||
fn has_active_modal(&mut self, cx: &mut App) -> bool;
|
||||
/// Return true, if there is an active Dialog.
|
||||
fn has_active_dialog(&mut self, cx: &mut App) -> bool;
|
||||
|
||||
/// Closes the last active Modal.
|
||||
fn close_modal(&mut self, cx: &mut App);
|
||||
/// Closes the last active Dialog.
|
||||
fn close_dialog(&mut self, cx: &mut App);
|
||||
|
||||
/// Closes all active Modals.
|
||||
fn close_all_modals(&mut self, cx: &mut App);
|
||||
/// Closes all active Dialogs.
|
||||
fn close_all_dialogs(&mut self, cx: &mut App);
|
||||
|
||||
/// Pushes a notification to the notification list.
|
||||
fn push_notification(&mut self, note: impl Into<Notification>, cx: &mut App);
|
||||
|
|
@ -114,21 +114,21 @@ impl WindowExt for Window {
|
|||
})
|
||||
}
|
||||
|
||||
fn open_modal<F>(&mut self, cx: &mut App, build: F)
|
||||
fn open_dialog<F>(&mut self, cx: &mut App, build: F)
|
||||
where
|
||||
F: Fn(Modal, &mut Window, &mut App) -> Modal + 'static,
|
||||
F: Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static,
|
||||
{
|
||||
Root::update(self, cx, move |root, window, cx| {
|
||||
// Only save focus handle if there are no active modals.
|
||||
// This is used to restore focus when all modals are closed.
|
||||
if root.active_modals.len() == 0 {
|
||||
// Only save focus handle if there are no active dialogs.
|
||||
// This is used to restore focus when all dialogs are closed.
|
||||
if root.active_dialogs.len() == 0 {
|
||||
root.previous_focus_handle = window.focused(cx);
|
||||
}
|
||||
|
||||
let focus_handle = cx.focus_handle();
|
||||
focus_handle.focus(window);
|
||||
|
||||
root.active_modals.push(ActiveModal {
|
||||
root.active_dialogs.push(ActiveDialog {
|
||||
focus_handle,
|
||||
builder: Rc::new(build),
|
||||
});
|
||||
|
|
@ -136,30 +136,30 @@ impl WindowExt for Window {
|
|||
})
|
||||
}
|
||||
|
||||
fn has_active_modal(&mut self, cx: &mut App) -> bool {
|
||||
Root::read(self, cx).active_modals.len() > 0
|
||||
fn has_active_dialog(&mut self, cx: &mut App) -> bool {
|
||||
Root::read(self, cx).active_dialogs.len() > 0
|
||||
}
|
||||
|
||||
fn close_modal(&mut self, cx: &mut App) {
|
||||
fn close_dialog(&mut self, cx: &mut App) {
|
||||
Root::update(self, cx, move |root, window, cx| {
|
||||
root.focused_input = None;
|
||||
root.active_modals.pop();
|
||||
root.active_dialogs.pop();
|
||||
|
||||
if let Some(top_modal) = root.active_modals.last() {
|
||||
// Focus the next modal.
|
||||
top_modal.focus_handle.focus(window);
|
||||
if let Some(top_dialog) = root.active_dialogs.last() {
|
||||
// Focus the next dialog.
|
||||
top_dialog.focus_handle.focus(window);
|
||||
} else {
|
||||
// Restore focus if there are no more modals.
|
||||
// Restore focus if there are no more dialogs.
|
||||
root.focus_back(window, cx);
|
||||
}
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
|
||||
fn close_all_modals(&mut self, cx: &mut App) {
|
||||
fn close_all_dialogs(&mut self, cx: &mut App) {
|
||||
Root::update(self, cx, |root, window, cx| {
|
||||
root.focused_input = None;
|
||||
root.active_modals.clear();
|
||||
root.active_dialogs.clear();
|
||||
root.focus_back(window, cx);
|
||||
cx.notify();
|
||||
})
|
||||
|
|
@ -208,13 +208,13 @@ impl WindowExt for Window {
|
|||
|
||||
/// Root is a view for the App window for as the top level view (Must be the first view in the window).
|
||||
///
|
||||
/// It is used to manage the Sheet, Modal, and Notification.
|
||||
/// It is used to manage the Sheet, Dialog, and Notification.
|
||||
pub struct Root {
|
||||
/// Used to store the focus handle of the previous view.
|
||||
/// When the Modal, Sheet closes, we will focus back to the previous view.
|
||||
/// When the Dialog, Sheet closes, we will focus back to the previous view.
|
||||
previous_focus_handle: Option<FocusHandle>,
|
||||
active_sheet: Option<ActiveSheet>,
|
||||
pub(crate) active_modals: Vec<ActiveModal>,
|
||||
pub(crate) active_dialogs: Vec<ActiveDialog>,
|
||||
pub(super) focused_input: Option<Entity<InputState>>,
|
||||
pub notification: Entity<NotificationList>,
|
||||
sheet_size: Option<DefiniteLength>,
|
||||
|
|
@ -229,9 +229,9 @@ struct ActiveSheet {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ActiveModal {
|
||||
pub(crate) struct ActiveDialog {
|
||||
focus_handle: FocusHandle,
|
||||
builder: Rc<dyn Fn(Modal, &mut Window, &mut App) -> Modal + 'static>,
|
||||
builder: Rc<dyn Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static>,
|
||||
}
|
||||
|
||||
impl Root {
|
||||
|
|
@ -239,7 +239,7 @@ impl Root {
|
|||
Self {
|
||||
previous_focus_handle: None,
|
||||
active_sheet: None,
|
||||
active_modals: Vec::new(),
|
||||
active_dialogs: Vec::new(),
|
||||
focused_input: None,
|
||||
notification: cx.new(|cx| NotificationList::new(window, cx)),
|
||||
sheet_size: None,
|
||||
|
|
@ -326,49 +326,49 @@ impl Root {
|
|||
None
|
||||
}
|
||||
|
||||
/// Render the Modal layer.
|
||||
pub fn render_modal_layer(window: &mut Window, cx: &mut App) -> Option<impl IntoElement> {
|
||||
/// Render the Dialog layer.
|
||||
pub fn render_dialog_layer(window: &mut Window, cx: &mut App) -> Option<impl IntoElement> {
|
||||
let root = window.root::<Root>()??;
|
||||
|
||||
let active_modals = root.read(cx).active_modals.clone();
|
||||
let active_dialogs = root.read(cx).active_dialogs.clone();
|
||||
|
||||
if active_modals.is_empty() {
|
||||
if active_dialogs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut show_overlay_ix = None;
|
||||
|
||||
let mut modals = active_modals
|
||||
let mut dialogs = active_dialogs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, active_modal)| {
|
||||
let mut modal = Modal::new(window, cx);
|
||||
.map(|(i, active_dialog)| {
|
||||
let mut dialog = Dialog::new(window, cx);
|
||||
|
||||
modal = (active_modal.builder)(modal, window, cx);
|
||||
dialog = (active_dialog.builder)(dialog, window, cx);
|
||||
|
||||
// Give the modal the focus handle, because `modal` is a temporary value, is not possible to
|
||||
// keep the focus handle in the modal.
|
||||
// Give the dialog the focus handle, because `dialog` is a temporary value, is not possible to
|
||||
// keep the focus handle in the dialog.
|
||||
//
|
||||
// So we keep the focus handle in the `active_modal`, this is owned by the `Root`.
|
||||
modal.focus_handle = active_modal.focus_handle.clone();
|
||||
// So we keep the focus handle in the `active_dialog`, this is owned by the `Root`.
|
||||
dialog.focus_handle = active_dialog.focus_handle.clone();
|
||||
|
||||
modal.layer_ix = i;
|
||||
// Find the modal which one needs to show overlay.
|
||||
if modal.has_overlay() {
|
||||
dialog.layer_ix = i;
|
||||
// Find the dialog which one needs to show overlay.
|
||||
if dialog.has_overlay() {
|
||||
show_overlay_ix = Some(i);
|
||||
}
|
||||
|
||||
modal
|
||||
dialog
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if let Some(ix) = show_overlay_ix {
|
||||
if let Some(modal) = modals.get_mut(ix) {
|
||||
modal.overlay_visible = true;
|
||||
if let Some(dialog) = dialogs.get_mut(ix) {
|
||||
dialog.overlay_visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
Some(div().children(modals))
|
||||
Some(div().children(dialogs))
|
||||
}
|
||||
|
||||
/// Return the root view of the Root.
|
||||
|
|
|
|||
|
|
@ -685,7 +685,7 @@ where
|
|||
}
|
||||
|
||||
fn enter(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context<Self>) {
|
||||
// Propagate the event to the parent view, for example to the Modal to support ENTER to confirm.
|
||||
// Propagate the event to the parent view, for example to the Dialog to support ENTER to confirm.
|
||||
cx.propagate();
|
||||
|
||||
if !self.open {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use gpui::{
|
|||
use crate::{
|
||||
actions::Cancel,
|
||||
button::{Button, ButtonVariants as _},
|
||||
dialog::overlay_color,
|
||||
h_flex,
|
||||
modal::overlay_color,
|
||||
title_bar::TITLE_BAR_HEIGHT,
|
||||
v_flex, ActiveTheme, IconName, Placement, Sizable, StyledExt as _, WindowExt as _,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ pub struct Theme {
|
|||
pub font_size: Pixels,
|
||||
/// Radius for the general elements.
|
||||
pub radius: Pixels,
|
||||
/// Radius for the large elements, e.g.: Modal, Notification border radius.
|
||||
/// Radius for the large elements, e.g.: Dialog, Notification border radius.
|
||||
pub radius_lg: Pixels,
|
||||
pub shadow: bool,
|
||||
pub transparent: Hsla,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub struct ThemeConfig {
|
|||
/// The border radius for general elements, default is 6.
|
||||
#[serde(rename = "radius")]
|
||||
pub radius: Option<usize>,
|
||||
/// The border radius for large elements like Modals and Notifications, default is 8.
|
||||
/// The border radius for large elements like Dialogs and Notifications, default is 8.
|
||||
#[serde(rename = "radius.lg")]
|
||||
pub radius_lg: Option<usize>,
|
||||
/// Set shadows in the theme, for example the Input and Button, default is true.
|
||||
|
|
|
|||
2
crates/ui/tests/fixtures/layout.json
vendored
2
crates/ui/tests/fixtures/layout.json
vendored
|
|
@ -46,7 +46,7 @@
|
|||
"children": [],
|
||||
"info": {
|
||||
"panel": {
|
||||
"story_klass": "ModalStory"
|
||||
"story_klass": "DialogStory"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
287
docs/docs/components/dialog.md
Normal file
287
docs/docs/components/dialog.md
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
---
|
||||
title: Dialog
|
||||
description: A dialog dialog for displaying content in a layer above the app.
|
||||
---
|
||||
|
||||
# Dialog
|
||||
|
||||
Dialog component for creating dialogs, confirmations, and alerts. Supports overlay, keyboard shortcuts, and various customizations.
|
||||
|
||||
## Import
|
||||
|
||||
```rust
|
||||
use gpui_component::dialog::DialogButtonProps;
|
||||
use gpui_component::WindowExt;
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Setup application root view for display of dialogs
|
||||
|
||||
You need to set up your application's root view to render the dialog layer. This is typically done in your main application struct's render method.
|
||||
|
||||
The [Root::render_dialog_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_dialog_layer) function handles rendering any active dialogs on top of your app content.
|
||||
|
||||
```rust
|
||||
use gpui_component::TitleBar;
|
||||
|
||||
struct MyApp {
|
||||
view: AnyView,
|
||||
}
|
||||
|
||||
impl Render for MyApp {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let dialog_layer = Root::render_dialog_layer(window, cx);
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.child(
|
||||
v_flex()
|
||||
.size_full()
|
||||
.child(TitleBar::new())
|
||||
.child(div().flex_1().overflow_hidden().child(self.view.clone())),
|
||||
)
|
||||
// Render the dialog layer on top of the app content
|
||||
.children(dialog_layer)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Basic Dialog
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.title("Welcome")
|
||||
.child("This is a dialog dialog.")
|
||||
})
|
||||
```
|
||||
|
||||
### Form Dialog
|
||||
|
||||
```rust
|
||||
let input = cx.new(|cx| InputState::new(window, cx));
|
||||
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.title("User Information")
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_3()
|
||||
.child("Please enter your details:")
|
||||
.child(Input::new(&input))
|
||||
)
|
||||
.footer(|_, _, _, _| {
|
||||
vec![
|
||||
Button::new("ok")
|
||||
.primary()
|
||||
.label("Submit")
|
||||
.on_click(|_, window, cx| {
|
||||
window.close_dialog(cx);
|
||||
}),
|
||||
Button::new("cancel")
|
||||
.label("Cancel")
|
||||
.on_click(|_, window, cx| {
|
||||
window.close_dialog(cx);
|
||||
}),
|
||||
]
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Confirm Dialog
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.confirm()
|
||||
.child("Are you sure you want to delete this item?")
|
||||
.on_ok(|_, window, cx| {
|
||||
window.push_notification("Item deleted", cx);
|
||||
true // Return true to close dialog
|
||||
})
|
||||
.on_cancel(|_, window, cx| {
|
||||
window.push_notification("Cancelled", cx);
|
||||
true
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Alert Dialog
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.alert()
|
||||
.child("Operation completed successfully!")
|
||||
.on_close(|_, window, cx| {
|
||||
window.push_notification("Alert closed", cx);
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Button Labels
|
||||
|
||||
```rust
|
||||
use gpui_component::button::ButtonVariant;
|
||||
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.confirm()
|
||||
.child("Update available. Restart now?")
|
||||
.button_props(
|
||||
DialogButtonProps::default()
|
||||
.cancel_text("Later")
|
||||
.cancel_variant(ButtonVariant::Secondary)
|
||||
.ok_text("Restart Now")
|
||||
.ok_variant(ButtonVariant::Danger)
|
||||
)
|
||||
.on_ok(|_, window, cx| {
|
||||
window.push_notification("Restarting...", cx);
|
||||
true
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Dialog with Icon
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, _, cx| {
|
||||
dialog
|
||||
.confirm()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_3()
|
||||
.child(Icon::new(IconName::TriangleAlert)
|
||||
.size_6()
|
||||
.text_color(cx.theme().warning))
|
||||
.child("This action cannot be undone.")
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
### Scrollable Dialog
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, window, cx| {
|
||||
dialog
|
||||
.h(px(450.))
|
||||
.title("Long Content")
|
||||
.child(TextView::markdown("content", long_markdown_text, window, cx))
|
||||
})
|
||||
```
|
||||
|
||||
### Dialog Options
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.title("Custom Dialog")
|
||||
.overlay(true) // Show overlay (default: true)
|
||||
.overlay_closable(true) // Click overlay to close (default: true)
|
||||
.keyboard(true) // ESC to close (default: true)
|
||||
.close_button(false) // Show close button (default: true)
|
||||
.child("Dialog content")
|
||||
})
|
||||
```
|
||||
|
||||
### Nested Dialogs
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.title("First Dialog")
|
||||
.child("This is the first dialog")
|
||||
.footer(|_, _, _, _| {
|
||||
vec![
|
||||
Button::new("open-another")
|
||||
.label("Open Another Dialog")
|
||||
.on_click(|_, window, cx| {
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.title("Second Dialog")
|
||||
.child("This is nested")
|
||||
});
|
||||
}),
|
||||
]
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Styling
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, _, cx| {
|
||||
dialog
|
||||
.rounded_lg()
|
||||
.bg(cx.theme().cyan)
|
||||
.text_color(cx.theme().info_foreground)
|
||||
.title("Custom Style")
|
||||
.child("Styled dialog content")
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Padding
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.p_3() // Custom padding
|
||||
.title("Custom Padding")
|
||||
.child("Dialog with custom spacing")
|
||||
})
|
||||
```
|
||||
|
||||
### Close Dialog Programmatically
|
||||
|
||||
The `close_dialog` method can be used to close the active dialog from anywhere within the window context.
|
||||
|
||||
```rust
|
||||
// Close top level active dialog.
|
||||
window.close_dialog(cx);
|
||||
|
||||
// Close and perform action
|
||||
Button::new("submit")
|
||||
.primary()
|
||||
.label("Submit")
|
||||
.on_click(|_, window, cx| {
|
||||
// Do something
|
||||
window.close_dialog(cx);
|
||||
})
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Delete Confirmation
|
||||
|
||||
```rust
|
||||
Button::new("delete")
|
||||
.danger()
|
||||
.label("Delete")
|
||||
.on_click(|_, window, cx| {
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.confirm()
|
||||
.child("Are you sure you want to delete this item?")
|
||||
.on_ok(|_, window, cx| {
|
||||
// Perform delete
|
||||
window.push_notification("Deleted", cx);
|
||||
true
|
||||
})
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Success Alert
|
||||
|
||||
```rust
|
||||
window.open_dialog(cx, |dialog, _, _| {
|
||||
dialog
|
||||
.confirm()
|
||||
.alert()
|
||||
.child("Your changes have been saved successfully!")
|
||||
.on_close(|_, _, _| {
|
||||
// Optional close handler
|
||||
})
|
||||
})
|
||||
```
|
||||
|
|
@ -320,6 +320,6 @@ GroupBox::new()
|
|||
## Related Components
|
||||
|
||||
- **Form**: Use GroupBox within forms to organize sections
|
||||
- **Modal**: GroupBox works well within modal dialogs for organizing content
|
||||
- **Dialog**: GroupBox works well within dialogs for organizing content
|
||||
- **Accordion**: For collapsible grouped content, consider using Accordion instead
|
||||
- **Card**: For elevated content containers with more visual weight
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ collapsed: false
|
|||
|
||||
- [DescriptionList](description-list) - Key-value pair display
|
||||
- [GroupBox](group-box) - Grouped content with borders
|
||||
- [Modal](modal) - Dialog and modal windows
|
||||
- [Dialog](dialog) - Dialog and modal windows
|
||||
- [Notification](notification) - Toast notifications
|
||||
- [Popover](popover) - Floating content display
|
||||
- [Resizable](resizable) - Resizable panels and containers
|
||||
|
|
|
|||
|
|
@ -1,327 +0,0 @@
|
|||
---
|
||||
title: Modal
|
||||
description: A modal dialog for displaying content in a layer above the app.
|
||||
---
|
||||
|
||||
# Modal
|
||||
|
||||
Modal component for creating dialogs, confirmations, and alerts. Supports overlay, keyboard shortcuts, and various customizations.
|
||||
|
||||
## Import
|
||||
|
||||
```rust
|
||||
use gpui_component::modal::ModalButtonProps;
|
||||
use gpui_component::WindowExt;
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Setup application root view for display of modals
|
||||
|
||||
You need to set up your application's root view to render the modal layer. This is typically done in your main application struct's render method.
|
||||
|
||||
The [Root::render_modal_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_modal_layer) function handles rendering any active modals on top of your app content.
|
||||
|
||||
```rust
|
||||
use gpui_component::TitleBar;
|
||||
|
||||
struct MyApp {
|
||||
view: AnyView,
|
||||
}
|
||||
|
||||
impl Render for MyApp {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let modal_layer = Root::render_modal_layer(window, cx);
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.child(
|
||||
v_flex()
|
||||
.size_full()
|
||||
.child(TitleBar::new())
|
||||
.child(div().flex_1().overflow_hidden().child(self.view.clone())),
|
||||
)
|
||||
// Render the modal layer on top of the app content
|
||||
.children(modal_layer)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Basic Modal
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.title("Welcome")
|
||||
.child("This is a modal dialog.")
|
||||
})
|
||||
```
|
||||
|
||||
### Form Modal
|
||||
|
||||
```rust
|
||||
let input = cx.new(|cx| InputState::new(window, cx));
|
||||
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.title("User Information")
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_3()
|
||||
.child("Please enter your details:")
|
||||
.child(Input::new(&input))
|
||||
)
|
||||
.footer(|_, _, _, _| {
|
||||
vec![
|
||||
Button::new("ok")
|
||||
.primary()
|
||||
.label("Submit")
|
||||
.on_click(|_, window, cx| {
|
||||
window.close_modal(cx);
|
||||
}),
|
||||
Button::new("cancel")
|
||||
.label("Cancel")
|
||||
.on_click(|_, window, cx| {
|
||||
window.close_modal(cx);
|
||||
}),
|
||||
]
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Confirm Modal
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.confirm()
|
||||
.child("Are you sure you want to delete this item?")
|
||||
.on_ok(|_, window, cx| {
|
||||
window.push_notification("Item deleted", cx);
|
||||
true // Return true to close modal
|
||||
})
|
||||
.on_cancel(|_, window, cx| {
|
||||
window.push_notification("Cancelled", cx);
|
||||
true
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Alert Modal
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.confirm()
|
||||
.alert()
|
||||
.child("Operation completed successfully!")
|
||||
.on_close(|_, window, cx| {
|
||||
window.push_notification("Alert closed", cx);
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Button Labels
|
||||
|
||||
```rust
|
||||
use gpui_component::button::ButtonVariant;
|
||||
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.confirm()
|
||||
.child("Update available. Restart now?")
|
||||
.button_props(
|
||||
ModalButtonProps::default()
|
||||
.cancel_text("Later")
|
||||
.cancel_variant(ButtonVariant::Secondary)
|
||||
.ok_text("Restart Now")
|
||||
.ok_variant(ButtonVariant::Danger)
|
||||
)
|
||||
.on_ok(|_, window, cx| {
|
||||
window.push_notification("Restarting...", cx);
|
||||
true
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Modal with Icon
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, _, cx| {
|
||||
modal
|
||||
.confirm()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_3()
|
||||
.child(Icon::new(IconName::TriangleAlert)
|
||||
.size_6()
|
||||
.text_color(cx.theme().warning))
|
||||
.child("This action cannot be undone.")
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
### Scrollable Modal
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, window, cx| {
|
||||
modal
|
||||
.h(px(450.))
|
||||
.title("Long Content")
|
||||
.child(TextView::markdown("content", long_markdown_text, window, cx))
|
||||
})
|
||||
```
|
||||
|
||||
### Modal Options
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.title("Custom Modal")
|
||||
.overlay(true) // Show overlay (default: true)
|
||||
.overlay_closable(true) // Click overlay to close (default: true)
|
||||
.keyboard(true) // ESC to close (default: true)
|
||||
.show_close(true) // Show close button (default: true)
|
||||
.child("Modal content")
|
||||
})
|
||||
```
|
||||
|
||||
### Nested Modals
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.title("First Modal")
|
||||
.child("This is the first modal")
|
||||
.footer(|_, _, _, _| {
|
||||
vec![
|
||||
Button::new("open-another")
|
||||
.label("Open Another Modal")
|
||||
.on_click(|_, window, cx| {
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.title("Second Modal")
|
||||
.child("This is nested")
|
||||
});
|
||||
}),
|
||||
]
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Styling
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, _, cx| {
|
||||
modal
|
||||
.rounded_lg()
|
||||
.bg(cx.theme().cyan)
|
||||
.text_color(cx.theme().info_foreground)
|
||||
.title("Custom Style")
|
||||
.child("Styled modal content")
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Padding
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.p_3() // Custom padding
|
||||
.title("Custom Padding")
|
||||
.child("Modal with custom spacing")
|
||||
})
|
||||
```
|
||||
|
||||
### Close Modal Programmatically
|
||||
|
||||
```rust
|
||||
// From inside modal
|
||||
window.close_modal(cx);
|
||||
|
||||
// Close and perform action
|
||||
Button::new("submit")
|
||||
.primary()
|
||||
.label("Submit")
|
||||
.on_click(|_, window, cx| {
|
||||
// Do something
|
||||
window.close_modal(cx);
|
||||
})
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Modal Builder
|
||||
|
||||
| Method | Description |
|
||||
| ------------------------ | ---------------------------------------- |
|
||||
| `title(str)` | Set modal title |
|
||||
| `child(el)` | Add content to modal body |
|
||||
| `footer(fn)` | Set footer with custom buttons |
|
||||
| `overlay(bool)` | Show/hide overlay (default: true) |
|
||||
| `overlay_closable(bool)` | Allow closing by clicking overlay |
|
||||
| `keyboard(bool)` | Allow closing with ESC key |
|
||||
| `show_close(bool)` | Show close button in header |
|
||||
| `confirm()` | Use confirm modal style |
|
||||
| `alert()` | Use alert modal style (single OK button) |
|
||||
| `button_props(props)` | Customize confirm/alert buttons |
|
||||
| `on_ok(fn)` | OK button callback (confirm/alert) |
|
||||
| `on_cancel(fn)` | Cancel button callback (confirm) |
|
||||
| `on_close(fn)` | Close callback (alert) |
|
||||
| `min_h(px)` | Set minimum height |
|
||||
| `h(px)` | Set fixed height |
|
||||
| `rounded_lg()` | Apply large border radius |
|
||||
| `p_*()` | Custom padding |
|
||||
| `bg()` | Custom background |
|
||||
|
||||
### ModalButtonProps
|
||||
|
||||
| Method | Description |
|
||||
| ------------------------- | ----------------------- |
|
||||
| `ok_text(str)` | Text for OK button |
|
||||
| `ok_variant(variant)` | Style for OK button |
|
||||
| `cancel_text(str)` | Text for Cancel button |
|
||||
| `cancel_variant(variant)` | Style for Cancel button |
|
||||
|
||||
### Window Extensions
|
||||
|
||||
| Method | Description |
|
||||
| -------------------- | ------------------- |
|
||||
| `open_modal(cx, fn)` | Open a modal dialog |
|
||||
| `close_modal(cx)` | Close current modal |
|
||||
|
||||
## Examples
|
||||
|
||||
### Delete Confirmation
|
||||
|
||||
```rust
|
||||
Button::new("delete")
|
||||
.danger()
|
||||
.label("Delete")
|
||||
.on_click(|_, window, cx| {
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.confirm()
|
||||
.child("Are you sure you want to delete this item?")
|
||||
.on_ok(|_, window, cx| {
|
||||
// Perform delete
|
||||
window.push_notification("Deleted", cx);
|
||||
true
|
||||
})
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
### Success Alert
|
||||
|
||||
```rust
|
||||
window.open_modal(cx, |modal, _, _| {
|
||||
modal
|
||||
.confirm()
|
||||
.alert()
|
||||
.child("Your changes have been saved successfully!")
|
||||
.on_close(|_, _, _| {
|
||||
// Optional close handler
|
||||
})
|
||||
})
|
||||
```
|
||||
|
|
@ -180,7 +180,7 @@ Explore the component documentation to learn more about each component:
|
|||
|
||||
- [Button](./components/button) - Interactive button component
|
||||
- [Input](./components/input) - Text input with validation
|
||||
- [Modal](./components/modal) - Dialog and modal windows
|
||||
- [Dialog](./components/dialog) - Dialog and modal windows
|
||||
- [Table](./components/table) - High-performance data tables
|
||||
- [More components...](./components/index)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ fn main() {
|
|||
|
||||
## Overlays
|
||||
|
||||
We have modals, drawers, notifications, we need placement for them to show, so [Root] provides methods to render these overlays:
|
||||
We have dialogs, sheets, notifications, we need placement for them to show, so [Root] provides methods to render these overlays:
|
||||
|
||||
- [Root::render_modal_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_modal_layer) - Render the current opened modals.
|
||||
- [Root::render_dialog_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_dialog_layer) - Render the current opened modals.
|
||||
- [Root::render_sheet_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_sheet_layer) - Render the current opened drawers.
|
||||
- [Root::render_notification_layer](https://docs.rs/gpui-component/latest/gpui_component/struct.Root.html#method.render_notification_layer) - Render the notification list.
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ impl Render for MyApp {
|
|||
div()
|
||||
.size_full()
|
||||
.child("My App Content")
|
||||
.children(Root::render_modal_layer(cx))
|
||||
.children(Root::render_dialog_layer(cx))
|
||||
.children(Root::render_sheet_layer(cx))
|
||||
.children(Root::render_notification_layer(cx))
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ impl Render for MyApp {
|
|||
```
|
||||
|
||||
:::tip
|
||||
Here the example we used `children` method, it because if there is no opened modals/drawers/notifications, these methods will return `None`, so GPUI will not render anything.
|
||||
Here the example we used `children` method, it because if there is no opened dialogs, sheets, notifications, these methods will return `None`, so GPUI will not render anything.
|
||||
:::
|
||||
|
||||
[Root]: https://docs.rs/gpui-component/latest/gpui_component/root/struct.Root.html
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
edition = "2021"
|
||||
name = "modal_overlay"
|
||||
description = "An example of using gpui-component to create a modal with overlay."
|
||||
name = "dialog_overlay"
|
||||
description = "An example of using gpui-component to create a Dialog with overlay."
|
||||
publish = false
|
||||
version = "0.4.0-preview1"
|
||||
|
||||
|
|
@ -6,9 +6,9 @@ actions!(class_menu, [Open, Delete, Export, Info]);
|
|||
pub struct HelloWorld;
|
||||
|
||||
impl HelloWorld {
|
||||
fn show_modal(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
|
||||
window.open_modal(cx, move |modal, _, _| {
|
||||
modal.title("Test Modal").child("Hello from Modal!")
|
||||
fn show_dialog(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
|
||||
window.open_dialog(cx, move |dialog, _, _| {
|
||||
dialog.title("Test dialog").child("Hello from dialog!")
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ impl Render for HelloWorld {
|
|||
div()
|
||||
.bg(gpui::white())
|
||||
.size_full()
|
||||
.child(TitleBar::new().child("Modal & Drawer"))
|
||||
.child(TitleBar::new().child("dialog & Drawer"))
|
||||
.child(
|
||||
div()
|
||||
.p_8()
|
||||
|
|
@ -37,8 +37,8 @@ impl Render for HelloWorld {
|
|||
.child(
|
||||
Button::new("btn1")
|
||||
.outline()
|
||||
.label("Open Modal")
|
||||
.on_click(cx.listener(Self::show_modal)),
|
||||
.label("Open dialog")
|
||||
.on_click(cx.listener(Self::show_dialog)),
|
||||
)
|
||||
.child(
|
||||
Button::new("btn2")
|
||||
|
|
@ -72,7 +72,7 @@ impl Render for HelloWorld {
|
|||
}),
|
||||
),
|
||||
)
|
||||
.children(Root::render_modal_layer(window, cx))
|
||||
.children(Root::render_dialog_layer(window, cx))
|
||||
.children(Root::render_sheet_layer(window, cx))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue