1use crate::{
2 makepad_derive_widget::*,
3 makepad_draw::*,
4 makepad_platform::{KeyCode, KeyEvent},
5 view::*,
6 widget::*
7};
8
9live_design!{
10 link widgets;
11 use link::widgets::*;
12 use link::theme::*;
13 use makepad_draw::shader::std::*;
14
15 pub ModalBase = {{Modal}} {}
16 pub Modal = <ModalBase> {
17 width: Fill
18 height: Fill
19 flow: Overlay
20 align: {x: 0.5, y: 0.5}
21
22 draw_bg: {
23 fn pixel(self) -> vec4 {
24 return vec4(0., 0., 0., 0.0)
25 }
26 }
27
28 bg_view: <View> {
29 width: Fill
30 height: Fill
31 show_bg: true
32 draw_bg: {
33 fn pixel(self) -> vec4 {
34 return vec4(0., 0., 0., 0.7)
35 }
36 }
37 }
38
39 content: <View> {
40 flow: Overlay
41 width: Fit
42 height: Fit
43 }
44 }
45}
46
47#[derive(Clone, Debug, DefaultNone)]
48pub enum ModalAction {
49 None,
50 Dismissed,
51}
52
53#[derive(Live, Widget)]
54pub struct Modal {
55 #[live]
56 #[find]
57 content: View,
58 #[live] #[area]
59 bg_view: View,
60
61 #[redraw]
62 #[rust(DrawList2d::new(cx))]
63 draw_list: DrawList2d,
64
65 #[live]
66 draw_bg: DrawQuad,
67 #[layout]
68 layout: Layout,
69 #[walk]
70 walk: Walk,
71
72 #[rust]
73 opened: bool,
74}
75
76impl LiveHook for Modal {
77 fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
78 self.draw_list.redraw(cx);
79 }
80}
81
82impl Widget for Modal {
83 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
84 if !self.opened {
85 return;
86 }
87
88 let area = self.draw_bg.area();
89
90 cx.sweep_unlock(area);
94 self.content.handle_event(cx, event, scope);
95 cx.sweep_lock(area);
96
97 let consumed_hit = event.hits_with_sweep_area(cx, area, area);
100
101 let should_close = event.back_pressed()
106 || matches!(event, Event::KeyUp(KeyEvent { key_code: KeyCode::Escape, .. }))
107 || match consumed_hit {
108 Hit::FingerUp(fe) => !self.content.area().rect(cx).contains(fe.abs),
109 _ => false,
110 };
111 if should_close {
112 let widget_uid = self.content.widget_uid();
113 cx.widget_action(widget_uid, &scope.path, ModalAction::Dismissed);
114 self.close(cx);
115 }
116 }
117
118 fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
119 self.draw_list.begin_overlay_reuse(cx);
120
121 cx.begin_turtle(walk, self.layout);
122 self.draw_bg.begin(cx, self.walk, self.layout);
123
124 if self.opened {
125 let _ = self
126 .bg_view
127 .draw_walk(cx, scope, walk);
128 let _ = self.content.draw_all(cx, scope);
129 }
130
131 self.draw_bg.end(cx);
132 cx.end_turtle();
133 self.draw_list.end(cx);
134 DrawStep::done()
135 }
136}
137
138impl Modal {
139 pub fn open(&mut self, cx: &mut Cx) {
140 self.opened = true;
141 self.draw_bg.redraw(cx);
142 cx.sweep_lock(self.draw_bg.area());
143 }
144
145 pub fn close(&mut self, cx: &mut Cx) {
146 self.content.handle_event(
148 cx,
149 &Event::Actions(vec![Box::new(ModalAction::Dismissed)]),
150 &mut Scope::empty(),
151 );
152 self.opened = false;
153 self.draw_bg.redraw(cx);
154 cx.sweep_unlock(self.draw_bg.area());
155 }
156
157 pub fn dismissed(&self, actions: &Actions) -> bool {
158 matches!(
159 actions.find_widget_action(self.widget_uid()).cast(),
160 ModalAction::Dismissed
161 )
162 }
163}
164
165impl ModalRef {
166 pub fn is_open(&self) -> bool {
167 if let Some(inner) = self.borrow() {
168 inner.opened
169 } else {
170 false
171 }
172 }
173
174 pub fn open(&self, cx: &mut Cx) {
175 if let Some(mut inner) = self.borrow_mut() {
176 inner.open(cx);
177 }
178 }
179
180 pub fn close(&self, cx: &mut Cx) {
181 if let Some(mut inner) = self.borrow_mut() {
182 inner.close(cx);
183 }
184 }
185
186 pub fn dismissed(&self, actions: &Actions) -> bool {
187 if let Some(inner) = self.borrow() {
188 inner.dismissed(actions)
189 } else {
190 false
191 }
192 }
193}