Skip to main content

Mountain/ApplicationState/State/FeatureState/NavigationHistory/
NavigationHistoryState.rs

1use std::sync::{Arc, Mutex as StandardMutex};
2
3use crate::dev_log;
4
5/// Tracks the editor navigation history stack (back/forward).
6///
7/// Implements a cursor-based navigation stack where `Index` points to the
8/// current position. GoBack decrements the index; GoForward increments it.
9/// Pushing a new entry truncates forward history.
10#[derive(Clone)]
11pub struct NavigationHistoryState {
12	/// The ordered list of visited URIs (oldest first).
13	Stack:Arc<StandardMutex<Vec<String>>>,
14	/// Current position in the stack (0-based). Points to the active entry.
15	Index:Arc<StandardMutex<usize>>,
16}
17
18impl Default for NavigationHistoryState {
19	fn default() -> Self {
20		dev_log!(
21			"history",
22			"[NavigationHistoryState] Initializing default navigation history state..."
23		);
24		Self {
25			Stack:Arc::new(StandardMutex::new(Vec::new())),
26			Index:Arc::new(StandardMutex::new(0)),
27		}
28	}
29}
30
31impl NavigationHistoryState {
32	/// Returns `true` if there is a previous location to navigate to.
33	pub fn CanGoBack(&self) -> bool {
34		let Stack = self.Stack.lock().ok().map(|G| G.len()).unwrap_or(0);
35		let Index = self.Index.lock().ok().as_deref().copied().unwrap_or(0);
36
37		Stack > 0 && Index > 0
38	}
39
40	/// Returns `true` if there is a next location to navigate to.
41	pub fn CanGoForward(&self) -> bool {
42		let Stack = self.Stack.lock().ok().map(|G| G.len()).unwrap_or(0);
43		let Index = self.Index.lock().ok().as_deref().copied().unwrap_or(0);
44
45		Stack > 0 && Index + 1 < Stack
46	}
47
48	/// Decrement the history index (go back). Returns the URI now active, or
49	/// `None` if already at the beginning.
50	pub fn GoBack(&self) -> Option<String> {
51		let Stack = self.Stack.lock().ok()?;
52
53		if Stack.is_empty() {
54			return None;
55		}
56
57		let mut Index = self.Index.lock().ok()?;
58
59		if *Index == 0 {
60			return None;
61		}
62
63		*Index -= 1;
64		let Uri = Stack.get(*Index).cloned();
65		dev_log!("history", "[NavigationHistoryState] GoBack → index={} uri={:?}", *Index, Uri);
66
67		Uri
68	}
69
70	/// Increment the history index (go forward). Returns the URI now active,
71	/// or `None` if already at the end.
72	pub fn GoForward(&self) -> Option<String> {
73		let Stack = self.Stack.lock().ok()?;
74
75		if Stack.is_empty() {
76			return None;
77		}
78
79		let mut Index = self.Index.lock().ok()?;
80
81		if *Index + 1 >= Stack.len() {
82			return None;
83		}
84
85		*Index += 1;
86		let Uri = Stack.get(*Index).cloned();
87		dev_log!("history", "[NavigationHistoryState] GoForward → index={} uri={:?}", *Index, Uri);
88
89		Uri
90	}
91
92	/// Push a URI onto the navigation stack. Truncates any forward history
93	/// beyond the current index.
94	pub fn Push(&self, Uri:String) {
95		if let (Ok(mut Stack), Ok(mut Index)) = (self.Stack.lock(), self.Index.lock()) {
96			// Truncate forward history
97			let NewIndex = if Stack.is_empty() { 0 } else { *Index + 1 };
98			Stack.truncate(NewIndex);
99			Stack.push(Uri.clone());
100			*Index = Stack.len() - 1;
101			dev_log!("history", "[NavigationHistoryState] Push uri={} index={}", Uri, *Index);
102		}
103	}
104
105	/// Clear the entire navigation stack.
106	pub fn Clear(&self) {
107		if let (Ok(mut Stack), Ok(mut Index)) = (self.Stack.lock(), self.Index.lock()) {
108			Stack.clear();
109			*Index = 0;
110			dev_log!("history", "[NavigationHistoryState] Stack cleared");
111		}
112	}
113
114	/// Return all URIs in the stack (oldest first).
115	pub fn GetStack(&self) -> Vec<String> { self.Stack.lock().ok().map(|G| G.clone()).unwrap_or_default() }
116}