1use super::{ItemHandle, SplitDirection};
2use crate::{Item, Settings, WeakItemHandle, Workspace};
3use anyhow::{anyhow, Result};
4use client::PeerId;
5use collections::{HashMap, VecDeque};
6use gpui::{
7 action,
8 elements::*,
9 geometry::{rect::RectF, vector::vec2f},
10 keymap::Binding,
11 platform::{CursorStyle, NavigationDirection},
12 AnyViewHandle, AppContext, Entity, MutableAppContext, Quad, RenderContext, Task, View,
13 ViewContext, ViewHandle, WeakViewHandle,
14};
15use project::{ProjectEntryId, ProjectPath};
16use std::{
17 any::{Any, TypeId},
18 cell::RefCell,
19 cmp, mem,
20 rc::Rc,
21};
22use util::ResultExt;
23
24action!(Split, SplitDirection);
25action!(ActivateItem, usize);
26action!(ActivatePrevItem);
27action!(ActivateNextItem);
28action!(CloseActiveItem);
29action!(CloseInactiveItems);
30action!(CloseItem, usize);
31action!(GoBack, Option<WeakViewHandle<Pane>>);
32action!(GoForward, Option<WeakViewHandle<Pane>>);
33
34const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
35
36pub fn init(cx: &mut MutableAppContext) {
37 cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
38 pane.activate_item(action.0, cx);
39 });
40 cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
41 pane.activate_prev_item(cx);
42 });
43 cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| {
44 pane.activate_next_item(cx);
45 });
46 cx.add_action(|pane: &mut Pane, _: &CloseActiveItem, cx| {
47 pane.close_active_item(cx);
48 });
49 cx.add_action(|pane: &mut Pane, _: &CloseInactiveItems, cx| {
50 pane.close_inactive_items(cx);
51 });
52 cx.add_action(|pane: &mut Pane, action: &CloseItem, cx| {
53 pane.close_item(action.0, cx);
54 });
55 cx.add_action(|pane: &mut Pane, action: &Split, cx| {
56 pane.split(action.0, cx);
57 });
58 cx.add_action(|workspace: &mut Workspace, action: &GoBack, cx| {
59 Pane::go_back(
60 workspace,
61 action
62 .0
63 .as_ref()
64 .and_then(|weak_handle| weak_handle.upgrade(cx)),
65 cx,
66 )
67 .detach();
68 });
69 cx.add_action(|workspace: &mut Workspace, action: &GoForward, cx| {
70 Pane::go_forward(
71 workspace,
72 action
73 .0
74 .as_ref()
75 .and_then(|weak_handle| weak_handle.upgrade(cx)),
76 cx,
77 )
78 .detach();
79 });
80
81 cx.add_bindings(vec![
82 Binding::new("shift-cmd-{", ActivatePrevItem, Some("Pane")),
83 Binding::new("shift-cmd-}", ActivateNextItem, Some("Pane")),
84 Binding::new("cmd-w", CloseActiveItem, Some("Pane")),
85 Binding::new("alt-cmd-w", CloseInactiveItems, Some("Pane")),
86 Binding::new("cmd-k up", Split(SplitDirection::Up), Some("Pane")),
87 Binding::new("cmd-k down", Split(SplitDirection::Down), Some("Pane")),
88 Binding::new("cmd-k left", Split(SplitDirection::Left), Some("Pane")),
89 Binding::new("cmd-k right", Split(SplitDirection::Right), Some("Pane")),
90 Binding::new("ctrl--", GoBack(None), Some("Pane")),
91 Binding::new("shift-ctrl-_", GoForward(None), Some("Pane")),
92 ]);
93}
94
95pub enum Event {
96 Activate,
97 Remove,
98 Split(SplitDirection),
99}
100
101pub struct Pane {
102 items: Vec<Box<dyn ItemHandle>>,
103 active_item_index: usize,
104 nav_history: Rc<RefCell<NavHistory>>,
105 toolbars: HashMap<TypeId, Box<dyn ToolbarHandle>>,
106 active_toolbar_type: Option<TypeId>,
107 active_toolbar_visible: bool,
108}
109
110pub(crate) struct FollowerState {
111 pub(crate) leader_id: PeerId,
112 pub(crate) current_view_id: usize,
113 pub(crate) items_by_leader_view_id: HashMap<usize, Box<dyn ItemHandle>>,
114}
115
116pub trait Toolbar: View {
117 fn active_item_changed(
118 &mut self,
119 item: Option<Box<dyn ItemHandle>>,
120 cx: &mut ViewContext<Self>,
121 ) -> bool;
122 fn on_dismiss(&mut self, cx: &mut ViewContext<Self>);
123}
124
125trait ToolbarHandle {
126 fn active_item_changed(
127 &self,
128 item: Option<Box<dyn ItemHandle>>,
129 cx: &mut MutableAppContext,
130 ) -> bool;
131 fn on_dismiss(&self, cx: &mut MutableAppContext);
132 fn to_any(&self) -> AnyViewHandle;
133}
134
135pub struct ItemNavHistory {
136 history: Rc<RefCell<NavHistory>>,
137 item: Rc<dyn WeakItemHandle>,
138}
139
140#[derive(Default)]
141pub struct NavHistory {
142 mode: NavigationMode,
143 backward_stack: VecDeque<NavigationEntry>,
144 forward_stack: VecDeque<NavigationEntry>,
145 paths_by_item: HashMap<usize, ProjectPath>,
146}
147
148#[derive(Copy, Clone)]
149enum NavigationMode {
150 Normal,
151 GoingBack,
152 GoingForward,
153 Disabled,
154}
155
156impl Default for NavigationMode {
157 fn default() -> Self {
158 Self::Normal
159 }
160}
161
162pub struct NavigationEntry {
163 pub item: Rc<dyn WeakItemHandle>,
164 pub data: Option<Box<dyn Any>>,
165}
166
167impl Pane {
168 pub fn new() -> Self {
169 Self {
170 items: Vec::new(),
171 active_item_index: 0,
172 nav_history: Default::default(),
173 toolbars: Default::default(),
174 active_toolbar_type: Default::default(),
175 active_toolbar_visible: false,
176 }
177 }
178
179 pub fn nav_history(&self) -> &Rc<RefCell<NavHistory>> {
180 &self.nav_history
181 }
182
183 pub fn activate(&self, cx: &mut ViewContext<Self>) {
184 cx.emit(Event::Activate);
185 }
186
187 pub fn go_back(
188 workspace: &mut Workspace,
189 pane: Option<ViewHandle<Pane>>,
190 cx: &mut ViewContext<Workspace>,
191 ) -> Task<()> {
192 Self::navigate_history(
193 workspace,
194 pane.unwrap_or_else(|| workspace.active_pane().clone()),
195 NavigationMode::GoingBack,
196 cx,
197 )
198 }
199
200 pub fn go_forward(
201 workspace: &mut Workspace,
202 pane: Option<ViewHandle<Pane>>,
203 cx: &mut ViewContext<Workspace>,
204 ) -> Task<()> {
205 Self::navigate_history(
206 workspace,
207 pane.unwrap_or_else(|| workspace.active_pane().clone()),
208 NavigationMode::GoingForward,
209 cx,
210 )
211 }
212
213 fn navigate_history(
214 workspace: &mut Workspace,
215 pane: ViewHandle<Pane>,
216 mode: NavigationMode,
217 cx: &mut ViewContext<Workspace>,
218 ) -> Task<()> {
219 workspace.activate_pane(pane.clone(), cx);
220
221 let to_load = pane.update(cx, |pane, cx| {
222 // Retrieve the weak item handle from the history.
223 let entry = pane.nav_history.borrow_mut().pop(mode)?;
224
225 // If the item is still present in this pane, then activate it.
226 if let Some(index) = entry
227 .item
228 .upgrade(cx)
229 .and_then(|v| pane.index_for_item(v.as_ref()))
230 {
231 if let Some(item) = pane.active_item() {
232 pane.nav_history.borrow_mut().set_mode(mode);
233 item.deactivated(cx);
234 pane.nav_history
235 .borrow_mut()
236 .set_mode(NavigationMode::Normal);
237 }
238
239 pane.active_item_index = index;
240 pane.focus_active_item(cx);
241 if let Some(data) = entry.data {
242 pane.active_item()?.navigate(data, cx);
243 }
244 cx.notify();
245 None
246 }
247 // If the item is no longer present in this pane, then retrieve its
248 // project path in order to reopen it.
249 else {
250 pane.nav_history
251 .borrow_mut()
252 .paths_by_item
253 .get(&entry.item.id())
254 .cloned()
255 .map(|project_path| (project_path, entry))
256 }
257 });
258
259 if let Some((project_path, entry)) = to_load {
260 // If the item was no longer present, then load it again from its previous path.
261 let pane = pane.downgrade();
262 let task = workspace.load_path(project_path, cx);
263 cx.spawn(|workspace, mut cx| async move {
264 let task = task.await;
265 if let Some(pane) = pane.upgrade(&cx) {
266 if let Some((project_entry_id, build_item)) = task.log_err() {
267 pane.update(&mut cx, |pane, cx| {
268 pane.nav_history.borrow_mut().set_mode(mode);
269 let item = pane.open_item(project_entry_id, cx, build_item);
270 pane.nav_history
271 .borrow_mut()
272 .set_mode(NavigationMode::Normal);
273 if let Some(data) = entry.data {
274 item.navigate(data, cx);
275 }
276 });
277 } else {
278 workspace
279 .update(&mut cx, |workspace, cx| {
280 Self::navigate_history(workspace, pane, mode, cx)
281 })
282 .await;
283 }
284 }
285 })
286 } else {
287 Task::ready(())
288 }
289 }
290
291 pub fn open_item(
292 &mut self,
293 project_entry_id: ProjectEntryId,
294 cx: &mut ViewContext<Self>,
295 build_item: impl FnOnce(&mut MutableAppContext) -> Box<dyn ItemHandle>,
296 ) -> Box<dyn ItemHandle> {
297 for (ix, item) in self.items.iter().enumerate() {
298 if item.project_entry_id(cx) == Some(project_entry_id) {
299 let item = item.boxed_clone();
300 self.activate_item(ix, cx);
301 return item;
302 }
303 }
304
305 let item = build_item(cx);
306 self.add_item(item.boxed_clone(), cx);
307 item
308 }
309
310 pub(crate) fn add_item(&mut self, mut item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
311 item.set_nav_history(self.nav_history.clone(), cx);
312 item.added_to_pane(cx);
313 let item_idx = cmp::min(self.active_item_index + 1, self.items.len());
314 self.items.insert(item_idx, item);
315 self.activate_item(item_idx, cx);
316 cx.notify();
317 }
318
319 pub(crate) fn set_follow_state(
320 &mut self,
321 follower_state: FollowerState,
322 cx: &mut ViewContext<Self>,
323 ) -> Result<()> {
324 let current_view_id = follower_state.current_view_id as usize;
325 let item = follower_state
326 .items_by_leader_view_id
327 .get(¤t_view_id)
328 .ok_or_else(|| anyhow!("invalid current view id"))?
329 .clone();
330 self.add_item(item, cx);
331 Ok(())
332 }
333
334 pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> {
335 self.items.iter()
336 }
337
338 pub fn active_item(&self) -> Option<Box<dyn ItemHandle>> {
339 self.items.get(self.active_item_index).cloned()
340 }
341
342 pub fn project_entry_id_for_item(
343 &self,
344 item: &dyn ItemHandle,
345 cx: &AppContext,
346 ) -> Option<ProjectEntryId> {
347 self.items.iter().find_map(|existing| {
348 if existing.id() == item.id() {
349 existing.project_entry_id(cx)
350 } else {
351 None
352 }
353 })
354 }
355
356 pub fn item_for_entry(
357 &self,
358 entry_id: ProjectEntryId,
359 cx: &AppContext,
360 ) -> Option<Box<dyn ItemHandle>> {
361 self.items.iter().find_map(|item| {
362 if item.project_entry_id(cx) == Some(entry_id) {
363 Some(item.boxed_clone())
364 } else {
365 None
366 }
367 })
368 }
369
370 pub fn index_for_item(&self, item: &dyn ItemHandle) -> Option<usize> {
371 self.items.iter().position(|i| i.id() == item.id())
372 }
373
374 pub fn activate_item(&mut self, index: usize, cx: &mut ViewContext<Self>) {
375 if index < self.items.len() {
376 let prev_active_item_ix = mem::replace(&mut self.active_item_index, index);
377 if prev_active_item_ix != self.active_item_index
378 && prev_active_item_ix < self.items.len()
379 {
380 self.items[prev_active_item_ix].deactivated(cx);
381 }
382 self.update_active_toolbar(cx);
383 self.focus_active_item(cx);
384 self.activate(cx);
385 cx.notify();
386 }
387 }
388
389 pub fn activate_prev_item(&mut self, cx: &mut ViewContext<Self>) {
390 let mut index = self.active_item_index;
391 if index > 0 {
392 index -= 1;
393 } else if self.items.len() > 0 {
394 index = self.items.len() - 1;
395 }
396 self.activate_item(index, cx);
397 }
398
399 pub fn activate_next_item(&mut self, cx: &mut ViewContext<Self>) {
400 let mut index = self.active_item_index;
401 if index + 1 < self.items.len() {
402 index += 1;
403 } else {
404 index = 0;
405 }
406 self.activate_item(index, cx);
407 }
408
409 pub fn close_active_item(&mut self, cx: &mut ViewContext<Self>) {
410 if !self.items.is_empty() {
411 self.close_item(self.items[self.active_item_index].id(), cx)
412 }
413 }
414
415 pub fn close_inactive_items(&mut self, cx: &mut ViewContext<Self>) {
416 if !self.items.is_empty() {
417 let active_item_id = self.items[self.active_item_index].id();
418 self.close_items(cx, |id| id != active_item_id);
419 }
420 }
421
422 pub fn close_item(&mut self, view_id_to_close: usize, cx: &mut ViewContext<Self>) {
423 self.close_items(cx, |view_id| view_id == view_id_to_close);
424 }
425
426 pub fn close_items(
427 &mut self,
428 cx: &mut ViewContext<Self>,
429 should_close: impl Fn(usize) -> bool,
430 ) {
431 let mut item_ix = 0;
432 let mut new_active_item_index = self.active_item_index;
433 self.items.retain(|item| {
434 if should_close(item.id()) {
435 if item_ix == self.active_item_index {
436 item.deactivated(cx);
437 }
438
439 if item_ix < self.active_item_index {
440 new_active_item_index -= 1;
441 }
442
443 let mut nav_history = self.nav_history.borrow_mut();
444 if let Some(path) = item.project_path(cx) {
445 nav_history.paths_by_item.insert(item.id(), path);
446 } else {
447 nav_history.paths_by_item.remove(&item.id());
448 }
449
450 item_ix += 1;
451 false
452 } else {
453 item_ix += 1;
454 true
455 }
456 });
457
458 if self.items.is_empty() {
459 cx.emit(Event::Remove);
460 } else {
461 self.active_item_index = cmp::min(new_active_item_index, self.items.len() - 1);
462 self.focus_active_item(cx);
463 self.activate(cx);
464 }
465 self.update_active_toolbar(cx);
466
467 cx.notify();
468 }
469
470 fn focus_active_item(&mut self, cx: &mut ViewContext<Self>) {
471 if let Some(active_item) = self.active_item() {
472 cx.focus(active_item);
473 }
474 }
475
476 pub fn split(&mut self, direction: SplitDirection, cx: &mut ViewContext<Self>) {
477 cx.emit(Event::Split(direction));
478 }
479
480 pub fn show_toolbar<F, V>(&mut self, cx: &mut ViewContext<Self>, build_toolbar: F)
481 where
482 F: FnOnce(&mut ViewContext<V>) -> V,
483 V: Toolbar,
484 {
485 let type_id = TypeId::of::<V>();
486 if self.active_toolbar_type != Some(type_id) {
487 self.dismiss_toolbar(cx);
488
489 let active_item = self.active_item();
490 self.toolbars
491 .entry(type_id)
492 .or_insert_with(|| Box::new(cx.add_view(build_toolbar)));
493
494 self.active_toolbar_type = Some(type_id);
495 self.active_toolbar_visible =
496 self.toolbars[&type_id].active_item_changed(active_item, cx);
497 cx.notify();
498 }
499 }
500
501 pub fn dismiss_toolbar(&mut self, cx: &mut ViewContext<Self>) {
502 if let Some(active_toolbar_type) = self.active_toolbar_type.take() {
503 self.toolbars
504 .get_mut(&active_toolbar_type)
505 .unwrap()
506 .on_dismiss(cx);
507 self.active_toolbar_visible = false;
508 self.focus_active_item(cx);
509 cx.notify();
510 }
511 }
512
513 pub fn toolbar<T: Toolbar>(&self) -> Option<ViewHandle<T>> {
514 self.toolbars
515 .get(&TypeId::of::<T>())
516 .and_then(|toolbar| toolbar.to_any().downcast())
517 }
518
519 pub fn active_toolbar(&self) -> Option<AnyViewHandle> {
520 let type_id = self.active_toolbar_type?;
521 let toolbar = self.toolbars.get(&type_id)?;
522 if self.active_toolbar_visible {
523 Some(toolbar.to_any())
524 } else {
525 None
526 }
527 }
528
529 fn update_active_toolbar(&mut self, cx: &mut ViewContext<Self>) {
530 let active_item = self.items.get(self.active_item_index);
531 for (toolbar_type_id, toolbar) in &self.toolbars {
532 let visible = toolbar.active_item_changed(active_item.cloned(), cx);
533 if Some(*toolbar_type_id) == self.active_toolbar_type {
534 self.active_toolbar_visible = visible;
535 }
536 }
537 }
538
539 fn render_tabs(&self, cx: &mut RenderContext<Self>) -> ElementBox {
540 let theme = cx.global::<Settings>().theme.clone();
541
542 enum Tabs {}
543 let tabs = MouseEventHandler::new::<Tabs, _, _>(0, cx, |mouse_state, cx| {
544 let mut row = Flex::row();
545 for (ix, item) in self.items.iter().enumerate() {
546 let is_active = ix == self.active_item_index;
547
548 row.add_child({
549 let tab_style = if is_active {
550 theme.workspace.active_tab.clone()
551 } else {
552 theme.workspace.tab.clone()
553 };
554 let title = item.tab_content(&tab_style, cx);
555
556 let mut style = if is_active {
557 theme.workspace.active_tab.clone()
558 } else {
559 theme.workspace.tab.clone()
560 };
561 if ix == 0 {
562 style.container.border.left = false;
563 }
564
565 EventHandler::new(
566 Container::new(
567 Flex::row()
568 .with_child(
569 Align::new({
570 let diameter = 7.0;
571 let icon_color = if item.has_conflict(cx) {
572 Some(style.icon_conflict)
573 } else if item.is_dirty(cx) {
574 Some(style.icon_dirty)
575 } else {
576 None
577 };
578
579 ConstrainedBox::new(
580 Canvas::new(move |bounds, _, cx| {
581 if let Some(color) = icon_color {
582 let square = RectF::new(
583 bounds.origin(),
584 vec2f(diameter, diameter),
585 );
586 cx.scene.push_quad(Quad {
587 bounds: square,
588 background: Some(color),
589 border: Default::default(),
590 corner_radius: diameter / 2.,
591 });
592 }
593 })
594 .boxed(),
595 )
596 .with_width(diameter)
597 .with_height(diameter)
598 .boxed()
599 })
600 .boxed(),
601 )
602 .with_child(
603 Container::new(Align::new(title).boxed())
604 .with_style(ContainerStyle {
605 margin: Margin {
606 left: style.spacing,
607 right: style.spacing,
608 ..Default::default()
609 },
610 ..Default::default()
611 })
612 .boxed(),
613 )
614 .with_child(
615 Align::new(
616 ConstrainedBox::new(if mouse_state.hovered {
617 let item_id = item.id();
618 enum TabCloseButton {}
619 let icon = Svg::new("icons/x.svg");
620 MouseEventHandler::new::<TabCloseButton, _, _>(
621 item_id,
622 cx,
623 |mouse_state, _| {
624 if mouse_state.hovered {
625 icon.with_color(style.icon_close_active)
626 .boxed()
627 } else {
628 icon.with_color(style.icon_close).boxed()
629 }
630 },
631 )
632 .with_padding(Padding::uniform(4.))
633 .with_cursor_style(CursorStyle::PointingHand)
634 .on_click(move |cx| {
635 cx.dispatch_action(CloseItem(item_id))
636 })
637 .named("close-tab-icon")
638 } else {
639 Empty::new().boxed()
640 })
641 .with_width(style.icon_width)
642 .boxed(),
643 )
644 .boxed(),
645 )
646 .boxed(),
647 )
648 .with_style(style.container)
649 .boxed(),
650 )
651 .on_mouse_down(move |cx| {
652 cx.dispatch_action(ActivateItem(ix));
653 true
654 })
655 .boxed()
656 })
657 }
658
659 row.add_child(
660 Empty::new()
661 .contained()
662 .with_border(theme.workspace.tab.container.border)
663 .flexible(0., true)
664 .named("filler"),
665 );
666
667 row.boxed()
668 });
669
670 ConstrainedBox::new(tabs.boxed())
671 .with_height(theme.workspace.tab.height)
672 .named("tabs")
673 }
674}
675
676impl Entity for Pane {
677 type Event = Event;
678}
679
680impl View for Pane {
681 fn ui_name() -> &'static str {
682 "Pane"
683 }
684
685 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
686 let this = cx.handle();
687
688 EventHandler::new(if let Some(active_item) = self.active_item() {
689 Flex::column()
690 .with_child(self.render_tabs(cx))
691 .with_children(
692 self.active_toolbar()
693 .as_ref()
694 .map(|view| ChildView::new(view).boxed()),
695 )
696 .with_child(ChildView::new(active_item).flexible(1., true).boxed())
697 .boxed()
698 } else {
699 Empty::new().boxed()
700 })
701 .on_navigate_mouse_down(move |direction, cx| {
702 let this = this.clone();
703 match direction {
704 NavigationDirection::Back => cx.dispatch_action(GoBack(Some(this))),
705 NavigationDirection::Forward => cx.dispatch_action(GoForward(Some(this))),
706 }
707
708 true
709 })
710 .named("pane")
711 }
712
713 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
714 self.focus_active_item(cx);
715 }
716}
717
718impl<T: Toolbar> ToolbarHandle for ViewHandle<T> {
719 fn active_item_changed(
720 &self,
721 item: Option<Box<dyn ItemHandle>>,
722 cx: &mut MutableAppContext,
723 ) -> bool {
724 self.update(cx, |this, cx| this.active_item_changed(item, cx))
725 }
726
727 fn on_dismiss(&self, cx: &mut MutableAppContext) {
728 self.update(cx, |this, cx| this.on_dismiss(cx));
729 }
730
731 fn to_any(&self) -> AnyViewHandle {
732 self.into()
733 }
734}
735
736impl ItemNavHistory {
737 pub fn new<T: Item>(history: Rc<RefCell<NavHistory>>, item: &ViewHandle<T>) -> Self {
738 Self {
739 history,
740 item: Rc::new(item.downgrade()),
741 }
742 }
743
744 pub fn history(&self) -> Rc<RefCell<NavHistory>> {
745 self.history.clone()
746 }
747
748 pub fn push<D: 'static + Any>(&self, data: Option<D>) {
749 self.history.borrow_mut().push(data, self.item.clone());
750 }
751}
752
753impl NavHistory {
754 pub fn disable(&mut self) {
755 self.mode = NavigationMode::Disabled;
756 }
757
758 pub fn enable(&mut self) {
759 self.mode = NavigationMode::Normal;
760 }
761
762 pub fn pop_backward(&mut self) -> Option<NavigationEntry> {
763 self.backward_stack.pop_back()
764 }
765
766 pub fn pop_forward(&mut self) -> Option<NavigationEntry> {
767 self.forward_stack.pop_back()
768 }
769
770 fn pop(&mut self, mode: NavigationMode) -> Option<NavigationEntry> {
771 match mode {
772 NavigationMode::Normal | NavigationMode::Disabled => None,
773 NavigationMode::GoingBack => self.pop_backward(),
774 NavigationMode::GoingForward => self.pop_forward(),
775 }
776 }
777
778 fn set_mode(&mut self, mode: NavigationMode) {
779 self.mode = mode;
780 }
781
782 pub fn push<D: 'static + Any>(&mut self, data: Option<D>, item: Rc<dyn WeakItemHandle>) {
783 match self.mode {
784 NavigationMode::Disabled => {}
785 NavigationMode::Normal => {
786 if self.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN {
787 self.backward_stack.pop_front();
788 }
789 self.backward_stack.push_back(NavigationEntry {
790 item,
791 data: data.map(|data| Box::new(data) as Box<dyn Any>),
792 });
793 self.forward_stack.clear();
794 }
795 NavigationMode::GoingBack => {
796 if self.forward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN {
797 self.forward_stack.pop_front();
798 }
799 self.forward_stack.push_back(NavigationEntry {
800 item,
801 data: data.map(|data| Box::new(data) as Box<dyn Any>),
802 });
803 }
804 NavigationMode::GoingForward => {
805 if self.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN {
806 self.backward_stack.pop_front();
807 }
808 self.backward_stack.push_back(NavigationEntry {
809 item,
810 data: data.map(|data| Box::new(data) as Box<dyn Any>),
811 });
812 }
813 }
814 }
815}