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:
parent
05755618c5
commit
39028cc161
2 changed files with 73 additions and 40 deletions
|
@ -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,26 +2546,47 @@ void Viewport::_post_gui_grab_click_focus() {
|
|||
|
||||
if (gui.mouse_focus == focus_grabber)
|
||||
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;
|
||||
mb.instance();
|
||||
|
||||
//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_button_index(gui.mouse_focus_button);
|
||||
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);
|
||||
|
||||
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(gui.mouse_focus_button);
|
||||
mb->set_button_index(i + 1);
|
||||
mb->set_pressed(true);
|
||||
gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue