using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine.EventSystems; namespace UnityEngine.UIElements { #if PACKAGE_UITOOLKIT /// /// A derived BaseRaycaster to raycast against UI Toolkit panel instances at runtime. /// [AddComponentMenu("UI Toolkit/Panel Raycaster (UI Toolkit)")] public class PanelRaycaster : BaseRaycaster, IRuntimePanelComponent { private BaseRuntimePanel m_Panel; /// /// The panel that this component relates to. If panel is null, this component will have no effect. /// Will be set to null automatically if panel is Disposed from an external source. /// public IPanel panel { get => m_Panel; set { var newPanel = (BaseRuntimePanel)value; if (m_Panel != newPanel) { UnregisterCallbacks(); m_Panel = newPanel; RegisterCallbacks(); } } } void RegisterCallbacks() { if (m_Panel != null) { m_Panel.destroyed += OnPanelDestroyed; } } void UnregisterCallbacks() { if (m_Panel != null) { m_Panel.destroyed -= OnPanelDestroyed; } } void OnPanelDestroyed() { panel = null; } private GameObject selectableGameObject => m_Panel?.selectableGameObject; public override int sortOrderPriority => (int)(m_Panel?.sortingPriority ?? 0f); public override int renderOrderPriority => ConvertFloatBitsToInt(m_Panel?.sortingPriority ?? 0f); public override void Raycast(PointerEventData eventData, List resultAppendList) { if (m_Panel == null) return; var eventPosition = Display.RelativeMouseAt(eventData.position); var displayIndex = m_Panel.targetDisplay; var originalEventPosition = eventPosition; if (eventPosition != Vector3.zero) { // We support multiple display and display identification based on event position. int eventDisplayIndex = (int)eventPosition.z; // Discard events that are not part of this display so the user does not interact with multiple displays at once. if (eventDisplayIndex != displayIndex) return; } else { // The multiple display system is not supported on all platforms, when it is not supported the returned position // will be all zeros so when the returned index is 0 we will default to the event data to be safe. eventPosition = eventData.position; #if UNITY_EDITOR if (Display.activeEditorGameViewTarget != displayIndex) return; eventPosition.z = Display.activeEditorGameViewTarget; #endif // We don't really know in which display the event occurred. We will process the event assuming it occurred in our display. } var position = eventPosition; var delta = eventData.delta; float h = Screen.height; if (displayIndex > 0 && displayIndex < Display.displays.Length) { h = Display.displays[displayIndex].systemHeight; } position.y = h - position.y; delta.y = -delta.y; var eventSystem = UIElementsRuntimeUtility.activeEventSystem as EventSystem; if (eventSystem == null || eventSystem.currentInputModule == null) return; var pointerId = eventSystem.currentInputModule.ConvertUIToolkitPointerId(eventData); var capturingElement = m_Panel.GetCapturingElement(pointerId); if (capturingElement is VisualElement ve && ve.panel != m_Panel) return; var capturingPanel = PointerDeviceState.GetPressedButtons(pointerId) != 0 ? PointerDeviceState.GetPlayerPanelWithSoftPointerCapture(pointerId) : null; if (capturingPanel != null && capturingPanel != m_Panel) return; if (capturingElement == null && capturingPanel == null) { if (!m_Panel.ScreenToPanel(position, delta, out var panelPosition, out _)) return; var pick = m_Panel.Pick(panelPosition); if (pick == null) return; } resultAppendList.Add(new RaycastResult { gameObject = selectableGameObject, module = this, screenPosition = eventPosition, displayIndex = m_Panel.targetDisplay, }); } public override Camera eventCamera => null; [StructLayout(LayoutKind.Explicit, Size = sizeof(int))] private struct FloatIntBits { [FieldOffset(0)] public float f; [FieldOffset(0)] public int i; } private static int ConvertFloatBitsToInt(float f) { FloatIntBits bits = new FloatIntBits {f = f}; return bits.i; } } #endif }