summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core/NavigationModel.cs
blob: 987e801c85cbf51d928c5f36c79ae36c49b2c7fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace Xamarin.Forms.Internals
{
	[EditorBrowsable(EditorBrowsableState.Never)]
	public class NavigationModel
	{
		readonly List<Page> _modalStack = new List<Page>();
		readonly List<List<Page>> _navTree = new List<List<Page>>();

		public Page CurrentPage
		{
			get
			{
				if (_navTree.Any())
					return _navTree.Last().Last();
				return null;
			}
		}

		public IEnumerable<Page> Modals
		{
			get { return _modalStack; }
		}

		public IEnumerable<Page> Roots
		{
			get
			{
				foreach (List<Page> list in _navTree)
				{
					yield return list[0];
				}
			}
		}

		public IReadOnlyList<IReadOnlyList<Page>> Tree
		{
			get { return _navTree; }
		}

		public void Clear()
		{
			_navTree.Clear();
		}

		public void InsertPageBefore(Page page, Page before)
		{
			List<Page> currentStack = _navTree.Last();
			int index = currentStack.IndexOf(before);

			if (index == -1)
				throw new ArgumentException("before must be in the current navigation context");

			currentStack.Insert(index, page);
		}

		public Page Pop(Page ancestralNav)
		{
			ancestralNav = AncestorToRoot(ancestralNav);
			foreach (List<Page> stack in _navTree)
			{
				if (stack.Contains(ancestralNav))
				{
					if (stack.Count <= 1)
						throw new InvalidNavigationException("Can not pop final item in stack");
					Page result = stack.Last();
					stack.Remove(result);
					return result;
				}
			}

			throw new InvalidNavigationException("Popped from unpushed item?");
		}

		public Page PopModal()
		{
			if (_navTree.Count <= 1)
				throw new InvalidNavigationException("Can't pop modal without any modals pushed");
			Page modal = _navTree.Last().First();
			_modalStack.Remove(modal);
			_navTree.Remove(_navTree.Last());
			return modal;
		}

		public Page PopTopPage()
		{
			Page itemToRemove;
			if (_navTree.Count == 1)
			{
				if (_navTree[0].Count > 1)
				{
					itemToRemove = _navTree[0].Last();
					_navTree[0].Remove(itemToRemove);
					return itemToRemove;
				}
				return null;
			}
			itemToRemove = _navTree.Last().Last();
			_navTree.Last().Remove(itemToRemove);
			if (!_navTree.Last().Any())
			{
				_navTree.RemoveAt(_navTree.Count - 1);
			}
			return itemToRemove;
		}

		public void PopToRoot(Page ancestralNav)
		{
			ancestralNav = AncestorToRoot(ancestralNav);
			foreach (List<Page> stack in _navTree)
			{
				if (stack.Contains(ancestralNav))
				{
					if (stack.Count <= 1)
						throw new InvalidNavigationException("Can not pop final item in stack");
					stack.RemoveRange(1, stack.Count - 1);
					return;
				}
			}

			throw new InvalidNavigationException("Popped from unpushed item?");
		}

		public void Push(Page page, Page ancestralNav)
		{
			if (ancestralNav == null)
			{
				if (_navTree.Any())
					throw new InvalidNavigationException("Ancestor must be provided for all pushes except first");
				_navTree.Add(new List<Page> { page });
				return;
			}

			ancestralNav = AncestorToRoot(ancestralNav);

			foreach (List<Page> stack in _navTree)
			{
				if (stack.Contains(ancestralNav))
				{
					stack.Add(page);
					return;
				}
			}

			throw new InvalidNavigationException("Invalid ancestor passed");
		}

		public void PushModal(Page page)
		{
			_navTree.Add(new List<Page> { page });
			_modalStack.Add(page);
		}

		public bool RemovePage(Page page)
		{
			bool found;
			List<Page> currentStack = _navTree.Last();
			var i = 0;
			while (!(found = currentStack.Remove(page)) && i < _navTree.Count - 1)
			{
				currentStack = _navTree[i++];
			}

			return found;
		}

		Page AncestorToRoot(Page ancestor)
		{
			Page result = ancestor;
			while (!Application.IsApplicationOrNull(result.RealParent))
				result = (Page)result.RealParent;
			return result;
		}
	}
}