/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine;
using UnityEngine.Assertions;
using System;
namespace Oculus.Interaction
{
public class PointableCanvasEventArgs
{
public readonly Canvas Canvas;
public readonly GameObject Hovered;
public readonly bool Dragging;
public PointableCanvasEventArgs(Canvas canvas, GameObject hovered, bool dragging)
{
Canvas = canvas;
Hovered = hovered;
Dragging = dragging;
}
}
///
/// IPointerInteractableModule manages all InteractableCanvas events in
/// the scene and translates them into pointer events for Unity Canvas UIs.
///
public class PointableCanvasModule : PointerInputModule
{
public static event Action WhenSelected;
public static event Action WhenUnselected;
public static event Action WhenSelectableHovered;
public static event Action WhenSelectableUnhovered;
[SerializeField]
private bool _useInitialPressPositionForDrag = true;
private Camera _pointerEventCamera;
private static PointableCanvasModule _instance = null;
private static PointableCanvasModule Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType();
}
return _instance;
}
}
public static void RegisterPointableCanvas(IPointableCanvas pointerCanvas)
{
Assert.IsNotNull(Instance, "A PointableCanvasModule is required in the scene.");
Instance.AddPointerCanvas(pointerCanvas);
}
public static void UnregisterPointableCanvas(IPointableCanvas pointerCanvas)
{
Instance?.RemovePointerCanvas(pointerCanvas);
}
private Dictionary _pointerMap = new Dictionary();
private List _raycastResultCache = new List();
private List _pointersForDeletion = new List();
private Dictionary> _pointerCanvasActionMap =
new Dictionary>();
private void AddPointerCanvas(IPointableCanvas pointerCanvas)
{
Action pointerCanvasAction = (args) => HandlePointerEvent(pointerCanvas.Canvas, args);
_pointerCanvasActionMap.Add(pointerCanvas, pointerCanvasAction);
pointerCanvas.OnPointerEvent += pointerCanvasAction;
}
private void RemovePointerCanvas(IPointableCanvas pointerCanvas)
{
Action pointerCanvasAction = _pointerCanvasActionMap[pointerCanvas];
_pointerCanvasActionMap.Remove(pointerCanvas);
pointerCanvas.OnPointerEvent -= pointerCanvasAction;
List pointerIDs = new List(_pointerMap.Keys);
foreach (int pointerID in pointerIDs)
{
Pointer pointer = _pointerMap[pointerID];
if (pointer.Canvas != pointerCanvas.Canvas)
{
continue;
}
pointer.MarkForDeletion();
_pointersForDeletion.Add(pointer);
_pointerMap.Remove(pointerID);
}
}
private void HandlePointerEvent(Canvas canvas, PointerArgs args)
{
Pointer pointer;
switch (args.PointerEvent)
{
case PointerEvent.Hover:
pointer = new Pointer(canvas);
pointer.PointerEventData = new PointerEventData(eventSystem);
pointer.SetPosition(args.Position);
_pointerMap.Add(args.Identifier, pointer);
break;
case PointerEvent.Unhover:
pointer = _pointerMap[args.Identifier];
_pointerMap.Remove(args.Identifier);
pointer.MarkForDeletion();
_pointersForDeletion.Add(pointer);
break;
case PointerEvent.Select:
pointer = _pointerMap[args.Identifier];
pointer.SetPosition(args.Position);
pointer.Press();
break;
case PointerEvent.Unselect:
pointer = _pointerMap[args.Identifier];
pointer.SetPosition(args.Position);
pointer.Release();
break;
case PointerEvent.Move:
pointer = _pointerMap[args.Identifier];
pointer.SetPosition(args.Position);
break;
}
}
///
/// Pointer class that is used for state associated with IPointables that are currently
/// tracked by any IPointableCanvases in the scene.
///
private class Pointer
{
public PointerEventData PointerEventData { get; set; }
public bool MarkedForDeletion { get; private set; }
private Canvas _canvas;
public Canvas Canvas => _canvas;
private Vector3 _position;
public Vector3 Position => _position;
private GameObject _hoveredSelectable;
public GameObject HoveredSelectable => _hoveredSelectable;
private bool _pressing = false;
private bool _pressed;
private bool _released;
public Pointer(Canvas canvas)
{
_canvas = canvas;
_pressed = _released = false;
}
public void Press()
{
if (_pressing) return;
_pressing = true;
_pressed = true;
}
public void Release()
{
if (!_pressing) return;
_pressing = false;
_released = true;
}
public void ReadAndResetPressedReleased(out bool pressed, out bool released)
{
pressed = _pressed;
released = _released;
_pressed = _released = false;
}
public void MarkForDeletion()
{
MarkedForDeletion = true;
Release();
}
public void SetPosition(Vector3 position)
{
_position = position;
}
public void SetHoveredSelectable(GameObject hoveredSelectable)
{
_hoveredSelectable = hoveredSelectable;
}
}
protected bool _started = false;
protected override void Start()
{
this.BeginStart(ref _started, base.Start);
this.EndStart(ref _started);
}
protected override void OnEnable()
{
base.OnEnable();
if (_started)
{
_pointerEventCamera = gameObject.AddComponent();
_pointerEventCamera.nearClipPlane = 0.1f;
// We do not need this camera to be enabled to serve this module's purposes:
// as a dependency for Canvases and for its WorldToScreenPoint functionality
_pointerEventCamera.enabled = false;
}
}
protected override void OnDisable()
{
if (_started)
{
Destroy(_pointerEventCamera);
_pointerEventCamera = null;
}
base.OnDisable();
}
// Based On FindFirstRaycast
protected static RaycastResult FindFirstRaycastWithinCanvas(List candidates, Canvas canvas)
{
GameObject candidateGameObject;
Canvas candidateCanvas;
for (var i = 0; i < candidates.Count; ++i)
{
candidateGameObject = candidates[i].gameObject;
if (candidateGameObject == null) continue;
candidateCanvas = candidateGameObject.GetComponentInParent