Add support for non-constrained and non-grabbing popups
By default, popups are automatically constrained to be completely within display bounds, so as not to cut off information and result in an unusable menu, or unreadable tooltip. In some cases, however, this is not wanted, so a property to toggle this behavior is added. There are also cases where the client may not want a popup menu to implicitly grab the keyboard focus, as is the default behavior, so popup menus now respect the focusable flag/property, as well as being able to toggle focus grabbing via SDL_SetWindowFocusable().
This commit is contained in:
parent
8abcc27535
commit
b871ac0d97
15 changed files with 281 additions and 156 deletions
|
@ -1188,6 +1188,15 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title, int
|
||||||
* Popup windows implicitly do not have a border/decorations and do not appear
|
* Popup windows implicitly do not have a border/decorations and do not appear
|
||||||
* on the taskbar/dock or in lists of windows such as alt-tab menus.
|
* on the taskbar/dock or in lists of windows such as alt-tab menus.
|
||||||
*
|
*
|
||||||
|
* By default, popup window positions will automatically be constrained to keep
|
||||||
|
* the entire window within display bounds. This can be overridden with the
|
||||||
|
* `SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN` property.
|
||||||
|
*
|
||||||
|
* By default, popup menus will automatically grab keyboard focus from the parent
|
||||||
|
* when shown. This behavior can be overridden by setting the `SDL_WINDOW_NOT_FOCUSABLE`
|
||||||
|
* flag, setting the `SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN` property to false, or
|
||||||
|
* toggling it after creation via the `SDL_SetWindowFocusable()` function.
|
||||||
|
*
|
||||||
* If a parent window is hidden or destroyed, any child popup windows will be
|
* If a parent window is hidden or destroyed, any child popup windows will be
|
||||||
* recursively hidden or destroyed as well. Child popup windows not explicitly
|
* recursively hidden or destroyed as well. Child popup windows not explicitly
|
||||||
* hidden will be restored when the parent is shown.
|
* hidden will be restored when the parent is shown.
|
||||||
|
@ -1228,6 +1237,9 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreatePopupWindow(SDL_Window *paren
|
||||||
* be always on top
|
* be always on top
|
||||||
* - `SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN`: true if the window has no
|
* - `SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN`: true if the window has no
|
||||||
* window decoration
|
* window decoration
|
||||||
|
* - `SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN`: true if the "tooltip" and
|
||||||
|
* "menu" window types should be automatically constrained to be entirely within
|
||||||
|
* display bounds (default), false if no constraints on the position are desired.
|
||||||
* - `SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN`: true if the
|
* - `SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN`: true if the
|
||||||
* window will be used with an externally managed graphics context.
|
* window will be used with an externally managed graphics context.
|
||||||
* - `SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN`: true if the window should
|
* - `SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN`: true if the window should
|
||||||
|
@ -1356,6 +1368,7 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindowWithProperties(SDL_Prop
|
||||||
|
|
||||||
#define SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN "SDL.window.create.always_on_top"
|
#define SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN "SDL.window.create.always_on_top"
|
||||||
#define SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN "SDL.window.create.borderless"
|
#define SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN "SDL.window.create.borderless"
|
||||||
|
#define SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN "SDL.window.create.constrain_popup"
|
||||||
#define SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN "SDL.window.create.focusable"
|
#define SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN "SDL.window.create.focusable"
|
||||||
#define SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN "SDL.window.create.external_graphics_context"
|
#define SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN "SDL.window.create.external_graphics_context"
|
||||||
#define SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER "SDL.window.create.flags"
|
#define SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER "SDL.window.create.flags"
|
||||||
|
|
|
@ -104,6 +104,7 @@ struct SDL_Window
|
||||||
bool last_position_pending; // This should NOT be cleared by the backend, as it is used for fullscreen positioning.
|
bool last_position_pending; // This should NOT be cleared by the backend, as it is used for fullscreen positioning.
|
||||||
bool last_size_pending; // This should be cleared by the backend if the new size cannot be applied.
|
bool last_size_pending; // This should be cleared by the backend if the new size cannot be applied.
|
||||||
bool update_fullscreen_on_display_changed;
|
bool update_fullscreen_on_display_changed;
|
||||||
|
bool constrain_popup;
|
||||||
bool is_destroying;
|
bool is_destroying;
|
||||||
bool is_dropping; // drag/drop in progress, expecting SDL_SendDropComplete().
|
bool is_dropping; // drag/drop in progress, expecting SDL_SendDropComplete().
|
||||||
|
|
||||||
|
@ -133,6 +134,9 @@ struct SDL_Window
|
||||||
|
|
||||||
SDL_WindowData *internal;
|
SDL_WindowData *internal;
|
||||||
|
|
||||||
|
// If a toplevel window, holds the current keyboard focus for grabbing popups.
|
||||||
|
SDL_Window *keyboard_focus;
|
||||||
|
|
||||||
SDL_Window *prev;
|
SDL_Window *prev;
|
||||||
SDL_Window *next;
|
SDL_Window *next;
|
||||||
|
|
||||||
|
@ -571,6 +575,8 @@ extern bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags);
|
||||||
extern bool SDL_HasWindows(void);
|
extern bool SDL_HasWindows(void);
|
||||||
extern void SDL_RelativeToGlobalForWindow(SDL_Window *window, int rel_x, int rel_y, int *abs_x, int *abs_y);
|
extern void SDL_RelativeToGlobalForWindow(SDL_Window *window, int rel_x, int rel_y, int *abs_x, int *abs_y);
|
||||||
extern void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs_y, int *rel_x, int *rel_y);
|
extern void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs_y, int *rel_x, int *rel_y);
|
||||||
|
extern bool SDL_ShouldFocusPopup(SDL_Window *window);
|
||||||
|
extern bool SDL_ShouldRelinquishPopupFocus(SDL_Window *window, SDL_Window **new_focus);
|
||||||
|
|
||||||
extern void SDL_OnDisplayAdded(SDL_VideoDisplay *display);
|
extern void SDL_OnDisplayAdded(SDL_VideoDisplay *display);
|
||||||
extern void SDL_OnDisplayMoved(SDL_VideoDisplay *display);
|
extern void SDL_OnDisplayMoved(SDL_VideoDisplay *display);
|
||||||
|
|
|
@ -2491,6 +2491,7 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
|
||||||
window->is_destroying = false;
|
window->is_destroying = false;
|
||||||
window->last_displayID = SDL_GetDisplayForWindow(window);
|
window->last_displayID = SDL_GetDisplayForWindow(window);
|
||||||
window->external_graphics_context = external_graphics_context;
|
window->external_graphics_context = external_graphics_context;
|
||||||
|
window->constrain_popup = SDL_GetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN, true);
|
||||||
|
|
||||||
if (_this->windows) {
|
if (_this->windows) {
|
||||||
_this->windows->prev = window;
|
_this->windows->prev = window;
|
||||||
|
@ -3692,6 +3693,48 @@ bool SDL_SetWindowModal(SDL_Window *window, bool modal)
|
||||||
return _this->SetWindowModal(_this, window, modal);
|
return _this->SetWindowModal(_this, window, modal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SDL_ShouldRelinquishPopupFocus(SDL_Window *window, SDL_Window **new_focus)
|
||||||
|
{
|
||||||
|
SDL_Window *focus = window->parent;
|
||||||
|
bool set_focus = !!(window->flags & SDL_WINDOW_INPUT_FOCUS);
|
||||||
|
|
||||||
|
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed, and can grab the keyboard focus.
|
||||||
|
while (SDL_WINDOW_IS_POPUP(focus) && ((focus->flags & SDL_WINDOW_NOT_FOCUSABLE) || focus->is_hiding || focus->is_destroying)) {
|
||||||
|
focus = focus->parent;
|
||||||
|
|
||||||
|
// If some window in the chain currently had focus, set it to the new lowest-level window.
|
||||||
|
if (!set_focus) {
|
||||||
|
set_focus = !!(focus->flags & SDL_WINDOW_INPUT_FOCUS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*new_focus = focus;
|
||||||
|
return set_focus;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDL_ShouldFocusPopup(SDL_Window *window)
|
||||||
|
{
|
||||||
|
SDL_Window *toplevel_parent;
|
||||||
|
for (toplevel_parent = window->parent; SDL_WINDOW_IS_POPUP(toplevel_parent); toplevel_parent = toplevel_parent->parent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Window *current_focus = toplevel_parent->keyboard_focus;
|
||||||
|
bool found_higher_focus = false;
|
||||||
|
|
||||||
|
/* Traverse the window tree from the currently focused window to the toplevel parent and see if we encounter
|
||||||
|
* the new focus request. If the new window is found, a higher-level window already has focus.
|
||||||
|
*/
|
||||||
|
SDL_Window *w;
|
||||||
|
for (w = current_focus; w != toplevel_parent; w = w->parent) {
|
||||||
|
if (w == window) {
|
||||||
|
found_higher_focus = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !found_higher_focus || w == toplevel_parent;
|
||||||
|
}
|
||||||
|
|
||||||
bool SDL_SetWindowFocusable(SDL_Window *window, bool focusable)
|
bool SDL_SetWindowFocusable(SDL_Window *window, bool focusable)
|
||||||
{
|
{
|
||||||
CHECK_WINDOW_MAGIC(window, false);
|
CHECK_WINDOW_MAGIC(window, false);
|
||||||
|
|
|
@ -146,7 +146,6 @@ typedef enum
|
||||||
@property(nonatomic) BOOL was_zoomed;
|
@property(nonatomic) BOOL was_zoomed;
|
||||||
@property(nonatomic) NSInteger window_number;
|
@property(nonatomic) NSInteger window_number;
|
||||||
@property(nonatomic) NSInteger flash_request;
|
@property(nonatomic) NSInteger flash_request;
|
||||||
@property(nonatomic) SDL_Window *keyboard_focus;
|
|
||||||
@property(nonatomic) SDL3Cocoa_WindowListener *listener;
|
@property(nonatomic) SDL3Cocoa_WindowListener *listener;
|
||||||
@property(nonatomic) NSModalSession modal_session;
|
@property(nonatomic) NSModalSession modal_session;
|
||||||
@property(nonatomic) SDL_CocoaVideoData *videodata;
|
@property(nonatomic) SDL_CocoaVideoData *videodata;
|
||||||
|
|
|
@ -707,10 +707,7 @@ static SDL_Window *GetParentToplevelWindow(SDL_Window *window)
|
||||||
static void Cocoa_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
|
static void Cocoa_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
|
||||||
{
|
{
|
||||||
SDL_Window *toplevel = GetParentToplevelWindow(window);
|
SDL_Window *toplevel = GetParentToplevelWindow(window);
|
||||||
SDL_CocoaWindowData *toplevel_data;
|
toplevel->keyboard_focus = window;
|
||||||
|
|
||||||
toplevel_data = (__bridge SDL_CocoaWindowData *)toplevel->internal;
|
|
||||||
toplevel_data.keyboard_focus = window;
|
|
||||||
|
|
||||||
if (set_active_focus && !window->is_hiding && !window->is_destroying) {
|
if (set_active_focus && !window->is_hiding && !window->is_destroying) {
|
||||||
SDL_SetKeyboardFocus(window);
|
SDL_SetKeyboardFocus(window);
|
||||||
|
@ -1252,7 +1249,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
|
||||||
|
|
||||||
// We're going to get keyboard events, since we're key.
|
// We're going to get keyboard events, since we're key.
|
||||||
// This needs to be done before restoring the relative mouse mode.
|
// This needs to be done before restoring the relative mouse mode.
|
||||||
Cocoa_SetKeyboardFocus(_data.keyboard_focus ? _data.keyboard_focus : window, true);
|
Cocoa_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window, true);
|
||||||
|
|
||||||
// If we just gained focus we need the updated mouse position
|
// If we just gained focus we need the updated mouse position
|
||||||
if (!(window->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE)) {
|
if (!(window->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE)) {
|
||||||
|
@ -2244,7 +2241,9 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow
|
||||||
[nswindow setIgnoresMouseEvents:YES];
|
[nswindow setIgnoresMouseEvents:YES];
|
||||||
[nswindow setAcceptsMouseMovedEvents:NO];
|
[nswindow setAcceptsMouseMovedEvents:NO];
|
||||||
} else if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_HIDDEN)) {
|
} else if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_HIDDEN)) {
|
||||||
Cocoa_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
|
if (!(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||||
|
Cocoa_SetKeyboardFocus(window, true);
|
||||||
|
}
|
||||||
Cocoa_UpdateMouseFocus();
|
Cocoa_UpdateMouseFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2334,7 +2333,7 @@ bool Cocoa_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properti
|
||||||
rect.origin.y -= screenRect.origin.y;
|
rect.origin.y -= screenRect.origin.y;
|
||||||
|
|
||||||
// Constrain the popup
|
// Constrain the popup
|
||||||
if (SDL_WINDOW_IS_POPUP(window)) {
|
if (SDL_WINDOW_IS_POPUP(window) && window->constrain_popup) {
|
||||||
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
|
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
|
||||||
rect.origin.x -= (rect.origin.x + rect.size.width) - (screenRect.origin.x + screenRect.size.width);
|
rect.origin.x -= (rect.origin.x + rect.size.width) - (screenRect.origin.x + screenRect.size.width);
|
||||||
}
|
}
|
||||||
|
@ -2490,7 +2489,7 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
ConvertNSRect(&rect);
|
ConvertNSRect(&rect);
|
||||||
|
|
||||||
// Position and constrain the popup
|
// Position and constrain the popup
|
||||||
if (SDL_WINDOW_IS_POPUP(window)) {
|
if (SDL_WINDOW_IS_POPUP(window) && window->constrain_popup) {
|
||||||
NSRect screenRect = [ScreenForRect(&rect) frame];
|
NSRect screenRect = [ScreenForRect(&rect) frame];
|
||||||
|
|
||||||
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
|
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
|
||||||
|
@ -2631,7 +2630,9 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
||||||
Cocoa_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
|
if (!(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||||
|
Cocoa_SetKeyboardFocus(window, true);
|
||||||
|
}
|
||||||
Cocoa_UpdateMouseFocus();
|
Cocoa_UpdateMouseFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2665,20 +2666,9 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
Cocoa_SetWindowModal(_this, window, false);
|
Cocoa_SetWindowModal(_this, window, false);
|
||||||
|
|
||||||
// Transfer keyboard focus back to the parent when closing a popup menu
|
// Transfer keyboard focus back to the parent when closing a popup menu
|
||||||
if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||||
SDL_Window *new_focus = window->parent;
|
SDL_Window *new_focus;
|
||||||
bool set_focus = window == SDL_GetKeyboardFocus();
|
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
|
||||||
|
|
||||||
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed.
|
|
||||||
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
|
|
||||||
new_focus = new_focus->parent;
|
|
||||||
|
|
||||||
// If some window in the chain currently had focus, set it to the new lowest-level window.
|
|
||||||
if (!set_focus) {
|
|
||||||
set_focus = new_focus == SDL_GetKeyboardFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Cocoa_SetKeyboardFocus(new_focus, set_focus);
|
Cocoa_SetKeyboardFocus(new_focus, set_focus);
|
||||||
Cocoa_UpdateMouseFocus();
|
Cocoa_UpdateMouseFocus();
|
||||||
} else if (window->parent && waskey) {
|
} else if (window->parent && waskey) {
|
||||||
|
@ -3105,20 +3095,19 @@ void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
|
|
||||||
#endif // SDL_VIDEO_OPENGL
|
#endif // SDL_VIDEO_OPENGL
|
||||||
SDL_Window *topmost = GetParentToplevelWindow(window);
|
SDL_Window *topmost = GetParentToplevelWindow(window);
|
||||||
SDL_CocoaWindowData *topmost_data = (__bridge SDL_CocoaWindowData *)topmost->internal;
|
|
||||||
|
|
||||||
/* Reset the input focus of the root window if this window is still set as keyboard focus.
|
/* Reset the input focus of the root window if this window is still set as keyboard focus.
|
||||||
* SDL_DestroyWindow will have already taken care of reassigning focus if this is the SDL
|
* SDL_DestroyWindow will have already taken care of reassigning focus if this is the SDL
|
||||||
* keyboard focus, this ensures that an inactive window with this window set as input focus
|
* keyboard focus, this ensures that an inactive window with this window set as input focus
|
||||||
* does not try to reference it the next time it gains focus.
|
* does not try to reference it the next time it gains focus.
|
||||||
*/
|
*/
|
||||||
if (topmost_data.keyboard_focus == window) {
|
if (topmost->keyboard_focus == window) {
|
||||||
SDL_Window *new_focus = window;
|
SDL_Window *new_focus = window;
|
||||||
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
|
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
|
||||||
new_focus = new_focus->parent;
|
new_focus = new_focus->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
topmost_data.keyboard_focus = new_focus;
|
topmost->keyboard_focus = new_focus;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([data.listener isInFullscreenSpace]) {
|
if ([data.listener isInFullscreenSpace]) {
|
||||||
|
@ -3283,6 +3272,20 @@ bool Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOper
|
||||||
|
|
||||||
bool Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
|
bool Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
|
||||||
{
|
{
|
||||||
|
if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
||||||
|
if (!(window->flags & SDL_WINDOW_HIDDEN)) {
|
||||||
|
if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
|
||||||
|
SDL_Window *new_focus;
|
||||||
|
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
|
||||||
|
Cocoa_SetKeyboardFocus(new_focus, set_focus);
|
||||||
|
} else if (focusable) {
|
||||||
|
if (SDL_ShouldFocusPopup(window)) {
|
||||||
|
Cocoa_SetKeyboardFocus(window, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true; // just succeed, the real work is done elsewhere.
|
return true; // just succeed, the real work is done elsewhere.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1824,7 +1824,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
|
||||||
seat->keyboard.focus = window;
|
seat->keyboard.focus = window;
|
||||||
|
|
||||||
// Restore the keyboard focus to the child popup that was holding it
|
// Restore the keyboard focus to the child popup that was holding it
|
||||||
SDL_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window->sdlwindow);
|
SDL_SetKeyboardFocus(window->sdlwindow->keyboard_focus ? window->sdlwindow->keyboard_focus : window->sdlwindow);
|
||||||
|
|
||||||
// Update the keyboard grab and any relative pointer grabs related to this keyboard focus.
|
// Update the keyboard grab and any relative pointer grabs related to this keyboard focus.
|
||||||
Wayland_SeatUpdateKeyboardGrab(seat);
|
Wayland_SeatUpdateKeyboardGrab(seat);
|
||||||
|
|
|
@ -632,6 +632,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
|
||||||
device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport;
|
device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport;
|
||||||
device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu;
|
device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu;
|
||||||
device->SyncWindow = Wayland_SyncWindow;
|
device->SyncWindow = Wayland_SyncWindow;
|
||||||
|
device->SetWindowFocusable = Wayland_SetWindowFocusable;
|
||||||
|
|
||||||
#ifdef SDL_USE_LIBDBUS
|
#ifdef SDL_USE_LIBDBUS
|
||||||
if (SDL_SystemTheme_Init())
|
if (SDL_SystemTheme_Init())
|
||||||
|
|
|
@ -1677,7 +1677,7 @@ static const struct wp_color_management_surface_feedback_v1_listener color_manag
|
||||||
feedback_surface_preferred_changed
|
feedback_surface_preferred_changed
|
||||||
};
|
};
|
||||||
|
|
||||||
static void SetKeyboardFocus(SDL_Window *window, bool set_focus)
|
static void Wayland_SetKeyboardFocus(SDL_Window *window, bool set_focus)
|
||||||
{
|
{
|
||||||
SDL_Window *toplevel = window;
|
SDL_Window *toplevel = window;
|
||||||
|
|
||||||
|
@ -1686,7 +1686,7 @@ static void SetKeyboardFocus(SDL_Window *window, bool set_focus)
|
||||||
toplevel = toplevel->parent;
|
toplevel = toplevel->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
toplevel->internal->keyboard_focus = window;
|
toplevel->keyboard_focus = window;
|
||||||
|
|
||||||
if (set_focus && !window->is_hiding && !window->is_destroying) {
|
if (set_focus && !window->is_hiding && !window->is_destroying) {
|
||||||
SDL_SetKeyboardFocus(window);
|
SDL_SetKeyboardFocus(window);
|
||||||
|
@ -1916,8 +1916,9 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
data->shell_surface.xdg.popup.xdg_positioner = xdg_wm_base_create_positioner(c->shell.xdg);
|
data->shell_surface.xdg.popup.xdg_positioner = xdg_wm_base_create_positioner(c->shell.xdg);
|
||||||
xdg_positioner_set_anchor(data->shell_surface.xdg.popup.xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
|
xdg_positioner_set_anchor(data->shell_surface.xdg.popup.xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
|
||||||
xdg_positioner_set_anchor_rect(data->shell_surface.xdg.popup.xdg_positioner, 0, 0, parent->internal->current.logical_width, parent->internal->current.logical_width);
|
xdg_positioner_set_anchor_rect(data->shell_surface.xdg.popup.xdg_positioner, 0, 0, parent->internal->current.logical_width, parent->internal->current.logical_width);
|
||||||
xdg_positioner_set_constraint_adjustment(data->shell_surface.xdg.popup.xdg_positioner,
|
|
||||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y);
|
const Uint32 constraint = window->constrain_popup ? (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) : XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE;
|
||||||
|
xdg_positioner_set_constraint_adjustment(data->shell_surface.xdg.popup.xdg_positioner, constraint);
|
||||||
xdg_positioner_set_gravity(data->shell_surface.xdg.popup.xdg_positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
|
xdg_positioner_set_gravity(data->shell_surface.xdg.popup.xdg_positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
|
||||||
xdg_positioner_set_size(data->shell_surface.xdg.popup.xdg_positioner, data->current.logical_width, data->current.logical_height);
|
xdg_positioner_set_size(data->shell_surface.xdg.popup.xdg_positioner, data->current.logical_width, data->current.logical_height);
|
||||||
|
|
||||||
|
@ -1946,8 +1947,8 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
wl_region_add(region, 0, 0, 0, 0);
|
wl_region_add(region, 0, 0, 0, 0);
|
||||||
wl_surface_set_input_region(data->surface, region);
|
wl_surface_set_input_region(data->surface, region);
|
||||||
wl_region_destroy(region);
|
wl_region_destroy(region);
|
||||||
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
} else if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||||
SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
|
Wayland_SetKeyboardFocus(window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER, data->shell_surface.xdg.popup.xdg_popup);
|
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER, data->shell_surface.xdg.popup.xdg_popup);
|
||||||
|
@ -2094,21 +2095,10 @@ static void Wayland_ReleasePopup(SDL_VideoDevice *_this, SDL_Window *popup)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (popup->flags & SDL_WINDOW_POPUP_MENU) {
|
if ((popup->flags & SDL_WINDOW_POPUP_MENU) && !(popup->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||||
SDL_Window *new_focus = popup->parent;
|
SDL_Window *new_focus;
|
||||||
bool set_focus = popup == SDL_GetKeyboardFocus();
|
const bool set_focus = SDL_ShouldRelinquishPopupFocus(popup, &new_focus);
|
||||||
|
Wayland_SetKeyboardFocus(new_focus, set_focus);
|
||||||
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed.
|
|
||||||
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
|
|
||||||
new_focus = new_focus->parent;
|
|
||||||
|
|
||||||
// If some window in the chain currently had focus, set it to the new lowest-level window.
|
|
||||||
if (!set_focus) {
|
|
||||||
set_focus = new_focus == SDL_GetKeyboardFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SetKeyboardFocus(new_focus, set_focus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xdg_popup_destroy(popupdata->shell_surface.xdg.popup.xdg_popup);
|
xdg_popup_destroy(popupdata->shell_surface.xdg.popup.xdg_popup);
|
||||||
|
@ -3002,6 +2992,27 @@ bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
|
||||||
|
{
|
||||||
|
if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
||||||
|
if (!(window->flags & SDL_WINDOW_HIDDEN)) {
|
||||||
|
if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
|
||||||
|
SDL_Window *new_focus;
|
||||||
|
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
|
||||||
|
Wayland_SetKeyboardFocus(new_focus, set_focus);
|
||||||
|
} else if (focusable) {
|
||||||
|
if (SDL_ShouldFocusPopup(window)) {
|
||||||
|
Wayland_SetKeyboardFocus(window, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_SetError("wayland: focus can only be toggled on popup menu windows");
|
||||||
|
}
|
||||||
|
|
||||||
void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
|
void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
|
||||||
{
|
{
|
||||||
SDL_WindowData *wind = window->internal;
|
SDL_WindowData *wind = window->internal;
|
||||||
|
|
|
@ -126,8 +126,6 @@ struct SDL_WindowData
|
||||||
SDL_DisplayData **outputs;
|
SDL_DisplayData **outputs;
|
||||||
int num_outputs;
|
int num_outputs;
|
||||||
|
|
||||||
SDL_Window *keyboard_focus;
|
|
||||||
|
|
||||||
char *app_id;
|
char *app_id;
|
||||||
double scale_factor;
|
double scale_factor;
|
||||||
|
|
||||||
|
@ -249,6 +247,7 @@ extern void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
|
||||||
extern void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
extern void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||||
extern bool Wayland_SuspendScreenSaver(SDL_VideoDevice *_this);
|
extern bool Wayland_SuspendScreenSaver(SDL_VideoDevice *_this);
|
||||||
extern bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
|
extern bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
|
||||||
|
extern bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
|
||||||
extern float Wayland_GetWindowContentScale(SDL_VideoDevice *_this, SDL_Window *window);
|
extern float Wayland_GetWindowContentScale(SDL_VideoDevice *_this, SDL_Window *window);
|
||||||
extern void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
|
extern void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
|
||||||
|
|
||||||
|
|
|
@ -354,7 +354,7 @@ static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus, DWORD pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetKeyboardFocus(data->keyboard_focus ? data->keyboard_focus : window);
|
SDL_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window);
|
||||||
|
|
||||||
// In relative mode we are guaranteed to have mouse focus if we have keyboard focus
|
// In relative mode we are guaranteed to have mouse focus if we have keyboard focus
|
||||||
if (!SDL_GetMouse()->relative_mode) {
|
if (!SDL_GetMouse()->relative_mode) {
|
||||||
|
|
|
@ -666,7 +666,7 @@ static void CleanupWindowData(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
|
|
||||||
static void WIN_ConstrainPopup(SDL_Window *window, bool output_to_pending)
|
static void WIN_ConstrainPopup(SDL_Window *window, bool output_to_pending)
|
||||||
{
|
{
|
||||||
// Clamp popup windows to the output borders
|
// Possibly clamp popup windows to the output borders
|
||||||
if (SDL_WINDOW_IS_POPUP(window)) {
|
if (SDL_WINDOW_IS_POPUP(window)) {
|
||||||
SDL_Window *w;
|
SDL_Window *w;
|
||||||
SDL_DisplayID displayID;
|
SDL_DisplayID displayID;
|
||||||
|
@ -677,6 +677,7 @@ static void WIN_ConstrainPopup(SDL_Window *window, bool output_to_pending)
|
||||||
const int height = window->last_size_pending ? window->pending.h : window->floating.h;
|
const int height = window->last_size_pending ? window->pending.h : window->floating.h;
|
||||||
int offset_x = 0, offset_y = 0;
|
int offset_x = 0, offset_y = 0;
|
||||||
|
|
||||||
|
if (window->constrain_popup) {
|
||||||
// Calculate the total offset from the parents
|
// Calculate the total offset from the parents
|
||||||
for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
|
for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
|
||||||
offset_x += w->x;
|
offset_x += w->x;
|
||||||
|
@ -699,6 +700,7 @@ static void WIN_ConstrainPopup(SDL_Window *window, bool output_to_pending)
|
||||||
}
|
}
|
||||||
abs_x = SDL_max(abs_x, rect.x);
|
abs_x = SDL_max(abs_x, rect.x);
|
||||||
abs_y = SDL_max(abs_y, rect.y);
|
abs_y = SDL_max(abs_y, rect.y);
|
||||||
|
}
|
||||||
|
|
||||||
if (output_to_pending) {
|
if (output_to_pending) {
|
||||||
window->pending.x = abs_x - offset_x;
|
window->pending.x = abs_x - offset_x;
|
||||||
|
@ -723,7 +725,7 @@ static void WIN_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
|
||||||
toplevel = toplevel->parent;
|
toplevel = toplevel->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
toplevel->internal->keyboard_focus = window;
|
toplevel->keyboard_focus = window;
|
||||||
|
|
||||||
if (set_active_focus && !window->is_hiding && !window->is_destroying) {
|
if (set_active_focus && !window->is_hiding && !window->is_destroying) {
|
||||||
SDL_SetKeyboardFocus(window);
|
SDL_SetKeyboardFocus(window);
|
||||||
|
@ -1082,8 +1084,8 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, window->internal->copybits_flag | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, window->internal->copybits_flag | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window->flags & SDL_WINDOW_POPUP_MENU && bActivate) {
|
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE) && bActivate) {
|
||||||
WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
|
WIN_SetKeyboardFocus(window, true);
|
||||||
}
|
}
|
||||||
if (window->flags & SDL_WINDOW_MODAL) {
|
if (window->flags & SDL_WINDOW_MODAL) {
|
||||||
WIN_SetWindowModal(_this, window, true);
|
WIN_SetWindowModal(_this, window, true);
|
||||||
|
@ -1100,21 +1102,10 @@ void WIN_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
|
|
||||||
ShowWindow(hwnd, SW_HIDE);
|
ShowWindow(hwnd, SW_HIDE);
|
||||||
|
|
||||||
// Transfer keyboard focus back to the parent
|
// Transfer keyboard focus back to the parent from a grabbing popup.
|
||||||
if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||||
SDL_Window *new_focus = window->parent;
|
SDL_Window *new_focus;
|
||||||
bool set_focus = window == SDL_GetKeyboardFocus();
|
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
|
||||||
|
|
||||||
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed.
|
|
||||||
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
|
|
||||||
new_focus = new_focus->parent;
|
|
||||||
|
|
||||||
// If some window in the chain currently had keyboard focus, set it to the new lowest-level window.
|
|
||||||
if (!set_focus) {
|
|
||||||
set_focus = new_focus == SDL_GetKeyboardFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WIN_SetKeyboardFocus(new_focus, set_focus);
|
WIN_SetKeyboardFocus(new_focus, set_focus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1152,7 +1143,7 @@ void WIN_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
}
|
}
|
||||||
if (bActivate) {
|
if (bActivate) {
|
||||||
SetForegroundWindow(hwnd);
|
SetForegroundWindow(hwnd);
|
||||||
if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||||
WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
|
WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -2319,6 +2310,7 @@ void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
|
||||||
|
|
||||||
bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
|
bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
|
||||||
{
|
{
|
||||||
|
if (!SDL_WINDOW_IS_POPUP(window)) {
|
||||||
SDL_WindowData *data = window->internal;
|
SDL_WindowData *data = window->internal;
|
||||||
HWND hwnd = data->hwnd;
|
HWND hwnd = data->hwnd;
|
||||||
const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE);
|
const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||||
|
@ -2338,6 +2330,21 @@ bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool foc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
||||||
|
if (!(window->flags & SDL_WINDOW_HIDDEN)) {
|
||||||
|
if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
|
||||||
|
SDL_Window *new_focus;
|
||||||
|
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
|
||||||
|
WIN_SetKeyboardFocus(new_focus, set_focus);
|
||||||
|
} else if (focusable) {
|
||||||
|
if (SDL_ShouldFocusPopup(window)) {
|
||||||
|
WIN_SetKeyboardFocus(window, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,6 @@ struct SDL_WindowData
|
||||||
bool destroy_parent_with_window;
|
bool destroy_parent_with_window;
|
||||||
SDL_DisplayID last_displayID;
|
SDL_DisplayID last_displayID;
|
||||||
WCHAR *ICMFileName;
|
WCHAR *ICMFileName;
|
||||||
SDL_Window *keyboard_focus;
|
|
||||||
SDL_WindowEraseBackgroundMode hint_erase_background_mode;
|
SDL_WindowEraseBackgroundMode hint_erase_background_mode;
|
||||||
bool taskbar_button_created;
|
bool taskbar_button_created;
|
||||||
struct SDL_VideoData *videodata;
|
struct SDL_VideoData *videodata;
|
||||||
|
|
|
@ -215,6 +215,7 @@ static void X11_ConstrainPopup(SDL_Window *window, bool output_to_pending)
|
||||||
int abs_y = window->last_position_pending ? window->pending.y : window->floating.y;
|
int abs_y = window->last_position_pending ? window->pending.y : window->floating.y;
|
||||||
int offset_x = 0, offset_y = 0;
|
int offset_x = 0, offset_y = 0;
|
||||||
|
|
||||||
|
if (window->constrain_popup) {
|
||||||
// Calculate the total offset from the parents
|
// Calculate the total offset from the parents
|
||||||
for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
|
for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
|
||||||
offset_x += w->x;
|
offset_x += w->x;
|
||||||
|
@ -237,6 +238,7 @@ static void X11_ConstrainPopup(SDL_Window *window, bool output_to_pending)
|
||||||
}
|
}
|
||||||
abs_x = SDL_max(abs_x, rect.x);
|
abs_x = SDL_max(abs_x, rect.x);
|
||||||
abs_y = SDL_max(abs_y, rect.y);
|
abs_y = SDL_max(abs_y, rect.y);
|
||||||
|
}
|
||||||
|
|
||||||
if (output_to_pending) {
|
if (output_to_pending) {
|
||||||
window->pending.x = abs_x - offset_x;
|
window->pending.x = abs_x - offset_x;
|
||||||
|
@ -257,7 +259,7 @@ static void X11_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
|
||||||
toplevel = toplevel->parent;
|
toplevel = toplevel->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
toplevel->internal->keyboard_focus = window;
|
toplevel->keyboard_focus = window;
|
||||||
|
|
||||||
if (set_active_focus && !window->is_hiding && !window->is_destroying) {
|
if (set_active_focus && !window->is_hiding && !window->is_destroying) {
|
||||||
SDL_SetKeyboardFocus(window);
|
SDL_SetKeyboardFocus(window);
|
||||||
|
@ -1550,9 +1552,9 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
X11_XFlush(display);
|
X11_XFlush(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Popup menus grab the keyboard
|
// Grabbing popup menus get keyboard focus.
|
||||||
if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||||
X11_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
|
X11_SetKeyboardFocus(window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get some valid border values, if we haven't received them yet
|
// Get some valid border values, if we haven't received them yet
|
||||||
|
@ -1609,20 +1611,9 @@ void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer keyboard focus back to the parent
|
// Transfer keyboard focus back to the parent
|
||||||
if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||||
SDL_Window *new_focus = window->parent;
|
SDL_Window *new_focus;
|
||||||
bool set_focus = window == SDL_GetKeyboardFocus();
|
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
|
||||||
|
|
||||||
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed.
|
|
||||||
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
|
|
||||||
new_focus = new_focus->parent;
|
|
||||||
|
|
||||||
// If some window in the chain currently had focus, set it to the new lowest-level window.
|
|
||||||
if (!set_focus) {
|
|
||||||
set_focus = new_focus == SDL_GetKeyboardFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
X11_SetKeyboardFocus(new_focus, set_focus);
|
X11_SetKeyboardFocus(new_focus, set_focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2340,6 +2331,7 @@ bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||||
|
|
||||||
bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
|
bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
|
||||||
{
|
{
|
||||||
|
if (!SDL_WINDOW_IS_POPUP(window)) {
|
||||||
SDL_WindowData *data = window->internal;
|
SDL_WindowData *data = window->internal;
|
||||||
Display *display = data->videodata->display;
|
Display *display = data->videodata->display;
|
||||||
XWMHints *wmhints;
|
XWMHints *wmhints;
|
||||||
|
@ -2354,6 +2346,21 @@ bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool foc
|
||||||
|
|
||||||
X11_XSetWMHints(display, data->xwindow, wmhints);
|
X11_XSetWMHints(display, data->xwindow, wmhints);
|
||||||
X11_XFree(wmhints);
|
X11_XFree(wmhints);
|
||||||
|
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
||||||
|
if (!(window->flags & SDL_WINDOW_HIDDEN)) {
|
||||||
|
if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
|
||||||
|
SDL_Window *new_focus;
|
||||||
|
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
|
||||||
|
X11_SetKeyboardFocus(new_focus, set_focus);
|
||||||
|
} else if (focusable) {
|
||||||
|
if (SDL_ShouldFocusPopup(window)) {
|
||||||
|
X11_SetKeyboardFocus(window, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,6 @@ struct SDL_WindowData
|
||||||
Window xdnd_source;
|
Window xdnd_source;
|
||||||
bool flashing_window;
|
bool flashing_window;
|
||||||
Uint64 flash_cancel_time;
|
Uint64 flash_cancel_time;
|
||||||
SDL_Window *keyboard_focus;
|
|
||||||
#ifdef SDL_VIDEO_OPENGL_EGL
|
#ifdef SDL_VIDEO_OPENGL_EGL
|
||||||
EGLSurface egl_surface;
|
EGLSurface egl_surface;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -49,6 +49,9 @@ struct PopupWindow
|
||||||
static struct PopupWindow *menus;
|
static struct PopupWindow *menus;
|
||||||
static struct PopupWindow tooltip;
|
static struct PopupWindow tooltip;
|
||||||
|
|
||||||
|
static bool no_constraints;
|
||||||
|
static bool no_grab;
|
||||||
|
|
||||||
/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
|
/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
|
||||||
static void quit(int rc)
|
static void quit(int rc)
|
||||||
{
|
{
|
||||||
|
@ -95,14 +98,27 @@ static bool create_popup(struct PopupWindow *new_popup, bool is_menu)
|
||||||
const int w = is_menu ? MENU_WIDTH : TOOLTIP_WIDTH;
|
const int w = is_menu ? MENU_WIDTH : TOOLTIP_WIDTH;
|
||||||
const int h = is_menu ? MENU_HEIGHT : TOOLTIP_HEIGHT;
|
const int h = is_menu ? MENU_HEIGHT : TOOLTIP_HEIGHT;
|
||||||
const int v_off = is_menu ? 0 : 32;
|
const int v_off = is_menu ? 0 : 32;
|
||||||
const SDL_WindowFlags flags = is_menu ? SDL_WINDOW_POPUP_MENU : SDL_WINDOW_TOOLTIP;
|
|
||||||
float x, y;
|
float x, y;
|
||||||
|
|
||||||
focus = SDL_GetMouseFocus();
|
focus = SDL_GetMouseFocus();
|
||||||
|
|
||||||
SDL_GetMouseState(&x, &y);
|
SDL_GetMouseState(&x, &y);
|
||||||
new_win = SDL_CreatePopupWindow(focus,
|
|
||||||
(int)x, (int)y + v_off, w, h, flags);
|
SDL_PropertiesID props = SDL_CreateProperties();
|
||||||
|
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, focus);
|
||||||
|
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN, !no_constraints);
|
||||||
|
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, !no_grab);
|
||||||
|
if (is_menu) {
|
||||||
|
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, true);
|
||||||
|
} else {
|
||||||
|
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, true);
|
||||||
|
}
|
||||||
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w);
|
||||||
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h);
|
||||||
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, (int)x);
|
||||||
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, (int)y + v_off);
|
||||||
|
new_win = SDL_CreateWindowWithProperties(props);
|
||||||
|
SDL_DestroyProperties(props);
|
||||||
|
|
||||||
if (new_win) {
|
if (new_win) {
|
||||||
new_renderer = SDL_CreateRenderer(new_win, state->renderdriver);
|
new_renderer = SDL_CreateRenderer(new_win, state->renderdriver);
|
||||||
|
@ -249,9 +265,31 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse commandline */
|
/* Parse commandline */
|
||||||
if (!SDLTest_CommonDefaultArgs(state, argc, argv)) {
|
for (i = 1; i < argc;) {
|
||||||
|
int consumed;
|
||||||
|
|
||||||
|
consumed = SDLTest_CommonArg(state, i);
|
||||||
|
if (consumed == 0) {
|
||||||
|
consumed = -1;
|
||||||
|
if (SDL_strcasecmp(argv[i], "--no-constraints") == 0) {
|
||||||
|
no_constraints = true;
|
||||||
|
consumed = 1;
|
||||||
|
} else if (SDL_strcasecmp(argv[i], "--no-grab") == 0) {
|
||||||
|
no_grab = true;
|
||||||
|
consumed = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (consumed < 0) {
|
||||||
|
static const char *options[] = {
|
||||||
|
"[--no-constraints]",
|
||||||
|
"[--no-grab]",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
SDLTest_CommonLogUsage(state, argv[0], options);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
i += consumed;
|
||||||
|
}
|
||||||
|
|
||||||
if (!SDLTest_CommonInit(state)) {
|
if (!SDLTest_CommonInit(state)) {
|
||||||
SDLTest_CommonQuit(state);
|
SDLTest_CommonQuit(state);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue