Merge pull request #46360 from nmrkr/drag-preview-crash-after-free

Fix crash during drag if user queue_free'd the drag preview
This commit is contained in:
Rémi Verschelde 2021-02-24 22:43:53 +01:00 committed by GitHub
commit 305683f83b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 23 deletions

View file

@ -869,7 +869,7 @@
<argument index="0" name="control" type="Control"> <argument index="0" name="control" type="Control">
</argument> </argument>
<description> <description>
Shows the given control at the mouse pointer. A good time to call this method is in [method get_drag_data]. The control must not be in the scene tree. Shows the given control at the mouse pointer. A good time to call this method is in [method get_drag_data]. The control must not be in the scene tree. You should not free the control, and you should not keep a reference to the control beyond the duration of the drag. It will be deleted automatically after the drag has ended.
[codeblocks] [codeblocks]
[gdscript] [gdscript]
export (Color, RGBA) var color = Color(1, 0, 0, 1) export (Color, RGBA) var color = Color(1, 0, 0, 1)

View file

@ -1770,19 +1770,22 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_
} }
} }
if (!c) { if (!c || c->data.mouse_filter == Control::MOUSE_FILTER_IGNORE) {
return nullptr; return nullptr;
} }
matrix.affine_invert(); matrix.affine_invert();
if (!c->has_point(matrix.xform(p_global))) {
//conditions for considering this as a valid control for return
if (c->data.mouse_filter != Control::MOUSE_FILTER_IGNORE && c->has_point(matrix.xform(p_global)) && (!gui.drag_preview || (c != gui.drag_preview && !gui.drag_preview->is_a_parent_of(c)))) {
r_inv_xform = matrix;
return c;
} else {
return nullptr; return nullptr;
} }
Control *drag_preview = _gui_get_drag_preview();
if (!drag_preview || (c != drag_preview && !drag_preview->is_a_parent_of(c))) {
r_inv_xform = matrix;
return c;
}
return nullptr;
} }
bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check) { bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check) {
@ -1920,9 +1923,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.drag_data = Variant(); gui.drag_data = Variant();
gui.dragging = false; gui.dragging = false;
if (gui.drag_preview) { Control *drag_preview = _gui_get_drag_preview();
memdelete(gui.drag_preview); if (drag_preview) {
gui.drag_preview = nullptr; memdelete(drag_preview);
gui.drag_preview_id = ObjectID();
} }
_propagate_viewport_notification(this, NOTIFICATION_DRAG_END); _propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
//change mouse accordingly //change mouse accordingly
@ -1935,9 +1939,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false); _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false);
} }
if (gui.drag_preview && mb->get_button_index() == BUTTON_LEFT) { Control *drag_preview = _gui_get_drag_preview();
memdelete(gui.drag_preview); if (drag_preview) {
gui.drag_preview = nullptr; memdelete(drag_preview);
gui.drag_preview_id = ObjectID();
} }
gui.drag_data = Variant(); gui.drag_data = Variant();
@ -2034,10 +2039,11 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.mouse_focus_mask = 0; gui.mouse_focus_mask = 0;
break; break;
} else { } else {
if (gui.drag_preview != nullptr) { Control *drag_preview = _gui_get_drag_preview();
if (drag_preview) {
ERR_PRINT("Don't set a drag preview and return null data. Preview was deleted and drag request ignored."); ERR_PRINT("Don't set a drag preview and return null data. Preview was deleted and drag request ignored.");
memdelete(gui.drag_preview); memdelete(drag_preview);
gui.drag_preview = nullptr; gui.drag_preview_id = ObjectID();
} }
gui.dragging = false; gui.dragging = false;
} }
@ -2177,8 +2183,9 @@ 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) {
//handle dragandrop //handle dragandrop
if (gui.drag_preview) { Control *drag_preview = _gui_get_drag_preview();
gui.drag_preview->set_position(mpos); if (drag_preview) {
drag_preview->set_position(mpos);
} }
gui.drag_mouse_over = over; gui.drag_mouse_over = over;
@ -2453,15 +2460,29 @@ void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) {
ERR_FAIL_COND(p_control->is_inside_tree()); ERR_FAIL_COND(p_control->is_inside_tree());
ERR_FAIL_COND(p_control->get_parent() != nullptr); ERR_FAIL_COND(p_control->get_parent() != nullptr);
if (gui.drag_preview) { Control *drag_preview = _gui_get_drag_preview();
memdelete(gui.drag_preview); if (drag_preview) {
memdelete(drag_preview);
} }
p_control->set_as_top_level(true); p_control->set_as_top_level(true);
p_control->set_position(gui.last_mouse_pos); p_control->set_position(gui.last_mouse_pos);
p_base->get_root_parent_control()->add_child(p_control); //add as child of viewport p_base->get_root_parent_control()->add_child(p_control); //add as child of viewport
p_control->raise(); p_control->raise();
gui.drag_preview = p_control; gui.drag_preview_id = p_control->get_instance_id();
}
Control *Viewport::_gui_get_drag_preview() {
if (gui.drag_preview_id.is_null()) {
return nullptr;
} else {
Control *drag_preview = Object::cast_to<Control>(ObjectDB::get_instance(gui.drag_preview_id));
if (!drag_preview) {
ERR_PRINT("Don't free the control set as drag preview.");
gui.drag_preview_id = ObjectID();
}
return drag_preview;
}
} }
void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) { void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) {

View file

@ -357,7 +357,7 @@ private:
Point2 drag_accum; Point2 drag_accum;
bool drag_attempted = false; bool drag_attempted = false;
Variant drag_data; Variant drag_data;
Control *drag_preview = nullptr; ObjectID drag_preview_id;
float tooltip_timer = -1.0; float tooltip_timer = -1.0;
float tooltip_delay = 0.0; float tooltip_delay = 0.0;
Transform2D focus_inv_xform; Transform2D focus_inv_xform;
@ -415,6 +415,7 @@ private:
void _gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control); void _gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control);
void _gui_set_drag_preview(Control *p_base, Control *p_control); void _gui_set_drag_preview(Control *p_base, Control *p_control);
Control *_gui_get_drag_preview();
void _gui_remove_focus_for_window(Node *p_window); void _gui_remove_focus_for_window(Node *p_window);
void _gui_remove_focus(); void _gui_remove_focus();