@@ -0,0 +1,309 @@
#if ODIN_INSPECTOR
using System ;
using System . Collections . Generic ;
using System . Linq ;
using Sirenix . OdinInspector . Editor ;
using Sirenix . Utilities ;
using Sirenix . Utilities . Editor ;
using UnityEditor ;
using UnityEngine ;
using Object = UnityEngine . Object ;
public class OdinWatchWindow : OdinEditorWindow
{
[ SerializeField ]
private List < TreeValuesHolder > _properties = new List < TreeValuesHolder > ( ) ;
private static OdinWatchWindow _instance ;
private bool _repaintSheduled ;
[ SerializeField ] private float _labelWidth = 200 ;
private bool _showSettings ;
[ MenuItem ( "Tools/Odin Watch Window" ) ]
public static void ShowMenu ( )
{
_instance = GetWindow < OdinWatchWindow > ( ) ;
_instance . Show ( ) ;
}
[ InitializeOnLoadMethod ]
private static void OnPropertyContextMenu ( )
{
EditorApplication . contextualPropertyMenu += ( menu , property ) =>
{
property = property . Copy ( ) ;
menu . AddItem ( new GUIContent ( "Watch" ) , false , ( ) =>
{
ShowMenu ( ) ;
PropertyTree tree = Sirenix . OdinInspector . Editor . PropertyTree . Create ( new SerializedObject ( property . serializedObject . targetObject ) ) ;
TreeValuesHolder holder = _instance . _properties . FirstOrDefault ( o => o . Tree . WeakTargets [ 0 ] == property . serializedObject . targetObject ) ;
if ( holder == null )
{
holder = new TreeValuesHolder ( tree ) ;
_instance . _properties . Add ( holder ) ;
tree . OnPropertyValueChanged += TreeOnOnPropertyValueChanged ;
}
holder . ValuePaths . Add ( property . propertyPath ) ;
} ) ;
} ;
}
private void PlayModeStateChanged ( PlayModeStateChange obj )
{
foreach ( TreeValuesHolder holder in _properties )
{
holder . CheckRefresh ( ) ;
}
}
private void OnInspectorUpdate ( )
{
Repaint ( ) ;
}
public static void AddWatch ( InspectorProperty property )
{
ShowMenu ( ) ;
PropertyTree tree = Sirenix . OdinInspector . Editor . PropertyTree . Create ( property . Tree . WeakTargets , new SerializedObject ( property . Tree . UnitySerializedObject . targetObject ) ) ;
TreeValuesHolder holder = _instance . _properties . FirstOrDefault ( o => o . Tree . WeakTargets [ 0 ] == property . Tree . WeakTargets [ 0 ] ) ;
if ( holder == null )
{
holder = new TreeValuesHolder ( tree ) ;
_instance . _properties . Add ( holder ) ;
tree . OnPropertyValueChanged += TreeOnOnPropertyValueChanged ;
}
holder . ValuePaths . Add ( property . Path ) ;
}
private static void TreeOnOnPropertyValueChanged ( InspectorProperty property , int selectionindex )
{
if ( ! _instance . _repaintSheduled )
_instance . Repaint ( ) ;
_instance . _repaintSheduled = true ;
}
private void OnDisable ( )
{
string json = EditorJsonUtility . ToJson ( this ) ;
EditorPrefs . SetString ( "OWW_props" , json ) ;
}
protected override void OnEnable ( )
{
_labelWidth = EditorPrefs . GetFloat ( "OWW_labelWidth" , 200 ) ;
string json = EditorPrefs . GetString ( "OWW_props" , "" ) ;
EditorJsonUtility . FromJsonOverwrite ( json , this ) ;
EditorApplication . playModeStateChanged -= PlayModeStateChanged ;
EditorApplication . playModeStateChanged += PlayModeStateChanged ;
wantsMouseMove = true ;
for ( int i = 0 ; i < _properties . Count ; i ++ )
{
TreeValuesHolder holder = _properties [ i ] ;
if ( ! holder . CheckRefresh ( ) )
{
_properties . RemoveAt ( i -- ) ;
}
}
}
protected override void OnGUI ( )
{
_repaintSheduled = false ;
GUILayout . BeginHorizontal ( ) ;
if ( GUILayout . Button ( "Clear" ) )
{
_properties . Clear ( ) ;
}
Rect settingsRect = GUILayoutUtility . GetRect ( 24 , 24 , GUILayout . ExpandWidth ( false ) ) . AlignLeft ( 20 ) . AlignCenterY ( 20 ) ;
if ( SirenixEditorGUI . IconButton ( settingsRect , _showSettings ? EditorIcons . SettingsCog . Inactive : EditorIcons . SettingsCog . Active , "Settings" ) )
{
_showSettings = ! _showSettings ;
}
GUILayout . EndHorizontal ( ) ;
if ( _showSettings )
{
GUILayout . BeginHorizontal ( ) ;
GUILayout . Space ( 40 ) ;
GUI . changed = false ;
Rect rect = GUILayoutUtility . GetRect ( 1 , EditorGUIUtility . singleLineHeight , GUILayout . ExpandWidth ( true ) ) ;
_labelWidth = GUI . HorizontalSlider ( rect , _labelWidth , rect . xMin , rect . xMax ) ;
if ( GUI . changed )
EditorPrefs . SetFloat ( "OWW_labelWidth" , _labelWidth ) ;
EditorGUILayout . LabelField ( "Label Width" , GUILayout . Width ( 70 ) ) ;
GUILayout . EndHorizontal ( ) ;
}
GUILayout . Space ( 5 ) ;
bool first = true ;
if ( _properties . Count == 0 )
{
EditorGUILayout . LabelField ( "Right-click any property in an Inspector and select 'Watch' to make it show up here." , SirenixGUIStyles . MultiLineCenteredLabel ) ;
}
GUIHelper . PushLabelWidth ( _labelWidth - 30 ) ;
for ( int i = 0 ; i < _properties . Count ; i ++ )
{
TreeValuesHolder holder = _properties [ i ] ;
holder . CheckRefresh ( ) ;
if ( ! first )
GUILayout . Space ( 5 ) ;
first = false ;
Rect titleRect = SirenixEditorGUI . BeginBox ( " " + holder . Tree . TargetType . Name ) ;
titleRect = titleRect . AlignTop ( 21 ) ;
if ( holder . ParentObject != null )
{
Rect alignRight = titleRect . AlignRight ( 200 ) . AlignCenterY ( 16 ) . AlignLeft ( 180 ) ;
GUIHelper . PushGUIEnabled ( false ) ;
SirenixEditorFields . UnityObjectField ( alignRight , holder . ParentObject , typeof ( GameObject ) , true ) ;
GUIHelper . PopGUIEnabled ( ) ;
}
if ( SirenixEditorGUI . IconButton ( titleRect . AlignRight ( 20 ) . AlignCenterY ( 18 ) , EditorIcons . X ) )
{
_properties . RemoveAt ( i -- ) ;
}
Rect titleDragDropRect = titleRect . AlignLeft ( 30 ) . AlignCenter ( 20 , 20 ) ;
EditorIcons . List . Draw ( titleDragDropRect ) ;
TreeValuesHolder treedragdrop = ( TreeValuesHolder ) DragAndDropUtilities . DragAndDropZone ( titleDragDropRect , holder , typeof ( TreeValuesHolder ) , false , false ) ;
if ( treedragdrop != holder )
{
int treeDragDropIndex = _properties . IndexOf ( treedragdrop ) ;
Swap ( _properties , treeDragDropIndex , i ) ;
}
InspectorUtilities . BeginDrawPropertyTree ( holder . Tree , true ) ;
for ( int index = 0 ; index < holder . ValuePaths . Count ; index ++ )
{
string path = holder . ValuePaths [ index ] ;
GUILayout . BeginHorizontal ( ) ;
Rect rect1 = GUILayoutUtility . GetRect ( EditorGUIUtility . singleLineHeight + 5 , EditorGUIUtility . singleLineHeight + 3 , GUILayout . ExpandWidth ( false ) ) . AlignRight ( EditorGUIUtility . singleLineHeight + 2 ) ;
EditorIcons . List . Draw ( rect1 ) ;
ValueDragDropHolder dragdrop = ( ValueDragDropHolder ) DragAndDropUtilities . DragAndDropZone ( rect1 , new ValueDragDropHolder ( holder , index ) , typeof ( ValueDragDropHolder ) , false , false ) ;
if ( dragdrop . TreeValuesHolder == holder && dragdrop . Index != index )
{
string ptemp = holder . ValuePaths [ index ] ;
holder . ValuePaths [ index ] = holder . ValuePaths [ dragdrop . Index ] ;
holder . ValuePaths [ dragdrop . Index ] = ptemp ;
}
InspectorProperty propertyAtPath = holder . Tree . GetPropertyAtPath ( path ) ;
if ( propertyAtPath == null )
propertyAtPath = holder . Tree . GetPropertyAtUnityPath ( path ) ;
if ( propertyAtPath != null )
{
propertyAtPath . Draw ( ) ;
}
else
EditorGUILayout . LabelField ( $ "Could not find property ({ path } )") ;
if ( SirenixEditorGUI . IconButton ( EditorIcons . X ) )
{
holder . ValuePaths . RemoveAt ( index -- ) ;
if ( holder . ValuePaths . Count == 0 )
_properties . RemoveAt ( i -- ) ;
}
GUILayout . Space ( 3 ) ;
GUILayout . EndHorizontal ( ) ;
}
InspectorUtilities . EndDrawPropertyTree ( holder . Tree ) ;
SirenixEditorGUI . EndBox ( ) ;
}
GUIHelper . PopLabelWidth ( ) ;
}
[ Serializable ]
private class TreeValuesHolder
{
[ NonSerialized ]
public PropertyTree Tree ;
[ NonSerialized ]
public Object Target ;
public int InstanceID ;
public List < string > ValuePaths = new List < string > ( ) ;
[ NonSerialized ]
public Object ParentObject ;
public TreeValuesHolder ( PropertyTree tree )
{
Tree = tree ;
Target = ( Object ) tree . WeakTargets [ 0 ] ;
InstanceID = Target . GetInstanceID ( ) ;
GetParentObject ( ) ;
}
public bool CheckRefresh ( )
{
if ( Tree == null || Tree . SecretRootProperty == null || Target == null )
{
Target = EditorUtility . InstanceIDToObject ( InstanceID ) ;
if ( Target == null )
return false ;
GetParentObject ( ) ;
Tree = Sirenix . OdinInspector . Editor . PropertyTree . Create ( new List < Object > { Target } , ParentObject != null ? new SerializedObject ( ParentObject ) : null ) ;
}
return true ;
}
private void GetParentObject ( )
{
ParentObject = ( Target as MonoBehaviour ) ;
if ( ParentObject == null )
ParentObject = ( Target as Component ) ;
if ( ParentObject == null )
ParentObject = Target as ScriptableObject ;
}
}
private struct ValueDragDropHolder
{
public TreeValuesHolder TreeValuesHolder ;
public int Index ;
public ValueDragDropHolder ( TreeValuesHolder treeValuesHolder , int index )
{
TreeValuesHolder = treeValuesHolder ;
Index = index ;
}
}
private void Swap < T > ( IList < T > list , int indexA , int indexB )
{
T tmp = list [ indexA ] ;
list [ indexA ] = list [ indexB ] ;
list [ indexB ] = tmp ;
}
}
[ DrawerPriority ( 100 , 0 , 0 ) ]
public class OdinWatchWindowContextMenuDrawer < T > : OdinValueDrawer < T > , IDefinesGenericMenuItems
{
public void PopulateGenericMenu ( InspectorProperty property , GenericMenu genericMenu )
{
genericMenu . AddItem ( new GUIContent ( "Watch" ) , false , ( ) => OdinWatchWindow . AddWatch ( property ) ) ;
}
protected override void DrawPropertyLayout ( GUIContent label )
{
this . CallNextDrawer ( label ) ;
}
}
#endif