Last active
September 26, 2025 09:48
-
-
Save yasirkula/06edc780beaa4d8705b3564d60886fa6 to your computer and use it in GitHub Desktop.
Revisions
-
yasirkula revised this gist
Jun 1, 2025 . 1 changed file with 27 additions and 15 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -38,7 +38,9 @@ public Entry( RectTransform rectTransform ) private static Vector2 lastRightPos; private static bool blockSceneViewInput; private static MethodInfo screenFittedRectGetter; private static FieldInfo editorWindowHostViewGetter; private static PropertyInfo hostViewContainerWindowGetter; private const float Padding = 1f; private float RowHeight { get { return EditorGUIUtility.singleLineHeight; } } @@ -56,8 +58,8 @@ private void ShowContextWindow( List<Entry> results ) ShowAsDropDown( new Rect(), new Vector2( preferredWidth + Padding * 2f, uiObjects.Count * RowHeight + Padding * 2f ) ); Rect rect = new Rect(GUIUtility.GUIToScreenPoint(Event.current.mousePosition) - new Vector2(0f, position.height), position.size); // Show dropdown above the cursor instead of below the cursor position = GetScreenFittedRect(rect, this); } private void InitializeUIObjectsRecursive( List<Entry> results, int depth, StringBuilder sb ) @@ -77,9 +79,7 @@ private void InitializeUIObjectsRecursive( List<Entry> results, int depth, Strin protected void OnEnable() { wantsMouseMove = wantsMouseEnterLeaveWindow = true; wantsLessLayoutEvents = false; blockSceneViewInput = true; } @@ -129,11 +129,7 @@ protected void OnGUI() [InitializeOnLoadMethod] private static void OnSceneViewGUI() { SceneView.duringSceneGui += ( SceneView sceneView ) => { /// Couldn't get <see cref="EventType.ContextClick"/> to work here in Unity 5.6 so implemented context click detection manually Event ev = Event.current; @@ -182,12 +178,10 @@ private static void OnSceneViewRightClicked( SceneView sceneView ) // Find all UI objects under the cursor Vector2 pointerPos = HandleUtility.GUIPointToScreenPixelCoordinate( Event.current.mousePosition ); Entry rootEntry = new Entry( null ); PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); if( prefabStage != null && prefabStage.stageHandle.IsValid() && prefabStage.prefabContentsRoot.transform is RectTransform prefabStageRoot ) CheckRectTransformRecursive( prefabStageRoot, pointerPos, sceneView.camera, false, rootEntry.Children ); else { #if UNITY_2022_3_OR_NEWER Canvas[] canvases = FindObjectsByType<Canvas>( FindObjectsSortMode.None ); @@ -216,7 +210,7 @@ private static void CheckRectTransformRecursive( RectTransform rectTransform, Ve if( canvas != null && !canvas.enabled ) return; if( RectTransformUtility.RectangleContainsScreenPoint( rectTransform, pointerPos, camera ) && ShouldCheckRectTransform( rectTransform, pointerPos, camera, ref culledByCanvasGroup ) ) { Entry entry = new Entry( rectTransform ); result.Add( entry ); @@ -233,13 +227,11 @@ private static void CheckRectTransformRecursive( RectTransform rectTransform, Ve private static bool ShouldCheckRectTransform( RectTransform rectTransform, Vector2 pointerPos, Camera camera, ref bool culledByCanvasGroup ) { if( SceneVisibilityManager.instance.IsHidden( rectTransform.gameObject, false ) ) return false; if( SceneVisibilityManager.instance.IsPickingDisabled( rectTransform.gameObject, false ) ) return false; CanvasRenderer canvasRenderer = rectTransform.GetComponent<CanvasRenderer>(); if( canvasRenderer != null && canvasRenderer.cull ) @@ -272,4 +264,24 @@ private static bool ShouldCheckRectTransform( RectTransform rectTransform, Vecto return !culledByCanvasGroup; } /// <summary> /// Restricts the given Rect within the screen's bounds. /// </summary> private static Rect GetScreenFittedRect(Rect originalRect, EditorWindow editorWindow) { screenFittedRectGetter ??= typeof(EditorWindow).Assembly.GetType("UnityEditor.ContainerWindow").GetMethod("FitRectToScreen", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (screenFittedRectGetter.GetParameters().Length == 3) return (Rect)screenFittedRectGetter.Invoke(null, new object[] { originalRect, true, true }); else { // New version introduced in Unity 2022.3.62f1, Unity 6.0.49f1 and Unity 6.1.0f1. // Usage example: https://github.com/Unity-Technologies/UnityCsReference/blob/10f8718268a7e34844ba7d59792117c28d75a99b/Editor/Mono/EditorWindow.cs#L1264 editorWindowHostViewGetter ??= typeof(EditorWindow).GetField("m_Parent", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); hostViewContainerWindowGetter ??= typeof(EditorWindow).Assembly.GetType("UnityEditor.HostView").GetProperty("window", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); return (Rect)screenFittedRectGetter.Invoke(null, new object[] { originalRect, originalRect.center, true, hostViewContainerWindowGetter.GetValue(editorWindowHostViewGetter.GetValue(editorWindow), null) }); } } } -
yasirkula revised this gist
Dec 12, 2023 . 1 changed file with 5 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -212,7 +212,11 @@ private static void OnSceneViewRightClicked( SceneView sceneView ) private static void CheckRectTransformRecursive( RectTransform rectTransform, Vector2 pointerPos, Camera camera, bool culledByCanvasGroup, List<Entry> result ) { Canvas canvas = rectTransform.GetComponent<Canvas>(); if( canvas != null && !canvas.enabled ) return; if ( RectTransformUtility.RectangleContainsScreenPoint( rectTransform, pointerPos, camera ) && ShouldCheckRectTransform( rectTransform, pointerPos, camera, ref culledByCanvasGroup ) ) { Entry entry = new Entry( rectTransform ); result.Add( entry ); -
yasirkula revised this gist
Dec 9, 2023 . 1 changed file with 5 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -252,6 +252,11 @@ private static bool ShouldCheckRectTransform( RectTransform rectTransform, Vecto if( !culledByCanvasGroup ) { // If the target is a MaskableGraphic that ignores masks (i.e. visible outside masks) and isn't fully transparent, accept it MaskableGraphic maskableGraphic = rectTransform.GetComponent<MaskableGraphic>(); if( maskableGraphic != null && !maskableGraphic.maskable && maskableGraphic.color.a > 0f ) return true; raycastFilters.Clear(); rectTransform.GetComponentsInParent( false, raycastFilters ); foreach( var raycastFilter in raycastFilters ) -
yasirkula revised this gist
Dec 8, 2023 . 1 changed file with 8 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -229,6 +229,14 @@ private static void CheckRectTransformRecursive( RectTransform rectTransform, Ve private static bool ShouldCheckRectTransform( RectTransform rectTransform, Vector2 pointerPos, Camera camera, ref bool culledByCanvasGroup ) { #if UNITY_2019_3_OR_NEWER if( SceneVisibilityManager.instance.IsHidden( rectTransform.gameObject, false ) ) return false; if( SceneVisibilityManager.instance.IsPickingDisabled( rectTransform.gameObject, false ) ) return false; #endif CanvasRenderer canvasRenderer = rectTransform.GetComponent<CanvasRenderer>(); if( canvasRenderer != null && canvasRenderer.cull ) return false; -
yasirkula created this gist
Dec 2, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,258 @@ using System; using System.Collections.Generic; using System.Reflection; using System.Text; using UnityEditor; using UnityEngine; using UnityEngine.UI; #if UNITY_2021_2_OR_NEWER using PrefabStage = UnityEditor.SceneManagement.PrefabStage; using PrefabStageUtility = UnityEditor.SceneManagement.PrefabStageUtility; #elif UNITY_2018_3_OR_NEWER using PrefabStage = UnityEditor.Experimental.SceneManagement.PrefabStage; using PrefabStageUtility = UnityEditor.Experimental.SceneManagement.PrefabStageUtility; #endif public class SceneViewUIObjectPickerContextWindow : EditorWindow { private struct Entry { public readonly RectTransform RectTransform; public readonly List<Entry> Children; public Entry( RectTransform rectTransform ) { RectTransform = rectTransform; Children = new List<Entry>( 2 ); } } private readonly List<RectTransform> uiObjects = new List<RectTransform>( 16 ); private readonly List<string> uiObjectLabels = new List<string>( 16 ); private static RectTransform hoveredUIObject; private static readonly Vector3[] hoveredUIObjectCorners = new Vector3[4]; private static readonly List<ICanvasRaycastFilter> raycastFilters = new List<ICanvasRaycastFilter>( 4 ); private static double lastRightClickTime; private static Vector2 lastRightPos; private static bool blockSceneViewInput; private readonly MethodInfo screenFittedRectGetter = typeof( EditorWindow ).Assembly.GetType( "UnityEditor.ContainerWindow" ).GetMethod( "FitRectToScreen", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ); private const float Padding = 1f; private float RowHeight { get { return EditorGUIUtility.singleLineHeight; } } private GUIStyle RowGUIStyle { get { return "MenuItem"; } } private void ShowContextWindow( List<Entry> results ) { StringBuilder sb = new StringBuilder( 100 ); InitializeUIObjectsRecursive( results, 0, sb ); GUIStyle rowGUIStyle = RowGUIStyle; float preferredWidth = 0f; foreach( string label in uiObjectLabels ) preferredWidth = Mathf.Max( preferredWidth, rowGUIStyle.CalcSize( new GUIContent( label ) ).x ); ShowAsDropDown( new Rect(), new Vector2( preferredWidth + Padding * 2f, uiObjects.Count * RowHeight + Padding * 2f ) ); // Show dropdown above the cursor instead of below the cursor position = (Rect) screenFittedRectGetter.Invoke( null, new object[3] { new Rect( GUIUtility.GUIToScreenPoint( Event.current.mousePosition ) - new Vector2( 0f, position.height ), position.size ), true, true } ); } private void InitializeUIObjectsRecursive( List<Entry> results, int depth, StringBuilder sb ) { foreach( Entry entry in results ) { sb.Length = 0; uiObjects.Add( entry.RectTransform ); uiObjectLabels.Add( sb.Append( ' ', depth * 4 ).Append( entry.RectTransform.name ).ToString() ); if( entry.Children.Count > 0 ) InitializeUIObjectsRecursive( entry.Children, depth + 1, sb ); } } protected void OnEnable() { wantsMouseMove = wantsMouseEnterLeaveWindow = true; #if UNITY_2020_1_OR_NEWER wantsLessLayoutEvents = false; #endif blockSceneViewInput = true; } protected void OnDisable() { hoveredUIObject = null; SceneView.RepaintAll(); } protected void OnGUI() { Event ev = Event.current; float rowWidth = position.width - Padding * 2f, rowHeight = RowHeight; GUIStyle rowGUIStyle = RowGUIStyle; int hoveredRowIndex = -1; for( int i = 0; i < uiObjects.Count; i++ ) { Rect rect = new Rect( Padding, Padding + i * rowHeight, rowWidth, rowHeight ); if( GUI.Button( rect, uiObjectLabels[i], rowGUIStyle ) ) { if( uiObjects[i] != null ) Selection.activeTransform = uiObjects[i]; blockSceneViewInput = false; ev.Use(); Close(); GUIUtility.ExitGUI(); } if( hoveredRowIndex < 0 && ev.type == EventType.MouseMove && rect.Contains( ev.mousePosition ) ) hoveredRowIndex = i; } if( ev.type == EventType.MouseMove || ev.type == EventType.MouseLeaveWindow ) { RectTransform hoveredUIObject = ( hoveredRowIndex >= 0 ) ? uiObjects[hoveredRowIndex] : null; if( hoveredUIObject != SceneViewUIObjectPickerContextWindow.hoveredUIObject ) { SceneViewUIObjectPickerContextWindow.hoveredUIObject = hoveredUIObject; Repaint(); SceneView.RepaintAll(); } } } [InitializeOnLoadMethod] private static void OnSceneViewGUI() { #if UNITY_2019_1_OR_NEWER SceneView.duringSceneGui += ( SceneView sceneView ) => #else SceneView.onSceneGUIDelegate += ( SceneView sceneView ) => #endif { /// Couldn't get <see cref="EventType.ContextClick"/> to work here in Unity 5.6 so implemented context click detection manually Event ev = Event.current; switch( ev.type ) { case EventType.MouseDown: { if( ev.button == 1 ) { lastRightClickTime = EditorApplication.timeSinceStartup; lastRightPos = ev.mousePosition; } else if( blockSceneViewInput ) { // User has clicked outside the context window to close it. Ignore this click in Scene view if it's left click blockSceneViewInput = false; if( ev.button == 0 ) { GUIUtility.hotControl = 0; ev.Use(); } } break; } case EventType.MouseUp: { if( ev.button == 1 && EditorApplication.timeSinceStartup - lastRightClickTime < 0.2 && ( ev.mousePosition - lastRightPos ).magnitude < 2f ) OnSceneViewRightClicked( sceneView ); break; } } if( hoveredUIObject != null ) { hoveredUIObject.GetWorldCorners( hoveredUIObjectCorners ); Handles.DrawSolidRectangleWithOutline( hoveredUIObjectCorners, new Color( 1f, 1f, 0f, 0.25f ), Color.black ); } }; } private static void OnSceneViewRightClicked( SceneView sceneView ) { // Find all UI objects under the cursor Vector2 pointerPos = HandleUtility.GUIPointToScreenPixelCoordinate( Event.current.mousePosition ); Entry rootEntry = new Entry( null ); #if UNITY_2018_3_OR_NEWER PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); if( prefabStage != null && prefabStage.stageHandle.IsValid() && prefabStage.prefabContentsRoot.transform is RectTransform prefabStageRoot ) CheckRectTransformRecursive( prefabStageRoot, pointerPos, sceneView.camera, false, rootEntry.Children ); else #endif { #if UNITY_2022_3_OR_NEWER Canvas[] canvases = FindObjectsByType<Canvas>( FindObjectsSortMode.None ); #else Canvas[] canvases = FindObjectsOfType<Canvas>(); #endif Array.Sort( canvases, ( c1, c2 ) => c1.sortingOrder.CompareTo( c2.sortingOrder ) ); foreach( Canvas canvas in canvases ) { if( canvas != null && canvas.gameObject.activeInHierarchy && canvas.isRootCanvas ) CheckRectTransformRecursive( (RectTransform) canvas.transform, pointerPos, sceneView.camera, false, rootEntry.Children ); } } // Remove non-Graphic root entries with no children from the results rootEntry.Children.RemoveAll( ( canvasEntry ) => canvasEntry.Children.Count == 0 && !canvasEntry.RectTransform.GetComponent<Graphic>() ); // If any results found, show the context window if( rootEntry.Children.Count > 0 ) CreateInstance<SceneViewUIObjectPickerContextWindow>().ShowContextWindow( rootEntry.Children ); } private static void CheckRectTransformRecursive( RectTransform rectTransform, Vector2 pointerPos, Camera camera, bool culledByCanvasGroup, List<Entry> result ) { if( RectTransformUtility.RectangleContainsScreenPoint( rectTransform, pointerPos, camera ) && ShouldCheckRectTransform( rectTransform, pointerPos, camera, ref culledByCanvasGroup ) ) { Entry entry = new Entry( rectTransform ); result.Add( entry ); result = entry.Children; } for( int i = 0, childCount = rectTransform.childCount; i < childCount; i++ ) { RectTransform childRectTransform = rectTransform.GetChild( i ) as RectTransform; if( childRectTransform != null && childRectTransform.gameObject.activeSelf ) CheckRectTransformRecursive( childRectTransform, pointerPos, camera, culledByCanvasGroup, result ); } } private static bool ShouldCheckRectTransform( RectTransform rectTransform, Vector2 pointerPos, Camera camera, ref bool culledByCanvasGroup ) { CanvasRenderer canvasRenderer = rectTransform.GetComponent<CanvasRenderer>(); if( canvasRenderer != null && canvasRenderer.cull ) return false; CanvasGroup canvasGroup = rectTransform.GetComponent<CanvasGroup>(); if( canvasGroup != null ) { if( canvasGroup.ignoreParentGroups ) culledByCanvasGroup = canvasGroup.alpha == 0f; else if( canvasGroup.alpha == 0f ) culledByCanvasGroup = true; } if( !culledByCanvasGroup ) { raycastFilters.Clear(); rectTransform.GetComponentsInParent( false, raycastFilters ); foreach( var raycastFilter in raycastFilters ) { if( !raycastFilter.IsRaycastLocationValid( pointerPos, camera ) ) return false; } } return !culledByCanvasGroup; } }