1use crate::{
2 makepad_derive_widget::*,
3 makepad_draw::*,
4 widget::*,
5 label::*,
6 button::*,
7 view::*,
8 WidgetMatchEvent,
9 WindowAction,
10};
11
12live_design!{
13 link widgets;
14 use link::widgets::*;
15 use link::theme::*;
16 use makepad_draw::shader::std::*;
17
18 pub StackNavigationViewBase = {{StackNavigationView}} {}
19 pub StackNavigationBase = {{StackNavigation}} {}
20
21 HEADER_HEIGHT = 80.0
24
25 pub StackViewHeader = <View> {
26 width: Fill, height: (HEADER_HEIGHT),
27 padding: {bottom: 10., top: 50.}
28 show_bg: true
29 draw_bg: {
30 color: (THEME_COLOR_APP_CAPTION_BAR)
31 }
32
33 content = <View> {
34 width: Fill, height: Fit,
35 flow: Overlay,
36
37 title_container = <View> {
38 width: Fill, height: Fit,
39 align: {x: 0.5, y: 0.5}
40
41 title = <H4> {
42 width: Fit, height: Fit,
43 margin: 0,
44 text: "Stack View Title"
45 }
46 }
47
48 button_container = <View> {
49 left_button = <Button> {
50 width: Fit, height: 68,
51 icon_walk: {width: 10, height: 68}
52 draw_bg: {
53 fn pixel(self) -> vec4 {
54 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
55 return sdf.result
56 }
57 }
58 draw_icon: {
59 svg_file: dep("crate://self/resources/icons/back.svg"),
60 color: (THEME_COLOR_LABEL_INNER);
61 brightness: 0.8;
62 }
63 }
64 }
65 }
66 }
67
68 pub StackNavigationView = <StackNavigationViewBase> {
69 visible: false
70 width: Fill, height: Fill,
71 flow: Overlay
72
73 show_bg: true
74 draw_bg: {
75 color: (THEME_COLOR_WHITE)
76 }
77
78 background = <View> {
80 width: Fill, height: Fill,
81 visible: false
82 }
83
84 body = <View> {
85 width: Fill, height: Fill,
86 flow: Down,
87
88 margin: {top: (HEADER_HEIGHT)},
90 }
91
92 header = <StackViewHeader> {}
93
94 offset: 4000.0
95
96 animator: {
97 slide = {
98 default: hide,
99 hide = {
100 redraw: true
101 ease: ExpDecay {d1: 0.80, d2: 0.97}
102 from: {all: Forward {duration: 5.0}}
103 apply: {offset: 4000.0}
106 }
107
108 show = {
109 redraw: true
110 ease: ExpDecay {d1: 0.82, d2: 0.95}
111 from: {all: Forward {duration: 0.5}}
112 apply: {offset: 0.0}
113 }
114 }
115 }
116 }
117
118 pub StackNavigation = <StackNavigationBase> {
119 width: Fill, height: Fill
120 flow: Overlay
121
122 root_view = <View> {}
123 }
124
125}
126
127#[derive(Clone, DefaultNone, Eq, Hash, PartialEq, Debug)]
128pub enum StackNavigationAction {
129 None,
130 NavigateTo(LiveId)
131}
132
133#[derive(Clone, Default, Eq, Hash, PartialEq, Debug)]
134pub enum StackNavigationViewState {
135 #[default] Inactive,
136 Active,
137}
138
139#[derive(Clone, DefaultNone, Eq, Hash, PartialEq, Debug)]
142pub enum StackNavigationTransitionAction {
143 None,
144 ShowBegin,
145 ShowDone,
146 HideBegin,
147 HideEnd,
148}
149
150#[derive(Live, LiveHook, Widget)]
151pub struct StackNavigationView {
152 #[deref]
153 view: View,
154
155 #[live]
156 offset: f64,
157
158 #[rust(10000.0)]
159 offset_to_hide: f64,
160
161 #[animator]
162 animator: Animator,
163
164 #[rust]
165 state: StackNavigationViewState,
166}
167
168impl Widget for StackNavigationView {
169 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
170 if self.animator_handle_event(cx, event).must_redraw() {
171 self.view.redraw(cx);
172 }
173 self.view.handle_event(cx, event, scope);
174
175 self.handle_stack_view_closure_request(cx, event, scope);
176 self.trigger_action_post_opening_if_done(cx);
177 self.finish_closure_animation_if_done(cx);
178 }
179
180 fn draw_walk(&mut self, cx:&mut Cx2d, scope:&mut Scope, walk:Walk) -> DrawStep{
181 self.view.draw_walk(
182 cx,
183 scope,
184 walk.with_abs_pos(DVec2 {
185 x: self.offset,
186 y: 0.,
187 }),
188 )
189 }
190}
191
192impl StackNavigationView {
193 fn hide_stack_view(&mut self, cx: &mut Cx) {
194 self.animator_play(cx, id!(slide.hide));
195 cx.widget_action(
196 self.widget_uid(),
197 &HeapLiveIdPath::default(),
198 StackNavigationTransitionAction::HideBegin,
199 );
200 }
201
202 fn handle_stack_view_closure_request(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
203 if matches!(self.state, StackNavigationViewState::Active) {
209 if event.back_pressed()
210 || matches!(event, Event::Actions(actions) if self.button(id!(left_button)).clicked(&actions))
211 || matches!(event, Event::MouseUp(mouse) if mouse.button.is_back())
212 {
213 self.hide_stack_view(cx);
214 }
215 }
216 }
217
218 fn finish_closure_animation_if_done(&mut self, cx: &mut Cx) {
219 if self.state == StackNavigationViewState::Active
220 && self.animator.animator_in_state(cx, id!(slide.hide))
221 {
222 if self.offset > self.offset_to_hide {
223 self.apply_over(cx, live! { visible: false });
224
225 cx.widget_action(
226 self.widget_uid(),
227 &HeapLiveIdPath::default(),
228 StackNavigationTransitionAction::HideEnd,
229 );
230
231 self.animator_cut(cx, id!(slide.hide));
232 self.state = StackNavigationViewState::Inactive;
233 }
234 }
235 }
236
237 fn trigger_action_post_opening_if_done(&mut self, cx: &mut Cx) {
238 if self.state == StackNavigationViewState::Inactive &&
239 self.animator.animator_in_state(cx, id!(slide.show))
240 {
241 const OPENING_OFFSET_THRESHOLD: f64 = 0.5;
242 if self.offset < OPENING_OFFSET_THRESHOLD {
243 cx.widget_action(
244 self.widget_uid(),
245 &HeapLiveIdPath::default(),
246 StackNavigationTransitionAction::ShowDone,
247 );
248 self.state = StackNavigationViewState::Active;
249 }
250 }
251 }
252}
253
254impl StackNavigationViewRef {
255 pub fn show(&self, cx: &mut Cx, root_width: f64) {
256 if let Some(mut inner) = self.borrow_mut() {
257 inner.apply_over(cx, live! {offset: (root_width), visible: true});
258 inner.offset_to_hide = root_width;
259 inner.animator_play(cx, id!(slide.show));
260 }
261 }
262
263 pub fn is_showing(&self, cx: &mut Cx) -> bool {
264 if let Some(inner) = self.borrow() {
265 inner.animator.animator_in_state(cx, id!(slide.show))
266 || inner.animator.is_track_animating(cx, id!(slide))
267 } else {
268 false
269 }
270 }
271
272 pub fn is_animating(&self, cx: &mut Cx) -> bool {
273 if let Some(inner) = self.borrow() {
274 inner.animator.is_track_animating(cx, id!(slide))
275 } else {
276 false
277 }
278 }
279
280 pub fn set_offset_to_hide(&self, offset_to_hide: f64) {
281 if let Some(mut inner) = self.borrow_mut() {
282 inner.offset_to_hide = offset_to_hide;
283 }
284 }
285}
286
287#[derive(Default)]
288enum ActiveStackView {
289 #[default]
290 None,
291 Active(LiveId),
292}
293
294#[derive(Live, LiveRegisterWidget, WidgetRef)]
295pub struct StackNavigation {
296 #[deref]
297 view: View,
298
299 #[rust]
300 screen_width: f64,
301
302 #[rust]
303 active_stack_view: ActiveStackView,
304}
305
306impl LiveHook for StackNavigation {
307 fn after_apply_from(&mut self, cx: &mut Cx, apply: &mut Apply) {
308 if apply.from.is_new_from_doc() {
309 self.active_stack_view = ActiveStackView::None;
310 } else {
311 if let ActiveStackView::Active(stack_view_id) = self.active_stack_view {
312 let stack_view_ref = self.stack_navigation_view(&[stack_view_id]);
314 stack_view_ref.apply_over(cx, live! {visible: true, offset: 0.0});
315 }
316 }
317 }
318}
319
320impl Widget for StackNavigation {
321 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
322 let mut visible_views = self.get_visible_views(cx);
326 if !event.requires_visibility() {
327 let root_view = self.view.widget(id!(root_view));
328 if !visible_views.contains(&root_view) {
329 visible_views.insert(0, root_view);
330 }
331 }
332 for widget_ref in visible_views {
333 widget_ref.handle_event(cx, event, scope);
334 }
335
336 self.widget_match_event(cx, event, scope);
340 }
341
342 fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
343 for widget_ref in self.get_visible_views(cx.cx).iter() {
344 widget_ref.draw_walk(cx, scope, walk) ?;
345 }
346 DrawStep::done()
347 }
348}
349
350impl WidgetNode for StackNavigation {
351 fn walk(&mut self, cx:&mut Cx) -> Walk{
352 self.view.walk(cx)
353 }
354 fn area(&self)->Area{self.view.area()}
355
356 fn redraw(&mut self, cx: &mut Cx) {
357 for widget_ref in self.get_visible_views(cx).iter() {
358 widget_ref.redraw(cx);
359 }
360 }
361
362 fn find_widgets(&self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
363 self.view.find_widgets(path, cached, results);
364 }
365
366 fn uid_to_widget(&self, uid:WidgetUid)->WidgetRef{
367 self.view.uid_to_widget(uid)
368 }
369
370}
371
372impl WidgetMatchEvent for StackNavigation {
373 fn handle_actions(&mut self, _cx: &mut Cx, actions: &Actions, _scope: &mut Scope) {
374 for action in actions {
375 if let WindowAction::WindowGeomChange(ce) = action.as_widget_action().cast() {
378 self.screen_width = ce.new_geom.inner_size.x * ce.new_geom.dpi_factor;
379 if let ActiveStackView::Active(stack_view_id) = self.active_stack_view {
380 let stack_view_ref = self.stack_navigation_view(&[stack_view_id]);
381 stack_view_ref.set_offset_to_hide(self.screen_width);
382 }
383 }
384
385 if let StackNavigationTransitionAction::HideEnd = action.as_widget_action().cast() {
387 self.active_stack_view = ActiveStackView::None;
388 }
389 }
390 }
391}
392
393
394impl StackNavigation {
395 pub fn show_stack_view_by_id(&mut self, stack_view_id: LiveId, cx: &mut Cx) {
396 if let ActiveStackView::None = self.active_stack_view {
397 let stack_view_ref = self.stack_navigation_view(&[stack_view_id]);
398 stack_view_ref.show(cx, self.screen_width);
399 self.active_stack_view = ActiveStackView::Active(stack_view_id);
400
401 cx.widget_action(
403 stack_view_ref.widget_uid(),
404 &HeapLiveIdPath::default(),
405 StackNavigationTransitionAction::ShowBegin,
406 );
407
408 self.redraw(cx);
409 }
410 }
411
412 fn get_visible_views(&mut self, cx: &mut Cx) -> Vec<WidgetRef> {
419 match self.active_stack_view {
420 ActiveStackView::None => {
421 vec![self.view.widget(id!(root_view))]
422 },
423 ActiveStackView::Active(stack_view_id) => {
424 let stack_view_ref = self.stack_navigation_view(&[stack_view_id]);
425 let mut views = vec![];
426
427 if stack_view_ref.is_showing(cx) {
428 if stack_view_ref.is_animating(cx) {
429 views.push(self.view.widget(id!(root_view)));
430 }
431 }
432
433 views.push(stack_view_ref.0.clone());
434 views
435 }
436 }
437 }
438}
439
440impl StackNavigationRef {
441 pub fn show_stack_view_by_id(&self, stack_view_id: LiveId, cx: &mut Cx) {
442 if let Some(mut inner) = self.borrow_mut() {
443 inner.show_stack_view_by_id(stack_view_id, cx);
444 }
445 }
446
447 pub fn handle_stack_view_actions(&self, cx: &mut Cx, actions: &Actions) {
448 for action in actions {
449 if let StackNavigationAction::NavigateTo(stack_view_id) = action.as_widget_action().cast() {
450 self.show_stack_view_by_id(stack_view_id, cx);
451 break;
452 }
453 }
454 }
455
456 pub fn set_title(&self, cx:&mut Cx, stack_view_id: LiveId, title: &str) {
457 if let Some(inner) = self.borrow_mut() {
458 let stack_view_ref = inner.stack_navigation_view(&[stack_view_id]);
459 stack_view_ref.label(id!(title)).set_text(cx, title);
460 }
461 }
462}