File: Platform\ViewExtensions.cs
Web Access
Project: src\src\Core\src\Core.csproj (Microsoft.Maui)
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
	}
}