UI: Register File Browser as Child/Dialog-Window for the OS
authorJulian Eisel <eiseljulian@gmail.com>
Thu, 3 Oct 2019 14:59:49 +0000 (16:59 +0200)
committerJulian Eisel <eiseljulian@gmail.com>
Thu, 3 Oct 2019 15:50:27 +0000 (17:50 +0200)
For many users, this will make the File Browser window behave more like
what they would expect. It addresses the issue of the File Browser
becoming hidden behind the main window by clicking anywhere in the
latter. It communicates the interruptive, but temporary nature of the
operation a bit better.
Further, on tiling window managers the File Browser now opens as
floating by default, like in other applications.

Note that this also makes sure the File Browser is always opened as
separate window, so it doesn't re-use the Preferences, or any other
temporary window anymore. This seems to have been a common annoyance.

More concretely, this makes the File Browser window behave as follows:
* Stays on top of its parent Blender window, but not on top of
  non-Blender windows.
* Minimizes with its parent window
* Can be moved independently
* Doesn't add an own item in task bars
* Doesn't block other Blender windows (we may want to have this though)
* Opens as floating window for tiling window managers (e.g. i3wm/Sway)

Further notes:
* When opening a file browser from the Preference window (or any
  temporary window), the main window, as the file browsers parent is
  moved on top of the Preferences, which makes it seem like the
  Preferences were closed. This is the general issue of bad secondary
  window handling as window activation changes. I made it so that the
  window is moved back once the file browser is closed.
  This behavior is confusing and would be nice to avoid. It's a separate
  issue though.
* On most window managers on Linux the temporary window can not be
  minimized and maximized, they disable that for dialog windows.
* On Windows and macOS, only minimizing is disabled, as there is no
  decent way yet to restore a window if it's not shown in the taskbar.

Reviewed By: Brecht van Lommel, Campbell Barton, William Reynish
Edits and macOS implementation by Brecht.

Differential Revision: https://developer.blender.org/D5810

Part of T69652.

27 files changed:
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_ISystem.h
intern/ghost/GHOST_IWindow.h
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_System.h
intern/ghost/intern/GHOST_SystemCocoa.h
intern/ghost/intern/GHOST_SystemCocoa.mm
intern/ghost/intern/GHOST_SystemSDL.cpp
intern/ghost/intern/GHOST_SystemSDL.h
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_SystemWin32.h
intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_SystemX11.h
intern/ghost/intern/GHOST_Window.h
intern/ghost/intern/GHOST_WindowCocoa.h
intern/ghost/intern/GHOST_WindowCocoa.mm
intern/ghost/intern/GHOST_WindowWin32.cpp
intern/ghost/intern/GHOST_WindowWin32.h
intern/ghost/intern/GHOST_WindowX11.cpp
intern/ghost/intern/GHOST_WindowX11.h
source/blender/editors/include/ED_screen.h
source/blender/editors/render/render_view.c
source/blender/editors/screen/screen_edit.c
source/blender/editors/screen/screen_ops.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_window.c

index 20bb144a9245b6691b1cf055b93b8ec40e38c06c..220e7aab8cdefe5a653daf885138617f95eb14ad 100644 (file)
@@ -176,6 +176,17 @@ extern GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle,
                                              GHOST_TDrawingContextType type,
                                              GHOST_GLSettings glSettings);
 
+extern GHOST_WindowHandle GHOST_CreateDialogWindow(GHOST_SystemHandle systemhandle,
+                                                   GHOST_WindowHandle parent_windowhandle,
+                                                   const char *title,
+                                                   GHOST_TInt32 left,
+                                                   GHOST_TInt32 top,
+                                                   GHOST_TUns32 width,
+                                                   GHOST_TUns32 height,
+                                                   GHOST_TWindowState state,
+                                                   GHOST_TDrawingContextType type,
+                                                   GHOST_GLSettings glSettings);
+
 /**
  * Create a new offscreen context.
  * Never explicitly delete the context, use disposeContext() instead.
@@ -207,6 +218,8 @@ extern GHOST_TUserDataPtr GHOST_GetWindowUserData(GHOST_WindowHandle windowhandl
  */
 extern void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, GHOST_TUserDataPtr userdata);
 
+extern int GHOST_IsDialogWindow(GHOST_WindowHandle windowhandle);
+
 /**
  * Dispose a window.
  * \param systemhandle The handle to the system
index 27be80a2f2015b4629aa3845927746b852a59d46..1eeb6cd852f2c4a0bf7fa5f0a6056c7659aba7f9 100644 (file)
@@ -236,6 +236,7 @@ class GHOST_ISystem {
    * \param type: The type of drawing context installed in this window.
    * \param glSettings: Misc OpenGL settings.
    * \param exclusive: Use to show the window on top and ignore others (used fullscreen).
+   * \param is_dialog: Stay on top of parent window, no icon in taskbar, not minimizable.
    * \param parentWindow: Parent (embedder) window
    * \return The new window (or 0 if creation failed).
    */
@@ -248,7 +249,8 @@ class GHOST_ISystem {
                                       GHOST_TDrawingContextType type,
                                       GHOST_GLSettings glSettings,
                                       const bool exclusive = false,
-                                      const GHOST_TEmbedderWindowID parentWindow = 0) = 0;
+                                      const bool is_dialog = false,
+                                      const GHOST_IWindow *parentWindow = NULL) = 0;
 
   /**
    * Dispose a window.
index 03a0db9abbea53d6dfc9476a231a77f569dd50ac..c19d4bdf6bd156935d470bfc403b1829b171c78a 100644 (file)
@@ -241,6 +241,8 @@ class GHOST_IWindow {
    */
   virtual void setUserData(const GHOST_TUserDataPtr userData) = 0;
 
+  virtual bool isDialog() const = 0;
+
   /**
    * Returns the tablet data (pressure etc).
    * \return The tablet data (pressure etc).
index a1a209af77af170efa21eabb0a258bf20a9fd406..d907c10570c675f6492368a19391c6e2af6d9a61 100644 (file)
@@ -140,7 +140,33 @@ GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle,
   GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
 
   return (GHOST_WindowHandle)system->createWindow(
-      title, left, top, width, height, state, type, glSettings, false);
+      title, left, top, width, height, state, type, glSettings, false, false);
+}
+
+GHOST_WindowHandle GHOST_CreateDialogWindow(GHOST_SystemHandle systemhandle,
+                                            GHOST_WindowHandle parent_windowhandle,
+                                            const char *title,
+                                            GHOST_TInt32 left,
+                                            GHOST_TInt32 top,
+                                            GHOST_TUns32 width,
+                                            GHOST_TUns32 height,
+                                            GHOST_TWindowState state,
+                                            GHOST_TDrawingContextType type,
+                                            GHOST_GLSettings glSettings)
+{
+  GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
+
+  return (GHOST_WindowHandle)system->createWindow(title,
+                                                  left,
+                                                  top,
+                                                  width,
+                                                  height,
+                                                  state,
+                                                  type,
+                                                  glSettings,
+                                                  false,
+                                                  true,
+                                                  (GHOST_IWindow *)parent_windowhandle);
 }
 
 GHOST_TUserDataPtr GHOST_GetWindowUserData(GHOST_WindowHandle windowhandle)
@@ -156,6 +182,13 @@ void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, GHOST_TUserDataPtr
   window->setUserData(userdata);
 }
 
+int GHOST_IsDialogWindow(GHOST_WindowHandle windowhandle)
+{
+  GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
+
+  return (int)window->isDialog();
+}
+
 GHOST_TSuccess GHOST_DisposeWindow(GHOST_SystemHandle systemhandle,
                                    GHOST_WindowHandle windowhandle)
 {
index fbf8af01e59712241d94374d30dd962fbe0d6c3a..2e45592e0559a940dc10e035fa7b5f4cc621db32 100644 (file)
@@ -104,14 +104,6 @@ class GHOST_System : public GHOST_ISystem {
    * Display/window management functionality
    ***************************************************************************************/
 
-  /**
-   * Inherited from GHOST_ISystem but left pure virtual
-   *
-   * virtual  GHOST_TUns8 getNumDisplays() const = 0;
-   * virtual void getMainDisplayDimensions(...) const = 0;
-   * virtual GHOST_IWindow* createWindow(..)
-   */
-
   /**
    * Dispose a window.
    * \param   window Pointer to the window to be disposed.
index 109dd6c180dc6b094bb9a4b3fcf478e08ea01160..1e44c3e31d4045ffe580aed122c8e3688623256f 100644 (file)
@@ -109,7 +109,8 @@ class GHOST_SystemCocoa : public GHOST_System {
                               GHOST_TDrawingContextType type,
                               GHOST_GLSettings glSettings,
                               const bool exclusive = false,
-                              const GHOST_TEmbedderWindowID parentWindow = 0);
+                              const bool is_dialog = false,
+                              const GHOST_IWindow *parentWindow = NULL);
 
   /**
    * Create a new offscreen context.
@@ -231,6 +232,11 @@ class GHOST_SystemCocoa : public GHOST_System {
    */
   GHOST_TSuccess handleApplicationBecomeActiveEvent();
 
+  /**
+   * \return True if any dialog window is open.
+   */
+  bool hasDialogWindow();
+
   /**
    * External objects should call this when they send an event outside processEvents.
    */
index 2026c1b7b4fffbc3b1d83d36a97f05b81b9267c5..68bac7d153bfb856ed469f2d9e4957926312dfb5 100644 (file)
@@ -707,7 +707,8 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const STR_String &title,
                                                GHOST_TDrawingContextType type,
                                                GHOST_GLSettings glSettings,
                                                const bool exclusive,
-                                               const GHOST_TEmbedderWindowID parentWindow)
+                                               const bool is_dialog,
+                                               const GHOST_IWindow *parentWindow)
 {
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   GHOST_IWindow *window = NULL;
@@ -735,7 +736,9 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const STR_String &title,
                                  state,
                                  type,
                                  glSettings.flags & GHOST_glStereoVisual,
-                                 glSettings.flags & GHOST_glDebugContext);
+                                 glSettings.flags & GHOST_glDebugContext,
+                                 is_dialog,
+                                 (GHOST_WindowCocoa *)parentWindow);
 
   if (window->getValid()) {
     // Store the pointer to the window
@@ -972,6 +975,13 @@ bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
 // Note: called from NSApplication delegate
 GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
 {
+  for (GHOST_IWindow *iwindow : m_windowManager->getWindows()) {
+    GHOST_WindowCocoa *window = (GHOST_WindowCocoa *)iwindow;
+    if (window->isDialog()) {
+      [window->getCocoaWindow() makeKeyAndOrderFront:nil];
+    }
+  }
+
   // Update the modifiers key mask, as its status may have changed when the application
   // was not active (that is when update events are sent to another application).
   unsigned int modifiers;
@@ -1021,6 +1031,17 @@ GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
   return GHOST_kSuccess;
 }
 
+bool GHOST_SystemCocoa::hasDialogWindow()
+{
+  for (GHOST_IWindow *iwindow : m_windowManager->getWindows()) {
+    GHOST_WindowCocoa *window = (GHOST_WindowCocoa *)iwindow;
+    if (window->isDialog()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void GHOST_SystemCocoa::notifyExternalEventProcessed()
 {
   m_outsideLoopEventProcessed = true;
index 520c62719f8efa3712fe0a0cd8c1230f70e5c60b..e3f6f4b6bb1269e543d18f6bb743eeed7be03a22 100644 (file)
@@ -58,6 +58,7 @@ GHOST_IWindow *GHOST_SystemSDL::createWindow(const STR_String &title,
                                              GHOST_TDrawingContextType type,
                                              GHOST_GLSettings glSettings,
                                              const bool exclusive,
+                                             const bool /* is_dialog */,
                                              const GHOST_TEmbedderWindowID parentWindow)
 {
   GHOST_WindowSDL *window = NULL;
index 7dbdc3ccec8be2e0377fecc82110050f008e281d..942b6297c227d28b1876a25a49d5304af5c2e0a5 100644 (file)
@@ -89,6 +89,7 @@ class GHOST_SystemSDL : public GHOST_System {
                               GHOST_TDrawingContextType type,
                               GHOST_GLSettings glSettings,
                               const bool exclusive = false,
+                              const bool is_dialog = false,
                               const GHOST_TEmbedderWindowID parentWindow = 0);
 
   /* SDL specific */
index 9073ed9944bfb9fe0dfca668de55408bb14bb47f..1b04e1fde79acd8aeee433999491384cd270bc54 100644 (file)
@@ -266,7 +266,8 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const STR_String &title,
                                                GHOST_TDrawingContextType type,
                                                GHOST_GLSettings glSettings,
                                                const bool exclusive,
-                                               const GHOST_TEmbedderWindowID parentWindow)
+                                               const bool is_dialog,
+                                               const GHOST_IWindow *parentWindow)
 {
   GHOST_WindowWin32 *window = new GHOST_WindowWin32(
       this,
@@ -279,8 +280,9 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const STR_String &title,
       type,
       ((glSettings.flags & GHOST_glStereoVisual) != 0),
       ((glSettings.flags & GHOST_glAlphaBackground) != 0),
-      parentWindow,
-      ((glSettings.flags & GHOST_glDebugContext) != 0));
+      (GHOST_WindowWin32 *)parentWindow,
+      ((glSettings.flags & GHOST_glDebugContext) != 0),
+      is_dialog);
 
   if (window->getValid()) {
     // Store the pointer to the window
index 7ac6a3e3e20f795fa08fccc2d167cb3430fdaa8d..a92dea84d705b037b5c387ec7c263a822a9603c8 100644 (file)
@@ -112,7 +112,7 @@ class GHOST_SystemWin32 : public GHOST_System {
    * \param   type    The type of drawing context installed in this window.
    * \param glSettings: Misc OpenGL settings.
    * \param exclusive: Use to show the window ontop and ignore others (used fullscreen).
-   * \param   parentWindow    Parent (embedder) window
+   * \param   parentWindow    Parent window
    * \return  The new window (or 0 if creation failed).
    */
   GHOST_IWindow *createWindow(const STR_String &title,
@@ -124,7 +124,8 @@ class GHOST_SystemWin32 : public GHOST_System {
                               GHOST_TDrawingContextType type,
                               GHOST_GLSettings glSettings,
                               const bool exclusive = false,
-                              const GHOST_TEmbedderWindowID parentWindow = 0);
+                              const bool is_dialog = false,
+                              const GHOST_IWindow *parentWindow = 0);
 
   /**
    * Create a new offscreen context.
index 639553f31bb51cc46303b9ca12a3d335d538ecaf..a9435f3faff9ba8dbc0bf4b4455f1f9159b555a0 100644 (file)
@@ -324,7 +324,7 @@ void GHOST_SystemX11::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32
  * \param   type    The type of drawing context installed in this window.
  * \param glSettings: Misc OpenGL settings.
  * \param exclusive: Use to show the window ontop and ignore others (used fullscreen).
- * \param   parentWindow    Parent (embedder) window
+ * \param   parentWindow    Parent window
  * \return  The new window (or 0 if creation failed).
  */
 GHOST_IWindow *GHOST_SystemX11::createWindow(const STR_String &title,
@@ -336,7 +336,8 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const STR_String &title,
                                              GHOST_TDrawingContextType type,
                                              GHOST_GLSettings glSettings,
                                              const bool exclusive,
-                                             const GHOST_TEmbedderWindowID parentWindow)
+                                             const bool is_dialog,
+                                             const GHOST_IWindow *parentWindow)
 {
   GHOST_WindowX11 *window = NULL;
 
@@ -351,8 +352,9 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const STR_String &title,
                                width,
                                height,
                                state,
-                               parentWindow,
+                               (GHOST_WindowX11 *)parentWindow,
                                type,
+                               is_dialog,
                                ((glSettings.flags & GHOST_glStereoVisual) != 0),
                                exclusive,
                                ((glSettings.flags & GHOST_glAlphaBackground) != 0),
index 1fe94b40f179e10bcc147c11aa35643836cd8c1a..6ad2d6decae1b138a083166496d690a0d7a886ab 100644 (file)
@@ -146,7 +146,8 @@ class GHOST_SystemX11 : public GHOST_System {
                               GHOST_TDrawingContextType type,
                               GHOST_GLSettings glSettings,
                               const bool exclusive = false,
-                              const GHOST_TEmbedderWindowID parentWindow = 0);
+                              const bool is_dialog = false,
+                              const GHOST_IWindow *parentWindow = 0);
 
   /**
    * Create a new offscreen context.
index 5f9e8ffdd5e2d23307036d058af03d5204d75c6b..553a7d89df4dbabc539cb805a2828ea537ff5acd 100644 (file)
@@ -109,6 +109,11 @@ class GHOST_Window : public GHOST_IWindow {
    */
   inline GHOST_TStandardCursor getCursorShape() const;
 
+  inline bool isDialog() const
+  {
+    return false;
+  }
+
   /**
    * Set the shape of the cursor.
    * \param   cursorShape: The new cursor shape type id.
index 66de8bcf7cc4792b3519dd5b5c1f157d58000996..d260d0eacbc62beff938e176b607bfd484d7795e 100644 (file)
@@ -66,7 +66,9 @@ class GHOST_WindowCocoa : public GHOST_Window {
                     GHOST_TWindowState state,
                     GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone,
                     const bool stereoVisual = false,
-                    bool is_debug = false);
+                    bool is_debug = false,
+                    bool dialog = false,
+                    GHOST_WindowCocoa *parentWindow = 0);
 
   /**
    * Destructor.
@@ -218,6 +220,8 @@ class GHOST_WindowCocoa : public GHOST_Window {
   NSCursor *getStandardCursor(GHOST_TStandardCursor cursor) const;
   void loadCursor(bool visible, GHOST_TStandardCursor cursor) const;
 
+  bool isDialog() const;
+
   const GHOST_TabletData *GetTabletData()
   {
     return &m_tablet;
@@ -328,6 +332,7 @@ class GHOST_WindowCocoa : public GHOST_Window {
 
   bool m_immediateDraw;
   bool m_debug_context;  // for debug messages during context setup
+  bool m_is_dialog;
 };
 
 #endif  // __GHOST_WINDOWCOCOA_H__
index 6087df978fa6a8f7432052e52f17ad7d7599994d..1d89da90a32fe10aeb909b328e5a00a90581d6ac 100644 (file)
 
 - (BOOL)canBecomeKeyWindow
 {
-  return YES;
+  /* Don't make other windows active when a dialog window is open. */
+  return (associatedWindow->isDialog() || !systemCocoa->hasDialogWindow());
 }
 
 //The drag'n'drop dragging destination methods
@@ -290,7 +291,9 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa,
                                      GHOST_TWindowState state,
                                      GHOST_TDrawingContextType type,
                                      const bool stereoVisual,
-                                     bool is_debug)
+                                     bool is_debug,
+                                     bool is_dialog,
+                                     GHOST_WindowCocoa *parentWindow)
     : GHOST_Window(width, height, state, stereoVisual, false),
       m_openGLView(nil),
       m_metalView(nil),
@@ -298,7 +301,8 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa,
       m_systemCocoa(systemCocoa),
       m_customCursor(0),
       m_immediateDraw(false),
-      m_debug_context(is_debug)
+      m_debug_context(is_debug),
+      m_is_dialog(is_dialog)
 {
   m_fullScreen = false;
 
@@ -313,12 +317,16 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa,
   rect.size.width = width;
   rect.size.height = height;
 
-  m_window = [[CocoaWindow alloc]
-      initWithContentRect:rect
-                styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
-                          NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable
-                  backing:NSBackingStoreBuffered
-                    defer:NO];
+  NSWindowStyleMask styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
+                                NSWindowStyleMaskResizable;
+  if (!is_dialog) {
+    styleMask |= NSWindowStyleMaskMiniaturizable;
+  }
+
+  m_window = [[CocoaWindow alloc] initWithContentRect:rect
+                                            styleMask:styleMask
+                                              backing:NSBackingStoreBuffered
+                                                defer:NO];
 
   if (m_window == nil) {
     [pool drain];
@@ -402,6 +410,10 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa,
   if (state == GHOST_kWindowStateFullScreen)
     setState(GHOST_kWindowStateFullScreen);
 
+  if (is_dialog && parentWindow) {
+    [parentWindow->getCocoaWindow() addChildWindow:m_window ordered:NSWindowAbove];
+  }
+
   setNativePixelSize();
 
   [pool drain];
@@ -548,10 +560,8 @@ void GHOST_WindowCocoa::getClientBounds(GHOST_Rect &bounds) const
   NSRect screenSize = [[m_window screen] visibleFrame];
 
   // Max window contents as screen size (excluding title bar...)
-  NSRect contentRect = [CocoaWindow
-      contentRectForFrameRect:screenSize
-                    styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
-                               NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)];
+  NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
+                                                  styleMask:[m_window styleMask]];
 
   rect = [m_window contentRectForFrameRect:[m_window frame]];
 
@@ -1045,6 +1055,11 @@ void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor shape) co
   [cursor set];
 }
 
+bool GHOST_WindowCocoa::isDialog() const
+{
+  return m_is_dialog;
+}
+
 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
 {
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
index d283e9e3aff8cb39cd6ac04e36314aad4ff4057c..e1c6aa1109cf295dfa5383582ecb2eb58ad1b59f 100644 (file)
@@ -23,6 +23,7 @@
 
 #define _USE_MATH_DEFINES
 
+#include "GHOST_WindowManager.h"
 #include "GHOST_WindowWin32.h"
 #include "GHOST_SystemWin32.h"
 #include "GHOST_DropTargetWin32.h"
@@ -66,8 +67,9 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
                                      GHOST_TDrawingContextType type,
                                      bool wantStereoVisual,
                                      bool alphaBackground,
-                                     GHOST_TEmbedderWindowID parentwindowhwnd,
-                                     bool is_debug)
+                                     GHOST_WindowWin32 *parentwindow,
+                                     bool is_debug,
+                                     bool dialog)
     : GHOST_Window(width, height, state, wantStereoVisual, false),
       m_inLiveResize(false),
       m_system(system),
@@ -82,7 +84,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
       m_fpGetPointerInfo(NULL),
       m_fpGetPointerPenInfo(NULL),
       m_fpGetPointerTouchInfo(NULL),
-      m_parentWindowHwnd(parentwindowhwnd),
+      m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : NULL),
       m_debug_context(is_debug)
 {
   // Initialize tablet variables
@@ -146,9 +148,9 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
       top = monitor.rcWork.top;
 
     int wintype = WS_OVERLAPPEDWINDOW;
-    if (m_parentWindowHwnd != 0) {
+    if ((m_parentWindowHwnd != 0) && !dialog) {
       wintype = WS_CHILD;
-      GetWindowRect((HWND)m_parentWindowHwnd, &rect);
+      GetWindowRect(m_parentWindowHwnd, &rect);
       left = 0;
       top = 0;
       width = rect.right - rect.left;
@@ -156,14 +158,14 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
     }
 
     wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
-    m_hWnd = ::CreateWindowW(s_windowClassName,         // pointer to registered class name
-                             title_16,                  // pointer to window name
-                             wintype,                   // window style
-                             left,                      // horizontal position of window
-                             top,                       // vertical position of window
-                             width,                     // window width
-                             height,                    // window height
-                             (HWND)m_parentWindowHwnd,  // handle to parent or owner window
+    m_hWnd = ::CreateWindowW(s_windowClassName,                // pointer to registered class name
+                             title_16,                         // pointer to window name
+                             wintype,                          // window style
+                             left,                             // horizontal position of window
+                             top,                              // vertical position of window
+                             width,                            // window width
+                             height,                           // window height
+                             dialog ? 0 : m_parentWindowHwnd,  // handle to parent or owner window
                              0,                     // handle to menu or child-window identifier
                              ::GetModuleHandle(0),  // handle to application instance
                              0);                    // pointer to window-creation data
@@ -267,7 +269,16 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
     }
   }
 
-  if (parentwindowhwnd != 0) {
+  if (dialog && parentwindow) {
+    ::SetWindowLongPtr(m_hWnd,
+                       GWL_STYLE,
+                       WS_VISIBLE | WS_CHILD | WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX |
+                           WS_SIZEBOX);
+    ::SetWindowLongPtr(m_hWnd, GWLP_HWNDPARENT, (LONG_PTR)m_parentWindowHwnd);
+    ::SetWindowPos(
+        m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+  }
+  else if (parentwindow) {
     RAWINPUTDEVICE device = {0};
     device.usUsagePage = 0x01; /* usUsagePage & usUsage for keyboard*/
     device.usUsage = 0x06;     /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
@@ -386,6 +397,16 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
   }
 
   if (m_hWnd) {
+    /* If this window is referenced by others as parent, clear that relation or windows will free
+     * the handle while we still reference it. */
+    for (GHOST_IWindow *iter_win : m_system->getWindowManager()->getWindows()) {
+      GHOST_WindowWin32 *iter_winwin = (GHOST_WindowWin32 *)iter_win;
+      if (iter_winwin->m_parentWindowHwnd == m_hWnd) {
+        ::SetWindowLongPtr(iter_winwin->m_hWnd, GWLP_HWNDPARENT, NULL);
+        iter_winwin->m_parentWindowHwnd = 0;
+      }
+    }
+
     if (m_dropTarget) {
       // Disable DragDrop
       RevokeDragDrop(m_hWnd);
@@ -528,7 +549,7 @@ GHOST_TWindowState GHOST_WindowWin32::getState() const
   // we need to find a way to combine parented windows + resizing if we simply set the
   // state as GHOST_kWindowStateEmbedded we will need to check for them somewhere else.
   // It's also strange that in Windows is the only platform we need to make this separation.
-  if (m_parentWindowHwnd != 0) {
+  if ((m_parentWindowHwnd != 0) && !isDialog()) {
     state = GHOST_kWindowStateEmbedded;
     return state;
   }
@@ -574,6 +595,7 @@ void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX,
 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
 {
   GHOST_TWindowState curstate = getState();
+  LONG_PTR newstyle = -1;
   WINDOWPLACEMENT wp;
   wp.length = sizeof(WINDOWPLACEMENT);
   ::GetWindowPlacement(m_hWnd, &wp);
@@ -587,7 +609,7 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
       break;
     case GHOST_kWindowStateMaximized:
       wp.showCmd = SW_SHOWMAXIMIZED;
-      ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
+      newstyle = WS_OVERLAPPEDWINDOW;
       break;
     case GHOST_kWindowStateFullScreen:
       if (curstate != state && curstate != GHOST_kWindowStateMinimized)
@@ -595,17 +617,21 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
       wp.showCmd = SW_SHOWMAXIMIZED;
       wp.ptMaxPosition.x = 0;
       wp.ptMaxPosition.y = 0;
-      ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_MAXIMIZE);
+      newstyle = WS_MAXIMIZE;
       break;
     case GHOST_kWindowStateEmbedded:
-      ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_CHILD);
+      newstyle = WS_CHILD;
       break;
     case GHOST_kWindowStateNormal:
     default:
       wp.showCmd = SW_SHOWNORMAL;
-      ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
+      newstyle = WS_OVERLAPPEDWINDOW;
       break;
   }
+  if ((newstyle >= 0) && !isDialog()) {
+    ::SetWindowLongPtr(m_hWnd, GWL_STYLE, newstyle);
+  }
+
   /* Clears window cache for SetWindowLongPtr */
   ::SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
 
@@ -737,6 +763,14 @@ void GHOST_WindowWin32::lostMouseCapture()
   }
 }
 
+bool GHOST_WindowWin32::isDialog() const
+{
+  HWND parent = (HWND)::GetWindowLongPtr(m_hWnd, GWLP_HWNDPARENT);
+  long int style = (long int)::GetWindowLongPtr(m_hWnd, GWL_STYLE);
+
+  return (parent != 0) && (style & WS_POPUPWINDOW);
+}
+
 void GHOST_WindowWin32::registerMouseClickEvent(int press)
 {
 
index 8dac142d6f439e70a3d1b510c568bcde33416a9e..f72f03855fdef32593d7318b53e4bac9d88973b3 100644 (file)
@@ -235,8 +235,9 @@ class GHOST_WindowWin32 : public GHOST_Window {
                     GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone,
                     bool wantStereoVisual = false,
                     bool alphaBackground = false,
-                    GHOST_TEmbedderWindowID parentWindowHwnd = 0,
-                    bool is_debug = false);
+                    GHOST_WindowWin32 *parentWindow = 0,
+                    bool is_debug = false,
+                    bool dialog = false);
 
   /**
    * Destructor.
@@ -381,6 +382,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
    */
   void lostMouseCapture();
 
+  bool isDialog() const;
+
   /**
    * Loads the windows equivalent of a standard GHOST cursor.
    * \param visible       Flag for cursor visibility.
@@ -528,8 +531,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
   GHOST_WIN32_GetPointerPenInfo m_fpGetPointerPenInfo;
   GHOST_WIN32_GetPointerTouchInfo m_fpGetPointerTouchInfo;
 
-  /** Hwnd to parent window */
-  GHOST_TEmbedderWindowID m_parentWindowHwnd;
+  HWND m_parentWindowHwnd;
 
 #ifdef WITH_INPUT_IME
   /** Handle input method editors event */
index 8aa4a4e284a92308b83656cbb7ab5749e2f22d9a..ae8d705fe4aa0764deaac9fe801e1fec31fd8b97 100644 (file)
@@ -72,7 +72,18 @@ typedef struct {
   long input_mode;
 } MotifWmHints;
 
-#define MWM_HINTS_DECORATIONS (1L << 1)
+enum {
+  MWM_HINTS_FUNCTIONS = (1L << 0),
+  MWM_HINTS_DECORATIONS = (1L << 1),
+};
+enum {
+  MWM_FUNCTION_ALL = (1L << 0),
+  MWM_FUNCTION_RESIZE = (1L << 1),
+  MWM_FUNCTION_MOVE = (1L << 2),
+  MWM_FUNCTION_MINIMIZE = (1L << 3),
+  MWM_FUNCTION_MAXIMIZE = (1L << 4),
+  MWM_FUNCTION_CLOSE = (1L << 5),
+};
 
 #ifndef HOST_NAME_MAX
 #  define HOST_NAME_MAX 64
@@ -191,8 +202,9 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
                                  GHOST_TUns32 width,
                                  GHOST_TUns32 height,
                                  GHOST_TWindowState state,
-                                 const GHOST_TEmbedderWindowID parentWindow,
+                                 GHOST_WindowX11 *parentWindow,
                                  GHOST_TDrawingContextType type,
+                                 const bool is_dialog,
                                  const bool stereoVisual,
                                  const bool exclusive,
                                  const bool alphaBackground,
@@ -259,7 +271,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
       m_display, RootWindow(m_display, m_visualInfo->screen), m_visualInfo->visual, AllocNone);
 
   /* create the window! */
-  if (parentWindow == 0) {
+  if ((parentWindow == 0) || is_dialog) {
     m_window = XCreateWindow(m_display,
                              RootWindow(m_display, m_visualInfo->screen),
                              left,
@@ -279,7 +291,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
     unsigned int w_return, h_return, border_w_return, depth_return;
 
     XGetGeometry(m_display,
-                 parentWindow,
+                 parentWindow->m_window,
                  &root_return,
                  &x_return,
                  &y_return,
@@ -294,7 +306,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
     height = h_return;
 
     m_window = XCreateWindow(m_display,
-                             parentWindow, /* reparent against embedder */
+                             parentWindow->m_window, /* reparent against embedder */
                              left,
                              top,
                              width,
@@ -306,7 +318,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
                              xattributes_valuemask,
                              &xattributes);
 
-    XSelectInput(m_display, parentWindow, SubstructureNotifyMask);
+    XSelectInput(m_display, parentWindow->m_window, SubstructureNotifyMask);
   }
 
 #ifdef WITH_XDND
@@ -356,6 +368,10 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
     m_post_state = GHOST_kWindowStateNormal;
   }
 
+  if (is_dialog && parentWindow) {
+    setDialogHints(parentWindow);
+  }
+
   /* Create some hints for the window manager on how
    * we want this window treated. */
   {
@@ -701,6 +717,42 @@ void GHOST_WindowX11::clientToScreen(GHOST_TInt32 inX,
   outY = ay;
 }
 
+GHOST_TSuccess GHOST_WindowX11::setDialogHints(GHOST_WindowX11 *parentWindow)
+{
+
+  Atom atom_window_type = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE", False);
+  Atom atom_dialog = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+  MotifWmHints hints = {0};
+
+  XChangeProperty(m_display,
+                  m_window,
+                  atom_window_type,
+                  XA_ATOM,
+                  32,
+                  PropModeReplace,
+                  (unsigned char *)&atom_dialog,
+                  1);
+  XSetTransientForHint(m_display, m_window, parentWindow->m_window);
+
+  /* Disable minimizing of the window for now.
+   * Actually, most window managers disable minimizing and maximizing for dialogs, ignoring this.
+   * Leaving it here anyway in the hope it brings back maximizing on some window managers at least,
+   * we'd preferably have it even for dialog windows (e.g. file browser). */
+  hints.flags = MWM_HINTS_FUNCTIONS;
+  hints.functions = MWM_FUNCTION_RESIZE | MWM_FUNCTION_MOVE | MWM_FUNCTION_MAXIMIZE |
+                    MWM_FUNCTION_CLOSE;
+  XChangeProperty(m_display,
+                  m_window,
+                  m_system->m_atom._MOTIF_WM_HINTS,
+                  m_system->m_atom._MOTIF_WM_HINTS,
+                  32,
+                  PropModeReplace,
+                  (unsigned char *)&hints,
+                  4);
+
+  return GHOST_kSuccess;
+}
+
 void GHOST_WindowX11::icccmSetState(int state)
 {
   XEvent xev;
@@ -1112,6 +1164,44 @@ GHOST_TSuccess GHOST_WindowX11::setOrder(GHOST_TWindowOrder order)
   return GHOST_kSuccess;
 }
 
+bool GHOST_WindowX11::isDialog() const
+{
+  Atom atom_window_type = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE", False);
+  Atom atom_dialog = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+
+  Atom *prop_ret;
+  unsigned long bytes_after, num_ret;
+  Atom type_ret;
+  bool st;
+  int format_ret, ret;
+
+  prop_ret = NULL;
+  st = False;
+  ret = XGetWindowProperty(m_display,
+                           m_window,
+                           atom_window_type,
+                           0,
+                           INT_MAX,
+                           False,
+                           XA_ATOM,
+                           &type_ret,
+                           &format_ret,
+                           &num_ret,
+                           &bytes_after,
+                           (unsigned char **)&prop_ret);
+  if ((ret == Success) && (prop_ret) && (format_ret == 32)) {
+    if (prop_ret[0] == atom_dialog) {
+      st = True;
+    }
+  }
+
+  if (prop_ret) {
+    XFree(prop_ret);
+  }
+
+  return st;
+}
+
 GHOST_TSuccess GHOST_WindowX11::invalidate()
 {
   /* So the idea of this function is to generate an expose event
index 0b8fe3a3a418428164c17b20368aaa49e47ee82c..faf3acba234b355d3e7af955130cbf5e5af4b2ae 100644 (file)
@@ -75,8 +75,9 @@ class GHOST_WindowX11 : public GHOST_Window {
                   GHOST_TUns32 width,
                   GHOST_TUns32 height,
                   GHOST_TWindowState state,
-                  const GHOST_TEmbedderWindowID parentWindow,
+                  GHOST_WindowX11 *parentWindow,
                   GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone,
+                  const bool is_dialog = false,
                   const bool stereoVisual = false,
                   const bool exclusive = false,
                   const bool alphaBackground = false,
@@ -92,6 +93,8 @@ class GHOST_WindowX11 : public GHOST_Window {
 
   void getClientBounds(GHOST_Rect &bounds) const;
 
+  bool isDialog() const;
+
   GHOST_TSuccess setClientWidth(GHOST_TUns32 width);
 
   GHOST_TSuccess setClientHeight(GHOST_TUns32 height);
@@ -185,6 +188,8 @@ class GHOST_WindowX11 : public GHOST_Window {
 
   GHOST_TSuccess endFullScreen() const;
 
+  GHOST_TSuccess setDialogHints(GHOST_WindowX11 *parentWindow);
+
   GHOST_TUns16 getDPIHint();
 
  protected:
index 7c3aac6c688ba46de1e02f1f1f453a3773c901bd..c3e61f5f2b2c7efc65ad165775e264c4bef5ee80 100644 (file)
@@ -246,7 +246,8 @@ ScrArea *ED_screen_temp_space_open(struct bContext *C,
                                    int sizex,
                                    int sizey,
                                    eSpace_Type space_type,
-                                   int display_type);
+                                   int display_type,
+                                   bool dialog);
 void ED_screens_header_tools_menu_create(struct bContext *C, struct uiLayout *layout, void *arg);
 void ED_screens_footer_tools_menu_create(struct bContext *C, struct uiLayout *layout, void *arg);
 void ED_screens_navigation_bar_tools_menu_create(struct bContext *C,
index 6873495e9622c1c53913e233158dd23aebdfa53b..a54701f8725506eaf84e0b882abf24ead2756317 100644 (file)
@@ -156,8 +156,8 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports)
     }
 
     /* changes context! */
-    if (WM_window_open_temp(C, IFACE_("Blender Render"), mx, my, sizex, sizey, SPACE_IMAGE) ==
-        NULL) {
+    if (WM_window_open_temp(
+            C, IFACE_("Blender Render"), mx, my, sizex, sizey, SPACE_IMAGE, false) == NULL) {
       BKE_report(reports, RPT_ERROR, "Failed to open window!");
       return NULL;
     }
index c8008fe3cc785057363dafd07ec8c9338942a905..bbdddfadc3028e91891d7ca6009776c42e5d7e38 100644 (file)
@@ -1375,13 +1375,14 @@ ScrArea *ED_screen_temp_space_open(bContext *C,
                                    int sizex,
                                    int sizey,
                                    eSpace_Type space_type,
-                                   int display_type)
+                                   int display_type,
+                                   bool dialog)
 {
   ScrArea *sa = NULL;
 
   switch (display_type) {
     case USER_TEMP_SPACE_DISPLAY_WINDOW:
-      if (WM_window_open_temp(C, title, x, y, sizex, sizey, (int)space_type)) {
+      if (WM_window_open_temp(C, title, x, y, sizex, sizey, (int)space_type, dialog)) {
         sa = CTX_wm_area(C);
       }
       break;
index 0b374617ccebec0e319de25bee14cc16dc4a13d7..866144c272d3c4f26b53b1d59c2b9994180eec07 100644 (file)
@@ -4828,9 +4828,14 @@ static int userpref_show_invoke(bContext *C, wmOperator *op, const wmEvent *even
   int sizey = 520 * UI_DPI_FAC;
 
   /* changes context! */
-  if (WM_window_open_temp(
-          C, IFACE_("Blender Preferences"), event->x, event->y, sizex, sizey, SPACE_USERPREF) !=
-      NULL) {
+  if (WM_window_open_temp(C,
+                          IFACE_("Blender Preferences"),
+                          event->x,
+                          event->y,
+                          sizex,
+                          sizey,
+                          SPACE_USERPREF,
+                          false) != NULL) {
     /* The header only contains the editor switcher and looks empty.
      * So hiding in the temp window makes sense. */
     ScrArea *area = CTX_wm_area(C);
@@ -4879,9 +4884,14 @@ static int drivers_editor_show_invoke(bContext *C, wmOperator *op, const wmEvent
   but = UI_context_active_but_prop_get(C, &ptr, &prop, &index);
 
   /* changes context! */
-  if (WM_window_open_temp(
-          C, IFACE_("Blender Drivers Editor"), event->x, event->y, sizex, sizey, SPACE_GRAPH) !=
-      NULL) {
+  if (WM_window_open_temp(C,
+                          IFACE_("Blender Drivers Editor"),
+                          event->x,
+                          event->y,
+                          sizex,
+                          sizey,
+                          SPACE_GRAPH,
+                          false) != NULL) {
     ED_drivers_editor_init(C, CTX_wm_area(C));
 
     /* activate driver F-Curve for the property under the cursor */
@@ -4939,9 +4949,14 @@ static int info_log_show_invoke(bContext *C, wmOperator *op, const wmEvent *even
   int shift_y = 480;
 
   /* changes context! */
-  if (WM_window_open_temp(
-          C, IFACE_("Blender Info Log"), event->x, event->y + shift_y, sizex, sizey, SPACE_INFO) !=
-      NULL) {
+  if (WM_window_open_temp(C,
+                          IFACE_("Blender Info Log"),
+                          event->x,
+                          event->y + shift_y,
+                          sizex,
+                          sizey,
+                          SPACE_INFO,
+                          false) != NULL) {
     return OPERATOR_FINISHED;
   }
   else {
index 2d9fa9e5ab66207d9afd8c5848eff97c7dd6a9aa..df253d619c9fe66fbde0289aaa97582d775c26a3 100644 (file)
@@ -156,8 +156,14 @@ void WM_opengl_context_activate(void *context);
 void WM_opengl_context_release(void *context);
 
 struct wmWindow *WM_window_open(struct bContext *C, const struct rcti *rect);
-struct wmWindow *WM_window_open_temp(
-    struct bContext *C, const char *title, int x, int y, int sizex, int sizey, int space_type);
+struct wmWindow *WM_window_open_temp(struct bContext *C,
+                                     const char *title,
+                                     int x,
+                                     int y,
+                                     int sizex,
+                                     int sizey,
+                                     int space_type,
+                                     bool dialog);
 void WM_window_set_dpi(wmWindow *win);
 
 bool WM_stereo3d_enabled(struct wmWindow *win, bool only_fullscreen_test);
index 6991dd2f9a6383d72968b713fef5a15a6d5e1987..1eec08c2e7adb626dfd85456cc470eae792a8046 100644 (file)
@@ -2352,7 +2352,8 @@ static int wm_handler_fileselect_do(bContext *C,
                                             U.file_space_data.temp_win_sizex * UI_DPI_FAC,
                                             U.file_space_data.temp_win_sizey * UI_DPI_FAC,
                                             SPACE_FILE,
-                                            U.filebrowser_display_type))) {
+                                            U.filebrowser_display_type,
+                                            true))) {
         ARegion *region_header = BKE_area_find_region_type(area, RGN_TYPE_HEADER);
 
         BLI_assert(area->spacetype == SPACE_FILE);
index 2c26a15dce0eb62122e67bbc8557847453d10fb5..1ae95917c99c3eabd26129dc3db7bdcb807ca479 100644 (file)
@@ -409,8 +409,10 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
 /* this is event from ghost, or exit-blender op */
 void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
 {
-  /* First check if there is another main window remaining. */
   wmWindow *win_other;
+  const bool is_dialog = GHOST_IsDialogWindow(win->ghostwin);
+
+  /* First check if there is another main window remaining. */
   for (win_other = wm->windows.first; win_other; win_other = win_other->next) {
     if (win_other != win && win_other->parent == NULL && !WM_window_is_temp_screen(win_other)) {
       break;
@@ -422,10 +424,15 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
     return;
   }
 
-  /* close child windows */
-  for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
-    if (win_child->parent == win) {
-      wm_window_close(C, wm, win_child);
+  /* Close child windows and bring windows back to front that dialogs have pushed behind the main
+   * window. */
+  for (wmWindow *iter_win = wm->windows.first; iter_win; iter_win = iter_win->next) {
+    if (iter_win->parent == win) {
+      wm_window_close(C, wm, iter_win);
+    }
+    else if (is_dialog && iter_win != win && iter_win->parent &&
+             (GHOST_GetWindowState(iter_win->ghostwin) != GHOST_kWindowStateMinimized)) {
+      wm_window_raise(iter_win);
     }
   }
 
@@ -547,7 +554,10 @@ static void wm_window_ensure_eventstate(wmWindow *win)
 }
 
 /* belongs to below */
-static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wmWindow *win)
+static void wm_window_ghostwindow_add(wmWindowManager *wm,
+                                      const char *title,
+                                      wmWindow *win,
+                                      bool is_dialog)
 {
   GHOST_WindowHandle ghostwin;
   GHOST_GLSettings glSettings = {0};
@@ -569,15 +579,29 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
   wmWindow *prev_windrawable = wm->windrawable;
   wm_window_clear_drawable(wm);
 
-  ghostwin = GHOST_CreateWindow(g_system,
-                                title,
-                                win->posx,
-                                posy,
-                                win->sizex,
-                                win->sizey,
-                                (GHOST_TWindowState)win->windowstate,
-                                GHOST_kDrawingContextTypeOpenGL,
-                                glSettings);
+  if (is_dialog && win->parent) {
+    ghostwin = GHOST_CreateDialogWindow(g_system,
+                                        win->parent->ghostwin,
+                                        title,
+                                        win->posx,
+                                        posy,
+                                        win->sizex,
+                                        win->sizey,
+                                        (GHOST_TWindowState)win->windowstate,
+                                        GHOST_kDrawingContextTypeOpenGL,
+                                        glSettings);
+  }
+  else {
+    ghostwin = GHOST_CreateWindow(g_system,
+                                  title,
+                                  win->posx,
+                                  posy,
+                                  win->sizex,
+                                  win->sizey,
+                                  (GHOST_TWindowState)win->windowstate,
+                                  GHOST_kDrawingContextTypeOpenGL,
+                                  glSettings);
+  }
 
   if (ghostwin) {
     GHOST_RectangleHandle bounds;
@@ -635,6 +659,68 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
   }
 }
 
+static void wm_window_ghostwindow_ensure(wmWindowManager *wm, wmWindow *win, bool is_dialog)
+{
+  wmKeyMap *keymap;
+
+  if (win->ghostwin == NULL) {
+    if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
+      win->posx = wm_init_state.start_x;
+      win->posy = wm_init_state.start_y;
+      win->sizex = wm_init_state.size_x;
+      win->sizey = wm_init_state.size_y;
+
+      if (wm_init_state.override_flag & WIN_OVERRIDE_GEOM) {
+        win->windowstate = GHOST_kWindowStateNormal;
+        wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
+      }
+      else {
+        win->windowstate = GHOST_WINDOW_STATE_DEFAULT;
+      }
+    }
+
+    if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
+      win->windowstate = wm_init_state.windowstate;
+      wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
+    }
+
+    /* without this, cursor restore may fail, T45456 */
+    if (win->cursor == 0) {
+      win->cursor = WM_CURSOR_DEFAULT;
+    }
+
+    wm_window_ghostwindow_add(wm, "Blender", win, is_dialog);
+  }
+
+  if (win->ghostwin != NULL) {
+    /* If we have no ghostwin this is a buggy window that should be removed.
+     * However we still need to initialize it correctly so the screen doesn't hang. */
+
+    /* happens after fileread */
+    wm_window_ensure_eventstate(win);
+  }
+
+  /* add keymap handlers (1 handler for all keys in map!) */
+  keymap = WM_keymap_ensure(wm->defaultconf, "Window", 0, 0);
+  WM_event_add_keymap_handler(&win->handlers, keymap);
+
+  keymap = WM_keymap_ensure(wm->defaultconf, "Screen", 0, 0);
+  WM_event_add_keymap_handler(&win->handlers, keymap);
+
+  keymap = WM_keymap_ensure(wm->defaultconf, "Screen Editing", 0, 0);
+  WM_event_add_keymap_handler(&win->modalhandlers, keymap);
+
+  /* add drop boxes */
+  {
+    ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
+    WM_event_add_dropbox_handler(&win->handlers, lb);
+  }
+  wm_window_title(wm, win);
+
+  /* add topbar */
+  ED_screen_global_areas_refresh(win);
+}
+
 /**
  * Initialize #wmWindow without ghostwin, open these and clear.
  *
@@ -650,9 +736,6 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
  */
 void wm_window_ghostwindows_ensure(wmWindowManager *wm)
 {
-  wmKeyMap *keymap;
-  wmWindow *win;
-
   BLI_assert(G.background == false);
 
   /* No command-line prefsize? then we set this.
@@ -682,63 +765,8 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm)
 #endif
   }
 
-  for (win = wm->windows.first; win; win = win->next) {
-    if (win->ghostwin == NULL) {
-      if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
-        win->posx = wm_init_state.start_x;
-        win->posy = wm_init_state.start_y;
-        win->sizex = wm_init_state.size_x;
-        win->sizey = wm_init_state.size_y;
-
-        if (wm_init_state.override_flag & WIN_OVERRIDE_GEOM) {
-          win->windowstate = GHOST_kWindowStateNormal;
-          wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
-        }
-        else {
-          win->windowstate = GHOST_WINDOW_STATE_DEFAULT;
-        }
-      }
-
-      if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
-        win->windowstate = wm_init_state.windowstate;
-        wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
-      }
-
-      /* without this, cursor restore may fail, T45456 */
-      if (win->cursor == 0) {
-        win->cursor = WM_CURSOR_DEFAULT;
-      }
-
-      wm_window_ghostwindow_add(wm, "Blender", win);
-    }
-
-    if (win->ghostwin != NULL) {
-      /* If we have no ghostwin this is a buggy window that should be removed.
-       * However we still need to initialize it correctly so the screen doesn't hang. */
-
-      /* happens after fileread */
-      wm_window_ensure_eventstate(win);
-    }
-
-    /* add keymap handlers (1 handler for all keys in map!) */
-    keymap = WM_keymap_ensure(wm->defaultconf, "Window", 0, 0);
-    WM_event_add_keymap_handler(&win->handlers, keymap);
-
-    keymap = WM_keymap_ensure(wm->defaultconf, "Screen", 0, 0);
-    WM_event_add_keymap_handler(&win->handlers, keymap);
-
-    keymap = WM_keymap_ensure(wm->defaultconf, "Screen Editing", 0, 0);
-    WM_event_add_keymap_handler(&win->modalhandlers, keymap);
-
-    /* add drop boxes */
-    {
-      ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
-      WM_event_add_dropbox_handler(&win->handlers, lb);
-    }
-    wm_window_title(wm, win);
-
-    /* add topbar */
-    ED_screen_global_areas_refresh(win);
+  for (wmWindow *win = wm->windows.first; win; win = win->next) {
+    wm_window_ghostwindow_ensure(wm, win, false);
   }
 }
 
@@ -795,10 +823,17 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect)
  * \param space_type: SPACE_VIEW3D, SPACE_INFO, ... (eSpace_Type)
  * \return the window or NULL in case of failure.
  */
-wmWindow *WM_window_open_temp(
-    bContext *C, const char *title, int x, int y, int sizex, int sizey, int space_type)
+wmWindow *WM_window_open_temp(bContext *C,
+                              const char *title,
+                              int x,
+                              int y,
+                              int sizex,
+                              int sizey,
+                              int space_type,
+                              bool dialog)
 {
   Main *bmain = CTX_data_main(C);
+  wmWindowManager *wm = CTX_wm_manager(C);
   wmWindow *win_prev = CTX_wm_window(C);
   wmWindow *win;
   bScreen *screen;
@@ -823,9 +858,10 @@ wmWindow *WM_window_open_temp(
   /* changes rect to fit within desktop */
   wm_window_check_position(&rect);
 
-  /* test if we have a temp screen already */
-  for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) {
-    if (WM_window_is_temp_screen(win)) {
+  /* Reuse temporary or dialog window if one is open (but don't use a dialog for a regular
+   * temporary window, or vice versa). */
+  for (win = wm->windows.first; win; win = win->next) {
+    if (WM_window_is_temp_screen(win) && (dialog == GHOST_IsDialogWindow(win->ghostwin))) {
       break;
     }
   }
@@ -843,11 +879,6 @@ wmWindow *WM_window_open_temp(
   win->sizex = BLI_rcti_size_x(&rect);
   win->sizey = BLI_rcti_size_y(&rect);
 
-  if (win->ghostwin) {
-    wm_window_set_size(win, win->sizex, win->sizey);
-    wm_window_raise(win);
-  }
-
   if (WM_window_get_active_workspace(win) == NULL) {
     WorkSpace *workspace = WM_window_get_active_workspace(win_prev);
     BKE_workspace_active_set(win->workspace_hook, workspace);
@@ -872,6 +903,9 @@ wmWindow *WM_window_open_temp(
 
   /* make window active, and validate/resize */
   CTX_wm_window_set(C, win);
+  if (!win->ghostwin) {
+    wm_window_ghostwindow_ensure(wm, win, dialog);
+  }
   WM_check(C);
 
   /* It's possible `win->ghostwin == NULL`.
@@ -887,15 +921,18 @@ wmWindow *WM_window_open_temp(
   ED_area_newspace(C, sa, space_type, false);
 
   ED_screen_change(C, screen);
-  ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */
+  ED_screen_refresh(wm, win); /* test scale */
 
   if (win->ghostwin) {
+    wm_window_set_size(win, win->sizex, win->sizey);
+    wm_window_raise(win);
+
     GHOST_SetTitle(win->ghostwin, title);
     return win;
   }
   else {
     /* very unlikely! but opening a new window can fail */
-    wm_window_close(C, CTX_wm_manager(C), win);
+    wm_window_close(C, wm, win);
     CTX_wm_window_set(C, win_prev);
 
     return NULL;