1use crate::common::*;
2use crate::{Button, Label};
3
4const ICON_DOWN_OPEN_BIG: &str = "\u{e75c}";
5#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum PanelEvent {
9 Open,
10 Close,
11}
12
13pub struct Panel {
14 header: Entity,
15 container1: Entity,
16 container2: Entity,
17 arrow: Entity,
18 collapsed: bool,
19 title: String,
20
21 container_height: f32,
22 container_width: f32,
23
24 expand_height_animation: Animation,
25 collapse_height_animation: Animation,
26 expand_width_animation: Animation,
27 collapse_width_animation: Animation,
28
29 fade_in_animation: Animation,
30 fade_out_animation: Animation,
31
32 move_up_animation: Animation,
33 move_down_animation: Animation,
34 move_left_animation: Animation,
35 move_right_animation: Animation,
36
37 arrow_cw_animation: Animation,
38 arrow_ccw_animation: Animation,
39}
40
41impl Panel {
42 pub fn new(title: &str) -> Self {
43 Panel {
44 header: Entity::default(),
45 container1: Entity::default(),
46 container2: Entity::default(),
47 arrow: Entity::default(),
48 title: title.to_string(),
49 collapsed: false,
50
51 container_height: 0.0,
52 container_width: 0.0,
53
54 expand_height_animation: Animation::default(),
55 collapse_height_animation: Animation::default(),
56 expand_width_animation: Animation::default(),
57 collapse_width_animation: Animation::default(),
58
59 fade_in_animation: Animation::default(),
60 fade_out_animation: Animation::default(),
61
62 move_up_animation: Animation::default(),
63 move_down_animation: Animation::default(),
64 move_left_animation: Animation::default(),
65 move_right_animation: Animation::default(),
66
67 arrow_cw_animation: Animation::default(),
68 arrow_ccw_animation: Animation::default(),
69 }
70 }
71
72 pub fn collapsed(mut self, flag: bool) -> Self {
73 self.collapsed = flag;
74
75 self
76 }
77}
78
79impl Widget for Panel {
80 type Ret = (Entity, Entity);
81 type Data = ();
82 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
83 entity
84 .set_focusable(state, false)
85 .set_height(state, Auto);
87
88 self.header = Button::new()
89 .on_release(move |_,state,_|
90 state.insert_event(
91 Event::new(PanelEvent::Open).target(entity)
92 )
93 )
94 .build(state, entity, |builder| {
95 builder
96 .set_layout_type(LayoutType::Row)
97 .class("header")
100 });
101
102 self.arrow = Element::new().build(state, self.header, |builder| {
103 builder
104 .set_text(ICON_DOWN_OPEN_BIG)
105 .set_font("icons")
106 .set_child_space(Stretch(1.0))
107 .set_top(Stretch(1.0))
108 .set_bottom(Stretch(1.0))
109 .set_width(Pixels(20.0))
110 .set_height(Pixels(20.0))
111 .set_hoverable(false)
112 .set_focusable(false)
113 .class("icon")
115 });
116
117 Label::new(&self.title).build(state, self.header, |builder| {
119 builder
120 .set_height(Stretch(1.0))
122 .set_left(Pixels(5.0))
123 .set_child_top(Stretch(1.0))
124 .set_child_bottom(Stretch(1.0))
125 .set_hoverable(false)
126 .set_focusable(false)
127 .class("label")
128 });
129
130 self.container1 = Element::new().build(state, entity, |builder| {
131 builder
132 .class("container1")
133 .set_focusable(false)
134 .set_width(Stretch(1.0))
135 .set_height(Auto)
136 .set_min_height(Pixels(0.0))
137 });
138
139 self.container2 = Element::new().build(state, self.container1, |builder| {
140 builder
141 .class("container2")
142 .set_focusable(false)
143 .set_clip_widget(self.container1)
144 .set_width(Stretch(1.0))
147 .set_height(Auto)
148 });
149
150 entity.set_element(state, "panel");
151
152 let container_expand_animation = AnimationState::new()
154 .with_duration(std::time::Duration::from_millis(100))
155 .with_keyframe((0.0, Units::Pixels(0.0)))
156 .with_keyframe((1.0, Units::Pixels(0.0)));
157
158 self.expand_height_animation = state
159 .style
160 .height
161 .insert_animation(container_expand_animation.clone());
162
163 self.expand_width_animation = state
164 .style
165 .width
166 .insert_animation(container_expand_animation.clone());
167
168 let container_collapse_animation = AnimationState::new()
169 .with_duration(std::time::Duration::from_millis(100))
170 .with_keyframe((0.0, Units::Pixels(0.0)))
171 .with_keyframe((1.0, Units::Pixels(0.0)));
172
173 self.collapse_height_animation = state
174 .style
175 .height
176 .insert_animation(container_collapse_animation.clone());
177
178 self.collapse_width_animation = state
179 .style
180 .width
181 .insert_animation(container_collapse_animation.clone());
182
183 let container_fade_in_animation = AnimationState::new()
184 .with_duration(std::time::Duration::from_millis(1))
185 .with_keyframe((0.0, Opacity(0.0)))
186 .with_keyframe((1.0, Opacity(1.0)));
187
188 let container_hide_animation = AnimationState::new()
189 .with_duration(std::time::Duration::from_millis(100))
190 .with_keyframe((0.0, Units::Pixels(0.0)))
191 .with_keyframe((1.0, Units::Pixels(0.0)));
192
193 self.move_up_animation = state
194 .style
195 .top
196 .insert_animation(container_hide_animation.clone());
197
198 self.move_left_animation = state
199 .style
200 .left
201 .insert_animation(container_hide_animation.clone());
202
203 let container_reveal_animation = AnimationState::new()
204 .with_duration(std::time::Duration::from_millis(100))
205 .with_keyframe((0.0, Units::Pixels(0.0)))
206 .with_keyframe((1.0, Units::Pixels(0.0)));
207
208 self.move_down_animation = state
209 .style
210 .top
211 .insert_animation(container_reveal_animation.clone());
212
213 self.move_right_animation = state
214 .style
215 .left
216 .insert_animation(container_reveal_animation.clone());
217
218 self.fade_in_animation = state
219 .style
220 .opacity
221 .insert_animation(container_fade_in_animation);
222
223 let container_fade_out_animation = AnimationState::new()
224 .with_duration(std::time::Duration::from_millis(100))
225 .with_delay(std::time::Duration::from_millis(100))
226 .with_keyframe((0.0, Opacity(1.0)))
227 .with_keyframe((1.0, Opacity(0.0)));
228
229 self.fade_out_animation = state
230 .style
231 .opacity
232 .insert_animation(container_fade_out_animation);
233
234 let arrow_cw_animation = AnimationState::new()
235 .with_duration(std::time::Duration::from_millis(100))
236 .with_keyframe((0.0, -90.0))
237 .with_keyframe((1.0, 0.0));
238
239 self.arrow_cw_animation = state.style.rotate.insert_animation(arrow_cw_animation);
240
241 let arrow_ccw_animation = AnimationState::new()
242 .with_duration(std::time::Duration::from_millis(100))
243 .with_keyframe((0.0, 0.0))
244 .with_keyframe((1.0, -90.0));
245
246 self.arrow_ccw_animation = state.style.rotate.insert_animation(arrow_ccw_animation);
247
248 (self.container2, self.header)
249 }
250
251 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
252 if let Some(panel_event) = event.message.downcast::<PanelEvent>() {
253 match panel_event {
254 PanelEvent::Open | PanelEvent::Close => {
255 if event.target == entity {
256 if self.collapsed {
257 self.collapsed = false;
258
259 entity.set_checked(state, true);
260
261 match entity.get_layout_type(state) {
262 LayoutType::Column => {
263 state.style.height.play_animation(
264 self.container1,
265 self.expand_height_animation,
266 );
267
268 self.container1.set_height(state, Units::Auto);
269
270 state
271 .style
272 .rotate
273 .play_animation(self.arrow, self.arrow_cw_animation);
274
275 self.arrow.set_rotate(state, 0.0);
276 }
277
278 LayoutType::Row => {
279 state.style.width.play_animation(
280 self.container1,
281 self.expand_width_animation,
282 );
283
284 self.container1.set_width(state, Units::Auto);
285
286 state
287 .style
288 .rotate
289 .play_animation(self.arrow, self.arrow_ccw_animation);
290
291 self.arrow.set_rotate(state, -90.0);
292 }
293
294 _ => {}
295 }
296
297 state
298 .style
299 .top
300 .play_animation(self.container2, self.move_down_animation);
301
302 self.container2.set_opacity(state, 1.0);
303 } else {
304 self.collapsed = true;
305
306 entity.set_checked(state, false);
307
308 match entity.get_layout_type(state) {
309 LayoutType::Column => {
310 if !state.style.height.is_animating(self.container1) {
311 let container_height =
312 state.data.get_height(self.container1);
313 if container_height != self.container_height {
316 if let Some(animation) = state
319 .style
320 .height
321 .get_animation_mut(self.expand_height_animation)
322 {
323 animation.keyframes.last_mut().unwrap().1 =
324 Units::Pixels(container_height);
325 }
326
327 if let Some(animation) = state
328 .style
329 .height
330 .get_animation_mut(self.collapse_height_animation)
331 {
332 animation.keyframes.first_mut().unwrap().1 =
333 Units::Pixels(container_height);
334 }
335
336 if let Some(animation) = state
337 .style
338 .top
339 .get_animation_mut(self.move_down_animation)
340 {
341 animation.keyframes.first_mut().unwrap().1 =
342 Units::Pixels(-container_height);
343 }
344
345 if let Some(animation) = state
346 .style
347 .top
348 .get_animation_mut(self.move_up_animation)
349 {
350 animation.keyframes.last_mut().unwrap().1 =
351 Units::Pixels(-container_height);
352 }
353
354 self.container_height = container_height;
355 }
356 }
357
358 state.style.height.play_animation(
359 self.container1,
360 self.collapse_height_animation,
361 );
362
363 self.container1.set_height(state, Units::Pixels(0.0));
364
365 state
366 .style
367 .rotate
368 .play_animation(self.arrow, self.arrow_ccw_animation);
369
370 self.arrow.set_rotate(state, -90.0);
371 }
372
373 LayoutType::Row => {
374 if !state.style.height.is_animating(self.container1) {
375 let container_width = state.data.get_width(self.container1);
376
377 if container_width != self.container_width {
378 if let Some(animation) = state
381 .style
382 .width
383 .get_animation_mut(self.expand_width_animation)
384 {
385 animation.keyframes.last_mut().unwrap().1 =
386 Units::Pixels(container_width);
387 }
388
389 if let Some(animation) = state
390 .style
391 .width
392 .get_animation_mut(self.collapse_width_animation)
393 {
394 animation.keyframes.first_mut().unwrap().1 =
395 Units::Pixels(container_width);
396 }
397
398 if let Some(animation) = state
399 .style
400 .left
401 .get_animation_mut(self.move_left_animation)
402 {
403 animation.keyframes.first_mut().unwrap().1 =
404 Units::Pixels(-container_width);
405 }
406
407 if let Some(animation) = state
408 .style
409 .left
410 .get_animation_mut(self.move_right_animation)
411 {
412 animation.keyframes.last_mut().unwrap().1 =
413 Units::Pixels(-container_width);
414 }
415
416 self.container_height = container_width;
417 }
418 }
419
420 state.style.width.play_animation(
421 self.container1,
422 self.collapse_width_animation,
423 );
424
425 self.container1.set_width(state, Units::Pixels(0.0));
426
427 state
428 .style
429 .rotate
430 .play_animation(self.arrow, self.arrow_cw_animation);
431
432 self.arrow.set_rotate(state, 0.0);
433 }
434
435 _ => {}
436 }
437
438 state
439 .style
440 .opacity
441 .play_animation(self.container2, self.fade_out_animation);
442
443 state
444 .style
445 .top
446 .play_animation(self.container2, self.move_up_animation);
447
448 self.container2.set_opacity(state, 0.0);
449 }
450 }
451 }
452 }
453 }
454
455 if let Some(window_event) = event.message.downcast::<WindowEvent>() {
456 match window_event {
457 WindowEvent::GeometryChanged(_) => {
458 if event.target == self.container1 {
459 match entity.get_layout_type(state) {
460 LayoutType::Row => {
461 self.arrow.set_rotate(state, -90.0);
462 }
463
464 _ => {}
465 }
466
467 event.consume();
468 }
469 }
470 _ => {}
471 }
472 }
473 }
474}