using System; using System.Collections.Generic; using UnityEngine.EventSystems; namespace UnityEngine.XR.Interaction.Toolkit.UI { /// /// A series of flags to determine if a button has been pressed or released since the last time checked. /// Useful for identifying press/release events that occur in a single frame or sample. /// [Flags] public enum ButtonDeltaState { /// /// No change since last time checked. /// NoChange = 0, /// /// Button pressed since last time checked. /// Pressed = 1 << 0, /// /// Button released since last time checked. /// Released = 1 << 1, } /// /// Represents the state of a single mouse button within the Unity UI (UGUI) system. Keeps track of various book-keeping regarding clicks, drags, and presses. /// Can be converted to and from PointerEventData for sending into Unity UI (UGUI). /// public struct MouseButtonModel { internal struct ImplementationData { /// /// Used to cache whether or not the current mouse button is being dragged. /// /// public bool isDragging { get; set; } /// /// Used to cache the last time this button was pressed. /// /// public float pressedTime { get; set; } /// /// The position on the screen that this button was last pressed. /// In the same scale as , and caches the same value as . /// /// public Vector2 pressedPosition { get; set; } /// /// The Raycast data from the time it was pressed. /// /// public RaycastResult pressedRaycast { get; set; } /// /// The last GameObject pressed on that can handle press or click events. /// /// public GameObject pressedGameObject { get; set; } /// /// The last GameObject pressed on regardless of whether it can handle events or not. /// /// public GameObject pressedGameObjectRaw { get; set; } /// /// The GameObject currently being dragged if any. /// /// public GameObject draggedGameObject { get; set; } /// /// Resets this object to its default, unused state. /// public void Reset() { isDragging = false; pressedTime = 0f; pressedPosition = Vector2.zero; pressedRaycast = new RaycastResult(); pressedGameObject = pressedGameObjectRaw = draggedGameObject = null; } } /// /// Used to store the current binary state of the button. When set, will also track the changes between calls of in . /// public bool isDown { get => m_IsDown; set { if (m_IsDown != value) { m_IsDown = value; lastFrameDelta |= value ? ButtonDeltaState.Pressed : ButtonDeltaState.Released; } } } /// /// A set of flags to identify the changes that have occurred between calls of . /// internal ButtonDeltaState lastFrameDelta { get; private set; } /// /// Resets this object to it's default, unused state. /// public void Reset() { lastFrameDelta = ButtonDeltaState.NoChange; m_IsDown = false; m_ImplementationData.Reset(); } /// /// Call this on each frame in order to reset properties that detect whether or not a certain condition was met this frame. /// public void OnFrameFinished() => lastFrameDelta = ButtonDeltaState.NoChange; /// /// Fills a with this mouse button's internally cached values. /// /// These objects are used to send data through the Unity UI (UGUI) system. public void CopyTo(PointerEventData eventData) { eventData.dragging = m_ImplementationData.isDragging; eventData.clickTime = m_ImplementationData.pressedTime; eventData.pressPosition = m_ImplementationData.pressedPosition; eventData.pointerPressRaycast = m_ImplementationData.pressedRaycast; eventData.pointerPress = m_ImplementationData.pressedGameObject; eventData.rawPointerPress = m_ImplementationData.pressedGameObjectRaw; eventData.pointerDrag = m_ImplementationData.draggedGameObject; } /// /// Fills this object with the values from a . /// /// These objects are used to send data through the Unity UI (UGUI) system. public void CopyFrom(PointerEventData eventData) { m_ImplementationData.isDragging = eventData.dragging; m_ImplementationData.pressedTime = eventData.clickTime; m_ImplementationData.pressedPosition = eventData.pressPosition; m_ImplementationData.pressedRaycast = eventData.pointerPressRaycast; m_ImplementationData.pressedGameObject = eventData.pointerPress; m_ImplementationData.pressedGameObjectRaw = eventData.rawPointerPress; m_ImplementationData.draggedGameObject = eventData.pointerDrag; } bool m_IsDown; ImplementationData m_ImplementationData; } struct PointerModel { internal struct InternalData { /// /// This tracks the current GUI targets being hovered over. /// /// public List hoverTargets { get; set; } /// /// Tracks the current enter/exit target being hovered over at any given moment. /// /// public GameObject pointerTarget { get; set; } public void Reset() { pointerTarget = null; if (hoverTargets == null) hoverTargets = new List(); else hoverTargets.Clear(); } } /// /// An Id representing a unique pointer. /// public int pointerId { get; } /// /// A boolean value representing whether any mouse data has changed this frame, meaning that events should be processed. /// /// /// This only checks for changes in mouse state (, , , , or ). /// public bool changedThisFrame { get; private set; } int m_DisplayIndex; /// /// The index of the display that the mouse is currently on. /// public int displayIndex { get => m_DisplayIndex; set { if (m_DisplayIndex != value) { m_DisplayIndex = value; changedThisFrame = true; } } } Vector2 m_Position; public Vector2 position { get => m_Position; set { if (m_Position != value) { deltaPosition = value - m_Position; m_Position = value; changedThisFrame = true; } } } /// /// The pixel-space change in since the last call to . /// public Vector2 deltaPosition { get; private set; } Vector2 m_ScrollDelta; /// /// The amount of scroll since the last call to . /// public Vector2 scrollDelta { get => m_ScrollDelta; set { if (m_ScrollDelta != value) { m_ScrollDelta = value; changedThisFrame = true; } } } MouseButtonModel m_LeftButton; /// /// Cached data and button state representing a left mouse button on a mouse. /// Used by Unity UI (UGUI) to keep track of persistent click, press, and drag states. /// public MouseButtonModel leftButton { get => m_LeftButton; set { changedThisFrame |= (value.lastFrameDelta != ButtonDeltaState.NoChange); m_LeftButton = value; } } /// /// Sets the pressed state of the left mouse button. /// public bool leftButtonPressed { set { changedThisFrame |= m_LeftButton.isDown != value; m_LeftButton.isDown = value; } } MouseButtonModel m_RightButton; /// /// Cached data and button state representing a right mouse button on a mouse. /// Unity UI (UGUI) uses this to keep track of persistent click, press, and drag states. /// public MouseButtonModel rightButton { get => m_RightButton; set { changedThisFrame |= (value.lastFrameDelta != ButtonDeltaState.NoChange); m_RightButton = value; } } /// /// Sets the pressed state of the right mouse button. /// public bool rightButtonPressed { set { changedThisFrame |= m_RightButton.isDown != value; m_RightButton.isDown = value; } } MouseButtonModel m_MiddleButton; /// /// Cached data and button state representing a middle mouse button on a mouse. /// Used by Unity UI (UGUI) to keep track of persistent click, press, and drag states. /// public MouseButtonModel middleButton { get => m_MiddleButton; set { changedThisFrame |= (value.lastFrameDelta != ButtonDeltaState.NoChange); m_MiddleButton = value; } } /// /// Sets the pressed state of the middle mouse button. /// public bool middleButtonPressed { set { changedThisFrame |= m_MiddleButton.isDown != value; m_MiddleButton.isDown = value; } } InternalData m_InternalData; public PointerModel(int pointerId) { this.pointerId = pointerId; changedThisFrame = false; m_DisplayIndex = 0; m_Position = Vector2.zero; deltaPosition = Vector2.zero; m_ScrollDelta = Vector2.zero; m_LeftButton = new MouseButtonModel(); m_RightButton = new MouseButtonModel(); m_MiddleButton = new MouseButtonModel(); m_LeftButton.Reset(); m_RightButton.Reset(); m_MiddleButton.Reset(); m_InternalData = new InternalData(); m_InternalData.Reset(); } /// /// Call this at the end of polling for per-frame changes. This resets delta values, such as , , and . /// public void OnFrameFinished() { changedThisFrame = false; deltaPosition = Vector2.zero; m_ScrollDelta = Vector2.zero; m_LeftButton.OnFrameFinished(); m_RightButton.OnFrameFinished(); m_MiddleButton.OnFrameFinished(); } public void CopyTo(PointerEventData eventData) { eventData.pointerId = pointerId; #if UNITY_2022_3_OR_NEWER eventData.displayIndex = m_DisplayIndex; #endif eventData.position = position; eventData.delta = deltaPosition; eventData.scrollDelta = scrollDelta; eventData.pointerEnter = m_InternalData.pointerTarget; eventData.hovered.Clear(); eventData.hovered.AddRange(m_InternalData.hoverTargets); } public void CopyFrom(PointerEventData eventData) { var hoverTargets = m_InternalData.hoverTargets; m_InternalData.hoverTargets.Clear(); m_InternalData.hoverTargets.AddRange(eventData.hovered); m_InternalData.hoverTargets = hoverTargets; m_InternalData.pointerTarget = eventData.pointerEnter; #if UNITY_2022_3_OR_NEWER m_DisplayIndex = eventData.displayIndex; #endif } } }