1use crate::{
2 Area, Color32, Context, Frame, Id, InnerResponse, Order, Response, Sense, Ui, UiBuilder, UiKind,
3};
4use emath::{Align2, Vec2};
5
6pub struct Modal {
13 pub area: Area,
14 pub backdrop_color: Color32,
15 pub frame: Option<Frame>,
16}
17
18impl Modal {
19 pub fn new(id: Id) -> Self {
21 Self {
22 area: Self::default_area(id),
23 backdrop_color: Color32::from_black_alpha(100),
24 frame: None,
25 }
26 }
27
28 pub fn default_area(id: Id) -> Area {
34 Area::new(id)
35 .kind(UiKind::Modal)
36 .sense(Sense::hover())
37 .anchor(Align2::CENTER_CENTER, Vec2::ZERO)
38 .order(Order::Foreground)
39 .interactable(true)
40 }
41
42 #[inline]
46 pub fn frame(mut self, frame: Frame) -> Self {
47 self.frame = Some(frame);
48 self
49 }
50
51 #[inline]
55 pub fn backdrop_color(mut self, color: Color32) -> Self {
56 self.backdrop_color = color;
57 self
58 }
59
60 #[inline]
64 pub fn area(mut self, area: Area) -> Self {
65 self.area = area;
66 self
67 }
68
69 pub fn show<T>(self, ctx: &Context, content: impl FnOnce(&mut Ui) -> T) -> ModalResponse<T> {
71 let Self {
72 area,
73 backdrop_color,
74 frame,
75 } = self;
76
77 let (is_top_modal, any_popup_open) = ctx.memory_mut(|mem| {
78 mem.set_modal_layer(area.layer());
79 (
80 mem.top_modal_layer() == Some(area.layer()),
81 mem.any_popup_open(),
82 )
83 });
84 let InnerResponse {
85 inner: (inner, backdrop_response),
86 response,
87 } = area.show(ctx, |ui| {
88 let bg_rect = ui.ctx().screen_rect();
89 let bg_sense = Sense::CLICK | Sense::DRAG;
90 let mut backdrop = ui.new_child(UiBuilder::new().sense(bg_sense).max_rect(bg_rect));
91 backdrop.set_min_size(bg_rect.size());
92 ui.painter().rect_filled(bg_rect, 0.0, backdrop_color);
93 let backdrop_response = backdrop.response();
94
95 let frame = frame.unwrap_or_else(|| Frame::popup(ui.style()));
96
97 let inner = ui
100 .scope_builder(UiBuilder::new().sense(Sense::CLICK | Sense::DRAG), |ui| {
101 frame.show(ui, content).inner
102 })
103 .inner;
104
105 (inner, backdrop_response)
106 });
107
108 ModalResponse {
109 response,
110 backdrop_response,
111 inner,
112 is_top_modal,
113 any_popup_open,
114 }
115 }
116}
117
118pub struct ModalResponse<T> {
120 pub response: Response,
122
123 pub backdrop_response: Response,
128
129 pub inner: T,
131
132 pub is_top_modal: bool,
134
135 pub any_popup_open: bool,
139}
140
141impl<T> ModalResponse<T> {
142 pub fn should_close(&self) -> bool {
147 let ctx = &self.response.ctx;
148
149 let escape_clicked =
151 || ctx.input_mut(|i| i.consume_key(crate::Modifiers::NONE, crate::Key::Escape));
152
153 self.backdrop_response.clicked()
154 || (self.is_top_modal && !self.any_popup_open && escape_clicked())
155 }
156}