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.
This commit is contained in:
Juan Linietsky 2018-11-28 09:31:17 -03:00
parent 05755618c5
commit 39028cc161
2 changed files with 73 additions and 40 deletions

View File

@ -189,7 +189,7 @@ Viewport::GUI::GUI() {
dragging = false; dragging = false;
mouse_focus = NULL; mouse_focus = NULL;
mouse_click_grabber = NULL; mouse_click_grabber = NULL;
mouse_focus_button = -1; mouse_focus_mask = 0;
key_focus = NULL; key_focus = NULL;
mouse_over = NULL; mouse_over = NULL;
@ -671,15 +671,7 @@ void Viewport::_notification(int p_what) {
case SceneTree::NOTIFICATION_WM_FOCUS_OUT: { case SceneTree::NOTIFICATION_WM_FOCUS_OUT: {
if (gui.mouse_focus) { if (gui.mouse_focus) {
//if mouse is being pressed, send a release event //if mouse is being pressed, send a release event
Ref<InputEventMouseButton> mb; _drop_mouse_focus();
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);
} }
} break; } break;
} }
@ -1686,10 +1678,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (mb->is_pressed()) { if (mb->is_pressed()) {
Size2 pos = mpos; Size2 pos = mpos;
if (gui.mouse_focus && mb->get_button_index() != gui.mouse_focus_button) { if (gui.mouse_focus_mask) {
//do not steal mouse focus and stuff
//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 { } else {
bool is_handled = false; 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 = _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) { if (!gui.mouse_focus) {
return; return;
@ -1837,6 +1829,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
//change mouse accordingly //change mouse accordingly
} }
gui.mouse_focus_mask &= ~(1 << (mb->get_button_index() - 1)); //remove from mask
if (!gui.mouse_focus) { if (!gui.mouse_focus) {
//release event is only sent if a mouse focus (previously pressed button) exists //release event is only sent if a mouse focus (previously pressed button) exists
return; return;
@ -1852,9 +1846,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Control *mouse_focus = gui.mouse_focus; 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 //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 = NULL;
gui.mouse_focus_button = -1;
} }
if (mouse_focus->can_process()) { 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) { if (gui.drag_data.get_type() != Variant::NIL) {
gui.mouse_focus = NULL; gui.mouse_focus = NULL;
gui.mouse_focus_mask = 0;
break; break;
} else { } else {
if (gui.drag_preview != NULL) { 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) { void Viewport::_gui_hid_control(Control *p_control) {
if (gui.mouse_focus == 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) { 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 = NULL;
gui.mouse_focus_mask = 0;
}
if (gui.key_focus == p_control) if (gui.key_focus == p_control)
gui.key_focus = NULL; gui.key_focus = NULL;
if (gui.mouse_over == p_control) if (gui.mouse_over == p_control)
@ -2489,6 +2485,27 @@ void Viewport::_gui_accept_event() {
set_input_as_handled(); 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) { List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) {
gui.modal_stack.push_back(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); 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) { if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus) && !gui.mouse_click_grabber) {
Ref<InputEventMouseButton> mb;
mb.instance(); _drop_mouse_focus();
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);
} }
return gui.modal_stack.back(); return gui.modal_stack.back();
@ -2536,26 +2546,47 @@ void Viewport::_post_gui_grab_click_focus() {
if (gui.mouse_focus == focus_grabber) if (gui.mouse_focus == focus_grabber)
return; return;
int mask = gui.mouse_focus_mask;
Point2 click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
for (int i = 0; i < 3; i++) {
if (mask & (1 << i)) {
Ref<InputEventMouseButton> mb; Ref<InputEventMouseButton> mb;
mb.instance(); mb.instance();
//send unclic //send unclic
Point2 click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
mb->set_position(click); mb->set_position(click);
mb->set_button_index(gui.mouse_focus_button); mb->set_button_index(i + 1);
mb->set_pressed(false); mb->set_pressed(false);
gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb); gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
}
}
gui.mouse_focus = focus_grabber; gui.mouse_focus = focus_grabber;
gui.focus_inv_xform = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse(); 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); click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
for (int i = 0; i < 3; i++) {
if (mask & (1 << i)) {
Ref<InputEventMouseButton> mb;
mb.instance();
//send clic
mb->set_position(click); mb->set_position(click);
mb->set_button_index(gui.mouse_focus_button); mb->set_button_index(i + 1);
mb->set_pressed(true); mb->set_pressed(true);
gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb); gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb);
} }
} }
}
}
/////////////////////////////// ///////////////////////////////

View File

@ -272,7 +272,7 @@ private:
bool key_event_accepted; bool key_event_accepted;
Control *mouse_focus; Control *mouse_focus;
Control *mouse_click_grabber; Control *mouse_click_grabber;
int mouse_focus_button; int mouse_focus_mask;
Control *key_focus; Control *key_focus;
Control *mouse_over; Control *mouse_over;
Control *tooltip; Control *tooltip;
@ -379,6 +379,8 @@ private:
void _canvas_layer_add(CanvasLayer *p_canvas_layer); void _canvas_layer_add(CanvasLayer *p_canvas_layer);
void _canvas_layer_remove(CanvasLayer *p_canvas_layer); void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
void _drop_mouse_focus();
protected: protected:
void _notification(int p_what); void _notification(int p_what);
static void _bind_methods(); static void _bind_methods();