freya_components/
popup.rs1use freya_animation::prelude::*;
2use freya_core::prelude::*;
3use torin::{
4 prelude::{
5 Alignment,
6 Position,
7 },
8 size::Size,
9};
10
11use crate::{
12 get_theme,
13 theming::component_themes::{
14 PopupTheme,
15 PopupThemePartial,
16 },
17};
18
19#[derive(Clone, PartialEq)]
21pub struct PopupBackground {
22 pub children: Element,
23 pub on_press: EventHandler<Event<PressEventData>>,
24 pub background: Color,
25}
26
27impl PopupBackground {
28 pub fn new(
29 children: Element,
30 on_press: impl Into<EventHandler<Event<PressEventData>>>,
31 background: Color,
32 ) -> Self {
33 Self {
34 children,
35 on_press: on_press.into(),
36 background,
37 }
38 }
39}
40
41impl Component for PopupBackground {
42 fn render(&self) -> impl IntoElement {
43 let on_press = self.on_press.clone();
44
45 rect()
46 .child(
47 rect()
48 .on_press(on_press)
49 .position(Position::new_global().top(0.).left(0.))
50 .height(Size::window_percent(100.))
51 .width(Size::window_percent(100.))
52 .background(self.background),
53 )
54 .child(
55 rect()
56 .position(Position::new_global().top(0.).left(0.))
57 .height(Size::window_percent(100.))
58 .width(Size::window_percent(100.))
59 .center()
60 .child(self.children.clone()),
61 )
62 }
63}
64
65#[doc(alias = "alert")]
111#[doc(alias = "dialog")]
112#[doc(alias = "window")]
113#[cfg_attr(feature = "docs",
114 doc = embed_doc_image::embed_image!("popup", "images/gallery_popup.png"),
115)]
116#[derive(Clone, PartialEq)]
117pub struct Popup {
118 pub(crate) theme: Option<PopupThemePartial>,
119 children: Vec<Element>,
120 show: Readable<bool>,
121 on_close_request: Option<EventHandler<()>>,
122 close_on_escape_key: bool,
123 width: Size,
124 key: DiffKey,
125}
126
127impl KeyExt for Popup {
128 fn write_key(&mut self) -> &mut DiffKey {
129 &mut self.key
130 }
131}
132
133impl Default for Popup {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139impl Popup {
140 pub fn new() -> Self {
141 Self {
142 theme: None,
143 children: vec![],
144 show: true.into(),
145 on_close_request: None,
146 close_on_escape_key: true,
147 width: Size::px(500.),
148 key: DiffKey::None,
149 }
150 }
151
152 pub fn show(mut self, show: impl Into<Readable<bool>>) -> Self {
153 self.show = show.into();
154 self
155 }
156
157 pub fn on_close_request(mut self, on_close_request: impl Into<EventHandler<()>>) -> Self {
158 self.on_close_request = Some(on_close_request.into());
159 self
160 }
161
162 pub fn width(mut self, width: impl Into<Size>) -> Self {
163 self.width = width.into();
164 self
165 }
166}
167
168impl ChildrenExt for Popup {
169 fn get_children(&mut self) -> &mut Vec<Element> {
170 &mut self.children
171 }
172}
173
174impl Component for Popup {
175 fn render(&self) -> impl IntoElement {
176 let show = *self.show.read();
177
178 let bg_animation = use_animation_with_dependencies(&show, |conf, show| {
179 conf.on_creation(OnCreation::Finish);
180 conf.on_change(OnChange::Rerun);
181
182 let value = AnimColor::new((0, 0, 0, 0), (0, 0, 0, 150)).time(150);
183
184 if *show { value } else { value.into_reversed() }
185 });
186
187 let content_animation = use_animation_with_dependencies(&show, |conf, _| {
189 conf.on_creation(OnCreation::Finish);
190 conf.on_change(OnChange::Rerun);
191
192 (
193 AnimNum::new(0.85, 1.)
194 .time(250)
195 .ease(Ease::Out)
196 .function(Function::Expo),
197 AnimNum::new(0.2, 1.)
198 .time(250)
199 .ease(Ease::Out)
200 .function(Function::Expo),
201 )
202 });
203
204 let should_render = show || *bg_animation.is_running().read();
205
206 let PopupTheme { background, color } = get_theme!(&self.theme, popup);
207
208 let request_to_close = {
209 let handler = self.on_close_request.clone();
210 move || {
211 if let Some(h) = &handler {
212 h.call(());
213 }
214 }
215 };
216
217 let on_global_key_down = {
218 let close = self.close_on_escape_key;
219 let req = request_to_close.clone();
220 move |e: Event<KeyboardEventData>| {
221 if close && e.key == Key::Named(NamedKey::Escape) {
222 req();
223 }
224 }
225 };
226
227 rect()
228 .layer(Layer::Overlay)
229 .position(Position::new_global())
230 .maybe_child(should_render.then(|| {
231 let bg_color = bg_animation.get().value();
232
233 let (scale, opacity) = &*content_animation.read();
234
235 let (scale, opacity) = if show {
236 (scale.value(), opacity.value())
237 } else {
238 (1., 0.)
239 };
240
241 PopupBackground::new(
242 rect()
243 .a11y_role(AccessibilityRole::Dialog)
244 .scale((scale, scale))
245 .opacity(opacity)
246 .corner_radius(12.)
247 .background(background)
248 .color(color)
249 .shadow(Shadow::new().y(4.).blur(5.).color((0, 0, 0, 30)))
250 .width(self.width.clone())
251 .height(Size::auto())
252 .spacing(4.)
253 .padding(8.)
254 .on_global_key_down(on_global_key_down)
255 .children(self.children.clone())
256 .into(),
257 move |_| {
258 request_to_close();
259 },
260 bg_color,
261 )
262 }))
263 }
264
265 fn render_key(&self) -> DiffKey {
266 self.key.clone().or(self.default_key())
267 }
268}
269
270#[derive(PartialEq)]
272pub struct PopupTitle {
273 text: Readable<String>,
274}
275
276impl PopupTitle {
277 pub fn new(text: impl Into<Readable<String>>) -> Self {
278 Self { text: text.into() }
279 }
280}
281
282impl Component for PopupTitle {
283 fn render(&self) -> impl IntoElement {
284 rect().font_size(18.).padding(8.).child(
285 label()
286 .a11y_role(AccessibilityRole::TitleBar)
287 .width(Size::fill())
288 .text(self.text.read().to_string()),
289 )
290 }
291}
292
293#[derive(Clone, PartialEq)]
295pub struct PopupContent {
296 children: Vec<Element>,
297}
298impl Default for PopupContent {
299 fn default() -> Self {
300 Self::new()
301 }
302}
303
304impl PopupContent {
305 pub fn new() -> Self {
306 Self { children: vec![] }
307 }
308}
309
310impl ChildrenExt for PopupContent {
311 fn get_children(&mut self) -> &mut Vec<Element> {
312 &mut self.children
313 }
314}
315
316impl Component for PopupContent {
317 fn render(&self) -> impl IntoElement {
318 rect()
319 .font_size(15.)
320 .padding(8.)
321 .children(self.children.clone())
322 }
323}
324
325#[derive(Clone, PartialEq)]
327pub struct PopupButtons {
328 pub children: Vec<Element>,
329}
330
331impl Default for PopupButtons {
332 fn default() -> Self {
333 Self::new()
334 }
335}
336
337impl PopupButtons {
338 pub fn new() -> Self {
339 Self { children: vec![] }
340 }
341}
342
343impl ChildrenExt for PopupButtons {
344 fn get_children(&mut self) -> &mut Vec<Element> {
345 &mut self.children
346 }
347}
348
349impl Component for PopupButtons {
350 fn render(&self) -> impl IntoElement {
351 rect()
352 .width(Size::fill())
353 .main_align(Alignment::End)
354 .padding(8.)
355 .spacing(4.)
356 .horizontal()
357 .children(self.children.clone())
358 }
359}