summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.MacOS/ModalPageTracker.cs
blob: c02c64e02a0cb07d0317a97937786aacd4282284 (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
´╗┐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);
		}

		internal void LayoutSubviews()
		{
			if (_renderer == null || _renderer.View == null)
				return;
			
			foreach(var modal in _modals)
			{
				var modalRenderer = Platform.GetRenderer(modal);
				if(modalRenderer != null)
					modalRenderer.SetElementSize(new Size(_renderer.View.Bounds.Width, _renderer.View.Bounds.Height));
			}
		}

		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;
		}
	}
}