From 29ba802c24e45335126e6bdeb336c1a320fd5f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=BCleyman=20Yasir=20KULA?= Date: Sun, 24 Nov 2019 17:13:51 +0300 Subject: [PATCH] Right clicking the Favorites and History lists' icons will now show a context menu to quickly select objects from these lists --- .../InspectPlus/Editor/CustomProjectWindow.cs | 22 ++-- .../InspectPlus/Editor/InspectPlusSettings.cs | 4 +- .../InspectPlus/Editor/InspectPlusWindow.cs | 105 +++++++++++++----- Plugins/InspectPlus/Editor/Utilities.cs | 10 ++ Plugins/InspectPlus/README.txt | 1 + README.md | 1 + 6 files changed, 107 insertions(+), 36 deletions(-) diff --git a/Plugins/InspectPlus/Editor/CustomProjectWindow.cs b/Plugins/InspectPlus/Editor/CustomProjectWindow.cs index 3fc804f..3e77c8e 100644 --- a/Plugins/InspectPlus/Editor/CustomProjectWindow.cs +++ b/Plugins/InspectPlus/Editor/CustomProjectWindow.cs @@ -40,6 +40,11 @@ public void Show( string directory ) rootDirectory = directory; } + public CustomProjectWindowDrawer GetTreeView() + { + return treeView; + } + public void Refresh() { if( treeView != null ) @@ -64,11 +69,6 @@ public void OnGUI() rect = GUILayoutUtility.GetRect( 0, 100000, 0, 100000 ); treeView.OnGUI( rect ); - - // This happens only when the mouse click is not captured by TreeView - Event e = Event.current; - if( e.type == EventType.MouseDown && e.button == 0 ) - treeView.SetSelection( new int[0] ); } } @@ -121,6 +121,8 @@ public void Refresh( string path ) private bool isSearching; + public bool SyncSelection; + public CustomProjectWindowDrawer( TreeViewState state, string rootDirectory ) : base( state ) { this.rootDirectory = rootDirectory; @@ -309,14 +311,13 @@ protected override bool CanBeParent( TreeViewItem item ) protected override void SelectionChanged( IList selectedIds ) { - if( selectedIds == null ) + if( !SyncSelection || selectedIds == null ) return; - int[] result = new int[selectedIds.Count]; - for( int i = 0; i < selectedIds.Count; i++ ) - result[i] = selectedIds[i]; + int[] selectionArray = new int[selectedIds.Count]; + selectedIds.CopyTo( selectionArray, 0 ); - Selection.instanceIDs = result; + Selection.instanceIDs = selectionArray; } protected override bool CanRename( TreeViewItem item ) @@ -344,6 +345,7 @@ protected override void ContextClicked() protected override void ContextClickedItem( int id ) { + ChangeUnitySelection(); EditorUtility.DisplayPopupMenu( new Rect( Event.current.mousePosition, new Vector2( 0f, 0f ) ), "Assets/", null ); Event.current.Use(); } diff --git a/Plugins/InspectPlus/Editor/InspectPlusSettings.cs b/Plugins/InspectPlus/Editor/InspectPlusSettings.cs index 5095b99..43bb4ef 100644 --- a/Plugins/InspectPlus/Editor/InspectPlusSettings.cs +++ b/Plugins/InspectPlus/Editor/InspectPlusSettings.cs @@ -51,9 +51,11 @@ public static InspectPlusSettings Instance public bool ShowHistoryByDefault = true; [Tooltip( "New windows should show object preview area in the Inspector by default (only shown when an object support preview)" )] public bool ShowPreviewByDefault = false; + [Tooltip( "While inspecting a folder, selecting files/folders inside the folder will update Unity's selection, as well" )] + public bool SyncProjectWindowSelection = true; [Tooltip( "Selecting an object in Favorites or History will highlight the object in Hierarchy/Project" )] public bool AutomaticallyPingSelectedObject = true; - [Tooltip( "Clearing the History via context menu will delete the inspected object's History entry, as well" )] + [Tooltip( "Clearing the History via context menu will delete the currently inspected object's History entry, as well" )] public bool ClearingHistoryRemovesActiveObject = false; private void OnEnable() diff --git a/Plugins/InspectPlus/Editor/InspectPlusWindow.cs b/Plugins/InspectPlus/Editor/InspectPlusWindow.cs index ca7abb8..48c4cba 100644 --- a/Plugins/InspectPlus/Editor/InspectPlusWindow.cs +++ b/Plugins/InspectPlus/Editor/InspectPlusWindow.cs @@ -55,17 +55,20 @@ private enum ButtonState { Normal = -1, LeftClicked = 0, RightClicked = 1, Middl private static Rect lastWindowPosition; // These are not readonly to support serialization of the data - // SerializeField makes history data persist between editor sessions + // SerializeField makes history data persist between editor sessions (unfortunately, only assets persist, not scene objects) [SerializeField] private List history = new List( 8 ); + [SerializeField] + private Object mainObject; + private List inspectorDrawers = new List( 16 ); private int inspectorDrawerCount; - private Object mainObject; - // Serializing CustomProjectWindow makes the TreeView's state (collapsed entries etc.) persist during domain reload + // Serializing CustomProjectWindow makes the TreeView's state (collapsed entries etc.) persist between editor sessions [SerializeField] private CustomProjectWindow projectWindow = new CustomProjectWindow(); private bool showProjectWindow; + private bool syncProjectWindowSelection; private bool shouldRepositionSelf; private bool shouldRepaint; @@ -132,6 +135,7 @@ private void Awake() showFavorites = InspectPlusSettings.Instance.ShowFavoritesByDefault; showHistory = InspectPlusSettings.Instance.ShowHistoryByDefault; showPreview = InspectPlusSettings.Instance.ShowPreviewByDefault; + syncProjectWindowSelection = InspectPlusSettings.Instance.SyncProjectWindowSelection; // Window is restored after Unity is closed and then reopened if( history.Count > 0 ) @@ -154,27 +158,18 @@ private void OnEnable() Undo.undoRedoPerformed -= OnUndoRedo; Undo.undoRedoPerformed += OnUndoRedo; - // Make sure that debug mode drawers are recreated - if( inspectorDrawerCount > 0 ) + if( mainObject ) { - try - { - if( inspectorDrawers[0].target ) - InspectInternal( inspectorDrawers[0].target, false ); - else - { - for( int i = 0; i < inspectorDrawers.Count; i++ ) - DestroyImmediate( inspectorDrawers[i] ); + // This also makes sure that debug mode drawers are recreated + InspectInternal( mainObject, false ); + } + else + { + for( int i = 0; i < inspectorDrawers.Count; i++ ) + DestroyImmediate( inspectorDrawers[i] ); - inspectorDrawers.Clear(); - inspectorDrawerCount = 0; - } - } - catch( NullReferenceException ) - { - // Some Editors (like folder editors) might throw NullReferenceException when accessing 'target' in OnEnable - InspectInternal( mainObject, false ); - } + inspectorDrawers.Clear(); + inspectorDrawerCount = 0; } historyHolder.Add( history ); @@ -203,8 +198,6 @@ private void OnDestroy() inspectorDrawers.Clear(); inspectorDrawerCount = 0; - - mainObject = null; } private void OnFocus() @@ -379,6 +372,20 @@ void IHasCustomMenu.AddItemsToMenu( GenericMenu menu ) menu.AddItem( new GUIContent( "Debug Mode" ), debugMode, () => debugMode = !debugMode ); menu.AddSeparator( "" ); + if( showProjectWindow ) + { + menu.AddItem( new GUIContent( "Synchronize Selection" ), syncProjectWindowSelection, () => + { + syncProjectWindowSelection = !syncProjectWindowSelection; + + CustomProjectWindowDrawer treeView = projectWindow.GetTreeView(); + if( treeView != null ) + treeView.SyncSelection = syncProjectWindowSelection; + } ); + + menu.AddSeparator( "" ); + } + menu.AddItem( new GUIContent( "Clear Favorites" ), false, () => { InspectPlusSettings.Instance.FavoriteAssets.Clear(); @@ -436,6 +443,34 @@ void IHasCustomMenu.AddItemsToMenu( GenericMenu menu ) } ); } + private void OnScrollViewIconRightClicked( GenericMenu menu, List> lists ) + { + List allObjects = new List( 8 ); + for( int i = 0; i < lists.Count; i++ ) + { + List list = lists[i]; + for( int j = 0; j < list.Count; j++ ) + { + if( !list[j] ) + continue; + + // Insert object into sorted list + // Credit: https://stackoverflow.com/a/12172412/2373034 + int index = allObjects.BinarySearch( list[j], Utilities.unityObjectComparer ); + if( index < 0 ) + index = ~index; + + allObjects.Insert( index, list[j] ); + } + } + + for( int i = 0; i < allObjects.Count; i++ ) + { + Object obj = allObjects[i]; + menu.AddItem( new GUIContent( string.Concat( obj.name, " (", obj.GetType().Name, ")" ) ), false, () => pendingInspectTarget = obj ); + } + } + private void OnScrollViewButtonRightClicked( GenericMenu menu, List list, int index ) { menu.AddItem( new GUIContent( "Remove" ), false, () => RemoveObjectFromList( list, index ) ); @@ -582,6 +617,8 @@ private void InspectInternal( Object obj, bool addHistoryEntry ) if( !string.IsNullOrEmpty( assetPath ) && AssetDatabase.IsValidFolder( assetPath ) ) { projectWindow.Show( assetPath ); + projectWindow.GetTreeView().SyncSelection = syncProjectWindowSelection; + showProjectWindow = true; } else @@ -762,6 +799,14 @@ private void OnGUI() { GUILayout.Space( -4 ); // Get rid of the free space above the project window's header projectWindow.OnGUI(); + + // This happens only when the mouse click is not captured by project window's TreeView + // In this case, clear project window's selection + if( ev.type == EventType.MouseDown && ev.button == 0 ) + { + projectWindow.GetTreeView().SetSelection( new int[0] ); + shouldRepaint = true; + } } if( showPreview ) @@ -799,6 +844,14 @@ private Vector2 DrawScrollableList( bool drawingFavorites ) GUILayout.BeginHorizontal(); GUILayout.Label( icon, scrollableListIconGuiStyle, scrollableListIconSize, height ); + if( ev.type == EventType.ContextClick && GUILayoutUtility.GetLastRect().Contains( ev.mousePosition ) ) + { + GenericMenu menu = new GenericMenu(); + OnScrollViewIconRightClicked( menu, lists ); + if( menu.GetItemCount() > 0 ) + menu.ShowAsContext(); + } + scrollPosition = GUILayout.BeginScrollView( scrollPosition, height ); GUILayout.BeginHorizontal(); @@ -948,8 +1001,10 @@ private ButtonState DraggableButton( GUIContent content, GUIStyle style, Object if( GUIUtility.hotControl == controlID ) { GUIUtility.hotControl = 0; - result = (ButtonState) ev.button; shouldRepaint = true; + + if( rect.Contains( ev.mousePosition ) ) + result = (ButtonState) ev.button; } break; case EventType.Repaint: diff --git a/Plugins/InspectPlus/Editor/Utilities.cs b/Plugins/InspectPlus/Editor/Utilities.cs index 581f25d..704dfbd 100644 --- a/Plugins/InspectPlus/Editor/Utilities.cs +++ b/Plugins/InspectPlus/Editor/Utilities.cs @@ -3,11 +3,20 @@ using System.Reflection; using System.Text; using UnityEngine; +using Object = UnityEngine.Object; namespace InspectPlusNamespace.Extras { public static class Utilities { + public class UnityObjectComparer : IComparer + { + public int Compare( Object x, Object y ) + { + return x.name.CompareTo( y.name ); + } + } + private const BindingFlags VARIABLE_BINDING_FLAGS = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; private static readonly HashSet primitiveUnityTypes = new HashSet() @@ -29,6 +38,7 @@ public static class Utilities private static readonly Dictionary typeToVariables = new Dictionary( 1024 ); private static readonly string reflectionNameSpace = typeof( Assembly ).Namespace; public static readonly StringBuilder stringBuilder = new StringBuilder( 256 ); + public static readonly UnityObjectComparer unityObjectComparer = new UnityObjectComparer(); // Get filtered variables for a type public static VariableGetterHolder[] GetFilteredVariablesForType( Type type ) diff --git a/Plugins/InspectPlus/README.txt b/Plugins/InspectPlus/README.txt index c047629..17d8ea1 100644 --- a/Plugins/InspectPlus/README.txt +++ b/Plugins/InspectPlus/README.txt @@ -13,6 +13,7 @@ This plugin helps you view an object's Inspector in a separate tab/window, copy& 3) right clicking a component in the Inspector - You can right click an object in the History list to add it to the Favorites list - You can drag&drop objects to the History and Favorites lists to quickly fill these lists +- You can right click the icons of the History and Favorites lists to quickly select an object from these lists - You can right click variables in the Inspector to copy&paste their values (variables that are not drawn with SerializedProperty don't support this feature) - You can right click the Inspect+ tab to enable Debug mode: you can inspect all variables of an object in this mode, including static, readonly and non-serializable variables - You can show the Inspect+ window from your editor scripts by calling the InspectPlusNamespace.InspectPlusWindow.Inspect functions \ No newline at end of file diff --git a/README.md b/README.md index 0b7f98a..c2833e3 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ After importing [InspectPlus.unitypackage](https://github.com/yasirkula/UnityIns - right clicking a component in the Inspector - You can right click an object in the **History** list to add it to the **Favorites** list - You can drag&drop objects to the History and Favorites lists to quickly fill these lists +- You can right click the icons of the History and Favorites lists to quickly select an object from these lists - You can right click variables in the Inspector to copy&paste their values (variables that are not drawn with *SerializedProperty* don't support this feature) - You can right click the Inspect+ tab to enable **Debug mode**: you can inspect all variables of an object in this mode, including static, readonly and non-serializable variables - You can show the Inspect+ window from your editor scripts by calling the `InspectPlusNamespace.InspectPlusWindow.Inspect` functions \ No newline at end of file