using System; using System.Collections.Generic; using UnityEngine.EventSystems; namespace UnityEngine.UIElements { // This code is disabled unless the com.unity.modules.uielements module is present. // The UIElements module is always present in the Editor but it can be stripped from a project build if unused. #if PACKAGE_UITOOLKIT /// /// Enables UI Toolkit interoperability with uGUI events. /// internal class UIToolkitInteroperabilityBridge { [Flags] public enum EventHandlerTypes { ScreenOverlay = 1, WorldSpace = 2, } private EventSystem m_EventSystem; internal EventSystem eventSystem { get => m_EventSystem; set { if (m_EventSystem == value) return; Debug.Assert(!m_Started && !m_Enabled, "Expect EventSystem set before OnEnable and Start"); m_EventSystem = value; } } private bool m_OverrideUIToolkitEvents = true; private EventHandlerTypes m_HandlerTypes = EventHandlerTypes.ScreenOverlay | EventHandlerTypes.WorldSpace; private LayerMask m_WorldPickingLayers = Physics.DefaultRaycastLayers; private float m_WorldPickingMaxDistance = Mathf.Infinity; private bool m_CreateDefaultPanelComponents = true; private bool m_Started; private bool m_Enabled; private bool m_IsTrackingPanels; private GameObject m_WorldSpaceGo; public bool overrideUIToolkitEvents { get => m_OverrideUIToolkitEvents; internal set { m_OverrideUIToolkitEvents = value; ApplyOverrideUIToolkitEvents(); } } public EventHandlerTypes handlerTypes { get => m_HandlerTypes; internal set { m_HandlerTypes = value; ApplyOtherProperties(); } } public int worldPickingLayers { get => m_WorldPickingLayers; internal set => m_WorldPickingLayers = value; } public float worldPickingMaxDistance { get => m_WorldPickingMaxDistance; internal set => m_WorldPickingMaxDistance = value; } public bool createDefaultPanelComponents { get => m_CreateDefaultPanelComponents; internal set { m_CreateDefaultPanelComponents = value; ApplyOtherProperties(); } } private bool shouldTrackPanels => overrideUIToolkitEvents && createDefaultPanelComponents && m_Started && m_Enabled; private void StartTrackingUIToolkitPanels() { if (m_IsTrackingPanels || !shouldTrackPanels) return; foreach (var panel in UIElementsRuntimeUtility.GetSortedPlayerPanels()) { StartTrackingPanel(panel); } UIElementsRuntimeUtility.onCreatePanel += StartTrackingPanel; m_IsTrackingPanels = true; } private readonly HashSet trackedPanels = new(); private void StartTrackingPanel(BaseRuntimePanel panel) { trackedPanels.Add(panel); } private void StopTrackingUIToolkitPanels() { if (!m_IsTrackingPanels) return; UIElementsRuntimeUtility.onCreatePanel -= StartTrackingPanel; m_IsTrackingPanels = false; foreach (var panel in trackedPanels) { DestroyPanelGameObject(panel); } trackedPanels.Clear(); DestroyWorldSpacePanelGameObject(); } private void UpdatePanelGameObject(BaseRuntimePanel panel) { var handlerType = panel.isFlat ? EventHandlerTypes.ScreenOverlay : EventHandlerTypes.WorldSpace; if ((m_HandlerTypes & handlerType) != 0) { CreatePanelGameObject(panel); } else { DestroyPanelGameObject(panel); } } private readonly Dictionary destroyedActions = new(); private void CreatePanelGameObject(BaseRuntimePanel panel) { if (panel.selectableGameObject == null) { var go = new GameObject(panel.name, typeof(PanelEventHandler), typeof(PanelRaycaster)); go.transform.SetParent(m_EventSystem.transform); panel.selectableGameObject = go; var destroyed = destroyedActions[panel] = () => UIRUtility.Destroy(go); panel.destroyed += destroyed; } } private void DestroyPanelGameObject(BaseRuntimePanel panel) { var go = panel.selectableGameObject; if (go != null) { if (!destroyedActions.Remove(panel, out var destroyed)) return; // this object wasn't created by us, so leave it untouched panel.destroyed -= destroyed; panel.selectableGameObject = null; UIRUtility.Destroy(go); } } private void CreateWorldSpacePanelGameObject() { // This will destroy m_WorldSpaceGo if raycaster components need to change. ApplyCameraProperties(); if (m_WorldSpaceGo == null) { var go = new GameObject("WorldDocumentRaycaster"); go.transform.SetParent(m_EventSystem.transform); if (m_InputSettings.defaultEventCameraIsMainCamera) { go.AddComponent(); } else { foreach (var cam in m_InputSettings.eventCameras) { go.AddComponent().camera = cam; } } m_WorldSpaceGo = go; } } private void DestroyWorldSpacePanelGameObject() { var go = m_WorldSpaceGo; m_WorldSpaceGo = null; UIRUtility.Destroy(go); } public void Start() { m_Started = true; StartTrackingUIToolkitPanels(); } public void OnEnable() { if (m_Enabled) return; m_Enabled = true; if (PanelInputConfiguration.current != null) Apply(PanelInputConfiguration.current); PanelInputConfiguration.onApply += Apply; if (m_Started) StartTrackingUIToolkitPanels(); if (m_OverrideUIToolkitEvents) UIElementsRuntimeUtility.RegisterEventSystem(m_EventSystem); } public void OnDisable() { if (!m_Enabled) return; m_Enabled = false; PanelInputConfiguration.onApply -= Apply; StopTrackingUIToolkitPanels(); UIElementsRuntimeUtility.UnregisterEventSystem(m_EventSystem); } public void Update() { UpdatePanelGameObjects(); } private PanelInputConfiguration.Settings m_InputSettings = PanelInputConfiguration.Settings.Default; void Apply(PanelInputConfiguration input) { m_InputSettings = input != null ? input.settings : PanelInputConfiguration.Settings.Default; m_OverrideUIToolkitEvents = m_InputSettings.panelInputRedirection != PanelInputConfiguration.PanelInputRedirection.Never; m_HandlerTypes = EventHandlerTypes.ScreenOverlay | (m_InputSettings.processWorldSpaceInput ? EventHandlerTypes.WorldSpace : 0); m_WorldPickingLayers = m_InputSettings.interactionLayers; m_WorldPickingMaxDistance = m_InputSettings.maxInteractionDistance; m_CreateDefaultPanelComponents = m_InputSettings.autoCreatePanelComponents; ApplyOverrideUIToolkitEvents(); ApplyCameraProperties(); ApplyOtherProperties(); } private bool m_OldOverrideUIToolkitEvents = true; private EventHandlerTypes m_OldHandlerTypes = EventHandlerTypes.ScreenOverlay | EventHandlerTypes.WorldSpace; private bool m_OldCreateDefaultPanelComponents = true; private bool m_OldDefaultEventCameraIsMainCamera = true; private long m_OldEventCamerasHash = 0; private void ApplyOverrideUIToolkitEvents() { if (m_OldOverrideUIToolkitEvents == m_OverrideUIToolkitEvents) return; m_OldOverrideUIToolkitEvents = m_OverrideUIToolkitEvents; if (!m_Enabled) return; if (m_OverrideUIToolkitEvents) { UIElementsRuntimeUtility.RegisterEventSystem(m_EventSystem); } else { UIElementsRuntimeUtility.UnregisterEventSystem(m_EventSystem); } UpdatePanelTracking(); } private void ApplyCameraProperties() { bool dirty = false; if (m_OldDefaultEventCameraIsMainCamera != m_InputSettings.defaultEventCameraIsMainCamera) { m_OldDefaultEventCameraIsMainCamera = m_InputSettings.defaultEventCameraIsMainCamera; dirty = true; } if (!m_InputSettings.defaultEventCameraIsMainCamera) { var hashCode = 0; foreach (var cam in m_InputSettings.eventCameras) { hashCode = (hashCode * 397) ^ cam.GetHashCode(); } if (m_OldEventCamerasHash != hashCode) { m_OldEventCamerasHash = hashCode; dirty = true; } } else { m_OldEventCamerasHash = 0; } if (dirty) DestroyWorldSpacePanelGameObject(); } private void ApplyOtherProperties() { bool dirty = false; if (m_OldHandlerTypes != m_HandlerTypes) { m_OldHandlerTypes = m_HandlerTypes; dirty = true; } if (m_OldCreateDefaultPanelComponents != m_CreateDefaultPanelComponents) { m_OldCreateDefaultPanelComponents = m_CreateDefaultPanelComponents; dirty = true; } if (dirty) UpdatePanelTracking(); } private void UpdatePanelTracking() { if (shouldTrackPanels) { StartTrackingUIToolkitPanels(); } else { StopTrackingUIToolkitPanels(); } } private void UpdatePanelGameObjects() { if (!m_IsTrackingPanels) return; bool isWorldSpaceActive = false; foreach (var panel in trackedPanels) { UpdatePanelGameObject(panel); isWorldSpaceActive |= !panel.isFlat; } if (isWorldSpaceActive && (m_HandlerTypes & EventHandlerTypes.WorldSpace) != 0) { CreateWorldSpacePanelGameObject(); } else { DestroyWorldSpacePanelGameObject(); } } } #endif }