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}
25
26impl PopupBackground {
27 pub fn new(
28 children: Element,
29 on_press: impl Into<EventHandler<Event<PressEventData>>>,
30 ) -> Self {
31 Self {
32 children,
33 on_press: on_press.into(),
34 }
35 }
36}
37
38impl Component for PopupBackground {
39 fn render(&self) -> impl IntoElement {
40 let animation = use_animation(|conf| {
41 conf.on_creation(OnCreation::Run);
42 AnimColor::new((0, 0, 0, 0), (0, 0, 0, 150)).time(150)
43 });
44 let background = animation.get().value();
45 let on_press = self.on_press.clone();
46
47 rect()
48 .layer(Layer::Overlay)
49 .position(Position::new_global())
50 .child(
51 rect()
52 .on_press(on_press)
53 .position(Position::new_global().top(0.).left(0.))
54 .height(Size::window_percent(100.))
55 .width(Size::window_percent(100.))
56 .background(background),
57 )
58 .child(
59 rect()
60 .position(Position::new_global().top(0.).left(0.))
61 .height(Size::window_percent(100.))
62 .width(Size::window_percent(100.))
63 .center()
64 .child(self.children.clone()),
65 )
66 }
67}
68
69#[doc(alias = "alert")]
102#[doc(alias = "dialog")]
103#[doc(alias = "window")]
104#[cfg_attr(feature = "docs",
105 doc = embed_doc_image::embed_image!("popup", "images/gallery_popup.png"),
106)]
107#[derive(Clone, PartialEq)]
108pub struct Popup {
109 pub(crate) theme: Option<PopupThemePartial>,
110 children: Vec<Element>,
111 on_close_request: Option<EventHandler<()>>,
112 close_on_escape_key: bool,
113 width: Size,
114 key: DiffKey,
115}
116
117impl KeyExt for Popup {
118 fn write_key(&mut self) -> &mut DiffKey {
119 &mut self.key
120 }
121}
122
123impl Default for Popup {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl Popup {
130 pub fn new() -> Self {
131 Self {
132 theme: None,
133 children: vec![],
134 on_close_request: None,
135 close_on_escape_key: true,
136 width: Size::px(500.),
137 key: DiffKey::None,
138 }
139 }
140
141 pub fn on_close_request(mut self, on_close_request: impl Into<EventHandler<()>>) -> Self {
142 self.on_close_request = Some(on_close_request.into());
143 self
144 }
145
146 pub fn width(mut self, width: impl Into<Size>) -> Self {
147 self.width = width.into();
148 self
149 }
150}
151
152impl ChildrenExt for Popup {
153 fn get_children(&mut self) -> &mut Vec<Element> {
154 &mut self.children
155 }
156}
157
158impl Component for Popup {
159 fn render(&self) -> impl IntoElement {
160 let animations = use_animation(|conf| {
161 conf.on_creation(OnCreation::Run);
162 (
163 AnimNum::new(0.85, 1.)
164 .time(250)
165 .ease(Ease::Out)
166 .function(Function::Expo),
167 AnimNum::new(0.2, 1.)
168 .time(250)
169 .ease(Ease::Out)
170 .function(Function::Expo),
171 )
172 });
173
174 let PopupTheme { background, color } = get_theme!(&self.theme, popup);
175
176 let (scale, opacity) = &*animations.read();
177
178 let request_to_close = {
179 let handler = self.on_close_request.clone();
180 move || {
181 if let Some(h) = &handler {
182 h.call(());
183 }
184 }
185 };
186
187 let on_global_key_down = {
188 let close = self.close_on_escape_key;
189 let req = request_to_close.clone();
190 move |e: Event<KeyboardEventData>| {
191 if close && e.key == Key::Named(NamedKey::Escape) {
192 req();
193 }
194 }
195 };
196
197 PopupBackground::new(
198 rect()
199 .a11y_role(AccessibilityRole::Dialog)
200 .scale((scale.value(), scale.value()))
201 .opacity(opacity.value())
202 .corner_radius(12.)
203 .background(background)
204 .color(color)
205 .shadow(Shadow::new().y(4.).blur(5.).color((0, 0, 0, 30)))
206 .width(self.width.clone())
207 .height(Size::auto())
208 .spacing(4.)
209 .padding(8.)
210 .on_global_key_down(on_global_key_down)
211 .children(self.children.clone())
212 .into(),
213 move |_| {
214 request_to_close();
215 },
216 )
217 }
218
219 fn render_key(&self) -> DiffKey {
220 self.key.clone().or(self.default_key())
221 }
222}
223
224#[derive(PartialEq)]
226pub struct PopupTitle {
227 text: Readable<String>,
228}
229
230impl PopupTitle {
231 pub fn new(text: impl Into<Readable<String>>) -> Self {
232 Self { text: text.into() }
233 }
234}
235
236impl Component for PopupTitle {
237 fn render(&self) -> impl IntoElement {
238 rect().font_size(18.).padding(8.).child(
239 label()
240 .a11y_role(AccessibilityRole::TitleBar)
241 .width(Size::fill())
242 .text(self.text.read().to_string()),
243 )
244 }
245}
246
247#[derive(Clone, PartialEq)]
249pub struct PopupContent {
250 children: Vec<Element>,
251}
252impl Default for PopupContent {
253 fn default() -> Self {
254 Self::new()
255 }
256}
257
258impl PopupContent {
259 pub fn new() -> Self {
260 Self { children: vec![] }
261 }
262}
263
264impl ChildrenExt for PopupContent {
265 fn get_children(&mut self) -> &mut Vec<Element> {
266 &mut self.children
267 }
268}
269
270impl Component for PopupContent {
271 fn render(&self) -> impl IntoElement {
272 rect()
273 .font_size(15.)
274 .padding(8.)
275 .children(self.children.clone())
276 }
277}
278
279#[derive(Clone, PartialEq)]
281pub struct PopupButtons {
282 pub children: Vec<Element>,
283}
284
285impl Default for PopupButtons {
286 fn default() -> Self {
287 Self::new()
288 }
289}
290
291impl PopupButtons {
292 pub fn new() -> Self {
293 Self { children: vec![] }
294 }
295}
296
297impl ChildrenExt for PopupButtons {
298 fn get_children(&mut self) -> &mut Vec<Element> {
299 &mut self.children
300 }
301}
302
303impl Component for PopupButtons {
304 fn render(&self) -> impl IntoElement {
305 rect()
306 .width(Size::fill())
307 .main_align(Alignment::End)
308 .padding(8.)
309 .spacing(4.)
310 .horizontal()
311 .children(self.children.clone())
312 }
313}