Merge pull request #109063 from bruvzg/safe_mouseover_ids
Use safe ObjectID for mouse over controls.
This commit is contained in:
@ -788,7 +788,7 @@ void Viewport::_process_picking() {
|
|||||||
SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
|
SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
|
||||||
bool parent_ignore_mouse = (parent_svc && parent_svc->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE);
|
bool parent_ignore_mouse = (parent_svc && parent_svc->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE);
|
||||||
bool create_passive_hover_event = true;
|
bool create_passive_hover_event = true;
|
||||||
if (gui.mouse_over || parent_ignore_mouse) {
|
if (gui.mouse_over.is_valid() || parent_ignore_mouse) {
|
||||||
// When the mouse is over a Control node, passive hovering would cause input events for Colliders, that are behind Control nodes.
|
// When the mouse is over a Control node, passive hovering would cause input events for Colliders, that are behind Control nodes.
|
||||||
// When parent SubViewportContainer ignores mouse, that setting should be respected.
|
// When parent SubViewportContainer ignores mouse, that setting should be respected.
|
||||||
create_passive_hover_event = false;
|
create_passive_hover_event = false;
|
||||||
@ -1950,7 +1950,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
|
|||||||
if (control->_is_focusable()) {
|
if (control->_is_focusable()) {
|
||||||
// Grabbing unhovered focus can cause issues when mouse is dragged
|
// Grabbing unhovered focus can cause issues when mouse is dragged
|
||||||
// with another button held down.
|
// with another button held down.
|
||||||
if (gui.mouse_over_hierarchy.has(control)) {
|
if (gui.mouse_over_hierarchy.has(control->get_instance_id())) {
|
||||||
// Hide the focus when it comes from a click.
|
// Hide the focus when it comes from a click.
|
||||||
control->grab_focus(true);
|
control->grab_focus(true);
|
||||||
}
|
}
|
||||||
@ -2526,7 +2526,8 @@ void Viewport::_gui_hide_control(Control *p_control) {
|
|||||||
if (gui.key_focus == p_control) {
|
if (gui.key_focus == p_control) {
|
||||||
gui_release_focus();
|
gui_release_focus();
|
||||||
}
|
}
|
||||||
if (gui.mouse_over == p_control || gui.mouse_over_hierarchy.has(p_control)) {
|
ObjectID over_id = p_control ? p_control->get_instance_id() : ObjectID();
|
||||||
|
if (gui.mouse_over == over_id || gui.mouse_over_hierarchy.has(over_id)) {
|
||||||
_drop_mouse_over(p_control->get_parent_control());
|
_drop_mouse_over(p_control->get_parent_control());
|
||||||
}
|
}
|
||||||
if (gui.drag_mouse_over == p_control) {
|
if (gui.drag_mouse_over == p_control) {
|
||||||
@ -2545,7 +2546,8 @@ void Viewport::_gui_remove_control(Control *p_control) {
|
|||||||
if (gui.key_focus == p_control) {
|
if (gui.key_focus == p_control) {
|
||||||
gui.key_focus = nullptr;
|
gui.key_focus = nullptr;
|
||||||
}
|
}
|
||||||
if (gui.mouse_over == p_control || gui.mouse_over_hierarchy.has(p_control)) {
|
ObjectID over_id = p_control ? p_control->get_instance_id() : ObjectID();
|
||||||
|
if (gui.mouse_over == over_id || gui.mouse_over_hierarchy.has(over_id)) {
|
||||||
_drop_mouse_over(p_control->get_parent_control());
|
_drop_mouse_over(p_control->get_parent_control());
|
||||||
}
|
}
|
||||||
if (gui.drag_mouse_over == p_control) {
|
if (gui.drag_mouse_over == p_control) {
|
||||||
@ -2561,7 +2563,7 @@ void Viewport::canvas_item_top_level_changed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Viewport::_gui_update_mouse_over() {
|
void Viewport::_gui_update_mouse_over() {
|
||||||
if (gui.mouse_over == nullptr || gui.mouse_over_hierarchy.is_empty()) {
|
if (gui.mouse_over.is_null() || gui.mouse_over_hierarchy.is_empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2574,17 +2576,19 @@ void Viewport::_gui_update_mouse_over() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild the mouse over hierarchy.
|
// Rebuild the mouse over hierarchy.
|
||||||
LocalVector<Control *> new_mouse_over_hierarchy;
|
LocalVector<ObjectID> new_mouse_over_hierarchy;
|
||||||
LocalVector<Control *> needs_enter;
|
LocalVector<ObjectID> needs_enter;
|
||||||
LocalVector<int> needs_exit;
|
LocalVector<int> needs_exit;
|
||||||
|
|
||||||
CanvasItem *ancestor = gui.mouse_over;
|
CanvasItem *over = ObjectDB::get_instance<CanvasItem>(gui.mouse_over);
|
||||||
|
CanvasItem *ancestor = over;
|
||||||
bool removing = false;
|
bool removing = false;
|
||||||
bool reached_top = false;
|
bool reached_top = false;
|
||||||
while (ancestor) {
|
while (ancestor) {
|
||||||
Control *ancestor_control = Object::cast_to<Control>(ancestor);
|
Control *ancestor_control = Object::cast_to<Control>(ancestor);
|
||||||
if (ancestor_control) {
|
if (ancestor_control) {
|
||||||
int found = gui.mouse_over_hierarchy.find(ancestor_control);
|
ObjectID ancestor_control_id = ancestor_control->get_instance_id();
|
||||||
|
int found = gui.mouse_over_hierarchy.find(ancestor_control_id);
|
||||||
if (found >= 0) {
|
if (found >= 0) {
|
||||||
// Remove the node if the propagation chain has been broken or it is now MOUSE_FILTER_IGNORE.
|
// Remove the node if the propagation chain has been broken or it is now MOUSE_FILTER_IGNORE.
|
||||||
if (removing || ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE) {
|
if (removing || ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE) {
|
||||||
@ -2599,10 +2603,10 @@ void Viewport::_gui_update_mouse_over() {
|
|||||||
reached_top = true;
|
reached_top = true;
|
||||||
}
|
}
|
||||||
if (!removing && ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
|
if (!removing && ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
|
||||||
new_mouse_over_hierarchy.push_back(ancestor_control);
|
new_mouse_over_hierarchy.push_back(ancestor_control_id);
|
||||||
// Add the node if it was not found and it is now not MOUSE_FILTER_IGNORE.
|
// Add the node if it was not found and it is now not MOUSE_FILTER_IGNORE.
|
||||||
if (found < 0) {
|
if (found < 0) {
|
||||||
needs_enter.push_back(ancestor_control);
|
needs_enter.push_back(ancestor_control_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
|
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
|
||||||
@ -2632,15 +2636,18 @@ void Viewport::_gui_update_mouse_over() {
|
|||||||
gui.sending_mouse_enter_exit_notifications = true;
|
gui.sending_mouse_enter_exit_notifications = true;
|
||||||
|
|
||||||
// Send Mouse Exit Self notification.
|
// Send Mouse Exit Self notification.
|
||||||
if (gui.mouse_over && !needs_exit.is_empty() && needs_exit[0] == (int)gui.mouse_over_hierarchy.size() - 1) {
|
if (over && !needs_exit.is_empty() && needs_exit[0] == (int)gui.mouse_over_hierarchy.size() - 1) {
|
||||||
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
|
over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
|
||||||
gui.mouse_over = nullptr;
|
gui.mouse_over = ObjectID();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send Mouse Exit notifications.
|
// Send Mouse Exit notifications.
|
||||||
for (int exit_control_index : needs_exit) {
|
for (int exit_control_index : needs_exit) {
|
||||||
gui.mouse_over_hierarchy[exit_control_index]->notification(Control::NOTIFICATION_MOUSE_EXIT);
|
Control *ctrl = ObjectDB::get_instance<Control>(gui.mouse_over_hierarchy[exit_control_index]);
|
||||||
gui.mouse_over_hierarchy[exit_control_index]->emit_signal(SceneStringName(mouse_exited));
|
if (ctrl) {
|
||||||
|
ctrl->notification(Control::NOTIFICATION_MOUSE_EXIT);
|
||||||
|
ctrl->emit_signal(SceneStringName(mouse_exited));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the mouse over hierarchy.
|
// Update the mouse over hierarchy.
|
||||||
@ -2651,8 +2658,11 @@ void Viewport::_gui_update_mouse_over() {
|
|||||||
|
|
||||||
// Send Mouse Enter notifications.
|
// Send Mouse Enter notifications.
|
||||||
for (int i = needs_enter.size() - 1; i >= 0; i--) {
|
for (int i = needs_enter.size() - 1; i >= 0; i--) {
|
||||||
needs_enter[i]->notification(Control::NOTIFICATION_MOUSE_ENTER);
|
Control *ctrl = ObjectDB::get_instance<Control>(needs_enter[i]);
|
||||||
needs_enter[i]->emit_signal(SceneStringName(mouse_entered));
|
if (ctrl) {
|
||||||
|
ctrl->notification(Control::NOTIFICATION_MOUSE_ENTER);
|
||||||
|
ctrl->emit_signal(SceneStringName(mouse_entered));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.sending_mouse_enter_exit_notifications = false;
|
gui.sending_mouse_enter_exit_notifications = false;
|
||||||
@ -3247,7 +3257,7 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (swrect_border.has_point(p_pos)) {
|
if (swrect_border.has_point(p_pos)) {
|
||||||
if (gui.mouse_over) {
|
if (gui.mouse_over.is_valid()) {
|
||||||
_drop_mouse_over();
|
_drop_mouse_over();
|
||||||
} else if (!gui.subwindow_over) {
|
} else if (!gui.subwindow_over) {
|
||||||
_drop_physics_mouseover();
|
_drop_physics_mouseover();
|
||||||
@ -3281,12 +3291,13 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
|
|||||||
|
|
||||||
// Look for Controls at mouse position.
|
// Look for Controls at mouse position.
|
||||||
Control *over = gui_find_control(p_pos);
|
Control *over = gui_find_control(p_pos);
|
||||||
|
ObjectID over_id = over ? over->get_instance_id() : ObjectID();
|
||||||
get_section_root_viewport()->gui.target_control = over;
|
get_section_root_viewport()->gui.target_control = over;
|
||||||
bool notify_embedded_viewports = false;
|
bool notify_embedded_viewports = false;
|
||||||
if (over != gui.mouse_over || (!over && !gui.mouse_over_hierarchy.is_empty())) {
|
if (over_id != gui.mouse_over || (!over && !gui.mouse_over_hierarchy.is_empty())) {
|
||||||
// Find the common ancestor of `gui.mouse_over` and `over`.
|
// Find the common ancestor of `gui.mouse_over` and `over`.
|
||||||
Control *common_ancestor = nullptr;
|
Control *common_ancestor = nullptr;
|
||||||
LocalVector<Control *> over_ancestors;
|
LocalVector<ObjectID> over_ancestors;
|
||||||
|
|
||||||
if (over) {
|
if (over) {
|
||||||
// Get all ancestors that the mouse is currently over and need an enter signal.
|
// Get all ancestors that the mouse is currently over and need an enter signal.
|
||||||
@ -3295,12 +3306,12 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
|
|||||||
Control *ancestor_control = Object::cast_to<Control>(ancestor);
|
Control *ancestor_control = Object::cast_to<Control>(ancestor);
|
||||||
if (ancestor_control) {
|
if (ancestor_control) {
|
||||||
if (ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
|
if (ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
|
||||||
int found = gui.mouse_over_hierarchy.find(ancestor_control);
|
int found = gui.mouse_over_hierarchy.find(ancestor_control->get_instance_id());
|
||||||
if (found >= 0) {
|
if (found >= 0) {
|
||||||
common_ancestor = gui.mouse_over_hierarchy[found];
|
common_ancestor = ObjectDB::get_instance<Control>(gui.mouse_over_hierarchy[found]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
over_ancestors.push_back(ancestor_control);
|
over_ancestors.push_back(ancestor_control->get_instance_id());
|
||||||
}
|
}
|
||||||
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
|
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
|
||||||
// MOUSE_FILTER_STOP breaks the propagation chain.
|
// MOUSE_FILTER_STOP breaks the propagation chain.
|
||||||
@ -3315,7 +3326,7 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gui.mouse_over || !gui.mouse_over_hierarchy.is_empty()) {
|
if (gui.mouse_over.is_valid() || !gui.mouse_over_hierarchy.is_empty()) {
|
||||||
// Send Mouse Exit Self and Mouse Exit notifications.
|
// Send Mouse Exit Self and Mouse Exit notifications.
|
||||||
_drop_mouse_over(common_ancestor);
|
_drop_mouse_over(common_ancestor);
|
||||||
} else {
|
} else {
|
||||||
@ -3323,21 +3334,25 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (over) {
|
if (over) {
|
||||||
gui.mouse_over = over;
|
gui.mouse_over = over_id;
|
||||||
gui.mouse_over_hierarchy.reserve(gui.mouse_over_hierarchy.size() + over_ancestors.size());
|
gui.mouse_over_hierarchy.reserve(gui.mouse_over_hierarchy.size() + over_ancestors.size());
|
||||||
|
|
||||||
gui.sending_mouse_enter_exit_notifications = true;
|
gui.sending_mouse_enter_exit_notifications = true;
|
||||||
|
|
||||||
// Send Mouse Enter notifications to parents first.
|
// Send Mouse Enter notifications to parents first.
|
||||||
for (int i = over_ancestors.size() - 1; i >= 0; i--) {
|
for (int i = over_ancestors.size() - 1; i >= 0; i--) {
|
||||||
gui.mouse_over_hierarchy.push_back(over_ancestors[i]);
|
Control *ctrl = ObjectDB::get_instance<Control>(over_ancestors[i]);
|
||||||
over_ancestors[i]->notification(Control::NOTIFICATION_MOUSE_ENTER);
|
if (ctrl) {
|
||||||
over_ancestors[i]->emit_signal(SceneStringName(mouse_entered));
|
gui.mouse_over_hierarchy.push_back(over_ancestors[i]);
|
||||||
|
ctrl->notification(Control::NOTIFICATION_MOUSE_ENTER);
|
||||||
|
ctrl->emit_signal(SceneStringName(mouse_entered));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send Mouse Enter Self notification.
|
// Send Mouse Enter Self notification.
|
||||||
if (gui.mouse_over) {
|
CanvasItem *ctrl = ObjectDB::get_instance<CanvasItem>(gui.mouse_over);
|
||||||
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_ENTER_SELF);
|
if (ctrl) {
|
||||||
|
ctrl->notification(Control::NOTIFICATION_MOUSE_ENTER_SELF);
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.sending_mouse_enter_exit_notifications = false;
|
gui.sending_mouse_enter_exit_notifications = false;
|
||||||
@ -3384,7 +3399,7 @@ void Viewport::_mouse_leave_viewport() {
|
|||||||
if (gui.subwindow_over) {
|
if (gui.subwindow_over) {
|
||||||
gui.subwindow_over->_mouse_leave_viewport();
|
gui.subwindow_over->_mouse_leave_viewport();
|
||||||
gui.subwindow_over = nullptr;
|
gui.subwindow_over = nullptr;
|
||||||
} else if (gui.mouse_over) {
|
} else if (gui.mouse_over.is_valid()) {
|
||||||
_drop_mouse_over();
|
_drop_mouse_over();
|
||||||
}
|
}
|
||||||
notification(NOTIFICATION_VP_MOUSE_EXIT);
|
notification(NOTIFICATION_VP_MOUSE_EXIT);
|
||||||
@ -3398,7 +3413,7 @@ void Viewport::_drop_mouse_over(Control *p_until_control) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_gui_cancel_tooltip();
|
_gui_cancel_tooltip();
|
||||||
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(gui.mouse_over);
|
SubViewportContainer *c = ObjectDB::get_instance<SubViewportContainer>(gui.mouse_over);
|
||||||
if (c) {
|
if (c) {
|
||||||
for (int i = 0; i < c->get_child_count(); i++) {
|
for (int i = 0; i < c->get_child_count(); i++) {
|
||||||
SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
|
SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
|
||||||
@ -3410,21 +3425,23 @@ void Viewport::_drop_mouse_over(Control *p_until_control) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gui.sending_mouse_enter_exit_notifications = true;
|
gui.sending_mouse_enter_exit_notifications = true;
|
||||||
if (gui.mouse_over && gui.mouse_over->is_inside_tree()) {
|
CanvasItem *over = ObjectDB::get_instance<CanvasItem>(gui.mouse_over);
|
||||||
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
|
if (over && over->is_inside_tree()) {
|
||||||
|
over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
|
||||||
}
|
}
|
||||||
Viewport *section_root = get_section_root_viewport();
|
Viewport *section_root = get_section_root_viewport();
|
||||||
if (section_root && section_root->gui.target_control == gui.mouse_over) {
|
if (section_root && section_root->gui.target_control == over) {
|
||||||
section_root->gui.target_control = nullptr;
|
section_root->gui.target_control = nullptr;
|
||||||
}
|
}
|
||||||
gui.mouse_over = nullptr;
|
gui.mouse_over = ObjectID();
|
||||||
|
|
||||||
// Send Mouse Exit notifications to children first. Don't send to p_until_control or above.
|
// Send Mouse Exit notifications to children first. Don't send to p_until_control or above.
|
||||||
int notification_until = p_until_control ? gui.mouse_over_hierarchy.find(p_until_control) + 1 : 0;
|
int notification_until = p_until_control ? gui.mouse_over_hierarchy.find(p_until_control->get_instance_id()) + 1 : 0;
|
||||||
for (int i = gui.mouse_over_hierarchy.size() - 1; i >= notification_until; i--) {
|
for (int i = gui.mouse_over_hierarchy.size() - 1; i >= notification_until; i--) {
|
||||||
if (gui.mouse_over_hierarchy[i]->is_inside_tree()) {
|
Control *ctrl = ObjectDB::get_instance<Control>(gui.mouse_over_hierarchy[i]);
|
||||||
gui.mouse_over_hierarchy[i]->notification(Control::NOTIFICATION_MOUSE_EXIT);
|
if (ctrl && ctrl->is_inside_tree()) {
|
||||||
gui.mouse_over_hierarchy[i]->emit_signal(SceneStringName(mouse_exited));
|
ctrl->notification(Control::NOTIFICATION_MOUSE_EXIT);
|
||||||
|
ctrl->emit_signal(SceneStringName(mouse_exited));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gui.mouse_over_hierarchy.resize(notification_until);
|
gui.mouse_over_hierarchy.resize(notification_until);
|
||||||
@ -3712,7 +3729,7 @@ Control *Viewport::gui_get_focus_owner() const {
|
|||||||
|
|
||||||
Control *Viewport::gui_get_hovered_control() const {
|
Control *Viewport::gui_get_hovered_control() const {
|
||||||
ERR_READ_THREAD_GUARD_V(nullptr);
|
ERR_READ_THREAD_GUARD_V(nullptr);
|
||||||
return gui.mouse_over;
|
return ObjectDB::get_instance<Control>(gui.mouse_over);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewport::set_msaa_2d(MSAA p_msaa) {
|
void Viewport::set_msaa_2d(MSAA p_msaa) {
|
||||||
|
|||||||
@ -373,8 +373,8 @@ private:
|
|||||||
BitField<MouseButtonMask> mouse_focus_mask = MouseButtonMask::NONE;
|
BitField<MouseButtonMask> mouse_focus_mask = MouseButtonMask::NONE;
|
||||||
Control *key_focus = nullptr;
|
Control *key_focus = nullptr;
|
||||||
bool hide_focus = false;
|
bool hide_focus = false;
|
||||||
Control *mouse_over = nullptr;
|
ObjectID mouse_over;
|
||||||
LocalVector<Control *> mouse_over_hierarchy;
|
LocalVector<ObjectID> mouse_over_hierarchy;
|
||||||
bool sending_mouse_enter_exit_notifications = false;
|
bool sending_mouse_enter_exit_notifications = false;
|
||||||
Window *subwindow_over = nullptr; // mouse_over and subwindow_over are mutually exclusive. At all times at least one of them is nullptr.
|
Window *subwindow_over = nullptr; // mouse_over and subwindow_over are mutually exclusive. At all times at least one of them is nullptr.
|
||||||
Window *windowmanager_window_over = nullptr; // Only used in root Viewport.
|
Window *windowmanager_window_over = nullptr; // Only used in root Viewport.
|
||||||
|
|||||||
Reference in New Issue
Block a user