Skip to main content

iced_navigation/widgets/
operations.rs

1use iced_core::{
2    Rectangle,
3    widget::{Id, Operation},
4};
5use iced_runtime::{Task, task::widget as operate};
6use std::{hash::Hash, marker::PhantomData};
7
8use crate::widgets::{self, NavigatorState};
9
10fn clear_history_op<T, Key>(target: Option<Id>) -> impl Operation<T>
11where
12    Key: 'static + Eq + Hash + Clone + Send,
13{
14    struct ClearHistory<Key> {
15        target: Option<Id>,
16        p: PhantomData<Key>,
17    }
18
19    impl<T, Key> Operation<T> for ClearHistory<Key>
20    where
21        Key: 'static + Eq + Hash + Clone + Send,
22    {
23        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
24            operate(self)
25        }
26
27        fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
28            #[cfg(feature = "stack")]
29            if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
30                value.request_update();
31
32                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
33                    return;
34                }
35
36                value.clear_history();
37            }
38
39            #[cfg(feature = "tabs")]
40            if let Some(value) = state.downcast_mut::<widgets::tabs_navigator::State<Key>>() {
41                value.request_update();
42
43                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
44                    return;
45                }
46
47                value.clear_history();
48            }
49
50            #[cfg(feature = "drawer")]
51            if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
52                value.request_update();
53
54                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
55                    return;
56                }
57
58                value.clear_history();
59            }
60        }
61    }
62
63    ClearHistory {
64        target,
65        p: PhantomData::<Key>,
66    }
67}
68
69fn pop_history_op<T, Key>(target: Option<Id>) -> impl Operation<T>
70where
71    Key: 'static + Eq + Hash + Clone + Send,
72{
73    struct PopHistory<Key> {
74        target: Option<Id>,
75        p: PhantomData<Key>,
76    }
77
78    impl<T, Key> Operation<T> for PopHistory<Key>
79    where
80        Key: 'static + Eq + Hash + Clone + Send,
81    {
82        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
83            operate(self)
84        }
85
86        fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
87            #[cfg(feature = "stack")]
88            if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
89                value.request_update();
90
91                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
92                    return;
93                }
94
95                value.pop_history();
96            }
97
98            #[cfg(feature = "tabs")]
99            if let Some(value) = state.downcast_mut::<widgets::tabs_navigator::State<Key>>() {
100                value.request_update();
101
102                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
103                    return;
104                }
105
106                value.pop_history();
107            }
108
109            #[cfg(feature = "drawer")]
110            if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
111                value.request_update();
112
113                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
114                    return;
115                }
116
117                value.pop_history();
118            }
119        }
120    }
121
122    PopHistory {
123        target,
124        p: PhantomData::<Key>,
125    }
126}
127
128fn go_back_op<T, Key>(target: Option<Id>) -> impl Operation<T>
129where
130    Key: 'static + Eq + Hash + Clone + Send,
131{
132    struct GoBack<Key> {
133        target: Option<Id>,
134        p: PhantomData<Key>,
135    }
136
137    impl<T, Key> Operation<T> for GoBack<Key>
138    where
139        Key: 'static + Eq + Hash + Clone + Send,
140    {
141        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
142            operate(self)
143        }
144
145        fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
146            #[cfg(feature = "stack")]
147            if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
148                value.request_update();
149
150                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
151                    return;
152                }
153
154                value.go_back();
155            }
156
157            #[cfg(feature = "tabs")]
158            if let Some(value) = state.downcast_mut::<widgets::tabs_navigator::State<Key>>() {
159                value.request_update();
160
161                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
162                    return;
163                }
164
165                value.go_back();
166            }
167
168            #[cfg(feature = "drawer")]
169            if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
170                value.request_update();
171
172                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
173                    return;
174                }
175
176                value.go_back();
177            }
178        }
179    }
180
181    GoBack {
182        target,
183        p: PhantomData::<Key>,
184    }
185}
186
187fn navigate_op<T, Key>(page: Key, target: Option<Id>) -> impl Operation<T>
188where
189    Key: 'static + Eq + Hash + Clone + Send,
190{
191    struct Navigate<Key> {
192        target: Option<Id>,
193        page: Option<Key>,
194    }
195
196    impl<T, Key> Operation<T> for Navigate<Key>
197    where
198        Key: 'static + Eq + Hash + Clone + Send,
199    {
200        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
201            operate(self)
202        }
203
204        fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
205            #[cfg(feature = "stack")]
206            if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
207                value.request_update();
208
209                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
210                    return;
211                }
212
213                value.navigate(self.page.take().unwrap());
214            }
215
216            #[cfg(feature = "tabs")]
217            if let Some(value) = state.downcast_mut::<widgets::tabs_navigator::State<Key>>() {
218                value.request_update();
219
220                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
221                    return;
222                }
223
224                value.navigate(self.page.take().unwrap());
225            }
226
227            #[cfg(feature = "drawer")]
228            if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
229                value.request_update();
230
231                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
232                    return;
233                }
234
235                value.navigate(self.page.take().unwrap());
236            }
237        }
238    }
239
240    Navigate {
241        target,
242        page: Some(page),
243    }
244}
245
246#[cfg(feature = "drawer")]
247fn open_drawer_op<T, Key>(target: Option<Id>) -> impl Operation<T>
248where
249    Key: 'static + Eq + Hash + Clone + Send,
250{
251    struct OpenDrawer<Key> {
252        target: Option<Id>,
253        p: PhantomData<Key>,
254    }
255
256    impl<T, Key> Operation<T> for OpenDrawer<Key>
257    where
258        Key: 'static + Eq + Hash + Clone + Send,
259    {
260        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
261            operate(self)
262        }
263
264        fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
265            if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
266                value.request_update();
267
268                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
269                    return;
270                }
271
272                value.open_drawer();
273            }
274        }
275    }
276
277    OpenDrawer {
278        target,
279        p: PhantomData::<Key>,
280    }
281}
282
283#[cfg(feature = "drawer")]
284fn close_drawer_op<T, Key>(target: Option<Id>) -> impl Operation<T>
285where
286    Key: 'static + Eq + Hash + Clone + Send,
287{
288    struct CloseDrawer<Key> {
289        target: Option<Id>,
290        p: PhantomData<Key>,
291    }
292
293    impl<T, Key> Operation<T> for CloseDrawer<Key>
294    where
295        Key: 'static + Eq + Hash + Clone + Send,
296    {
297        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
298            operate(self)
299        }
300
301        fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
302            if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
303                value.request_update();
304
305                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
306                    return;
307                }
308
309                value.close_drawer();
310            }
311        }
312    }
313
314    CloseDrawer {
315        target,
316        p: PhantomData::<Key>,
317    }
318}
319
320#[cfg(feature = "stack")]
321fn push_op<T, Key>(page: Key, target: Option<Id>) -> impl Operation<T>
322where
323    Key: 'static + Eq + Hash + Clone + Send,
324{
325    struct Push<Key> {
326        target: Option<Id>,
327        page: Option<Key>,
328    }
329
330    impl<T, Key> Operation<T> for Push<Key>
331    where
332        Key: 'static + Eq + Hash + Clone + Send,
333    {
334        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
335            operate(self)
336        }
337
338        fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
339            if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
340                value.request_update();
341
342                if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
343                    return;
344                }
345
346                value.push(self.page.take().unwrap());
347            }
348        }
349    }
350
351    Push {
352        target,
353        page: Some(page),
354    }
355}
356
357pub fn navigate<T, P>(page: P) -> Task<T>
358where
359    P: 'static + Eq + Hash + Clone + Send,
360    T: 'static + Send,
361{
362    operate(navigate_op::<T, P>(page, None))
363}
364
365pub fn navigate_by_id<T, P>(page: P, target: Id) -> Task<T>
366where
367    P: 'static + Eq + Hash + Clone + Send,
368    T: 'static + Send,
369{
370    operate(navigate_op::<T, P>(page, Some(target)))
371}
372
373pub fn go_back<T, P>() -> Task<T>
374where
375    P: 'static + Eq + Hash + Clone + Send,
376    T: 'static + Send,
377{
378    operate(go_back_op::<T, P>(None))
379}
380
381pub fn go_back_by_id<T, P>(target: Id) -> Task<T>
382where
383    P: 'static + Eq + Hash + Clone + Send,
384    T: 'static + Send,
385{
386    operate(go_back_op::<T, P>(Some(target)))
387}
388
389pub fn clear_history<T, P>() -> Task<T>
390where
391    P: 'static + Eq + Hash + Clone + Send,
392    T: 'static + Send,
393{
394    operate(clear_history_op::<T, P>(None))
395}
396
397pub fn clear_history_by_id<T, P>(target: Id) -> Task<T>
398where
399    P: 'static + Eq + Hash + Clone + Send,
400    T: 'static + Send,
401{
402    operate(clear_history_op::<T, P>(Some(target)))
403}
404
405pub fn pop_history<T, P>() -> Task<T>
406where
407    P: 'static + Eq + Hash + Clone + Send,
408    T: 'static + Send,
409{
410    operate(pop_history_op::<T, P>(None))
411}
412
413pub fn pop_history_by_id<T, P>(target: Id) -> Task<T>
414where
415    P: 'static + Eq + Hash + Clone + Send,
416    T: 'static + Send,
417{
418    operate(pop_history_op::<T, P>(Some(target)))
419}
420
421#[cfg(feature = "drawer")]
422pub fn open_drawer<T, P>() -> Task<T>
423where
424    P: 'static + Eq + Hash + Clone + Send,
425    T: 'static + Send,
426{
427    operate(open_drawer_op::<T, P>(None))
428}
429
430#[cfg(feature = "drawer")]
431pub fn open_drawer_by_id<T, P>(target: Id) -> Task<T>
432where
433    P: 'static + Eq + Hash + Clone + Send,
434    T: 'static + Send,
435{
436    operate(open_drawer_op::<T, P>(Some(target)))
437}
438
439#[cfg(feature = "drawer")]
440pub fn close_drawer<T, P>() -> Task<T>
441where
442    P: 'static + Eq + Hash + Clone + Send,
443    T: 'static + Send,
444{
445    operate(close_drawer_op::<T, P>(None))
446}
447
448#[cfg(feature = "drawer")]
449pub fn close_drawer_by_id<T, P>(target: Id) -> Task<T>
450where
451    P: 'static + Eq + Hash + Clone + Send,
452    T: 'static + Send,
453{
454    operate(close_drawer_op::<T, P>(Some(target)))
455}
456
457#[cfg(feature = "stack")]
458pub fn push<T, P>(page: P) -> Task<T>
459where
460    P: 'static + Eq + Hash + Clone + Send,
461    T: 'static + Send,
462{
463    operate(push_op::<T, P>(page, None))
464}
465
466#[cfg(feature = "stack")]
467pub fn push_by_id<T, P>(page: P, target: Id) -> Task<T>
468where
469    P: 'static + Eq + Hash + Clone + Send,
470    T: 'static + Send,
471{
472    operate(push_op::<T, P>(page, Some(target)))
473}