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