summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2018-11-28 09:31:17 -0300
committerJuan Linietsky <reduzio@gmail.com>2018-11-28 09:33:13 -0300
commit39028cc161048ffccd0fae8677c523da648311a5 (patch)
tree63d97034b8d5a79e7b3ceffd6c0f6cca25418617
parent05755618c5017799eb584b13b069d58aeb91eed0 (diff)
Improved the mouse focus system (mouse keeps focus on a control while buttons are pressed). Fixes #19154 and likely many others.
WARNING: Test well in every OS, if mouse wheel events are not properly sent as pressed+unpressed pair, it will fail and break (and the OS needs to be fixed). Only tested on X11 so far.
-rw-r--r--scene/main/viewport.cpp109
-rw-r--r--scene/main/viewport.h4
2 files changed, 73 insertions, 40 deletions
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 8545efb966..a98ae01c17 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -189,7 +189,7 @@ Viewport::GUI::GUI() {
dragging = false;
mouse_focus = NULL;
mouse_click_grabber = NULL;
- mouse_focus_button = -1;
+ mouse_focus_mask = 0;
key_focus = NULL;
mouse_over = NULL;
@@ -671,15 +671,7 @@ void Viewport::_notification(int p_what) {
case SceneTree::NOTIFICATION_WM_FOCUS_OUT: {
if (gui.mouse_focus) {
//if mouse is being pressed, send a release event
- Ref<InputEventMouseButton> mb;
- mb.instance();
- mb->set_position(gui.mouse_focus->get_local_mouse_position());
- mb->set_global_position(gui.mouse_focus->get_local_mouse_position());
- mb->set_button_index(gui.mouse_focus_button);
- mb->set_pressed(false);
- Control *c = gui.mouse_focus;
- gui.mouse_focus = NULL;
- c->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
+ _drop_mouse_focus();
}
} break;
}
@@ -1686,10 +1678,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (mb->is_pressed()) {
Size2 pos = mpos;
- if (gui.mouse_focus && mb->get_button_index() != gui.mouse_focus_button) {
-
- //do not steal mouse focus and stuff
+ if (gui.mouse_focus_mask) {
+ //do not steal mouse focus and stuff while a focus mask exists
+ gui.mouse_focus_mask |= 1 << (mb->get_button_index() - 1); //add the button to the mask
} else {
bool is_handled = false;
@@ -1734,7 +1726,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
*/
gui.mouse_focus = _gui_find_control(pos);
- gui.mouse_focus_button = mb->get_button_index();
+ gui.mouse_focus_mask = 1 << (mb->get_button_index() - 1);
if (!gui.mouse_focus) {
return;
@@ -1837,6 +1829,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
//change mouse accordingly
}
+ gui.mouse_focus_mask &= ~(1 << (mb->get_button_index() - 1)); //remove from mask
+
if (!gui.mouse_focus) {
//release event is only sent if a mouse focus (previously pressed button) exists
return;
@@ -1852,9 +1846,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Control *mouse_focus = gui.mouse_focus;
//disable mouse focus if needed before calling input, this makes popups on mouse press event work better, as the release will never be received otherwise
- if (mb->get_button_index() == gui.mouse_focus_button) {
+ if (gui.mouse_focus_mask == 0) {
gui.mouse_focus = NULL;
- gui.mouse_focus_button = -1;
}
if (mouse_focus->can_process()) {
@@ -1900,6 +1893,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.drag_data.get_type() != Variant::NIL) {
gui.mouse_focus = NULL;
+ gui.mouse_focus_mask = 0;
break;
} else {
if (gui.drag_preview != NULL) {
@@ -2412,7 +2406,7 @@ void Viewport::_gui_unfocus_control(Control *p_control) {
void Viewport::_gui_hid_control(Control *p_control) {
if (gui.mouse_focus == p_control) {
- gui.mouse_focus = NULL;
+ _drop_mouse_focus();
}
/* ???
@@ -2439,8 +2433,10 @@ void Viewport::_gui_hid_control(Control *p_control) {
void Viewport::_gui_remove_control(Control *p_control) {
- if (gui.mouse_focus == p_control)
+ if (gui.mouse_focus == p_control) {
gui.mouse_focus = NULL;
+ gui.mouse_focus_mask = 0;
+ }
if (gui.key_focus == p_control)
gui.key_focus = NULL;
if (gui.mouse_over == p_control)
@@ -2489,6 +2485,27 @@ void Viewport::_gui_accept_event() {
set_input_as_handled();
}
+void Viewport::_drop_mouse_focus() {
+
+ Control *c = gui.mouse_focus;
+ int mask = gui.mouse_focus_mask;
+ gui.mouse_focus = NULL;
+ gui.mouse_focus_mask = 0;
+
+ for (int i = 0; i < 3; i++) {
+
+ if (mask & (1 << i)) {
+ Ref<InputEventMouseButton> mb;
+ mb.instance();
+ mb->set_position(c->get_local_mouse_position());
+ mb->set_global_position(gui.mouse_focus->get_local_mouse_position());
+ mb->set_button_index(i + 1);
+ mb->set_pressed(false);
+ c->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
+ }
+ }
+}
+
List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) {
gui.modal_stack.push_back(p_control);
@@ -2498,15 +2515,8 @@ List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) {
p_control->_modal_set_prev_focus_owner(0);
if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus) && !gui.mouse_click_grabber) {
- Ref<InputEventMouseButton> mb;
- mb.instance();
- mb->set_position(gui.mouse_focus->get_local_mouse_position());
- mb->set_global_position(gui.mouse_focus->get_local_mouse_position());
- mb->set_button_index(gui.mouse_focus_button);
- mb->set_pressed(false);
- Control *c = gui.mouse_focus;
- gui.mouse_focus = NULL;
- c->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
+
+ _drop_mouse_focus();
}
return gui.modal_stack.back();
@@ -2536,24 +2546,45 @@ void Viewport::_post_gui_grab_click_focus() {
if (gui.mouse_focus == focus_grabber)
return;
- Ref<InputEventMouseButton> mb;
- mb.instance();
-
- //send unclic
+ int mask = gui.mouse_focus_mask;
Point2 click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
- mb->set_position(click);
- mb->set_button_index(gui.mouse_focus_button);
- mb->set_pressed(false);
- gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
+
+ for (int i = 0; i < 3; i++) {
+
+ if (mask & (1 << i)) {
+
+ Ref<InputEventMouseButton> mb;
+ mb.instance();
+
+ //send unclic
+
+ mb->set_position(click);
+ mb->set_button_index(i + 1);
+ mb->set_pressed(false);
+ gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
+ }
+ }
gui.mouse_focus = focus_grabber;
gui.focus_inv_xform = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse();
click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
- mb->set_position(click);
- mb->set_button_index(gui.mouse_focus_button);
- mb->set_pressed(true);
- gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb);
+
+ for (int i = 0; i < 3; i++) {
+
+ if (mask & (1 << i)) {
+
+ Ref<InputEventMouseButton> mb;
+ mb.instance();
+
+ //send clic
+
+ mb->set_position(click);
+ mb->set_button_index(i + 1);
+ mb->set_pressed(true);
+ gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb);
+ }
+ }
}
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 44fb322ae2..278350b1c9 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -272,7 +272,7 @@ private:
bool key_event_accepted;
Control *mouse_focus;
Control *mouse_click_grabber;
- int mouse_focus_button;
+ int mouse_focus_mask;
Control *key_focus;
Control *mouse_over;
Control *tooltip;
@@ -379,6 +379,8 @@ private:
void _canvas_layer_add(CanvasLayer *p_canvas_layer);
void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
+ void _drop_mouse_focus();
+
protected:
void _notification(int p_what);
static void _bind_methods();