Skip to main content

ptsd/interface/
navigation.rs

1use prism::drawable::{Component, Drawable, SizedTree, RequestTree, Rect, DynClone, clone_trait_object};
2use prism::{Context, Request};
3use prism::canvas::{Area as CanvasArea, Item as CanvasItem};
4use prism::event::{OnEvent, Event};
5use prism::layout::{Area, Stack};
6use prism::display::{EitherOr, Enum};
7
8// should this be a trait so that "FlowStorage" and other variables stay alive?
9#[derive(Debug, Component, Clone)]
10pub struct Flow {
11    layout: Stack,
12    pub current: Option<Box<dyn AppPage>>,
13    #[skip] pub stored: Vec<Box<dyn AppPage>>,
14    #[skip] pub index: usize
15}
16
17impl Flow {
18    pub fn new(mut pages: Vec<Box<dyn AppPage>>) -> Self {
19        Flow {
20            layout: Stack::default(),
21            current: Some(pages.remove(0)),
22            stored: pages,
23            index: 0
24        }
25    }
26}
27
28impl OnEvent for Flow {
29    fn on_event(&mut self, ctx: &mut Context, _sized: &SizedTree, mut event: Box<dyn Event>) -> Vec<Box<dyn Event>> {
30        if let Some(event) = event.downcast_mut::<NavigationEvent>() {
31            let i = self.index;
32            match event {
33                NavigationEvent::Pop => {
34                    if self.index == 0 {
35                        self.index = 0;
36                        ctx.send(Request::event(NavigationEvent::Reset));
37                    } else {
38                        self.index -= 1;
39                    }
40                },
41                NavigationEvent::Next if self.index < self.stored.len() => self.index += 1,
42                _ => {}
43            }
44            
45            self.stored.insert(i, self.current.take().unwrap()); 
46        
47            if self.stored.get(self.index).is_some() {
48                self.current = Some(self.stored.remove(self.index));
49            }
50        }
51        vec![event]
52    }
53}
54
55#[derive(Debug, Component, Clone)]
56pub struct Pages {
57    layout: Stack,
58    #[allow(clippy::type_complexity)] inner: EitherOr<Enum<Box<dyn AppPage>>, Option<Box<dyn FlowContainer>>>,
59    #[skip] history: Vec<Box<dyn FlowContainer>>
60}
61
62impl OnEvent for Pages {
63    fn on_event(&mut self, _ctx: &mut Context, _sized: &SizedTree, mut event: Box<dyn Event>) -> Vec<Box<dyn Event>> {
64        if let Some(e) = event.downcast_mut::<NavigationEvent>() {
65            match e {
66                NavigationEvent::Push(flow, ..) => self.push(flow.take().unwrap()),
67                NavigationEvent::Reset => self.try_back(),
68                NavigationEvent::Root(root) => self.root(Some(root.to_string())),
69                _ => {return vec![event]}
70            }
71            return vec![];
72        }
73
74        vec![event]
75    }
76}
77
78impl Pages {
79    pub fn new(roots: Vec<(String, Box<dyn AppPage>)>) -> Self {
80        let first = roots[0].0.to_string();
81        let roots = Enum::new(roots, first);
82        Pages {
83            layout: Stack::default(),
84            inner: EitherOr::new(roots, None),
85            history: Vec::new(),
86        }
87    }
88
89    pub fn root(&mut self, page: Option<String>) {
90        self.inner.display_left(true);
91        if let Some(p) = page { self.inner.left().display(&p); }
92        self.history = vec![];
93        *self.inner.right() = None;
94    }
95
96    pub fn try_back(&mut self) {
97        if let Some(flow) = self.history.pop() {
98            self.inner.right().replace(flow);
99        } else {
100            self.root(None);
101        }
102    }
103
104    pub fn push(&mut self, flow: Box<dyn FlowContainer>) {
105        if let Some(old) = self.inner.right().replace(flow) { 
106            self.history.push(old);
107        }
108        self.inner.display_left(false);
109    }
110
111    pub fn current(&mut self) -> &mut Box<dyn AppPage> {
112        if !self.history.is_empty() || self.inner.right().is_some() {
113            self.inner.right().as_mut().unwrap().flow().current.as_mut().unwrap()
114        } else {
115            self.inner.left().drawable().inner()
116        }
117    }
118}
119
120#[derive(Debug, Clone)]
121pub enum NavigationEvent {
122    Pop,
123    Push(Option<Box<dyn FlowContainer>>, Vec<usize>),
124    Reset,
125    Root(String),
126    Error(String),
127    Next,
128}
129
130impl NavigationEvent {
131    pub fn push(flow: impl FlowContainer + 'static) -> Self {
132        NavigationEvent::Push(Some(Box::new(flow)), vec![])
133    }
134}
135
136impl Event for NavigationEvent {
137    fn pass(self: Box<Self>, _ctx: &mut Context, children: &[Area]) -> Vec<Option<Box<dyn Event>>> {
138        let v = match self.as_ref() {
139            NavigationEvent::Push(Some(_), v) => Some(v.clone()),
140            _ => None
141        };
142
143        if v.is_none() {
144            return children.iter().map(|_| Some(self.clone() as Box<dyn Event>)).collect();
145        }
146
147        let v = v.unwrap();
148
149        let mut x = Some(self as Box<dyn Event>);
150        children.iter().enumerate().map(|(i, _)| if v.contains(&i) {None} else {x.take()}).collect()
151    }
152}
153
154// impl Event for NavigationEvent {
155//     fn pass(self: Box<Self>, _ctx: &mut Context, children: &[Area]) -> Vec<Option<Box<dyn Event>>> {
156//         println!("Children {:?}", children.len());
157//         children.iter().enumerate().map(|(i, _)| match *self {
158//             NavigationEvent::Push(_, ref v) => {
159//                 (!v.contains(&i)).then(|| {
160//                     let mut e: NavigationEvent = (*self).clone();
161//                     if let NavigationEvent::Push(_, ref mut v2) = e {v2.clear();}
162//                     Box::new(e) as Box<dyn Event>
163//                 })
164//             },
165//             _ =>  Some(Box::new((*self).clone()) as Box<dyn Event>),
166//         }).collect()
167//     }
168// }
169
170pub trait FlowContainer: Drawable + DynClone + std::fmt::Debug + 'static {
171    fn flow(&mut self) -> &mut Flow;
172}
173
174clone_trait_object!(FlowContainer);
175
176impl Drawable for Box<dyn FlowContainer> {
177    fn request_size(&self) -> RequestTree {Drawable::request_size(&**self)}
178    fn build(&self, size: (f32, f32), request: RequestTree) -> SizedTree {
179        Drawable::build(&**self, size, request)
180    }
181    fn draw(&self, sized: &SizedTree, offset: (f32, f32), bound: Rect) -> Vec<(CanvasArea, CanvasItem)> {
182        Drawable::draw(&**self, sized, offset, bound)
183    }
184
185    fn name(&self) -> String {Drawable::name(&**self)}
186
187    fn event(&mut self, ctx: &mut Context, sized: &SizedTree, event: Box<dyn Event>) {
188        Drawable::event(&mut **self, ctx, sized, event)
189    }
190}
191
192pub trait AppPage: Drawable + DynClone + std::fmt::Debug + 'static {}
193downcast_rs::impl_downcast!(AppPage);
194impl Drawable for Box<dyn AppPage> {
195    fn request_size(&self) -> RequestTree {Drawable::request_size(&**self)}
196    fn build(&self, size: (f32, f32), request: RequestTree) -> SizedTree {
197        Drawable::build(&**self, size, request)
198    }
199    fn draw(&self, sized: &SizedTree, offset: (f32, f32), bound: Rect) -> Vec<(CanvasArea, CanvasItem)> {
200        Drawable::draw(&**self, sized, offset, bound)
201    }
202
203    fn name(&self) -> String {Drawable::name(&**self)}
204
205    fn event(&mut self, ctx: &mut Context, sized: &SizedTree, event: Box<dyn Event>) {
206        Drawable::event(&mut **self, ctx, sized, event)
207    }
208}
209
210clone_trait_object!(AppPage);