freya_components/
popup.rs

1use 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/// Popup background wrapper.
20#[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/// Floating popup / dialog.
70#[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/// Popup title.
181#[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/// Popup content wrapper.
204#[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/// Popup buttons container.
236#[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}