summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.MacOS/ModalPageTracker.cs
blob: c492f4cd1c6c42c7b7d214320e38bad3b6274f56 (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
using System;
using System.Threading.Tasks;
using System.Linq;
using AppKit;
using System.Collections.Generic;

namespace Xamarin.Forms.Platform.MacOS
{
	internal class ModalPageTracker : IDisposable
	{
		NSViewController _renderer;
		List<Page> _modals;
		bool _disposed;

		public ModalPageTracker(NSViewController mainRenderer)
		{
			if (mainRenderer == null)
				throw new ArgumentNullException(nameof(mainRenderer));
			_renderer = mainRenderer;
			_renderer.View.WantsLayer = true;
			_modals = new List<Page>();
		}

		public List<Page> ModalStack => _modals;

		public Task PushAsync(Page modal, bool animated)
		{
			_modals.Add(modal);
			modal.DescendantRemoved += HandleChildRemoved;
			Platform.NativeToolbarTracker.TryHide(modal as NavigationPage);
			return PresentModalAsync(modal, animated);
		}

		public Task<Page> PopAsync(bool animated)
		{
			var modal = _modals.LastOrDefault();
			if (modal == null)
				throw new InvalidOperationException("No Modal pages found in the stack, make sure you pushed a modal page");
			_modals.Remove(modal);
			modal.DescendantRemoved -= HandleChildRemoved;
			return HideModalAsync(modal, animated);
		}

		public void Dispose()
		{
			Dispose(true);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (!_disposed)
			{
				if (disposing)
				{
					foreach (var modal in _modals)
						Platform.DisposeModelAndChildrenRenderers(modal);
					_renderer = null;
				}
				_disposed = true;
			}
		}

		void HandleChildRemoved(object sender, ElementEventArgs e)
		{
			var view = e.Element;
			Platform.DisposeModelAndChildrenRenderers(view);
		}

		Task PresentModalAsync(Page modal, bool animated)
		{
			var modalRenderer = Platform.GetRenderer(modal);
			if (modalRenderer == null)
			{
				modalRenderer = Platform.CreateRenderer(modal);
				Platform.SetRenderer(modal, modalRenderer);
				modalRenderer.SetElementSize(new Size(_renderer.View.Bounds.Width, _renderer.View.Bounds.Height));
			}

			var toViewController = modalRenderer as NSViewController;

			var i = Math.Max(0, _renderer.ChildViewControllers.Length - 1);
			var fromViewController = _renderer.ChildViewControllers[i];

			_renderer.AddChildViewController(toViewController);

			NSViewControllerTransitionOptions option = animated
				? NSViewControllerTransitionOptions.SlideUp
				: NSViewControllerTransitionOptions.None;

			var task = _renderer.HandleAsyncAnimation(fromViewController, toViewController, option,
				() =>
				{
					//Hack: adjust if needed
					toViewController.View.Frame = _renderer.View.Bounds;
					fromViewController.View.Layer.Hidden = true;
				}, true);
			return task;
		}

		Task<Page> HideModalAsync(Page modal, bool animated)
		{
			var controller = Platform.GetRenderer(modal) as NSViewController;

			var i = Math.Max(0, _renderer.ChildViewControllers.Length - 2);
			var toViewController = _renderer.ChildViewControllers[i];

			toViewController.View.Layer.Hidden = false;

			NSViewControllerTransitionOptions option = animated
							? NSViewControllerTransitionOptions.SlideDown
							: NSViewControllerTransitionOptions.None;

			var task = _renderer.HandleAsyncAnimation(controller, toViewController, option,
				() => Platform.DisposeModelAndChildrenRenderers(modal), modal);
			return task;
		}
	}
}