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 {
90 click: true,
91 drag: true,
92 focusable: false,
93 };
94 let mut backdrop = ui.new_child(UiBuilder::new().sense(bg_sense).max_rect(bg_rect));
95 backdrop.set_min_size(bg_rect.size());
96 ui.painter().rect_filled(bg_rect, 0.0, backdrop_color);
97 let backdrop_response = backdrop.response();
98
99 let frame = frame.unwrap_or_else(|| Frame::popup(ui.style()));
100
101 let inner = ui
104 .scope_builder(
105 UiBuilder::new().sense(Sense {
106 click: true,
107 drag: true,
108 focusable: false,
109 }),
110 |ui| frame.show(ui, content).inner,
111 )
112 .inner;
113
114 (inner, backdrop_response)
115 });
116
117 ModalResponse {
118 response,
119 backdrop_response,
120 inner,
121 is_top_modal,
122 any_popup_open,
123 }
124 }
125}
126
127pub struct ModalResponse<T> {
129 pub response: Response,
131
132 pub backdrop_response: Response,
137
138 pub inner: T,
140
141 pub is_top_modal: bool,
143
144 pub any_popup_open: bool,
148}
149
150impl<T> ModalResponse<T> {
151 pub fn should_close(&self) -> bool {
156 let ctx = &self.response.ctx;
157
158 let escape_clicked =
160 || ctx.input_mut(|i| i.consume_key(crate::Modifiers::NONE, crate::Key::Escape));
161
162 self.backdrop_response.clicked()
163 || (self.is_top_modal && !self.any_popup_open && escape_clicked())
164 }
165}