439 lines
16 KiB
C#
439 lines
16 KiB
C#
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!
|
||
//修改过,原因:ScrollView拖拽会强制触发pointerUpHandler,就给它干掉了
|
||
//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);
|
||
}
|
||
}
|
||
}
|