#24 Unity Hotkeys. L - lock inspector. D - debug inspector

using System;
using System.Reflection;
using UnityEditor;
using UnityEditor.ShortcutManagement;
using UnityEngine;

/// <summary>
/// L = lock last-focused Inspector;
/// D = toggle Debug mode in last-focused Inspector;
/// </summary>
[InitializeOnLoad]
public static class InspectorHotkeysEditor
{
    private static EditorWindow s_lastFocusedInspector;
    private static Type s_inspectorWindowType;
    private static PropertyInfo s_isLockedProperty;
    private static FieldInfo s_inspectorModeField;
    private static MethodInfo s_setModeMethod;

    static InspectorHotkeysEditor()
    {
        EditorApplication.update += OnUpdate;
    }

    private static void OnUpdate()
    {
        // Track last focused Inspector so L/D work even when another pane (e.g. Scene) has focus
        var focused = EditorWindow.focusedWindow;
        if (focused != null && IsInspectorWindow(focused))
            s_lastFocusedInspector = focused;
    }

    private static bool IsGameViewFocused()
    {
        var w = EditorWindow.focusedWindow;
        const string gameViewName = "GameView";
        return w != null && w.GetType().Name == gameViewName;
    }

    private static bool IsSceneViewFocused()
    {
        var w = EditorWindow.focusedWindow;
        const string sceneViewName = "SceneView";
        return w != null && w.GetType().Name == sceneViewName;
    }

    /// <summary>L and D are only allowed when Inspector or Hierarchy has focus (not Scene or Game view).</summary>
    private static bool AllowInspectorHotkeys()
    {
        if (IsGameViewFocused() || IsSceneViewFocused()) return false;
        return true;
    }

    private static bool IsInspectorWindow(EditorWindow w)
    {
        const string inspectorWindowName = "InspectorWindow";
        return w != null && w.GetType().Name == inspectorWindowName;
    }

    private static EditorWindow GetInspectorToActOn()
    {
        // Prefer the currently focused inspector, else the last focused one
        var focused = EditorWindow.focusedWindow;
        if (IsInspectorWindow(focused))
            return focused;
        return s_lastFocusedInspector;
    }

    private static void EnsureReflection()
    {
        const string inspectorWindowTypeName = "UnityEditor.InspectorWindow";
        const string isLockedPropertyName = "isLocked";
        const string inspectorModeFieldName = "m_InspectorMode";
        const string setModeMethodName = "SetMode";

        if (s_inspectorWindowType != null) return;
        var asm = typeof(UnityEditor.Editor).Assembly;
        s_inspectorWindowType = asm.GetType(inspectorWindowTypeName);
        if (s_inspectorWindowType == null) return;
        s_isLockedProperty = s_inspectorWindowType.GetProperty(isLockedPropertyName);
        s_inspectorModeField = s_inspectorWindowType.GetField(inspectorModeFieldName, BindingFlags.NonPublic | BindingFlags.Instance);
        s_setModeMethod = s_inspectorWindowType.GetMethod(setModeMethodName, BindingFlags.NonPublic | BindingFlags.Instance);
    }

    [Shortcut("Tools/Inspector Lock", KeyCode.L)]
    private static void ToggleInspectorLock()
    {
        if (!AllowInspectorHotkeys()) return;
        var inspector = GetInspectorToActOn();
        if (inspector == null) return;
        EnsureReflection();
        if (s_inspectorWindowType == null || s_isLockedProperty == null) return;
        if (!s_inspectorWindowType.IsInstanceOfType(inspector)) return;
        try
        {
            bool current = (bool)s_isLockedProperty.GetValue(inspector, null);
            s_isLockedProperty.SetValue(inspector, !current, null);
            inspector.Repaint();
        }
        catch (Exception) { /* ignore */ }
    }

    [Shortcut("Tools/Inspector Toggle Debug", KeyCode.D)]
    private static void ToggleInspectorDebug()
    {
        if (!AllowInspectorHotkeys()) return;
        var inspector = GetInspectorToActOn();
        if (inspector == null) return;
        EnsureReflection();
        if (s_inspectorWindowType == null || s_inspectorModeField == null || s_setModeMethod == null) return;
        if (!s_inspectorWindowType.IsInstanceOfType(inspector)) return;
        try
        {
            var mode = (InspectorMode)s_inspectorModeField.GetValue(inspector);
            var next = mode == InspectorMode.Normal ? InspectorMode.Debug : InspectorMode.Normal;
            s_setModeMethod.Invoke(inspector, new object[] { next });
            inspector.Repaint();
        }
        catch (Exception) { /* ignore */ }
    }
}