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