1use crate::anim_ext::AnimatedContent;
2use crate::{Box, ViewExt};
3use repose_core::*;
4use std::any::Any;
5use std::cell::RefCell;
6use std::collections::{HashMap, VecDeque};
7use std::rc::Rc;
8
9pub struct NavController {
10 stack: RefCell<VecDeque<NavEntry>>,
11 pub current: Signal<String>,
12 pub transitions: Signal<Option<Transition>>,
13}
14
15pub struct NavEntry {
16 pub route: String,
17 pub args: HashMap<String, String>,
18 pub state: Box<dyn Any>,
19}
20
21#[derive(Clone)]
22pub enum Transition {
23 Push { from: String, to: String },
24 Pop { from: String, to: String },
25 Replace { from: String, to: String },
26}
27
28impl NavController {
29 pub fn new(initial: impl Into<String>) -> Rc<Self> {
30 let route = initial.into();
31 Rc::new(Self {
32 stack: RefCell::new({
33 let mut dq = VecDeque::new();
34 dq.push_back(NavEntry {
35 route: route.clone(),
36 args: HashMap::new(),
37 state: Box::new(()),
38 });
39 dq
40 }),
41 current: signal(route),
42 transitions: signal(None),
43 })
44 }
45
46 pub fn navigate(&self, route: impl Into<String>) {
47 let route = route.into();
48 let mut stack = self.stack.borrow_mut();
49 let from = self.current.get();
50
51 stack.push_back(NavEntry {
52 route: route.clone(),
53 args: HashMap::new(),
54 state: Box::new(()),
55 });
56
57 self.transitions.set(Some(Transition::Push {
58 from,
59 to: route.clone(),
60 }));
61 self.current.set(route);
62 }
63
64 pub fn replace(&self, route: impl Into<String>) {
65 let route = route.into();
66 let mut stack = self.stack.borrow_mut();
67 let from = self.current.get();
68
69 if let Some(_last) = stack.pop_back() {
70 }
72 stack.push_back(NavEntry {
73 route: route.clone(),
74 args: HashMap::new(),
75 state: Box::new(()),
76 });
77 self.transitions.set(Some(Transition::Replace {
78 from,
79 to: route.clone(),
80 }));
81 self.current.set(route);
82 }
83
84 pub fn pop(&self) -> bool {
85 let mut stack = self.stack.borrow_mut();
86 if stack.len() > 1 {
87 let from = self.current.get();
88 stack.pop_back();
89 if let Some(entry) = stack.back() {
90 self.transitions.set(Some(Transition::Pop {
91 from,
92 to: entry.route.clone(),
93 }));
94 self.current.set(entry.route.clone());
95 return true;
96 }
97 }
98 false
99 }
100
101 pub fn take_transition(&self) -> Option<Transition> {
102 let t = self.transitions.get();
103 self.transitions.set(None);
104 t
105 }
106}
107
108pub fn NavHost(
109 controller: Rc<NavController>,
110 routes: HashMap<String, Box<dyn Fn() -> View>>,
111) -> View {
112 let current = controller.current.get();
113 let trans = controller.take_transition();
114
115 if let Some(builder) = routes.get(¤t) {
116 let page = Box(Modifier::new().fill_max_size()).child((builder)());
117 AnimatedContent(current.clone(), trans, page)
118 } else {
119 Box(Modifier::new().fill_max_size())
121 }
122}