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