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 if local {
400 self.focus_active_item(cx);
401 self.activate(cx);
402 }
403 cx.notify();
404 }
405 }
406
407 pub fn activate_prev_item(&mut self, cx: &mut ViewContext<Self>) {
408 let mut index = self.active_item_index;
409 if index > 0 {
410 index -= 1;
411 } else if self.items.len() > 0 {
412 index = self.items.len() - 1;
413 }
414 self.activate_item(index, true, cx);
415 }
416
417 pub fn activate_next_item(&mut self, cx: &mut ViewContext<Self>) {
418 let mut index = self.active_item_index;
419 if index + 1 < self.items.len() {
420 index += 1;
421 } else {
422 index = 0;
423 }
424 self.activate_item(index, true, cx);
425 }
426
427 pub fn close_active_item(&mut self, cx: &mut ViewContext<Self>) {
428 if !self.items.is_empty() {
429 self.close_item(self.items[self.active_item_index].id(), cx)
430 }
431 }
432
433 pub fn close_inactive_items(&mut self, cx: &mut ViewContext<Self>) {
434 if !self.items.is_empty() {
435 let active_item_id = self.items[self.active_item_index].id();
436 self.close_items(cx, |id| id != active_item_id);
437 }
438 }
439
440 pub fn close_item(&mut self, view_id_to_close: usize, cx: &mut ViewContext<Self>) {
441 self.close_items(cx, |view_id| view_id == view_id_to_close);
442 }
443
444 pub fn close_items(
445 &mut self,
446 cx: &mut ViewContext<Self>,
447 should_close: impl Fn(usize) -> bool,
448 ) {
449 let mut item_ix = 0;
450 let mut new_active_item_index = self.active_item_index;
451 self.items.retain(|item| {
452 if should_close(item.id()) {
453 if item_ix == self.active_item_index {
454 item.deactivated(cx);
455 }
456
457 if item_ix < self.active_item_index {
458 new_active_item_index -= 1;
459 }
460
461 let mut nav_history = self.nav_history.borrow_mut();
462 if let Some(path) = item.project_path(cx) {
463 nav_history.paths_by_item.insert(item.id(), path);
464 } else {
465 nav_history.paths_by_item.remove(&item.id());
466 }
467
468 item_ix += 1;
469 false
470 } else {
471 item_ix += 1;
472 true
473 }
474 });
475
476 if self.items.is_empty() {
477 cx.emit(Event::Remove);
478 } else {
479 self.active_item_index = cmp::min(new_active_item_index, self.items.len() - 1);
480 self.focus_active_item(cx);
481 self.activate(cx);
482 }
483 self.update_active_toolbar(cx);
484
485 cx.notify();
486 }
487
488 fn focus_active_item(&mut self, cx: &mut ViewContext<Self>) {
489 if let Some(active_item) = self.active_item() {
490 cx.focus(active_item);
491 }
492 }
493
494 pub fn split(&mut self, direction: SplitDirection, cx: &mut ViewContext<Self>) {
495 cx.emit(Event::Split(direction));
496 }
497
498 pub fn show_toolbar<F, V>(&mut self, cx: &mut ViewContext<Self>, build_toolbar: F)
499 where
500 F: FnOnce(&mut ViewContext<V>) -> V,
501 V: Toolbar,
502 {
503 let type_id = TypeId::of::<V>();
504 if self.active_toolbar_type != Some(type_id) {
505 self.dismiss_toolbar(cx);
506
507 let active_item = self.active_item();
508 self.toolbars
509 .entry(type_id)
510 .or_insert_with(|| Box::new(cx.add_view(build_toolbar)));
511
512 self.active_toolbar_type = Some(type_id);
513 self.active_toolbar_visible =
514 self.toolbars[&type_id].active_item_changed(active_item, cx);
515 cx.notify();
516 }
517 }
518
519 pub fn dismiss_toolbar(&mut self, cx: &mut ViewContext<Self>) {
520 if let Some(active_toolbar_type) = self.active_toolbar_type.take() {
521 self.toolbars
522 .get_mut(&active_toolbar_type)
523 .unwrap()
524 .on_dismiss(cx);
525 self.active_toolbar_visible = false;
526 self.focus_active_item(cx);
527 cx.notify();
528 }
529 }
530
531 pub fn toolbar<T: Toolbar>(&self) -> Option<ViewHandle<T>> {
532 self.toolbars
533 .get(&TypeId::of::<T>())
534 .and_then(|toolbar| toolbar.to_any().downcast())
535 }
536
537 pub fn active_toolbar(&self) -> Option<AnyViewHandle> {
538 let type_id = self.active_toolbar_type?;
539 let toolbar = self.toolbars.get(&type_id)?;
540 if self.active_toolbar_visible {
541 Some(toolbar.to_any())
542 } else {
543 None
544 }
545 }
546
547 fn update_active_toolbar(&mut self, cx: &mut ViewContext<Self>) {
548 let active_item = self.items.get(self.active_item_index);
549 for (toolbar_type_id, toolbar) in &self.toolbars {
550 let visible = toolbar.active_item_changed(active_item.cloned(), cx);
551 if Some(*toolbar_type_id) == self.active_toolbar_type {
552 self.active_toolbar_visible = visible;
553 }
554 }
555 }
556
557 fn render_tabs(&self, cx: &mut RenderContext<Self>) -> ElementBox {
558 let theme = cx.global::<Settings>().theme.clone();
559
560 enum Tabs {}
561 let tabs = MouseEventHandler::new::<Tabs, _, _>(0, cx, |mouse_state, cx| {
562 let mut row = Flex::row();
563 for (ix, item) in self.items.iter().enumerate() {
564 let is_active = ix == self.active_item_index;
565
566 row.add_child({
567 let tab_style = if is_active {
568 theme.workspace.active_tab.clone()
569 } else {
570 theme.workspace.tab.clone()
571 };
572 let title = item.tab_content(&tab_style, cx);
573
574 let mut style = if is_active {
575 theme.workspace.active_tab.clone()
576 } else {
577 theme.workspace.tab.clone()
578 };
579 if ix == 0 {
580 style.container.border.left = false;
581 }
582
583 EventHandler::new(
584 Container::new(
585 Flex::row()
586 .with_child(
587 Align::new({
588 let diameter = 7.0;
589 let icon_color = if item.has_conflict(cx) {
590 Some(style.icon_conflict)
591 } else if item.is_dirty(cx) {
592 Some(style.icon_dirty)
593 } else {
594 None
595 };
596
597 ConstrainedBox::new(
598 Canvas::new(move |bounds, _, cx| {
599 if let Some(color) = icon_color {
600 let square = RectF::new(
601 bounds.origin(),
602 vec2f(diameter, diameter),
603 );
604 cx.scene.push_quad(Quad {
605 bounds: square,
606 background: Some(color),
607 border: Default::default(),
608 corner_radius: diameter / 2.,
609 });
610 }
611 })
612 .boxed(),
613 )
614 .with_width(diameter)
615 .with_height(diameter)
616 .boxed()
617 })
618 .boxed(),
619 )
620 .with_child(
621 Container::new(Align::new(title).boxed())
622 .with_style(ContainerStyle {
623 margin: Margin {
624 left: style.spacing,
625 right: style.spacing,
626 ..Default::default()
627 },
628 ..Default::default()
629 })
630 .boxed(),
631 )
632 .with_child(
633 Align::new(
634 ConstrainedBox::new(if mouse_state.hovered {
635 let item_id = item.id();
636 enum TabCloseButton {}
637 let icon = Svg::new("icons/x.svg");
638 MouseEventHandler::new::<TabCloseButton, _, _>(
639 item_id,
640 cx,
641 |mouse_state, _| {
642 if mouse_state.hovered {
643 icon.with_color(style.icon_close_active)
644 .boxed()
645 } else {
646 icon.with_color(style.icon_close).boxed()
647 }
648 },
649 )
650 .with_padding(Padding::uniform(4.))
651 .with_cursor_style(CursorStyle::PointingHand)
652 .on_click(move |cx| {
653 cx.dispatch_action(CloseItem(item_id))
654 })
655 .named("close-tab-icon")
656 } else {
657 Empty::new().boxed()
658 })
659 .with_width(style.icon_width)
660 .boxed(),
661 )
662 .boxed(),
663 )
664 .boxed(),
665 )
666 .with_style(style.container)
667 .boxed(),
668 )
669 .on_mouse_down(move |cx| {
670 cx.dispatch_action(ActivateItem(ix));
671 true
672 })
673 .boxed()
674 })
675 }
676
677 row.add_child(
678 Empty::new()
679 .contained()
680 .with_border(theme.workspace.tab.container.border)
681 .flexible(0., true)
682 .named("filler"),
683 );
684
685 row.boxed()
686 });
687
688 ConstrainedBox::new(tabs.boxed())
689 .with_height(theme.workspace.tab.height)
690 .named("tabs")
691 }
692}
693
694impl Entity for Pane {
695 type Event = Event;
696}
697
698impl View for Pane {
699 fn ui_name() -> &'static str {
700 "Pane"
701 }
702
703 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
704 let this = cx.handle();
705
706 EventHandler::new(if let Some(active_item) = self.active_item() {
707 Flex::column()
708 .with_child(self.render_tabs(cx))
709 .with_children(
710 self.active_toolbar()
711 .as_ref()
712 .map(|view| ChildView::new(view).boxed()),
713 )
714 .with_child(ChildView::new(active_item).flexible(1., true).boxed())
715 .boxed()
716 } else {
717 Empty::new().boxed()
718 })
719 .on_navigate_mouse_down(move |direction, cx| {
720 let this = this.clone();
721 match direction {
722 NavigationDirection::Back => cx.dispatch_action(GoBack(Some(this))),
723 NavigationDirection::Forward => cx.dispatch_action(GoForward(Some(this))),
724 }
725
726 true
727 })
728 .named("pane")
729 }
730
731 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
732 self.focus_active_item(cx);
733 }
734}
735
736impl<T: Toolbar> ToolbarHandle for ViewHandle<T> {
737 fn active_item_changed(
738 &self,
739 item: Option<Box<dyn ItemHandle>>,
740 cx: &mut MutableAppContext,
741 ) -> bool {
742 self.update(cx, |this, cx| this.active_item_changed(item, cx))
743 }
744
745 fn on_dismiss(&self, cx: &mut MutableAppContext) {
746 self.update(cx, |this, cx| this.on_dismiss(cx));
747 }
748
749 fn to_any(&self) -> AnyViewHandle {
750 self.into()
751 }
752}
753
754impl ItemNavHistory {
755 pub fn new<T: Item>(history: Rc<RefCell<NavHistory>>, item: &ViewHandle<T>) -> Self {
756 Self {
757 history,
758 item: Rc::new(item.downgrade()),
759 }
760 }
761
762 pub fn history(&self) -> Rc<RefCell<NavHistory>> {
763 self.history.clone()
764 }
765
766 pub fn push<D: 'static + Any>(&self, data: Option<D>) {
767 self.history.borrow_mut().push(data, self.item.clone());
768 }
769}
770
771impl NavHistory {
772 pub fn disable(&mut self) {
773 self.mode = NavigationMode::Disabled;
774 }
775
776 pub fn enable(&mut self) {
777 self.mode = NavigationMode::Normal;
778 }
779
780 pub fn pop_backward(&mut self) -> Option<NavigationEntry> {
781 self.backward_stack.pop_back()
782 }
783
784 pub fn pop_forward(&mut self) -> Option<NavigationEntry> {
785 self.forward_stack.pop_back()
786 }
787
788 fn pop(&mut self, mode: NavigationMode) -> Option<NavigationEntry> {
789 match mode {
790 NavigationMode::Normal | NavigationMode::Disabled => None,
791 NavigationMode::GoingBack => self.pop_backward(),
792 NavigationMode::GoingForward => self.pop_forward(),
793 }
794 }
795
796 fn set_mode(&mut self, mode: NavigationMode) {
797 self.mode = mode;
798 }
799
800 pub fn push<D: 'static + Any>(&mut self, data: Option<D>, item: Rc<dyn WeakItemHandle>) {
801 match self.mode {
802 NavigationMode::Disabled => {}
803 NavigationMode::Normal => {
804 if self.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN {
805 self.backward_stack.pop_front();
806 }
807 self.backward_stack.push_back(NavigationEntry {
808 item,
809 data: data.map(|data| Box::new(data) as Box<dyn Any>),
810 });
811 self.forward_stack.clear();
812 }
813 NavigationMode::GoingBack => {
814 if self.forward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN {
815 self.forward_stack.pop_front();
816 }
817 self.forward_stack.push_back(NavigationEntry {
818 item,
819 data: data.map(|data| Box::new(data) as Box<dyn Any>),
820 });
821 }
822 NavigationMode::GoingForward => {
823 if self.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN {
824 self.backward_stack.pop_front();
825 }
826 self.backward_stack.push_back(NavigationEntry {
827 item,
828 data: data.map(|data| Box::new(data) as Box<dyn Any>),
829 });
830 }
831 }
832 }
833}