I have a custom EditorWindow that I want to show, without Unity automatically focusing it.
It’s an overlay similar to a tooltip, that appears depending on context to provide some information.
The problem I’m currently facing is that when the window is shown, it receives the focus and it causes an active textfield to longer work, for example.
Is there a way to show a borderless EditorWindow, that will not mess with the currently focused editor UI control? I’ve tried all the “Show” methods on EditorWindow, but they all seem to steal the focus.
I don’t know of one, I’m afraid. I’ve done some digging in the reference source for somewhat related use cases but unfortunately the window system is tightly coupled enough to make spoofing things via reflection prohibitively difficult (I’m not convinced it’s impossible, but it will take some serious effort).
For one project I did almost go so far as to start digging through Win32 and creating my own windows from scratch, but thankfully I realized how crazy that was and didn’t.
Have you gone down the path of trying to restore focus to the previous element immediately? I realize that in IMGUI world that’s kind of a nebulous concept, but maybe there’s some internal state shenanigans you can leverage. Maybe as UIElements becomes ubiquitous this will be easier.
I’ve gone down that path, but it was a very rocky one and I eventually fell over. Restoring the focus didn’t work robustly in my tests and then I didn’t look into it any further. I haven’t looked into UIElements yet, maybe it’s about time to do that.
Unfortunately that path is also short and rocky, as afaik most if not all of the editors still use IMGUI views inside the UIElement architecture, and it’s obviously not backwards compatible at all if you’re doing anything public-facing. I haven’t done anything in UIElements myself, but if you’re doing a custom editor window in 2019+ perhaps there’s some hope for you there.
If I ever have the opportunity, I will look into focus-restoring a bit myself. Maybe a second pair of eyes will help uncover something usable. It would be a helpful tool in the toolbag for the kind of editor messes I get into.
The EditorWindow class does contain an internal method called ShowPopupWithMode which can be used to show a popup styled window without giving it focus.
var window = ScriptableObject.CreateInstance<MyEditorWindow>();
var showWithMode = typeof(EditorWindow).GetMethod("ShowPopupWithMode", BindingFlags.Instance | BindingFlags.NonPublic);
var popupMenu = 1;
showWithMode.Invoke(window, new object[] { popupMenu, false });
Note that you’ll need to handle destroying the window manually. Popup windows normally get destroyed automatically when they lose focus, but that won’t happen in this case since the window will only look like a popup window.
I love being wrong! I’ve never noticed that method in all my reference source diving. Is it recent? I haven’t really looked in depth at 2018-2019.
EDIT: Yep, this showed up in 2018.3. That explains why I wasn’t aware of it. Unfortunately that also means that it’s not a backwards compatible solution with 2018.2 and older.
EDIT 2: No reflection at all necessary for 2018.3+. The ShowTooltip method doesn’t grab focus. That method is internal, I’m just blind.
Based on the lead provided by @SisusCo , I’ve gone ahead and put together an (untested) extension method that hopefully allows backwards and forwards compatibility. The 2018.3+ version uses @SisusCo 's method, while for older versions it mimics the source of the EditorWindow.ShowWithMode method.
public static class EditorWindowExtensions {
public enum ShowMode {
NormalWindow = 0,
PopupMenu = 1,
Utility = 2,
NoShadow = 3,
MainWindow = 4,
AuxWindow = 5,
Tooltip = 6,
ModalUtility = 7
}
public static void ShowWithMode( this EditorWindow window, ShowMode mode, bool focus ) {
var _EditorWindow = typeof(EditorWindow);
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
#if UNITY_2018_3_OR_NEWER
var showWithMode = _EditorWindow.GetMethod( "ShowPopupWithMode", flags );
showWithMode.Invoke( window, new object[] { mode, focus } );
#else
// todo: missing gui state stuff b/c I am lazy
// SavedGUIState oldState = SavedGUIState.Create();
var _ContainerWindow = _EditorWindow.Assembly.GetType( "UnityEditor.ContainerWindow" );
var _HostView = _EditorWindow.Assembly.GetType("UnityEditor.HostView");
var cw = ScriptableObject.CreateInstance( _ContainerWindow );
var host = ScriptableObject.CreateInstance( _HostView );
var _actualView = _HostView.GetProperty( "actualView", flags );
_actualView.SetValue( host, window );
var _borderSize = host.GetProperty( "borderSize", flags );
var offset = (RectOffset)_borderSize.GetValue( host );
var position = window.position;
var r = offset.Add( new Rect(position.x, position.y, position.width, position.height) );
var _position = _ContainerWindow.GetProperty( "position", flags );
_position.SetValue( cw, r );
var _rootView = _ContainerWindow.GetProperty( "rootView", flags );
_rootView.SetValue( cw, host );
var _MakeParentsSettingsMatchMe = _EditorWindow.GetMethod( "MakeParentsSettingsMatchMe", flags );
_MakeParentsSettingsMatchMe.Invoke( window, null );
var _Show = _ContainerWindow.GetMethod( "Show", flags );
_Show.Invoke( cw, new object[] { (int)mode, true, true, focus } );
var _SetMinMaxSize = _ContainerWindow.GetMethod( "SetMinMaxSizes", flags );
_SetMinMaxSize.Invoke( cw, new object[] { window.minSize, window.maxSize } );
// todo
// oldState.ApplyAndForget();
#endif
}
}