// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Physics;
using System.Collections.Generic;
using Unity.Profiling;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Microsoft.MixedReality.Toolkit
{
///
/// Extension methods for Unity's EventSystem
///
public static class EventSystemExtensions
{
private static readonly List RaycastResults = new List();
private static readonly RaycastResultComparer RaycastResultComparer = new RaycastResultComparer();
private static readonly ProfilerMarker RaycastPerfMarker = new ProfilerMarker("[MRTK] EventSystemExtensions.Raycast");
///
/// Executes a raycast all and returns the closest element.
/// Fixes the current issue with Unity's raycast sorting which does not consider separate canvases.
///
///
/// Takes an optional RaycastResultComparer, which will be used to select the highest priority raycast result.
///
/// RaycastResult if hit, or an empty RaycastResult if nothing was hit
public static RaycastResult Raycast(this EventSystem eventSystem, PointerEventData pointerEventData, LayerMask[] layerMasks, RaycastResultComparer raycastResultComparer = null)
{
using (RaycastPerfMarker.Auto())
{
eventSystem.RaycastAll(pointerEventData, RaycastResults);
return PrioritizeRaycastResult(layerMasks, raycastResultComparer);
}
}
private static readonly ProfilerMarker PrioritizeRaycastResultPerfMarker = new ProfilerMarker("[MRTK] EventSystemExtensions.PrioritizeRaycastResult");
///
/// Sorts the available Raycasts in to a priority order for query.
///
/// The layer mask priority.
///
private static RaycastResult PrioritizeRaycastResult(LayerMask[] priority, RaycastResultComparer raycastResultComparer)
{
using (PrioritizeRaycastResultPerfMarker.Auto())
{
// If not specified, default to the in-box RaycastResultComparer.
if (raycastResultComparer == null)
{
raycastResultComparer = RaycastResultComparer;
}
ComparableRaycastResult maxResult = default(ComparableRaycastResult);
for (var i = 0; i < RaycastResults.Count; i++)
{
if (RaycastResults[i].gameObject == null) { continue; }
var layerMaskIndex = RaycastResults[i].gameObject.layer.FindLayerListIndex(priority);
if (layerMaskIndex == -1) { continue; }
var result = new ComparableRaycastResult(RaycastResults[i], layerMaskIndex);
if (maxResult.RaycastResult.module == null || raycastResultComparer.Compare(maxResult, result) < 0)
{
maxResult = result;
}
}
return maxResult.RaycastResult;
}
}
///
/// Bubbles up an event to the parents of the root game object if the event data is not already used.
///
/// The EventFunction type.
/// Events start executing on the parent of this game object.
/// Data associated with the Executing event.
/// Function to execute on the gameObject components.
/// GameObject that handled the event
public static GameObject ExecuteHierarchyUpward(GameObject root, BaseEventData eventData, ExecuteEvents.EventFunction callbackFunction) where T : IEventSystemHandler
{
if (!eventData.used && root != null)
{
var parent = root.transform.parent;
if (parent != null)
{
return ExecuteEvents.ExecuteHierarchy(parent.gameObject, eventData, callbackFunction);
}
}
return null;
}
}
}