modal: Add confirm Modal with default Ok, Cancel buttons. (#558)
<img width="581" alt="image" src="https://github.com/user-attachments/assets/ad53fb63-f3be-41e6-90c0-f03cba48c4db" /> - Added `overlay_closable` method to Modal to control overlay click to close modal. - Added `on_ok`, `on_cancel` callback method. - Added `confirm` to setup to use default confirm style (With a Ok, Cancel button). ## Break Changes - The `footer` method has been changed to with a fn not a element.
This commit is contained in:
parent
ad04b699fc
commit
20feab5f40
6 changed files with 290 additions and 56 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -3439,7 +3439,7 @@ version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate 1.3.1",
|
"proc-macro-crate 3.2.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.95",
|
"syn 2.0.95",
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use ui::{
|
||||||
h_flex,
|
h_flex,
|
||||||
input::TextInput,
|
input::TextInput,
|
||||||
list::{List, ListDelegate, ListItem},
|
list::{List, ListDelegate, ListItem},
|
||||||
|
modal::ModalButtonProps,
|
||||||
notification::{Notification, NotificationType},
|
notification::{Notification, NotificationType},
|
||||||
theme::ActiveTheme as _,
|
theme::ActiveTheme as _,
|
||||||
v_flex,
|
v_flex,
|
||||||
|
|
@ -358,47 +359,49 @@ impl ModalStory {
|
||||||
.child(dropdown.clone())
|
.child(dropdown.clone())
|
||||||
.child(date_picker.clone()),
|
.child(date_picker.clone()),
|
||||||
)
|
)
|
||||||
.footer(
|
.footer({
|
||||||
h_flex()
|
let view = view.clone();
|
||||||
.gap_6()
|
let input1 = input1.clone();
|
||||||
.items_center()
|
let date_picker = date_picker.clone();
|
||||||
.child(Button::new("confirm").primary().label("Confirm").on_click({
|
move |_, _, _cx| {
|
||||||
let view = view.clone();
|
vec![
|
||||||
let input1 = input1.clone();
|
Button::new("confirm").primary().label("Confirm").on_click({
|
||||||
let date_picker = date_picker.clone();
|
let view = view.clone();
|
||||||
move |_, cx| {
|
let input1 = input1.clone();
|
||||||
cx.close_modal();
|
let date_picker = date_picker.clone();
|
||||||
|
move |_, cx| {
|
||||||
|
cx.close_modal();
|
||||||
|
|
||||||
view.update(cx, |view, cx| {
|
view.update(cx, |view, cx| {
|
||||||
view.selected_value = Some(
|
view.selected_value = Some(
|
||||||
format!(
|
format!(
|
||||||
"Hello, {}, date: {}",
|
"Hello, {}, date: {}",
|
||||||
input1.read(cx).text(),
|
input1.read(cx).text(),
|
||||||
date_picker.read(cx).date()
|
date_picker.read(cx).date()
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
)
|
)
|
||||||
.into(),
|
});
|
||||||
)
|
}
|
||||||
});
|
}),
|
||||||
}
|
Button::new("new-modal").label("Open Other Modal").on_click(
|
||||||
}))
|
move |_, cx| {
|
||||||
.child(Button::new("new-modal").label("Open Other Modal").on_click(
|
cx.open_modal(move |modal, _| {
|
||||||
move |_, cx| {
|
modal
|
||||||
cx.open_modal(move |modal, _| {
|
.title("Other Modal")
|
||||||
modal
|
.child("This is another modal.")
|
||||||
.title("Other Modal")
|
.min_h(px(300.))
|
||||||
.child("This is another modal.")
|
});
|
||||||
.min_h(px(300.))
|
},
|
||||||
});
|
),
|
||||||
},
|
|
||||||
))
|
|
||||||
.child(
|
|
||||||
Button::new("cancel")
|
Button::new("cancel")
|
||||||
.label("Cancel")
|
.label("Cancel")
|
||||||
.on_click(move |_, cx| {
|
.on_click(move |_, cx| {
|
||||||
cx.close_modal();
|
cx.close_modal();
|
||||||
}),
|
}),
|
||||||
),
|
]
|
||||||
)
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
self.input1.focus_handle(cx).focus(cx);
|
self.input1.focus_handle(cx).focus(cx);
|
||||||
|
|
@ -553,9 +556,33 @@ impl Render for ModalStory {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
Button::new("show-modal")
|
h_flex().gap_3().flex_wrap().child(
|
||||||
.label("Open Modal...")
|
Button::new("show-modal")
|
||||||
.on_click(cx.listener(|this, _, cx| this.show_modal(cx))),
|
.label("Open Modal...")
|
||||||
|
.on_click(cx.listener(|this, _, cx| this.show_modal(cx))),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("show-confirm-modal")
|
||||||
|
.label("Confirm Modal...")
|
||||||
|
.on_click(cx.listener(|_, _, cx| {
|
||||||
|
cx.open_modal(|modal, _| {
|
||||||
|
modal
|
||||||
|
.confirm()
|
||||||
|
.child("Are you sure to delete this item?")
|
||||||
|
.button_props(
|
||||||
|
ModalButtonProps::default()
|
||||||
|
.cancel_text("Abort")
|
||||||
|
.cancel_variant(ButtonVariant::Danger))
|
||||||
|
.on_ok(|_, cx| {
|
||||||
|
cx.push_notification("You have pressed Ok.");
|
||||||
|
true
|
||||||
|
}).on_cancel(|_, cx| {
|
||||||
|
cx.push_notification("You have pressed Abort.");
|
||||||
|
true
|
||||||
|
})
|
||||||
|
});
|
||||||
|
})),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
image = "0.25.1"
|
image = "0.25.1"
|
||||||
itertools = "0.13.0"
|
itertools = "0.13.0"
|
||||||
|
|
|
||||||
|
|
@ -111,3 +111,12 @@ Dock:
|
||||||
en: Expand
|
en: Expand
|
||||||
zh-CN: 展开
|
zh-CN: 展开
|
||||||
zh-HK: 展開
|
zh-HK: 展開
|
||||||
|
Modal:
|
||||||
|
ok:
|
||||||
|
en: OK
|
||||||
|
zh-CN: 确定
|
||||||
|
zh-HK: 確定
|
||||||
|
cancel:
|
||||||
|
en: Cancel
|
||||||
|
zh-CN: 取消
|
||||||
|
zh-HK: 取消
|
||||||
|
|
|
||||||
|
|
@ -6,34 +6,91 @@ use gpui::{
|
||||||
InteractiveElement, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point,
|
InteractiveElement, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point,
|
||||||
RenderOnce, SharedString, Styled, WindowContext,
|
RenderOnce, SharedString, Styled, WindowContext,
|
||||||
};
|
};
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
animation::cubic_bezier,
|
animation::cubic_bezier,
|
||||||
button::{Button, ButtonVariants as _},
|
button::{Button, ButtonVariant, ButtonVariants as _},
|
||||||
|
h_flex,
|
||||||
theme::ActiveTheme as _,
|
theme::ActiveTheme as _,
|
||||||
v_flex, ContextModal, IconName, Sizable as _,
|
v_flex, ContextModal, IconName, Sizable as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
actions!(modal, [Escape]);
|
actions!(modal, [Escape, Enter]);
|
||||||
|
|
||||||
const CONTEXT: &str = "Modal";
|
const CONTEXT: &str = "Modal";
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.bind_keys([KeyBinding::new("escape", Escape, Some(CONTEXT))])
|
cx.bind_keys([
|
||||||
|
KeyBinding::new("escape", Escape, Some(CONTEXT)),
|
||||||
|
KeyBinding::new("enter", Enter, Some(CONTEXT)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenderButtonFn = Box<dyn FnOnce(&mut WindowContext) -> AnyElement>;
|
||||||
|
type FooterFn = Box<dyn Fn(RenderButtonFn, RenderButtonFn, &mut WindowContext) -> Vec<AnyElement>>;
|
||||||
|
|
||||||
|
/// Modal button props.
|
||||||
|
pub struct ModalButtonProps {
|
||||||
|
ok_text: Option<SharedString>,
|
||||||
|
ok_variant: ButtonVariant,
|
||||||
|
cancel_text: Option<SharedString>,
|
||||||
|
cancel_variant: ButtonVariant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ModalButtonProps {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
ok_text: None,
|
||||||
|
ok_variant: ButtonVariant::Primary,
|
||||||
|
cancel_text: None,
|
||||||
|
cancel_variant: ButtonVariant::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModalButtonProps {
|
||||||
|
/// 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());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the variant of the OK button. Default is `ButtonVariant::Primary`.
|
||||||
|
pub fn ok_variant(mut self, ok_variant: ButtonVariant) -> Self {
|
||||||
|
self.ok_variant = ok_variant;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the text of the Cancel button. Default is `Cancel`.
|
||||||
|
pub fn cancel_text(mut self, cancel_text: impl Into<SharedString>) -> Self {
|
||||||
|
self.cancel_text = Some(cancel_text.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the variant of the Cancel button. Default is `ButtonVariant::default()`.
|
||||||
|
pub fn cancel_variant(mut self, cancel_variant: ButtonVariant) -> Self {
|
||||||
|
self.cancel_variant = cancel_variant;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct Modal {
|
pub struct Modal {
|
||||||
base: Div,
|
base: Div,
|
||||||
title: Option<AnyElement>,
|
title: Option<AnyElement>,
|
||||||
footer: Option<AnyElement>,
|
footer: Option<FooterFn>,
|
||||||
content: Div,
|
content: Div,
|
||||||
width: Pixels,
|
width: Pixels,
|
||||||
max_width: Option<Pixels>,
|
max_width: Option<Pixels>,
|
||||||
margin_top: Option<Pixels>,
|
margin_top: Option<Pixels>,
|
||||||
|
|
||||||
on_close: Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
|
on_close: Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
|
||||||
|
on_ok: Rc<dyn Fn(&ClickEvent, &mut WindowContext) -> bool + 'static>,
|
||||||
|
on_cancel: Rc<dyn Fn(&ClickEvent, &mut WindowContext) -> bool + 'static>,
|
||||||
|
button_props: ModalButtonProps,
|
||||||
show_close: bool,
|
show_close: bool,
|
||||||
overlay: bool,
|
overlay: bool,
|
||||||
|
overlay_closable: bool,
|
||||||
keyboard: 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 modal, the focus handle is create when open the modal.
|
||||||
|
|
@ -62,7 +119,7 @@ impl Modal {
|
||||||
.border_color(cx.theme().border)
|
.border_color(cx.theme().border)
|
||||||
.rounded_lg()
|
.rounded_lg()
|
||||||
.shadow_xl()
|
.shadow_xl()
|
||||||
.min_h_48()
|
.min_h_24()
|
||||||
.p_4()
|
.p_4()
|
||||||
.gap_4();
|
.gap_4();
|
||||||
|
|
||||||
|
|
@ -80,7 +137,11 @@ impl Modal {
|
||||||
layer_ix: 0,
|
layer_ix: 0,
|
||||||
overlay_visible: true,
|
overlay_visible: true,
|
||||||
on_close: Rc::new(|_, _| {}),
|
on_close: Rc::new(|_, _| {}),
|
||||||
|
on_ok: Rc::new(|_, _| true),
|
||||||
|
on_cancel: Rc::new(|_, _| true),
|
||||||
|
button_props: ModalButtonProps::default(),
|
||||||
show_close: true,
|
show_close: true,
|
||||||
|
overlay_closable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,8 +152,37 @@ impl Modal {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the footer of the modal.
|
/// Set the footer of the modal.
|
||||||
pub fn footer(mut self, footer: impl IntoElement) -> Self {
|
///
|
||||||
self.footer = Some(footer.into_any_element());
|
/// The `footer` is a function that takes two `RenderButtonFn` and a `WindowContext` and returns a list of `AnyElement`.
|
||||||
|
///
|
||||||
|
/// - First `RenderButtonFn` is the render function for the OK button.
|
||||||
|
/// - Second `RenderButtonFn` is the render function for the CANCEL button.
|
||||||
|
///
|
||||||
|
/// When you set the footer, the footer will be placed default footer buttons.
|
||||||
|
pub fn footer<E, F>(mut self, footer: F) -> Self
|
||||||
|
where
|
||||||
|
E: IntoElement,
|
||||||
|
F: Fn(RenderButtonFn, RenderButtonFn, &mut WindowContext) -> Vec<E> + 'static,
|
||||||
|
{
|
||||||
|
self.footer = Some(Box::new(move |ok, cancel, cx| {
|
||||||
|
footer(ok, cancel, cx)
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| e.into_any_element())
|
||||||
|
.collect()
|
||||||
|
}));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set to use confirm modal, with OK and CANCEL buttons.
|
||||||
|
pub fn confirm(self) -> Self {
|
||||||
|
self.footer(|ok, cancel, cx| vec![cancel(cx), ok(cx)])
|
||||||
|
.overlay_closable(false)
|
||||||
|
.show_close(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the button props of the modal.
|
||||||
|
pub fn button_props(mut self, button_props: ModalButtonProps) -> Self {
|
||||||
|
self.button_props = button_props;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +195,28 @@ impl Modal {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the callback for when the modal is has been confirmed.
|
||||||
|
///
|
||||||
|
/// The callback should return `true` to close the modal, if return `false` the modal will not be closed.
|
||||||
|
pub fn on_ok(
|
||||||
|
mut self,
|
||||||
|
on_ok: impl Fn(&ClickEvent, &mut WindowContext) -> bool + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.on_ok = Rc::new(on_ok);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the callback for when the modal is has been canceled.
|
||||||
|
///
|
||||||
|
/// The callback should return `true` to close the modal, if return `false` the modal will not be closed.
|
||||||
|
pub fn on_cancel(
|
||||||
|
mut self,
|
||||||
|
on_cancel: impl Fn(&ClickEvent, &mut WindowContext) -> bool + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.on_cancel = Rc::new(on_cancel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the false to hide close icon, default: true
|
/// Sets the false to hide close icon, default: true
|
||||||
pub fn show_close(mut self, show_close: bool) -> Self {
|
pub fn show_close(mut self, show_close: bool) -> Self {
|
||||||
self.show_close = show_close;
|
self.show_close = show_close;
|
||||||
|
|
@ -135,6 +247,14 @@ impl Modal {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the overlay closable of the modal, defaults to `true`.
|
||||||
|
///
|
||||||
|
/// When the overlay is clicked, the modal 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 modal, defaults to `true`.
|
||||||
pub fn keyboard(mut self, keyboard: bool) -> Self {
|
pub fn keyboard(mut self, keyboard: bool) -> Self {
|
||||||
self.keyboard = keyboard;
|
self.keyboard = keyboard;
|
||||||
|
|
@ -162,6 +282,61 @@ 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 on_ok = self.on_ok.clone();
|
||||||
|
let on_cancel = self.on_cancel.clone();
|
||||||
|
|
||||||
|
let render_ok: RenderButtonFn = Box::new({
|
||||||
|
let on_ok = on_ok.clone();
|
||||||
|
let on_close = on_close.clone();
|
||||||
|
let ok_text = self
|
||||||
|
.button_props
|
||||||
|
.ok_text
|
||||||
|
.unwrap_or_else(|| t!("Modal.ok").into());
|
||||||
|
let ok_variant = self.button_props.ok_variant;
|
||||||
|
move |_| {
|
||||||
|
Button::new("ok")
|
||||||
|
.label(ok_text)
|
||||||
|
.with_variant(ok_variant)
|
||||||
|
.on_click({
|
||||||
|
let on_ok = on_ok.clone();
|
||||||
|
let on_close = on_close.clone();
|
||||||
|
|
||||||
|
move |_, cx| {
|
||||||
|
if on_ok(&ClickEvent::default(), cx) {
|
||||||
|
on_close(&ClickEvent::default(), cx);
|
||||||
|
cx.close_modal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let render_cancel: RenderButtonFn = Box::new({
|
||||||
|
let on_cancel = on_cancel.clone();
|
||||||
|
let on_close = on_close.clone();
|
||||||
|
let cancel_text = self
|
||||||
|
.button_props
|
||||||
|
.cancel_text
|
||||||
|
.unwrap_or_else(|| t!("Modal.cancel").into());
|
||||||
|
let cancel_variant = self.button_props.cancel_variant;
|
||||||
|
move |_| {
|
||||||
|
Button::new("cancel")
|
||||||
|
.label(cancel_text)
|
||||||
|
.with_variant(cancel_variant)
|
||||||
|
.on_click({
|
||||||
|
let on_cancel = on_cancel.clone();
|
||||||
|
let on_close = on_close.clone();
|
||||||
|
move |_, cx| {
|
||||||
|
if on_cancel(&ClickEvent::default(), cx) {
|
||||||
|
on_close(&ClickEvent::default(), cx);
|
||||||
|
cx.close_modal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let window_paddings = crate::window_border::window_paddings(cx);
|
let window_paddings = crate::window_border::window_paddings(cx);
|
||||||
let view_size = cx.viewport_size()
|
let view_size = cx.viewport_size()
|
||||||
- gpui::size(
|
- gpui::size(
|
||||||
|
|
@ -187,12 +362,16 @@ impl RenderOnce for Modal {
|
||||||
.when(self.overlay_visible, |this| {
|
.when(self.overlay_visible, |this| {
|
||||||
this.bg(overlay_color(self.overlay, cx))
|
this.bg(overlay_color(self.overlay, cx))
|
||||||
})
|
})
|
||||||
.on_mouse_down(MouseButton::Left, {
|
.when(self.overlay_closable, |this| {
|
||||||
let on_close = self.on_close.clone();
|
this.on_mouse_down(MouseButton::Left, {
|
||||||
move |_, cx| {
|
let on_cancel = on_cancel.clone();
|
||||||
on_close(&ClickEvent::default(), cx);
|
let on_close = on_close.clone();
|
||||||
cx.close_modal();
|
move |_, cx| {
|
||||||
}
|
on_cancel(&ClickEvent::default(), cx);
|
||||||
|
on_close(&ClickEvent::default(), cx);
|
||||||
|
cx.close_modal();
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
self.base
|
self.base
|
||||||
|
|
@ -201,16 +380,28 @@ impl RenderOnce for Modal {
|
||||||
.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_cancel = on_cancel.clone();
|
||||||
|
let on_close = 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_cancel(&ClickEvent::default(), cx);
|
||||||
on_close(&ClickEvent::default(), cx);
|
on_close(&ClickEvent::default(), cx);
|
||||||
cx.close_modal();
|
cx.close_modal();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.on_action({
|
||||||
|
let on_ok = on_ok.clone();
|
||||||
|
let on_close = on_close.clone();
|
||||||
|
move |_: &Enter, cx| {
|
||||||
|
if on_ok(&ClickEvent::default(), cx) {
|
||||||
|
on_close(&ClickEvent::default(), cx);
|
||||||
|
cx.close_modal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.absolute()
|
.absolute()
|
||||||
.occlude()
|
.occlude()
|
||||||
|
|
@ -235,14 +426,23 @@ impl RenderOnce for Modal {
|
||||||
.icon(IconName::Close)
|
.icon(IconName::Close)
|
||||||
.on_click(
|
.on_click(
|
||||||
move |_, cx| {
|
move |_, cx| {
|
||||||
|
on_cancel(&ClickEvent::default(), cx);
|
||||||
on_close(&ClickEvent::default(), cx);
|
on_close(&ClickEvent::default(), cx);
|
||||||
cx.close_modal();
|
cx.close_modal();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.child(self.content)
|
.child(div().w_full().flex_1().child(self.content))
|
||||||
.children(self.footer)
|
.when(self.footer.is_some(), |this| {
|
||||||
|
let footer = self.footer.unwrap();
|
||||||
|
|
||||||
|
this.child(h_flex().gap_2().justify_end().children(footer(
|
||||||
|
render_ok,
|
||||||
|
render_cancel,
|
||||||
|
cx,
|
||||||
|
)))
|
||||||
|
})
|
||||||
.with_animation(
|
.with_animation(
|
||||||
"slide-down",
|
"slide-down",
|
||||||
Animation::new(Duration::from_secs_f64(0.25))
|
Animation::new(Duration::from_secs_f64(0.25))
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ pub trait ContextModal: Sized {
|
||||||
fn open_modal<F>(&mut self, build: F)
|
fn open_modal<F>(&mut self, build: F)
|
||||||
where
|
where
|
||||||
F: Fn(Modal, &mut WindowContext) -> Modal + 'static;
|
F: Fn(Modal, &mut WindowContext) -> Modal + 'static;
|
||||||
|
|
||||||
/// Return true, if there is an active Modal.
|
/// Return true, if there is an active Modal.
|
||||||
fn has_active_modal(&self) -> bool;
|
fn has_active_modal(&self) -> bool;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue