Skip to content

Commit

Permalink
Fix Focus Bug for Popups on Windows
Browse files Browse the repository at this point in the history
Fixes that the Popups no longer steal focus, when they are popuped.

Since the previous window no longer loses focus the popup has to emulate
it. Therefor the behaviour in the PopupMenu was moved into the Popup
itself.

This commit provides the general structure for the implementation, but only
the the Windows Display Server is implemented.

Partial fix of godotengine#37531 along with godotengine#37771
  • Loading branch information
HaSa1002 committed May 29, 2020
1 parent ef5910e commit 2a0186e
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 32 deletions.
37 changes: 33 additions & 4 deletions platform/windows/display_server_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,9 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod
if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
wd.no_focus = true;
}
if (p_flags & WINDOW_FLAG_INPUT_WITHOUT_FOCUS_BIT) {
wd.input_without_focus = true;
}

_update_window_style(window_id);

Expand Down Expand Up @@ -1013,6 +1016,9 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
} break;
case WINDOW_FLAG_MAX:
break;
case WINDOW_FLAG_INPUT_WITHOUT_FOCUS: {
wd.input_without_focus = p_enabled;
}
}
}

Expand All @@ -1039,6 +1045,9 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window
} break;
case WINDOW_FLAG_MAX:
break;
case WINDOW_FLAG_INPUT_WITHOUT_FOCUS: {
return wd.input_without_focus;
} break;
}

return false;
Expand Down Expand Up @@ -1716,6 +1725,17 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)
return;
}
callable.call((const Variant **)&evp, 1, ret, ce);

//send to all windows that request always input and aren't the current window and are unfocused
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
if (E->key() == event_from_window->get_window_id() || E->key() == MAIN_WINDOW_ID || (!E->get().input_without_focus && E->get().no_focus))
continue;
Callable callable = E->get().input_event_callback;
if (callable.is_null()) {
continue;
}
callable.call((const Variant **)&evp, 1, ret, ce);
}
} else {
//send to all windows
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
Expand Down Expand Up @@ -1839,6 +1859,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA

return 0; // Jump Back
}
case WM_MOUSEACTIVATE: {
if (windows[window_id].no_focus)
return MA_NOACTIVATE;

} break;
case WM_MOUSELEAVE: {
old_invalid = true;
outside = true;
Expand Down Expand Up @@ -1917,7 +1942,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
*/
}

if (windows[window_id].window_has_focus && mm->get_relative() != Vector2())
if ((windows[window_id].window_has_focus || windows[window_id].input_without_focus) && mm->get_relative() != Vector2())
Input::get_singleton()->accumulate_input_event(mm);
}
delete[] lpb;
Expand Down Expand Up @@ -2152,7 +2177,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
old_x = mm->get_position().x;
old_y = mm->get_position().y;
if (windows[window_id].window_has_focus) {
if (windows[window_id].window_has_focus || windows[window_id].input_without_focus) {
Input::get_singleton()->accumulate_input_event(mm);
}

Expand Down Expand Up @@ -2257,7 +2282,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
old_x = mm->get_position().x;
old_y = mm->get_position().y;
if (windows[window_id].window_has_focus)
if (windows[window_id].window_has_focus || windows[window_id].input_without_focus)
Input::get_singleton()->accumulate_input_event(mm);

} break;
Expand Down Expand Up @@ -2814,9 +2839,13 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);

WindowID id = window_id_counter;

if (id > 0) {
dwExStyle |= WS_EX_TOOLWINDOW;
}

{
WindowData wd;

wd.hWnd = CreateWindowExW(
dwExStyle,
L"Engine", L"",
Expand Down
1 change: 1 addition & 0 deletions platform/windows/display_server_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ class DisplayServerWindows : public DisplayServer {
bool always_on_top = false;
bool no_focus = false;
bool window_has_focus = false;
bool input_without_focus = false;

HANDLE wtctx;
LOGCONTEXTW wtlc;
Expand Down
4 changes: 4 additions & 0 deletions scene/gui/menu_button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ void MenuButton::pressed() {
print_line("windowpos: " + w->get_position());
}
}
if (popup->is_visible()) {
return;
}

Size2 size = get_size();

Point2 gp = get_screen_position();
Expand Down
3 changes: 3 additions & 0 deletions scene/gui/option_button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ void OptionButton::_selected(int p_which) {

void OptionButton::pressed() {
Size2 size = get_size();
if (popup->is_visible()) {
return;
}
popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
popup->set_size(Size2(size.width, 0));
popup->popup();
Expand Down
29 changes: 27 additions & 2 deletions scene/gui/popup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,25 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
if (key.is_valid() && key->is_pressed() && key->get_keycode() == KEY_ESCAPE) {
_close_pressed();
}

Ref<InputEventMouseButton> button = p_event;
if (button.is_valid() && !button->is_pressed() && button->get_window_id() != get_window_id()) {
if (invalidated_click) {
invalidated_click = false;
} else {
_close_pressed();
}
}

Ref<InputEventMouseMotion> mouse = p_event;
if (mouse.is_valid()) {
if (invalidated_click) {
moved += mouse->get_relative();
if (moved.length() > 4) {
invalidated_click = false;
}
}
}
}

void Popup::_parent_focused() {
Expand Down Expand Up @@ -96,6 +115,12 @@ void Popup::set_as_minsize() {
set_size(get_contents_minimum_size());
}

void Popup::popup(const Rect2 &p_bounds) {
moved = Vector2();
invalidated_click = true;
Window::popup(p_bounds);
}

void Popup::_bind_methods() {
ADD_SIGNAL(MethodInfo("popup_hide"));
}
Expand Down Expand Up @@ -130,13 +155,13 @@ Rect2i Popup::_popup_adjust_rect() const {
}

Popup::Popup() {
parent_visible = nullptr;

set_wrap_controls(true);
set_visible(false);
set_transient(true);
set_flag(FLAG_BORDERLESS, true);
set_flag(FLAG_RESIZE_DISABLED, true);
set_flag(FLAG_NO_FOCUS, true);
set_flag(FLAG_INPUT_WITHOUT_FOCUS, true);

connect("window_input", callable_mp(this, &Popup::_input_from_window));
}
Expand Down
6 changes: 5 additions & 1 deletion scene/gui/popup.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
class Popup : public Window {
GDCLASS(Popup, Window);

Window *parent_visible;
Window *parent_visible = nullptr;

bool invalidated_click = false;
Vector2 moved = Vector2();

void _input_from_window(const Ref<InputEvent> &p_event);
void _parent_focused();
Expand All @@ -50,6 +53,7 @@ class Popup : public Window {

public:
void set_as_minsize();
virtual void popup(const Rect2 &p_bounds = Rect2());
Popup();
~Popup();
};
Expand Down
22 changes: 2 additions & 20 deletions scene/gui/popup_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {

int over = _get_mouse_over(b->get_position());

if (invalidated_click) {
invalidated_click = false;
break;
}
if (over < 0) {
if (over < 0 || get_window_id() != b->get_window_id()) {
if (!was_during_grabbed_click) {
hide();
}
Expand All @@ -335,14 +331,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {

Ref<InputEventMouseMotion> m = p_event;

if (m.is_valid()) {
if (invalidated_click) {
moved += m->get_relative();
if (moved.length() > 4) {
invalidated_click = false;
}
}

if (m.is_valid() && get_window_id() == m->get_window_id()) {
for (List<Rect2>::Element *E = autohide_areas.front(); E; E = E->next()) {
if (!Rect2(Point2(), get_size()).has_point(m->get_position()) && E->get().has_point(m->get_position())) {
_close_pressed();
Expand Down Expand Up @@ -1422,12 +1411,6 @@ void PopupMenu::_bind_methods() {
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
}

void PopupMenu::popup(const Rect2 &p_bounds) {
moved = Vector2();
invalidated_click = true;
Popup::popup(p_bounds);
}

PopupMenu::PopupMenu() {
control = memnew(Control);
add_child(control);
Expand All @@ -1440,7 +1423,6 @@ PopupMenu::PopupMenu() {
submenu_over = -1;
initial_button_mask = 0;
during_grabbed_click = false;
invalidated_click = false;

allow_search = true;
search_time_msec = 0;
Expand Down
4 changes: 0 additions & 4 deletions scene/gui/popup_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,9 @@ class PopupMenu : public Popup {
void _activate_submenu(int over);
void _submenu_timeout();

bool invalidated_click;
bool hide_on_item_selection;
bool hide_on_checkable_item_selection;
bool hide_on_multistate_item_selection;
Vector2 moved;

Array _get_items() const;
void _set_items(const Array &p_items);
Expand Down Expand Up @@ -213,8 +211,6 @@ class PopupMenu : public Popup {
void set_allow_search(bool p_allow);
bool get_allow_search() const;

virtual void popup(const Rect2 &p_bounds = Rect2());

void take_mouse_focus();

PopupMenu();
Expand Down
1 change: 1 addition & 0 deletions scene/main/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class Window : public Viewport {
FLAG_ALWAYS_ON_TOP = DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP,
FLAG_TRANSPARENT = DisplayServer::WINDOW_FLAG_TRANSPARENT,
FLAG_NO_FOCUS = DisplayServer::WINDOW_FLAG_NO_FOCUS,
FLAG_INPUT_WITHOUT_FOCUS = DisplayServer::WINDOW_FLAG_INPUT_WITHOUT_FOCUS,
FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX,
};

Expand Down
4 changes: 3 additions & 1 deletion servers/display_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ class DisplayServer : public Object {
WINDOW_FLAG_ALWAYS_ON_TOP,
WINDOW_FLAG_TRANSPARENT,
WINDOW_FLAG_NO_FOCUS,
WINDOW_FLAG_INPUT_WITHOUT_FOCUS,
WINDOW_FLAG_MAX,
};

Expand All @@ -208,7 +209,8 @@ class DisplayServer : public Object {
WINDOW_FLAG_BORDERLESS_BIT = (1 << WINDOW_FLAG_BORDERLESS),
WINDOW_FLAG_ALWAYS_ON_TOP_BIT = (1 << WINDOW_FLAG_ALWAYS_ON_TOP),
WINDOW_FLAG_TRANSPARENT_BIT = (1 << WINDOW_FLAG_TRANSPARENT),
WINDOW_FLAG_NO_FOCUS_BIT = (1 << WINDOW_FLAG_NO_FOCUS)
WINDOW_FLAG_NO_FOCUS_BIT = (1 << WINDOW_FLAG_NO_FOCUS),
WINDOW_FLAG_INPUT_WITHOUT_FOCUS_BIT = (1 << WINDOW_FLAG_INPUT_WITHOUT_FOCUS),
};

virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i & = Rect2i());
Expand Down

0 comments on commit 2a0186e

Please sign in to comment.