浏览代码

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.

Juan Linietsky 6 年之前
父节点
当前提交
39028cc161
共有 2 个文件被更改,包括 73 次插入40 次删除
  1. 70 39
      scene/main/viewport.cpp
  2. 3 1
      scene/main/viewport.h

+ 70 - 39
scene/main/viewport.cpp

@@ -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;
-				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;
 		} 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) {
-
-				//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 {
 			} 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();
-		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();
 	return gui.modal_stack.back();
@@ -2536,24 +2546,45 @@ void Viewport::_post_gui_grab_click_focus() {
 
 
 		if (gui.mouse_focus == focus_grabber)
 		if (gui.mouse_focus == focus_grabber)
 			return;
 			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);
 		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.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);
-		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);
+			}
+		}
 	}
 	}
 }
 }
 
 

+ 3 - 1
scene/main/viewport.h

@@ -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();