UI: Register File Browser as Child/Dialog-Window for the OS

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.
This commit is contained in:
Julian Eisel 2019-10-03 16:59:49 +02:00
parent 35ae1da16c
commit edffb0e9b1
27 changed files with 464 additions and 170 deletions

@ -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

@ -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.

@ -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).

@ -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)
{

@ -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.

@ -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.
*/

@ -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;

@ -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;

@ -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 */

@ -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

@ -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.

@ -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),

@ -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.

@ -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.

@ -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__

@ -166,7 +166,8 @@
- (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];

@ -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)
{

@ -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 */

@ -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

@ -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:

@ -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,

@ -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;
}

@ -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;

@ -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 {

@ -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);

@ -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);

@ -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;