-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
ViewExtensions.cs
244 lines (209 loc) · 6.31 KB
/
ViewExtensions.cs
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
using System;
using System.Numerics;
using Microsoft.Maui.Graphics;
using System.Threading.Tasks;
using Microsoft.Maui.Media;
using System.IO;
using System.Collections.Generic;
#if (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID)
using IPlatformViewHandler = Microsoft.Maui.IViewHandler;
#endif
#if IOS || MACCATALYST
using PlatformView = UIKit.UIView;
using ParentView = UIKit.UIView;
#elif ANDROID
using PlatformView = Android.Views.View;
using ParentView = Android.Views.IViewParent;
#elif WINDOWS
using PlatformView = Microsoft.UI.Xaml.FrameworkElement;
using ParentView = Microsoft.UI.Xaml.DependencyObject;
#elif TIZEN
using PlatformView = Tizen.NUI.BaseComponents.View;
using ParentView = Tizen.NUI.BaseComponents.View;
#else
using PlatformView = System.Object;
using ParentView = System.Object;
#endif
namespace Microsoft.Maui.Platform
{
/// <include file="../../docs/Microsoft.Maui/ViewExtensions.xml" path="Type[@FullName='Microsoft.Maui.ViewExtensions']/Docs/*" />
public static partial class ViewExtensions
{
internal static Vector3 ExtractPosition(this Matrix4x4 matrix) => matrix.Translation;
internal static Vector3 ExtractScale(this Matrix4x4 matrix) => new Vector3(matrix.M11, matrix.M22, matrix.M33);
internal static double ExtractAngleInRadians(this Matrix4x4 matrix) => Math.Atan2(matrix.M21, matrix.M11);
internal static double ExtractAngleInDegrees(this Matrix4x4 matrix) => ExtractAngleInRadians(matrix) * 180 / Math.PI;
public static IPlatformViewHandler ToHandler(this IView view, IMauiContext context) =>
(IPlatformViewHandler)ElementExtensions.ToHandler(view, context);
internal static T? GetParentOfType<T>(this ParentView? view)
#if ANDROID
where T : class, ParentView
#elif PLATFORM
where T : ParentView
#else
where T : class
#endif
{
if (view is T t)
return t;
while (view != null)
{
T? parent = view?.GetParent() as T;
if (parent != null)
return parent;
view = view?.GetParent() as ParentView;
}
return default;
}
// Only Windows and Android have different types for the Parent type
#if WINDOWS || ANDROID
internal static ParentView? FindParent(this PlatformView? view, Func<ParentView?, bool> searchExpression)
{
if (view?.Parent is ParentView pv)
{
if (searchExpression(pv))
return pv;
return pv.FindParent(searchExpression);
}
return default;
}
#else
internal
#endif
static ParentView? FindParent(this ParentView? view, Func<ParentView?, bool> searchExpression)
{
while (view != null)
{
var parent = view?.GetParent() as ParentView;
if (searchExpression(parent))
return parent;
view = view?.GetParent() as ParentView;
}
return default;
}
#if WINDOWS || ANDROID
internal static T? GetParentOfType<T>(this PlatformView view)
#if ANDROID
where T : class, ParentView
#elif PLATFORM
where T : ParentView
#else
where T : class
#endif
{
if (view is T t)
return t;
return view.GetParent()?.GetParentOfType<T>();
}
#endif
internal static bool IsThisMyPlatformView(this IElement? element, PlatformView platformView)
{
if (element is not null &&
element.Handler is IPlatformViewHandler pvh)
{
return pvh.PlatformView == platformView || pvh.ContainerView == platformView;
}
return false;
}
internal static IDisposable OnUnloaded(this IElement element, Action action)
{
#if PLATFORM
if (element.Handler is IPlatformViewHandler platformViewHandler &&
platformViewHandler.PlatformView is not null)
{
#if IOS
if (platformViewHandler.ContainerView is IUIViewLifeCycleEvents)
return platformViewHandler.ContainerView.OnUnloaded(action);
#endif
return platformViewHandler.PlatformView.OnUnloaded(action);
}
throw new InvalidOperationException("Handler is not set on element");
#else
throw new NotImplementedException();
#endif
}
internal static IDisposable OnLoaded(this IElement element, Action action)
{
#if PLATFORM
if (element.Handler is IPlatformViewHandler platformViewHandler &&
platformViewHandler.PlatformView is not null)
{
#if IOS
if (platformViewHandler.ContainerView is IUIViewLifeCycleEvents)
return platformViewHandler.ContainerView.OnLoaded(action);
#endif
return platformViewHandler.PlatformView.OnLoaded(action);
}
throw new InvalidOperationException("Handler is not set on element");
#else
throw new NotImplementedException();
#endif
}
#if PLATFORM
internal static Task OnUnloadedAsync(this PlatformView platformView, TimeSpan? timeOut = null)
{
timeOut = timeOut ?? TimeSpan.FromSeconds(2);
TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>();
IDisposable? token = null;
token = platformView.OnUnloaded(() =>
{
taskCompletionSource.SetResult(true);
token?.Dispose();
token = null;
});
return taskCompletionSource.Task.WaitAsync(timeOut.Value);
}
internal static Task OnLoadedAsync(this PlatformView platformView, TimeSpan? timeOut = null)
{
timeOut = timeOut ?? TimeSpan.FromSeconds(2);
TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>();
IDisposable? token = null;
token = platformView.OnLoaded(() =>
{
taskCompletionSource.SetResult(true);
token?.Dispose();
token = null;
});
return taskCompletionSource.Task.WaitAsync(timeOut.Value);
}
#endif
internal static bool IsLoadedOnPlatform(this IElement element)
{
if (element.Handler is not IPlatformViewHandler pvh)
return false;
#if PLATFORM
return pvh.PlatformView?.IsLoaded() == true;
#else
return true;
#endif
}
#if PLATFORM
internal static T? FindDescendantView<T>(this PlatformView view, Func<PlatformView, bool> predicate) where T : PlatformView
{
var queue = new Queue<PlatformView>();
queue.Enqueue(view);
while (queue.Count > 0)
{
var descendantView = queue.Dequeue();
if (descendantView is T result && predicate.Invoke(result))
return result;
int i = 0;
PlatformView? child;
while ((child = descendantView?.GetChildAt<PlatformView>(i)) is not null)
{
#if TIZEN
// I had to add this check for Tizen to compile.
// I think Tizen isn't accounting for the null check
// in the while loop correctly
if (child is null)
break;
#endif
queue.Enqueue(child);
i++;
}
}
return null;
}
#endif
}
}