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 Render 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#[derive(Clone, PartialEq)]
71pub struct Popup {
72 pub(crate) theme: Option<PopupThemePartial>,
73 children: Vec<Element>,
74 on_close_request: Option<EventHandler<()>>,
75 close_on_escape_key: bool,
76 key: DiffKey,
77}
78
79impl KeyExt for Popup {
80 fn write_key(&mut self) -> &mut DiffKey {
81 &mut self.key
82 }
83}
84
85impl Default for Popup {
86 fn default() -> Self {
87 Self::new()
88 }
89}
90
91impl Popup {
92 pub fn new() -> Self {
93 Self {
94 theme: None,
95 children: vec![],
96 on_close_request: None,
97 close_on_escape_key: true,
98 key: DiffKey::None,
99 }
100 }
101
102 pub fn on_close_request(mut self, on_close_request: impl Into<EventHandler<()>>) -> Self {
103 self.on_close_request = Some(on_close_request.into());
104 self
105 }
106}
107
108impl ChildrenExt for Popup {
109 fn get_children(&mut self) -> &mut Vec<Element> {
110 &mut self.children
111 }
112}
113
114impl Render for Popup {
115 fn render(&self) -> impl IntoElement {
116 let animations = use_animation(|conf| {
117 conf.on_creation(OnCreation::Run);
118 (
119 AnimNum::new(0.85, 1.)
120 .time(250)
121 .ease(Ease::Out)
122 .function(Function::Expo),
123 AnimNum::new(0.2, 1.)
124 .time(250)
125 .ease(Ease::Out)
126 .function(Function::Expo),
127 )
128 });
129
130 let PopupTheme { background, color } = get_theme!(&self.theme, popup);
131
132 let (scale, opacity) = &*animations.read();
133
134 let request_to_close = {
135 let handler = self.on_close_request.clone();
136 move || {
137 if let Some(h) = &handler {
138 h.call(());
139 }
140 }
141 };
142
143 let on_global_key_down = {
144 let close = self.close_on_escape_key;
145 let req = request_to_close.clone();
146 move |e: Event<KeyboardEventData>| {
147 if close && e.key == Key::Named(NamedKey::Escape) {
148 req();
149 }
150 }
151 };
152
153 PopupBackground::new(
154 rect()
155 .a11y_role(AccessibilityRole::Dialog)
156 .scale((scale.value(), scale.value()))
157 .opacity(opacity.value())
158 .corner_radius(12.)
159 .background(background)
160 .color(color)
161 .shadow(Shadow::new().y(4.).blur(5.).color((0, 0, 0, 30)))
162 .width(Size::px(500.))
163 .height(Size::auto())
164 .spacing(4.)
165 .padding(8.)
166 .on_global_key_down(on_global_key_down)
167 .children(self.children.clone())
168 .into(),
169 move |_| {
170 request_to_close();
171 },
172 )
173 }
174
175 fn render_key(&self) -> DiffKey {
176 self.key.clone().or(self.default_key())
177 }
178}
179
180#[derive(PartialEq)]
182pub struct PopupTitle {
183 text: ReadState<String>,
184}
185
186impl PopupTitle {
187 pub fn new(text: impl Into<ReadState<String>>) -> Self {
188 Self { text: text.into() }
189 }
190}
191
192impl Render for PopupTitle {
193 fn render(&self) -> impl IntoElement {
194 rect().font_size(18.).padding(8.).child(
195 label()
196 .a11y_role(AccessibilityRole::TitleBar)
197 .width(Size::fill())
198 .text(self.text.read().to_string()),
199 )
200 }
201}
202
203#[derive(Clone, PartialEq)]
205pub struct PopupContent {
206 children: Vec<Element>,
207}
208impl Default for PopupContent {
209 fn default() -> Self {
210 Self::new()
211 }
212}
213
214impl PopupContent {
215 pub fn new() -> Self {
216 Self { children: vec![] }
217 }
218}
219
220impl ChildrenExt for PopupContent {
221 fn get_children(&mut self) -> &mut Vec<Element> {
222 &mut self.children
223 }
224}
225
226impl Render for PopupContent {
227 fn render(&self) -> impl IntoElement {
228 rect()
229 .font_size(15.)
230 .padding(8.)
231 .children(self.children.clone())
232 }
233}
234
235#[derive(Clone, PartialEq)]
237pub struct PopupButtons {
238 pub children: Vec<Element>,
239}
240
241impl Default for PopupButtons {
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247impl PopupButtons {
248 pub fn new() -> Self {
249 Self { children: vec![] }
250 }
251}
252
253impl ChildrenExt for PopupButtons {
254 fn get_children(&mut self) -> &mut Vec<Element> {
255 &mut self.children
256 }
257}
258
259impl Render for PopupButtons {
260 fn render(&self) -> impl IntoElement {
261 rect()
262 .width(Size::fill())
263 .main_align(Alignment::End)
264 .padding(8.)
265 .spacing(4.)
266 .horizontal()
267 .children(self.children.clone())
268 }
269}