You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
16 KiB
437 lines
16 KiB
using System.Collections.Generic; |
|
using System.Text; |
|
using UnityEngine.UI; |
|
|
|
namespace UnityEngine.EventSystems |
|
{ |
|
/// <summary> |
|
/// A BaseInputModule for pointer input. |
|
/// </summary> |
|
public abstract class PointerInputModule : BaseInputModule |
|
{ |
|
/// <summary> |
|
/// Id of the cached left mouse pointer event. |
|
/// </summary> |
|
public const int kMouseLeftId = -1; |
|
|
|
/// <summary> |
|
/// Id of the cached right mouse pointer event. |
|
/// </summary> |
|
public const int kMouseRightId = -2; |
|
|
|
/// <summary> |
|
/// Id of the cached middle mouse pointer event. |
|
/// </summary> |
|
public const int kMouseMiddleId = -3; |
|
|
|
/// <summary> |
|
/// Touch id for when simulating touches on a non touch device. |
|
/// </summary> |
|
public const int kFakeTouchesId = -4; |
|
|
|
protected Dictionary<int, PointerEventData> m_PointerData = new Dictionary<int, PointerEventData>(); |
|
|
|
/// <summary> |
|
/// Search the cache for currently active pointers, return true if found. |
|
/// </summary> |
|
/// <param name="id">Touch ID</param> |
|
/// <param name="data">Found data</param> |
|
/// <param name="create">If not found should it be created</param> |
|
/// <returns>True if pointer is found.</returns> |
|
protected bool GetPointerData(int id, out PointerEventData data, bool create) |
|
{ |
|
if (!m_PointerData.TryGetValue(id, out data) && create) |
|
{ |
|
data = new PointerEventData(eventSystem) |
|
{ |
|
pointerId = id, |
|
}; |
|
m_PointerData.Add(id, data); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Remove the PointerEventData from the cache. |
|
/// </summary> |
|
protected void RemovePointerData(PointerEventData data) |
|
{ |
|
m_PointerData.Remove(data.pointerId); |
|
} |
|
|
|
/// <summary> |
|
/// Given a touch populate the PointerEventData and return if we are pressed or released. |
|
/// </summary> |
|
/// <param name="input">Touch being processed</param> |
|
/// <param name="pressed">Are we pressed this frame</param> |
|
/// <param name="released">Are we released this frame</param> |
|
/// <returns></returns> |
|
protected PointerEventData GetTouchPointerEventData(Touch input, out bool pressed, out bool released) |
|
{ |
|
PointerEventData pointerData; |
|
var created = GetPointerData(input.fingerId, out pointerData, true); |
|
|
|
pointerData.Reset(); |
|
|
|
pressed = created || (input.phase == TouchPhase.Began); |
|
released = (input.phase == TouchPhase.Canceled) || (input.phase == TouchPhase.Ended); |
|
|
|
if (created) |
|
pointerData.position = input.position; |
|
|
|
if (pressed) |
|
pointerData.delta = Vector2.zero; |
|
else |
|
pointerData.delta = input.position - pointerData.position; |
|
|
|
pointerData.position = input.position; |
|
|
|
pointerData.button = PointerEventData.InputButton.Left; |
|
|
|
if (input.phase == TouchPhase.Canceled) |
|
{ |
|
pointerData.pointerCurrentRaycast = new RaycastResult(); |
|
} |
|
else |
|
{ |
|
eventSystem.RaycastAll(pointerData, m_RaycastResultCache); |
|
|
|
var raycast = FindFirstRaycast(m_RaycastResultCache); |
|
pointerData.pointerCurrentRaycast = raycast; |
|
m_RaycastResultCache.Clear(); |
|
} |
|
|
|
pointerData.pressure = input.pressure; |
|
pointerData.altitudeAngle = input.altitudeAngle; |
|
pointerData.azimuthAngle = input.azimuthAngle; |
|
pointerData.radius = Vector2.one * input.radius; |
|
pointerData.radiusVariance = Vector2.one * input.radiusVariance; |
|
|
|
return pointerData; |
|
} |
|
|
|
/// <summary> |
|
/// Copy one PointerEventData to another. |
|
/// </summary> |
|
protected void CopyFromTo(PointerEventData @from, PointerEventData @to) |
|
{ |
|
@to.position = @from.position; |
|
@to.delta = @from.delta; |
|
@to.scrollDelta = @from.scrollDelta; |
|
@to.pointerCurrentRaycast = @from.pointerCurrentRaycast; |
|
@to.pointerEnter = @from.pointerEnter; |
|
|
|
@to.pressure = @from.pressure; |
|
@to.tangentialPressure = @from.tangentialPressure; |
|
@to.altitudeAngle = @from.altitudeAngle; |
|
@to.azimuthAngle = @from.azimuthAngle; |
|
@to.twist = @from.twist; |
|
@to.radius = @from.radius; |
|
@to.radiusVariance = @from.radiusVariance; |
|
} |
|
|
|
/// <summary> |
|
/// Given a mouse button return the current state for the frame. |
|
/// </summary> |
|
/// <param name="buttonId">Mouse button ID</param> |
|
protected PointerEventData.FramePressState StateForMouseButton(int buttonId) |
|
{ |
|
var pressed = input.GetMouseButtonDown(buttonId); |
|
var released = input.GetMouseButtonUp(buttonId); |
|
if (pressed && released) |
|
return PointerEventData.FramePressState.PressedAndReleased; |
|
if (pressed) |
|
return PointerEventData.FramePressState.Pressed; |
|
if (released) |
|
return PointerEventData.FramePressState.Released; |
|
return PointerEventData.FramePressState.NotChanged; |
|
} |
|
|
|
protected class ButtonState |
|
{ |
|
private PointerEventData.InputButton m_Button = PointerEventData.InputButton.Left; |
|
|
|
public MouseButtonEventData eventData |
|
{ |
|
get { return m_EventData; } |
|
set { m_EventData = value; } |
|
} |
|
|
|
public PointerEventData.InputButton button |
|
{ |
|
get { return m_Button; } |
|
set { m_Button = value; } |
|
} |
|
|
|
private MouseButtonEventData m_EventData; |
|
} |
|
|
|
protected class MouseState |
|
{ |
|
private List<ButtonState> m_TrackedButtons = new List<ButtonState>(); |
|
|
|
public bool AnyPressesThisFrame() |
|
{ |
|
var trackedButtonsCount = m_TrackedButtons.Count; |
|
for (int i = 0; i < trackedButtonsCount; i++) |
|
{ |
|
if (m_TrackedButtons[i].eventData.PressedThisFrame()) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
public bool AnyReleasesThisFrame() |
|
{ |
|
var trackedButtonsCount = m_TrackedButtons.Count; |
|
for (int i = 0; i < trackedButtonsCount; i++) |
|
{ |
|
if (m_TrackedButtons[i].eventData.ReleasedThisFrame()) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
public ButtonState GetButtonState(PointerEventData.InputButton button) |
|
{ |
|
ButtonState tracked = null; |
|
var trackedButtonsCount = m_TrackedButtons.Count; |
|
for (int i = 0; i < trackedButtonsCount; i++) |
|
{ |
|
if (m_TrackedButtons[i].button == button) |
|
{ |
|
tracked = m_TrackedButtons[i]; |
|
break; |
|
} |
|
} |
|
|
|
if (tracked == null) |
|
{ |
|
tracked = new ButtonState { button = button, eventData = new MouseButtonEventData() }; |
|
m_TrackedButtons.Add(tracked); |
|
} |
|
return tracked; |
|
} |
|
|
|
public void SetButtonState(PointerEventData.InputButton button, PointerEventData.FramePressState stateForMouseButton, PointerEventData data) |
|
{ |
|
var toModify = GetButtonState(button); |
|
toModify.eventData.buttonState = stateForMouseButton; |
|
toModify.eventData.buttonData = data; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Information about a mouse button event. |
|
/// </summary> |
|
public class MouseButtonEventData |
|
{ |
|
/// <summary> |
|
/// The state of the button this frame. |
|
/// </summary> |
|
public PointerEventData.FramePressState buttonState; |
|
|
|
/// <summary> |
|
/// Pointer data associated with the mouse event. |
|
/// </summary> |
|
public PointerEventData buttonData; |
|
|
|
/// <summary> |
|
/// Was the button pressed this frame? |
|
/// </summary> |
|
public bool PressedThisFrame() |
|
{ |
|
return buttonState == PointerEventData.FramePressState.Pressed || buttonState == PointerEventData.FramePressState.PressedAndReleased; |
|
} |
|
|
|
/// <summary> |
|
/// Was the button released this frame? |
|
/// </summary> |
|
public bool ReleasedThisFrame() |
|
{ |
|
return buttonState == PointerEventData.FramePressState.Released || buttonState == PointerEventData.FramePressState.PressedAndReleased; |
|
} |
|
} |
|
|
|
private readonly MouseState m_MouseState = new MouseState(); |
|
|
|
/// <summary> |
|
/// Return the current MouseState. Using the default pointer. |
|
/// </summary> |
|
protected virtual MouseState GetMousePointerEventData() |
|
{ |
|
return GetMousePointerEventData(0); |
|
} |
|
|
|
/// <summary> |
|
/// Return the current MouseState. |
|
/// </summary> |
|
protected virtual MouseState GetMousePointerEventData(int id) |
|
{ |
|
// Populate the left button... |
|
PointerEventData leftData; |
|
var created = GetPointerData(kMouseLeftId, out leftData, true); |
|
|
|
leftData.Reset(); |
|
|
|
if (created) |
|
leftData.position = input.mousePosition; |
|
|
|
Vector2 pos = input.mousePosition; |
|
if (Cursor.lockState == CursorLockMode.Locked) |
|
{ |
|
// We don't want to do ANY cursor-based interaction when the mouse is locked |
|
leftData.position = new Vector2(-1.0f, -1.0f); |
|
leftData.delta = Vector2.zero; |
|
} |
|
else |
|
{ |
|
leftData.delta = pos - leftData.position; |
|
leftData.position = pos; |
|
} |
|
leftData.scrollDelta = input.mouseScrollDelta; |
|
leftData.button = PointerEventData.InputButton.Left; |
|
eventSystem.RaycastAll(leftData, m_RaycastResultCache); |
|
var raycast = FindFirstRaycast(m_RaycastResultCache); |
|
leftData.pointerCurrentRaycast = raycast; |
|
m_RaycastResultCache.Clear(); |
|
|
|
// copy the apropriate data into right and middle slots |
|
PointerEventData rightData; |
|
GetPointerData(kMouseRightId, out rightData, true); |
|
rightData.Reset(); |
|
|
|
CopyFromTo(leftData, rightData); |
|
rightData.button = PointerEventData.InputButton.Right; |
|
|
|
PointerEventData middleData; |
|
GetPointerData(kMouseMiddleId, out middleData, true); |
|
middleData.Reset(); |
|
|
|
CopyFromTo(leftData, middleData); |
|
middleData.button = PointerEventData.InputButton.Middle; |
|
|
|
m_MouseState.SetButtonState(PointerEventData.InputButton.Left, StateForMouseButton(0), leftData); |
|
m_MouseState.SetButtonState(PointerEventData.InputButton.Right, StateForMouseButton(1), rightData); |
|
m_MouseState.SetButtonState(PointerEventData.InputButton.Middle, StateForMouseButton(2), middleData); |
|
|
|
return m_MouseState; |
|
} |
|
|
|
/// <summary> |
|
/// Return the last PointerEventData for the given touch / mouse id. |
|
/// </summary> |
|
protected PointerEventData GetLastPointerEventData(int id) |
|
{ |
|
PointerEventData data; |
|
GetPointerData(id, out data, false); |
|
return data; |
|
} |
|
|
|
private static bool ShouldStartDrag(Vector2 pressPos, Vector2 currentPos, float threshold, bool useDragThreshold) |
|
{ |
|
if (!useDragThreshold) |
|
return true; |
|
|
|
return (pressPos - currentPos).sqrMagnitude >= threshold * threshold; |
|
} |
|
|
|
/// <summary> |
|
/// Process movement for the current frame with the given pointer event. |
|
/// </summary> |
|
protected virtual void ProcessMove(PointerEventData pointerEvent) |
|
{ |
|
var targetGO = (Cursor.lockState == CursorLockMode.Locked ? null : pointerEvent.pointerCurrentRaycast.gameObject); |
|
HandlePointerExitAndEnter(pointerEvent, targetGO); |
|
} |
|
|
|
/// <summary> |
|
/// Process the drag for the current frame with the given pointer event. |
|
/// </summary> |
|
protected virtual void ProcessDrag(PointerEventData pointerEvent) |
|
{ |
|
if (!pointerEvent.IsPointerMoving() || |
|
Cursor.lockState == CursorLockMode.Locked || |
|
pointerEvent.pointerDrag == null) |
|
return; |
|
|
|
if (!pointerEvent.dragging |
|
&& ShouldStartDrag(pointerEvent.pressPosition, pointerEvent.position, eventSystem.pixelDragThreshold, pointerEvent.useDragThreshold)) |
|
{ |
|
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.beginDragHandler); |
|
pointerEvent.dragging = true; |
|
} |
|
|
|
// Drag notification |
|
if (pointerEvent.dragging) |
|
{ |
|
// Before doing drag we should cancel any pointer down state |
|
// And clear selection! |
|
if (pointerEvent.pointerPress != pointerEvent.pointerDrag) |
|
{ |
|
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler); |
|
|
|
pointerEvent.eligibleForClick = false; |
|
pointerEvent.pointerPress = null; |
|
pointerEvent.rawPointerPress = null; |
|
} |
|
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.dragHandler); |
|
} |
|
} |
|
|
|
public override bool IsPointerOverGameObject(int pointerId) |
|
{ |
|
var lastPointer = GetLastPointerEventData(pointerId); |
|
if (lastPointer != null) |
|
return lastPointer.pointerEnter != null; |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Clear all pointers and deselect any selected objects in the EventSystem. |
|
/// </summary> |
|
protected void ClearSelection() |
|
{ |
|
var baseEventData = GetBaseEventData(); |
|
|
|
foreach (var pointer in m_PointerData.Values) |
|
{ |
|
// clear all selection |
|
HandlePointerExitAndEnter(pointer, null); |
|
} |
|
|
|
m_PointerData.Clear(); |
|
eventSystem.SetSelectedGameObject(null, baseEventData); |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
var sb = new StringBuilder("<b>Pointer Input Module of type: </b>" + GetType()); |
|
sb.AppendLine(); |
|
foreach (var pointer in m_PointerData) |
|
{ |
|
if (pointer.Value == null) |
|
continue; |
|
sb.AppendLine("<B>Pointer:</b> " + pointer.Key); |
|
sb.AppendLine(pointer.Value.ToString()); |
|
} |
|
return sb.ToString(); |
|
} |
|
|
|
/// <summary> |
|
/// Deselect the current selected GameObject if the currently pointed-at GameObject is different. |
|
/// </summary> |
|
/// <param name="currentOverGo">The GameObject the pointer is currently over.</param> |
|
/// <param name="pointerEvent">Current event data.</param> |
|
protected void DeselectIfSelectionChanged(GameObject currentOverGo, BaseEventData pointerEvent) |
|
{ |
|
// Selection tracking |
|
var selectHandlerGO = ExecuteEvents.GetEventHandler<ISelectHandler>(currentOverGo); |
|
// if we have clicked something new, deselect the old thing |
|
// leave 'selection handling' up to the press event though. |
|
if (selectHandlerGO != eventSystem.currentSelectedGameObject) |
|
eventSystem.SetSelectedGameObject(null, pointerEvent); |
|
} |
|
} |
|
}
|
|
|