Skip to content

Commit 5cb2bfc

Browse files
committed
Merge pull request godotengine#109063 from bruvzg/safe_mouseover_ids
Use safe ObjectID for mouse over controls.
2 parents 8cb0cc8 + 179d1b7 commit 5cb2bfc

File tree

2 files changed

+61
-44
lines changed

2 files changed

+61
-44
lines changed

scene/main/viewport.cpp

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ void Viewport::_process_picking() {
788788
SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
789789
bool parent_ignore_mouse = (parent_svc && parent_svc->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE);
790790
bool create_passive_hover_event = true;
791-
if (gui.mouse_over || parent_ignore_mouse) {
791+
if (gui.mouse_over.is_valid() || parent_ignore_mouse) {
792792
// When the mouse is over a Control node, passive hovering would cause input events for Colliders, that are behind Control nodes.
793793
// When parent SubViewportContainer ignores mouse, that setting should be respected.
794794
create_passive_hover_event = false;
@@ -1950,7 +1950,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
19501950
if (control->_is_focusable()) {
19511951
// Grabbing unhovered focus can cause issues when mouse is dragged
19521952
// with another button held down.
1953-
if (gui.mouse_over_hierarchy.has(control)) {
1953+
if (gui.mouse_over_hierarchy.has(control->get_instance_id())) {
19541954
// Hide the focus when it comes from a click.
19551955
control->grab_focus(true);
19561956
}
@@ -2526,7 +2526,8 @@ void Viewport::_gui_hide_control(Control *p_control) {
25262526
if (gui.key_focus == p_control) {
25272527
gui_release_focus();
25282528
}
2529-
if (gui.mouse_over == p_control || gui.mouse_over_hierarchy.has(p_control)) {
2529+
ObjectID over_id = p_control ? p_control->get_instance_id() : ObjectID();
2530+
if (gui.mouse_over == over_id || gui.mouse_over_hierarchy.has(over_id)) {
25302531
_drop_mouse_over(p_control->get_parent_control());
25312532
}
25322533
if (gui.drag_mouse_over == p_control) {
@@ -2545,7 +2546,8 @@ void Viewport::_gui_remove_control(Control *p_control) {
25452546
if (gui.key_focus == p_control) {
25462547
gui.key_focus = nullptr;
25472548
}
2548-
if (gui.mouse_over == p_control || gui.mouse_over_hierarchy.has(p_control)) {
2549+
ObjectID over_id = p_control ? p_control->get_instance_id() : ObjectID();
2550+
if (gui.mouse_over == over_id || gui.mouse_over_hierarchy.has(over_id)) {
25492551
_drop_mouse_over(p_control->get_parent_control());
25502552
}
25512553
if (gui.drag_mouse_over == p_control) {
@@ -2561,7 +2563,7 @@ void Viewport::canvas_item_top_level_changed() {
25612563
}
25622564

25632565
void Viewport::_gui_update_mouse_over() {
2564-
if (gui.mouse_over == nullptr || gui.mouse_over_hierarchy.is_empty()) {
2566+
if (gui.mouse_over.is_null() || gui.mouse_over_hierarchy.is_empty()) {
25652567
return;
25662568
}
25672569

@@ -2574,17 +2576,19 @@ void Viewport::_gui_update_mouse_over() {
25742576
}
25752577

25762578
// Rebuild the mouse over hierarchy.
2577-
LocalVector<Control *> new_mouse_over_hierarchy;
2578-
LocalVector<Control *> needs_enter;
2579+
LocalVector<ObjectID> new_mouse_over_hierarchy;
2580+
LocalVector<ObjectID> needs_enter;
25792581
LocalVector<int> needs_exit;
25802582

2581-
CanvasItem *ancestor = gui.mouse_over;
2583+
CanvasItem *over = ObjectDB::get_instance<CanvasItem>(gui.mouse_over);
2584+
CanvasItem *ancestor = over;
25822585
bool removing = false;
25832586
bool reached_top = false;
25842587
while (ancestor) {
25852588
Control *ancestor_control = Object::cast_to<Control>(ancestor);
25862589
if (ancestor_control) {
2587-
int found = gui.mouse_over_hierarchy.find(ancestor_control);
2590+
ObjectID ancestor_control_id = ancestor_control->get_instance_id();
2591+
int found = gui.mouse_over_hierarchy.find(ancestor_control_id);
25882592
if (found >= 0) {
25892593
// Remove the node if the propagation chain has been broken or it is now MOUSE_FILTER_IGNORE.
25902594
if (removing || ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE) {
@@ -2599,10 +2603,10 @@ void Viewport::_gui_update_mouse_over() {
25992603
reached_top = true;
26002604
}
26012605
if (!removing && ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
2602-
new_mouse_over_hierarchy.push_back(ancestor_control);
2606+
new_mouse_over_hierarchy.push_back(ancestor_control_id);
26032607
// Add the node if it was not found and it is now not MOUSE_FILTER_IGNORE.
26042608
if (found < 0) {
2605-
needs_enter.push_back(ancestor_control);
2609+
needs_enter.push_back(ancestor_control_id);
26062610
}
26072611
}
26082612
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
@@ -2632,15 +2636,18 @@ void Viewport::_gui_update_mouse_over() {
26322636
gui.sending_mouse_enter_exit_notifications = true;
26332637

26342638
// Send Mouse Exit Self notification.
2635-
if (gui.mouse_over && !needs_exit.is_empty() && needs_exit[0] == (int)gui.mouse_over_hierarchy.size() - 1) {
2636-
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
2637-
gui.mouse_over = nullptr;
2639+
if (over && !needs_exit.is_empty() && needs_exit[0] == (int)gui.mouse_over_hierarchy.size() - 1) {
2640+
over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
2641+
gui.mouse_over = ObjectID();
26382642
}
26392643

26402644
// Send Mouse Exit notifications.
26412645
for (int exit_control_index : needs_exit) {
2642-
gui.mouse_over_hierarchy[exit_control_index]->notification(Control::NOTIFICATION_MOUSE_EXIT);
2643-
gui.mouse_over_hierarchy[exit_control_index]->emit_signal(SceneStringName(mouse_exited));
2646+
Control *ctrl = ObjectDB::get_instance<Control>(gui.mouse_over_hierarchy[exit_control_index]);
2647+
if (ctrl) {
2648+
ctrl->notification(Control::NOTIFICATION_MOUSE_EXIT);
2649+
ctrl->emit_signal(SceneStringName(mouse_exited));
2650+
}
26442651
}
26452652

26462653
// Update the mouse over hierarchy.
@@ -2651,8 +2658,11 @@ void Viewport::_gui_update_mouse_over() {
26512658

26522659
// Send Mouse Enter notifications.
26532660
for (int i = needs_enter.size() - 1; i >= 0; i--) {
2654-
needs_enter[i]->notification(Control::NOTIFICATION_MOUSE_ENTER);
2655-
needs_enter[i]->emit_signal(SceneStringName(mouse_entered));
2661+
Control *ctrl = ObjectDB::get_instance<Control>(needs_enter[i]);
2662+
if (ctrl) {
2663+
ctrl->notification(Control::NOTIFICATION_MOUSE_ENTER);
2664+
ctrl->emit_signal(SceneStringName(mouse_entered));
2665+
}
26562666
}
26572667

26582668
gui.sending_mouse_enter_exit_notifications = false;
@@ -3247,7 +3257,7 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
32473257
}
32483258

32493259
if (swrect_border.has_point(p_pos)) {
3250-
if (gui.mouse_over) {
3260+
if (gui.mouse_over.is_valid()) {
32513261
_drop_mouse_over();
32523262
} else if (!gui.subwindow_over) {
32533263
_drop_physics_mouseover();
@@ -3281,12 +3291,13 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
32813291

32823292
// Look for Controls at mouse position.
32833293
Control *over = gui_find_control(p_pos);
3294+
ObjectID over_id = over ? over->get_instance_id() : ObjectID();
32843295
get_section_root_viewport()->gui.target_control = over;
32853296
bool notify_embedded_viewports = false;
3286-
if (over != gui.mouse_over || (!over && !gui.mouse_over_hierarchy.is_empty())) {
3297+
if (over_id != gui.mouse_over || (!over && !gui.mouse_over_hierarchy.is_empty())) {
32873298
// Find the common ancestor of `gui.mouse_over` and `over`.
32883299
Control *common_ancestor = nullptr;
3289-
LocalVector<Control *> over_ancestors;
3300+
LocalVector<ObjectID> over_ancestors;
32903301

32913302
if (over) {
32923303
// 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) {
32953306
Control *ancestor_control = Object::cast_to<Control>(ancestor);
32963307
if (ancestor_control) {
32973308
if (ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
3298-
int found = gui.mouse_over_hierarchy.find(ancestor_control);
3309+
int found = gui.mouse_over_hierarchy.find(ancestor_control->get_instance_id());
32993310
if (found >= 0) {
3300-
common_ancestor = gui.mouse_over_hierarchy[found];
3311+
common_ancestor = ObjectDB::get_instance<Control>(gui.mouse_over_hierarchy[found]);
33013312
break;
33023313
}
3303-
over_ancestors.push_back(ancestor_control);
3314+
over_ancestors.push_back(ancestor_control->get_instance_id());
33043315
}
33053316
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
33063317
// MOUSE_FILTER_STOP breaks the propagation chain.
@@ -3315,29 +3326,33 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
33153326
}
33163327
}
33173328

3318-
if (gui.mouse_over || !gui.mouse_over_hierarchy.is_empty()) {
3329+
if (gui.mouse_over.is_valid() || !gui.mouse_over_hierarchy.is_empty()) {
33193330
// Send Mouse Exit Self and Mouse Exit notifications.
33203331
_drop_mouse_over(common_ancestor);
33213332
} else {
33223333
_drop_physics_mouseover();
33233334
}
33243335

33253336
if (over) {
3326-
gui.mouse_over = over;
3337+
gui.mouse_over = over_id;
33273338
gui.mouse_over_hierarchy.reserve(gui.mouse_over_hierarchy.size() + over_ancestors.size());
33283339

33293340
gui.sending_mouse_enter_exit_notifications = true;
33303341

33313342
// Send Mouse Enter notifications to parents first.
33323343
for (int i = over_ancestors.size() - 1; i >= 0; i--) {
3333-
gui.mouse_over_hierarchy.push_back(over_ancestors[i]);
3334-
over_ancestors[i]->notification(Control::NOTIFICATION_MOUSE_ENTER);
3335-
over_ancestors[i]->emit_signal(SceneStringName(mouse_entered));
3344+
Control *ctrl = ObjectDB::get_instance<Control>(over_ancestors[i]);
3345+
if (ctrl) {
3346+
gui.mouse_over_hierarchy.push_back(over_ancestors[i]);
3347+
ctrl->notification(Control::NOTIFICATION_MOUSE_ENTER);
3348+
ctrl->emit_signal(SceneStringName(mouse_entered));
3349+
}
33363350
}
33373351

33383352
// Send Mouse Enter Self notification.
3339-
if (gui.mouse_over) {
3340-
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_ENTER_SELF);
3353+
CanvasItem *ctrl = ObjectDB::get_instance<CanvasItem>(gui.mouse_over);
3354+
if (ctrl) {
3355+
ctrl->notification(Control::NOTIFICATION_MOUSE_ENTER_SELF);
33413356
}
33423357

33433358
gui.sending_mouse_enter_exit_notifications = false;
@@ -3384,7 +3399,7 @@ void Viewport::_mouse_leave_viewport() {
33843399
if (gui.subwindow_over) {
33853400
gui.subwindow_over->_mouse_leave_viewport();
33863401
gui.subwindow_over = nullptr;
3387-
} else if (gui.mouse_over) {
3402+
} else if (gui.mouse_over.is_valid()) {
33883403
_drop_mouse_over();
33893404
}
33903405
notification(NOTIFICATION_VP_MOUSE_EXIT);
@@ -3398,7 +3413,7 @@ void Viewport::_drop_mouse_over(Control *p_until_control) {
33983413
}
33993414

34003415
_gui_cancel_tooltip();
3401-
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(gui.mouse_over);
3416+
SubViewportContainer *c = ObjectDB::get_instance<SubViewportContainer>(gui.mouse_over);
34023417
if (c) {
34033418
for (int i = 0; i < c->get_child_count(); i++) {
34043419
SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
@@ -3410,21 +3425,23 @@ void Viewport::_drop_mouse_over(Control *p_until_control) {
34103425
}
34113426

34123427
gui.sending_mouse_enter_exit_notifications = true;
3413-
if (gui.mouse_over && gui.mouse_over->is_inside_tree()) {
3414-
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
3428+
CanvasItem *over = ObjectDB::get_instance<CanvasItem>(gui.mouse_over);
3429+
if (over && over->is_inside_tree()) {
3430+
over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
34153431
}
34163432
Viewport *section_root = get_section_root_viewport();
3417-
if (section_root && section_root->gui.target_control == gui.mouse_over) {
3433+
if (section_root && section_root->gui.target_control == over) {
34183434
section_root->gui.target_control = nullptr;
34193435
}
3420-
gui.mouse_over = nullptr;
3436+
gui.mouse_over = ObjectID();
34213437

34223438
// Send Mouse Exit notifications to children first. Don't send to p_until_control or above.
3423-
int notification_until = p_until_control ? gui.mouse_over_hierarchy.find(p_until_control) + 1 : 0;
3439+
int notification_until = p_until_control ? gui.mouse_over_hierarchy.find(p_until_control->get_instance_id()) + 1 : 0;
34243440
for (int i = gui.mouse_over_hierarchy.size() - 1; i >= notification_until; i--) {
3425-
if (gui.mouse_over_hierarchy[i]->is_inside_tree()) {
3426-
gui.mouse_over_hierarchy[i]->notification(Control::NOTIFICATION_MOUSE_EXIT);
3427-
gui.mouse_over_hierarchy[i]->emit_signal(SceneStringName(mouse_exited));
3441+
Control *ctrl = ObjectDB::get_instance<Control>(gui.mouse_over_hierarchy[i]);
3442+
if (ctrl && ctrl->is_inside_tree()) {
3443+
ctrl->notification(Control::NOTIFICATION_MOUSE_EXIT);
3444+
ctrl->emit_signal(SceneStringName(mouse_exited));
34283445
}
34293446
}
34303447
gui.mouse_over_hierarchy.resize(notification_until);
@@ -3712,7 +3729,7 @@ Control *Viewport::gui_get_focus_owner() const {
37123729

37133730
Control *Viewport::gui_get_hovered_control() const {
37143731
ERR_READ_THREAD_GUARD_V(nullptr);
3715-
return gui.mouse_over;
3732+
return ObjectDB::get_instance<Control>(gui.mouse_over);
37163733
}
37173734

37183735
void Viewport::set_msaa_2d(MSAA p_msaa) {

scene/main/viewport.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,8 @@ class Viewport : public Node {
373373
BitField<MouseButtonMask> mouse_focus_mask = MouseButtonMask::NONE;
374374
Control *key_focus = nullptr;
375375
bool hide_focus = false;
376-
Control *mouse_over = nullptr;
377-
LocalVector<Control *> mouse_over_hierarchy;
376+
ObjectID mouse_over;
377+
LocalVector<ObjectID> mouse_over_hierarchy;
378378
bool sending_mouse_enter_exit_notifications = false;
379379
Window *subwindow_over = nullptr; // mouse_over and subwindow_over are mutually exclusive. At all times at least one of them is nullptr.
380380
Window *windowmanager_window_over = nullptr; // Only used in root Viewport.

0 commit comments

Comments
 (0)