diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 85b502544ec4..ced04bb17745 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -371,6 +371,12 @@
Causes the window to grab focus, allowing it to receive user input.
+
+
+
+ Moves the window to the current mouse position.
+
+
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 76a7e3e3a55a..9aa7082c807f 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1854,8 +1854,8 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_
return nullptr;
}
- Control *drag_preview = _gui_get_drag_preview();
- if (!drag_preview || (c != drag_preview && !drag_preview->is_ancestor_of(c))) {
+ Window *drag_preview = _gui_get_drag_preview();
+ if (!drag_preview || !drag_preview->is_ancestor_of(c)) {
return c;
}
@@ -2029,7 +2029,7 @@ void Viewport::_gui_input_event(Ref p_event) {
gui.dragging = true;
break;
} else {
- Control *drag_preview = _gui_get_drag_preview();
+ Window *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.");
memdelete(drag_preview);
@@ -2150,10 +2150,9 @@ void Viewport::_gui_input_event(Ref p_event) {
if (gui.dragging) {
// Handle drag & drop. This happens in the viewport where dragging started.
- Control *drag_preview = _gui_get_drag_preview();
+ Window *drag_preview = _gui_get_drag_preview();
if (drag_preview) {
- Vector2 pos = drag_preview->get_canvas_transform().affine_inverse().xform(mpos);
- drag_preview->set_position(pos);
+ drag_preview->move_to_mouse();
}
gui.drag_mouse_over = section_root->gui.target_control;
@@ -2390,7 +2389,7 @@ void Viewport::gui_perform_drop_at(const Point2 &p_pos, Control *p_control) {
gui.drag_successful = false;
}
- Control *drag_preview = _gui_get_drag_preview();
+ Window *drag_preview = _gui_get_drag_preview();
if (drag_preview) {
memdelete(drag_preview);
gui.drag_preview_id = ObjectID();
@@ -2460,23 +2459,34 @@ 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->get_parent() != nullptr);
- Control *drag_preview = _gui_get_drag_preview();
+ Window *drag_preview = _gui_get_drag_preview();
if (drag_preview) {
memdelete(drag_preview);
}
+
+ Window *window = memnew(Window);
+
+ window->wrap_controls = true;
+ window->set_flag(Window::FLAG_BORDERLESS, true);
+ window->set_flag(Window::FLAG_ALWAYS_ON_TOP, true);
+ window->set_flag(Window::FLAG_MOUSE_PASSTHROUGH, true);
+ window->set_flag(Window::FLAG_NO_FOCUS, true);
+ window->set_flag(Window::FLAG_TRANSPARENT, true);
+ window->set_size(Size2i(0, 0));
+ p_base->get_root_parent_control()->add_child(window);
+
p_control->set_as_top_level(true);
- p_control->set_position(gui.last_mouse_pos);
- p_base->get_root_parent_control()->add_child(p_control); // Add as child of viewport.
p_control->move_to_front();
-
- gui.drag_preview_id = p_control->get_instance_id();
+ window->add_child(p_control);
+ window->visible = true;
+ gui.drag_preview_id = window->get_instance_id();
}
-Control *Viewport::_gui_get_drag_preview() {
+Window *Viewport::_gui_get_drag_preview() {
if (gui.drag_preview_id.is_null()) {
return nullptr;
} else {
- Control *drag_preview = ObjectDB::get_instance(gui.drag_preview_id);
+ Window *drag_preview = 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();
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 0ed3864b08ed..7a0d49fbe331 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -455,7 +455,7 @@ class Viewport : public Node {
void _gui_force_drag_cancel();
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);
- Control *_gui_get_drag_preview();
+ Window *_gui_get_drag_preview();
void _gui_remove_focus_for_window(Node *p_window);
void _gui_unfocus_control(Control *p_control);
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 5d1694973cf7..7f2ac5e3609a 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -380,6 +380,20 @@ Point2i Window::get_position() const {
return position;
}
+void Window::move_to_mouse() {
+ ERR_MAIN_THREAD_GUARD;
+ ERR_FAIL_COND(!is_inside_tree());
+
+ Point2i mouse_pos;
+ if (is_embedded() && !force_native) {
+ mouse_pos = get_embedder()->get_mouse_position();
+ set_position(mouse_pos + Point2i(10, 10));
+ } else {
+ mouse_pos = DisplayServer::get_singleton()->mouse_get_position();
+ set_position(mouse_pos + Point2i(10, 10));
+ }
+}
+
void Window::move_to_center() {
ERR_MAIN_THREAD_GUARD;
ERR_FAIL_COND(!is_inside_tree());
@@ -3153,6 +3167,7 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_position", "position"), &Window::set_position);
ClassDB::bind_method(D_METHOD("get_position"), &Window::get_position);
ClassDB::bind_method(D_METHOD("move_to_center"), &Window::move_to_center);
+ ClassDB::bind_method(D_METHOD("move_to_mouse"), &Window::move_to_mouse);
ClassDB::bind_method(D_METHOD("set_size", "size"), &Window::set_size);
ClassDB::bind_method(D_METHOD("get_size"), &Window::get_size);
diff --git a/scene/main/window.h b/scene/main/window.h
index 64d99a4ff821..d414be7cb982 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -312,6 +312,7 @@ class Window : public Viewport {
void set_position(const Point2i &p_position);
Point2i get_position() const;
void move_to_center();
+ void move_to_mouse();
void set_size(const Size2i &p_size);
Size2i get_size() const;
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index d40bdbbf4aa1..4a70a604212c 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -2402,7 +2402,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 4);
CHECK(text_edit->get_selection_origin_line() == 0);
CHECK(text_edit->get_selection_origin_column() == 0);
- SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 11).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ // Increased offset to ensure drag preview window moves before dropping, otherwise drop data would be consumed by drag preview window
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 11).get_center() + Point2i(20, 20), MouseButtonMask::LEFT, Key::NONE);
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(1, 11).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
CHECK(text_edit->get_text() == " test\ndrop here 'drag'");