summaryrefslogtreecommitdiffstats
path: root/scene/main
diff options
context:
space:
mode:
Diffstat (limited to 'scene/main')
-rw-r--r--scene/main/node.cpp54
-rw-r--r--scene/main/node.h31
-rw-r--r--scene/main/scene_tree.cpp73
-rw-r--r--scene/main/scene_tree.h16
-rw-r--r--scene/main/viewport.cpp314
-rw-r--r--scene/main/viewport.h27
-rw-r--r--scene/main/window.compat.inc48
-rw-r--r--scene/main/window.cpp68
-rw-r--r--scene/main/window.h6
9 files changed, 319 insertions, 318 deletions
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 0396f3ab4a..858fc2246b 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -138,6 +138,12 @@ void Node::_notification(int p_notification) {
get_tree()->nodes_in_tree_count++;
orphan_node_count--;
+
+ // Allow physics interpolated nodes to automatically reset when added to the tree
+ // (this is to save the user from doing this manually each time).
+ if (get_tree()->is_physics_interpolation_enabled()) {
+ _set_physics_interpolation_reset_requested(true);
+ }
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -437,6 +443,18 @@ void Node::_propagate_physics_interpolated(bool p_interpolated) {
data.blocked--;
}
+void Node::_propagate_physics_interpolation_reset_requested(bool p_requested) {
+ if (is_physics_interpolated()) {
+ data.physics_interpolation_reset_requested = p_requested;
+ }
+
+ data.blocked++;
+ for (KeyValue<StringName, Node *> &K : data.children) {
+ K.value->_propagate_physics_interpolation_reset_requested(p_requested);
+ }
+ data.blocked--;
+}
+
void Node::move_child(Node *p_child, int p_index) {
ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Moving child node positions inside the SceneTree is only allowed from the main thread. Use call_deferred(\"move_child\",child,index).");
ERR_FAIL_NULL(p_child);
@@ -739,7 +757,7 @@ void Node::rpc_config(const StringName &p_method, const Variant &p_config) {
}
}
-const Variant Node::get_node_rpc_config() const {
+Variant Node::get_rpc_config() const {
return data.rpc_config;
}
@@ -752,8 +770,7 @@ Error Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallErro
return ERR_INVALID_PARAMETER;
}
- Variant::Type type = p_args[0]->get_type();
- if (type != Variant::STRING_NAME && type != Variant::STRING) {
+ if (!p_args[0]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
@@ -781,8 +798,7 @@ Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallE
return ERR_INVALID_PARAMETER;
}
- Variant::Type type = p_args[1]->get_type();
- if (type != Variant::STRING_NAME && type != Variant::STRING) {
+ if (!p_args[1]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING_NAME;
@@ -890,15 +906,23 @@ void Node::set_physics_interpolation_mode(PhysicsInterpolationMode p_mode) {
}
// If swapping from interpolated to non-interpolated, use this as an extra means to cause a reset.
- if (is_physics_interpolated() && !interpolate) {
- reset_physics_interpolation();
+ if (is_physics_interpolated() && !interpolate && is_inside_tree()) {
+ propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
}
_propagate_physics_interpolated(interpolate);
}
void Node::reset_physics_interpolation() {
- propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
+ if (is_inside_tree()) {
+ propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
+
+ // If `reset_physics_interpolation()` is called explicitly by the user
+ // (e.g. from scripts) then we prevent deferred auto-resets taking place.
+ // The user is trusted to call reset in the right order, and auto-reset
+ // will interfere with their control of prev / curr, so should be turned off.
+ _propagate_physics_interpolation_reset_requested(false);
+ }
}
bool Node::_is_enabled() const {
@@ -2783,9 +2807,11 @@ Node *Node::duplicate(int p_flags) const {
ERR_THREAD_GUARD_V(nullptr);
Node *dupe = _duplicate(p_flags);
+ ERR_FAIL_NULL_V_MSG(dupe, nullptr, "Failed to duplicate node.");
+
_duplicate_properties(this, this, dupe, p_flags);
- if (dupe && (p_flags & DUPLICATE_SIGNALS)) {
+ if (p_flags & DUPLICATE_SIGNALS) {
_duplicate_signals(this, dupe);
}
@@ -2801,6 +2827,8 @@ Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, con
int flags = DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANTIATION | DUPLICATE_FROM_EDITOR;
Node *dupe = _duplicate(flags, &r_duplimap);
+ ERR_FAIL_NULL_V_MSG(dupe, nullptr, "Failed to duplicate node.");
+
_duplicate_properties(this, this, dupe, flags);
// This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated.
@@ -3406,7 +3434,7 @@ Variant Node::_call_deferred_thread_group_bind(const Variant **p_args, int p_arg
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ if (!p_args[0]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
@@ -3429,7 +3457,7 @@ Variant Node::_call_thread_safe_bind(const Variant **p_args, int p_argcount, Cal
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ if (!p_args[0]->is_string()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
@@ -3610,6 +3638,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
ClassDB::bind_method(D_METHOD("rpc_config", "method", "config"), &Node::rpc_config);
+ ClassDB::bind_method(D_METHOD("get_rpc_config"), &Node::get_rpc_config);
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description);
@@ -3825,6 +3854,9 @@ Node::Node() {
data.unhandled_key_input = false;
data.physics_interpolated = true;
+ data.physics_interpolation_reset_requested = false;
+ data.physics_interpolated_client_side = false;
+ data.use_identity_transform = false;
data.parent_owned = false;
data.in_constructor = true;
diff --git a/scene/main/node.h b/scene/main/node.h
index ee195ddef9..dc65513fca 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -199,7 +199,7 @@ private:
void *process_group = nullptr; // to avoid cyclic dependency
int multiplayer_authority = 1; // Server by default.
- Variant rpc_config;
+ Variant rpc_config = Dictionary();
// Variables used to properly sort the node when processing, ignored otherwise.
int process_priority = 0;
@@ -225,6 +225,21 @@ private:
// is switched on.
bool physics_interpolated : 1;
+ // We can auto-reset physics interpolation when e.g. adding a node for the first time.
+ bool physics_interpolation_reset_requested : 1;
+
+ // Most nodes need not be interpolated in the scene tree, physics interpolation
+ // is normally only needed in the RenderingServer. However if we need to read the
+ // interpolated transform of a node in the SceneTree, it is necessary to duplicate
+ // the interpolation logic client side, in order to prevent stalling the RenderingServer
+ // by reading back.
+ bool physics_interpolated_client_side : 1;
+
+ // For certain nodes (e.g. CPU particles in global mode)
+ // it can be useful to not send the instance transform to the
+ // RenderingServer, and specify the mesh in world space.
+ bool use_identity_transform : 1;
+
bool parent_owned : 1;
bool in_constructor : 1;
bool use_placeholder : 1;
@@ -263,6 +278,7 @@ private:
void _propagate_exit_tree();
void _propagate_after_exit_tree();
void _propagate_physics_interpolated(bool p_interpolated);
+ void _propagate_physics_interpolation_reset_requested(bool p_requested);
void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification);
void _propagate_groups_dirty();
Array _get_node_and_resource(const NodePath &p_path);
@@ -334,6 +350,15 @@ protected:
void _set_owner_nocheck(Node *p_owner);
void _set_name_nocheck(const StringName &p_name);
+ void _set_physics_interpolated_client_side(bool p_enable) { data.physics_interpolated_client_side = p_enable; }
+ bool _is_physics_interpolated_client_side() const { return data.physics_interpolated_client_side; }
+
+ void _set_physics_interpolation_reset_requested(bool p_enable) { data.physics_interpolation_reset_requested = p_enable; }
+ bool _is_physics_interpolation_reset_requested() const { return data.physics_interpolation_reset_requested; }
+
+ void _set_use_identity_transform(bool p_enable) { data.use_identity_transform = p_enable; }
+ bool _is_using_identity_transform() const { return data.use_identity_transform; }
+
//call from SceneTree
void _call_input(const Ref<InputEvent> &p_event);
void _call_shortcut_input(const Ref<InputEvent> &p_event);
@@ -632,7 +657,7 @@ public:
return binds;
}
- void replace_by(Node *p_node, bool p_keep_data = false);
+ void replace_by(Node *p_node, bool p_keep_groups = false);
void set_process_mode(ProcessMode p_mode);
ProcessMode get_process_mode() const;
@@ -692,7 +717,7 @@ public:
bool is_multiplayer_authority() const;
void rpc_config(const StringName &p_method, const Variant &p_config); // config a local method for RPC
- const Variant get_node_rpc_config() const;
+ Variant get_rpc_config() const;
template <typename... VarArgs>
Error rpc(const StringName &p_method, VarArgs... p_args);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index ced6d9aaa6..106130872d 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -59,6 +59,7 @@
#include "servers/navigation_server_3d.h"
#include "servers/physics_server_2d.h"
#ifndef _3D_DISABLED
+#include "scene/3d/node_3d.h"
#include "scene/resources/3d/world_3d.h"
#include "servers/physics_server_3d.h"
#endif // _3D_DISABLED
@@ -118,6 +119,29 @@ void SceneTreeTimer::release_connections() {
SceneTreeTimer::SceneTreeTimer() {}
+#ifndef _3D_DISABLED
+// This should be called once per physics tick, to make sure the transform previous and current
+// is kept up to date on the few Node3Ds that are using client side physics interpolation.
+void SceneTree::ClientPhysicsInterpolation::physics_process() {
+ for (SelfList<Node3D> *E = _node_3d_list.first(); E;) {
+ Node3D *node_3d = E->self();
+
+ SelfList<Node3D> *current = E;
+
+ // Get the next element here BEFORE we potentially delete one.
+ E = E->next();
+
+ // This will return false if the Node3D has timed out ..
+ // i.e. if get_global_transform_interpolated() has not been called
+ // for a few seconds, we can delete from the list to keep processing
+ // to a minimum.
+ if (!node_3d->update_client_physics_interpolation_data()) {
+ _node_3d_list.remove(current);
+ }
+ }
+}
+#endif
+
void SceneTree::tree_changed() {
emit_signal(tree_changed_name);
}
@@ -466,9 +490,31 @@ bool SceneTree::is_physics_interpolation_enabled() const {
return _physics_interpolation_enabled;
}
+#ifndef _3D_DISABLED
+void SceneTree::client_physics_interpolation_add_node_3d(SelfList<Node3D> *p_elem) {
+ // This ensures that _update_physics_interpolation_data() will be called at least once every
+ // physics tick, to ensure the previous and current transforms are kept up to date.
+ _client_physics_interpolation._node_3d_list.add(p_elem);
+}
+
+void SceneTree::client_physics_interpolation_remove_node_3d(SelfList<Node3D> *p_elem) {
+ _client_physics_interpolation._node_3d_list.remove(p_elem);
+}
+#endif
+
void SceneTree::iteration_prepare() {
if (_physics_interpolation_enabled) {
+ // Make sure any pending transforms from the last tick / frame
+ // are flushed before pumping the interpolation prev and currents.
+ flush_transform_notifications();
RenderingServer::get_singleton()->tick();
+
+#ifndef _3D_DISABLED
+ // Any objects performing client physics interpolation
+ // should be given an opportunity to keep their previous transforms
+ // up to date before each new physics tick.
+ _client_physics_interpolation.physics_process();
+#endif
}
}
@@ -503,6 +549,14 @@ bool SceneTree::physics_process(double p_time) {
return _quit;
}
+void SceneTree::iteration_end() {
+ // When physics interpolation is active, we want all pending transforms
+ // to be flushed to the RenderingServer before finishing a physics tick.
+ if (_physics_interpolation_enabled) {
+ flush_transform_notifications();
+ }
+}
+
bool SceneTree::process(double p_time) {
if (MainLoop::process(p_time)) {
_quit = true;
@@ -570,6 +624,10 @@ bool SceneTree::process(double p_time) {
#endif // _3D_DISABLED
#endif // TOOLS_ENABLED
+ if (_physics_interpolation_enabled) {
+ RenderingServer::get_singleton()->pre_draw(true);
+ }
+
return _quit;
}
@@ -1257,8 +1315,8 @@ void SceneTree::_call_group_flags(const Variant **p_args, int p_argcount, Callab
ERR_FAIL_COND(p_argcount < 3);
ERR_FAIL_COND(!p_args[0]->is_num());
- ERR_FAIL_COND(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING);
- ERR_FAIL_COND(p_args[2]->get_type() != Variant::STRING_NAME && p_args[2]->get_type() != Variant::STRING);
+ ERR_FAIL_COND(!p_args[1]->is_string());
+ ERR_FAIL_COND(!p_args[2]->is_string());
int flags = *p_args[0];
StringName group = *p_args[1];
@@ -1271,8 +1329,8 @@ void SceneTree::_call_group(const Variant **p_args, int p_argcount, Callable::Ca
r_error.error = Callable::CallError::CALL_OK;
ERR_FAIL_COND(p_argcount < 2);
- ERR_FAIL_COND(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING);
- ERR_FAIL_COND(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING);
+ ERR_FAIL_COND(!p_args[0]->is_string());
+ ERR_FAIL_COND(!p_args[1]->is_string());
StringName group = *p_args[0];
StringName method = *p_args[1];
@@ -1761,6 +1819,13 @@ SceneTree::SceneTree() {
set_physics_interpolation_enabled(GLOBAL_DEF("physics/common/physics_interpolation", false));
+ // Always disable jitter fix if physics interpolation is enabled -
+ // Jitter fix will interfere with interpolation, and is not necessary
+ // when interpolation is active.
+ if (is_physics_interpolation_enabled()) {
+ Engine::get_singleton()->set_physics_jitter_fix(0);
+ }
+
// Initialize network state.
set_multiplayer(MultiplayerAPI::create_default_interface());
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 6f0a61ec51..7e44541105 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -41,6 +41,9 @@
class PackedScene;
class Node;
+#ifndef _3D_DISABLED
+class Node3D;
+#endif
class Window;
class Material;
class Mesh;
@@ -120,6 +123,13 @@ private:
bool changed = false;
};
+#ifndef _3D_DISABLED
+ struct ClientPhysicsInterpolation {
+ SelfList<Node3D>::List _node_3d_list;
+ void physics_process();
+ } _client_physics_interpolation;
+#endif
+
Window *root = nullptr;
double physics_process_time = 0.0;
@@ -315,6 +325,7 @@ public:
virtual void iteration_prepare() override;
virtual bool physics_process(double p_time) override;
+ virtual void iteration_end() override;
virtual bool process(double p_time) override;
virtual void finalize() override;
@@ -423,6 +434,11 @@ public:
void set_physics_interpolation_enabled(bool p_enabled);
bool is_physics_interpolation_enabled() const;
+#ifndef _3D_DISABLED
+ void client_physics_interpolation_add_node_3d(SelfList<Node3D> *p_elem);
+ void client_physics_interpolation_remove_node_3d(SelfList<Node3D> *p_elem);
+#endif
+
SceneTree();
~SceneTree();
};
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 1302e3c53e..a4e27a3d16 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -655,9 +655,7 @@ void Viewport::_notification(int p_what) {
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
_gui_cancel_tooltip();
_drop_physics_mouseover();
- if (gui.mouse_focus && !gui.forced_mouse_focus) {
- _drop_mouse_focus();
- }
+ _drop_mouse_focus();
// When the window focus changes, we want to end mouse_focus, but
// not the mouse_over. Note: The OS will trigger a separate mouse
// exit event if the change in focus results in the mouse exiting
@@ -884,6 +882,10 @@ void Viewport::_process_picking() {
}
#ifndef _3D_DISABLED
+ if (physics_object_picking_first_only && is_input_handled()) {
+ continue;
+ }
+
CollisionObject3D *capture_object = nullptr;
if (physics_object_capture.is_valid()) {
capture_object = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_capture));
@@ -1233,14 +1235,16 @@ Ref<World2D> Viewport::find_world_2d() const {
}
}
-void Viewport::_propagate_viewport_notification(Node *p_node, int p_what) {
+void Viewport::_propagate_drag_notification(Node *p_node, int p_what) {
+ // Send notification to p_node and all children and descendant nodes of p_node, except to SubViewports which are not children of a SubViewportContainer.
p_node->notification(p_what);
+ bool is_svc = Object::cast_to<SubViewportContainer>(p_node);
for (int i = 0; i < p_node->get_child_count(); i++) {
Node *c = p_node->get_child(i);
- if (Object::cast_to<Viewport>(c)) {
+ if (!is_svc && Object::cast_to<SubViewport>(c)) {
continue;
}
- _propagate_viewport_notification(c, p_what);
+ Viewport::_propagate_drag_notification(c, p_what);
}
}
@@ -1343,7 +1347,7 @@ Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
Vector2 Viewport::get_mouse_position() const {
ERR_READ_THREAD_GUARD_V(Vector2());
- if (!is_directly_attached_to_screen()) {
+ if (get_section_root_viewport() != SceneTree::get_singleton()->get_root()) {
// Rely on the most recent mouse coordinate from an InputEventMouse in push_input.
// In this case get_screen_transform is not applicable, because it is ambiguous.
return gui.last_mouse_pos;
@@ -1511,6 +1515,7 @@ void Viewport::_gui_show_tooltip() {
r.size *= win_scale;
vr = window->get_usable_parent_rect();
}
+ r.size = r.size.ceil();
r.size = r.size.min(panel->get_max_size());
if (r.size.x + r.position.x > vr.size.x + vr.position.x) {
@@ -1545,8 +1550,7 @@ void Viewport::_gui_show_tooltip() {
gui.tooltip_popup->child_controls_changed();
}
-bool Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_input) {
- bool stopped = false;
+void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_input) {
Ref<InputEvent> ev = p_input;
// Returns true if an event should be impacted by a control's mouse filter.
@@ -1570,19 +1574,15 @@ bool Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
if (!control->is_inside_tree() || control->is_set_as_top_level()) {
break;
}
- if (gui.key_event_accepted) {
- stopped = true;
- break;
- }
if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP && is_pointer_event && !(is_scroll_event && control->data.force_pass_scroll_events)) {
// Mouse, ScreenDrag and ScreenTouch events are stopped by default with MOUSE_FILTER_STOP, unless we have a scroll event and force_pass_scroll_events set to true
- stopped = true;
+ set_input_as_handled();
break;
}
}
if (is_input_handled()) {
- // Break after Physics Picking in SubViewport.
+ // Break when the event is set to handled in a child Control node or after physics picking in SubViewport.
break;
}
@@ -1593,7 +1593,6 @@ bool Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
ev = ev->xformed_by(ci->get_transform()); // Transform event upwards.
ci = ci->get_parent_item();
}
- return stopped;
}
void Viewport::_gui_call_notification(Control *p_control, int p_what) {
@@ -1698,14 +1697,15 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_
}
bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check) {
- // Attempt grab, try parent controls too.
+ // Attempt drop, try parent controls too.
CanvasItem *ci = p_at_control;
+ Viewport *section_root = get_section_root_viewport();
while (ci) {
Control *control = Object::cast_to<Control>(ci);
if (control) {
- if (control->can_drop_data(p_at_pos, gui.drag_data)) {
+ if (control->can_drop_data(p_at_pos, section_root->gui.drag_data)) {
if (!p_just_check) {
- control->drop_data(p_at_pos, gui.drag_data);
+ control->drop_data(p_at_pos, section_root->gui.drag_data);
}
return true;
@@ -1733,8 +1733,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- gui.key_event_accepted = false;
-
Point2 mpos = mb->get_position();
if (mb->is_pressed()) {
MouseButtonMask button_mask = mouse_button_to_mask(mb->get_button_index());
@@ -1777,7 +1775,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Control *control = Object::cast_to<Control>(ci);
if (control) {
if (control->get_focus_mode() != Control::FOCUS_NONE) {
- if (control != gui.key_focus) {
+ // Grabbing unhovered focus can cause issues when mouse is dragged
+ // with another button held down.
+ if (control != gui.key_focus && gui.mouse_over_hierarchy.has(control)) {
control->grab_focus();
}
break;
@@ -1796,20 +1796,19 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- bool stopped = gui.mouse_focus && gui.mouse_focus->can_process() && _gui_call_input(gui.mouse_focus, mb);
- if (stopped) {
- set_input_as_handled();
+ if (gui.mouse_focus && gui.mouse_focus->can_process()) {
+ _gui_call_input(gui.mouse_focus, mb);
}
if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
// Alternate drop use (when using force_drag(), as proposed by #5342).
- _perform_drop(gui.mouse_focus, pos);
+ _perform_drop(gui.mouse_focus);
}
_gui_cancel_tooltip();
} else {
if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
- _perform_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos);
+ _perform_drop(gui.drag_mouse_over);
}
gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask.
@@ -1830,23 +1829,21 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
// as the release will never be received otherwise.
if (gui.mouse_focus_mask.is_empty()) {
gui.mouse_focus = nullptr;
- gui.forced_mouse_focus = false;
}
- bool stopped = mouse_focus && mouse_focus->can_process() && _gui_call_input(mouse_focus, mb);
- if (stopped) {
- set_input_as_handled();
+ if (mouse_focus && mouse_focus->can_process()) {
+ _gui_call_input(mouse_focus, mb);
}
}
}
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- gui.key_event_accepted = false;
Point2 mpos = mm->get_position();
// Drag & drop.
- if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
+ Viewport *section_root = get_section_root_viewport();
+ if (!gui.drag_attempted && gui.mouse_focus && section_root && !section_root->gui.global_dragging && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
gui.drag_accum += mm->get_relative();
float len = gui.drag_accum.length();
if (len > 10) {
@@ -1855,12 +1852,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
while (ci) {
Control *control = Object::cast_to<Control>(ci);
if (control) {
- gui.dragging = true;
- gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum));
- if (gui.drag_data.get_type() != Variant::NIL) {
+ section_root->gui.global_dragging = true;
+ section_root->gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum));
+ if (section_root->gui.drag_data.get_type() != Variant::NIL) {
gui.mouse_focus = nullptr;
- gui.forced_mouse_focus = false;
gui.mouse_focus_mask.clear();
+ gui.dragging = true;
break;
} else {
Control *drag_preview = _gui_get_drag_preview();
@@ -1869,7 +1866,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
memdelete(drag_preview);
gui.drag_preview_id = ObjectID();
}
- gui.dragging = false;
+ section_root->gui.global_dragging = false;
}
if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
@@ -1887,7 +1884,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.drag_attempted = true;
if (gui.dragging) {
- _propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN);
+ Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_BEGIN);
}
}
}
@@ -1932,7 +1929,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- if (!is_tooltip_shown && over->can_process()) {
+ // If the tooltip timer isn't running, start it.
+ // Otherwise, only reset the timer if the mouse has moved more than 5 pixels.
+ if (!is_tooltip_shown && over->can_process() &&
+ (gui.tooltip_timer.is_null() ||
+ Math::is_zero_approx(gui.tooltip_timer->get_time_left()) ||
+ mm->get_relative().length() > 5.0)) {
if (gui.tooltip_timer.is_valid()) {
gui.tooltip_timer->release_connections();
gui.tooltip_timer = Ref<SceneTreeTimer>();
@@ -1973,112 +1975,35 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
ds_cursor_shape = (DisplayServer::CursorShape)cursor_shape;
- bool stopped = over->can_process() && _gui_call_input(over, mm);
- if (stopped) {
- set_input_as_handled();
+ if (over->can_process()) {
+ _gui_call_input(over, mm);
}
}
if (gui.dragging) {
- // Handle drag & drop.
+ // Handle drag & drop. This happens in the viewport where dragging started.
Control *drag_preview = _gui_get_drag_preview();
if (drag_preview) {
drag_preview->set_position(mpos);
}
- gui.drag_mouse_over = over;
- gui.drag_mouse_over_pos = Vector2();
-
- // Find the window this is above of.
- // See if there is an embedder.
- Viewport *embedder = nullptr;
- Vector2 viewport_pos;
-
- if (is_embedding_subwindows()) {
- embedder = this;
- viewport_pos = mpos;
- } else {
- // Not an embedder, but may be a subwindow of an embedder.
- Window *w = Object::cast_to<Window>(this);
- if (w) {
- if (w->is_embedded()) {
- embedder = w->get_embedder();
-
- viewport_pos = get_final_transform().xform(mpos) + w->get_position(); // To parent coords.
- }
- }
- }
-
- Viewport *viewport_under = nullptr;
-
- if (embedder) {
- // Use embedder logic.
-
- for (int i = embedder->gui.sub_windows.size() - 1; i >= 0; i--) {
- Window *sw = embedder->gui.sub_windows[i].window;
- Rect2 swrect = Rect2i(sw->get_position(), sw->get_size());
- if (!sw->get_flag(Window::FLAG_BORDERLESS)) {
- int title_height = sw->theme_cache.title_height;
- swrect.position.y -= title_height;
- swrect.size.y += title_height;
- }
-
- if (swrect.has_point(viewport_pos)) {
- viewport_under = sw;
- viewport_pos -= sw->get_position();
- }
- }
-
- if (!viewport_under) {
- // Not in a subwindow, likely in embedder.
- viewport_under = embedder;
- }
- } else {
- // Use DisplayServer logic.
- Vector2i screen_mouse_pos = DisplayServer::get_singleton()->mouse_get_position();
-
- DisplayServer::WindowID window_id = DisplayServer::get_singleton()->get_window_at_screen_position(screen_mouse_pos);
-
- if (window_id != DisplayServer::INVALID_WINDOW_ID) {
- ObjectID object_under = DisplayServer::get_singleton()->window_get_attached_instance_id(window_id);
-
- if (object_under != ObjectID()) { // Fetch window.
- Window *w = Object::cast_to<Window>(ObjectDB::get_instance(object_under));
- if (w) {
- viewport_under = w;
- viewport_pos = w->get_final_transform().affine_inverse().xform(screen_mouse_pos - w->get_position());
- }
- }
- }
- }
-
- if (viewport_under) {
- if (viewport_under != this) {
- Transform2D ai = viewport_under->get_final_transform().affine_inverse();
- viewport_pos = ai.xform(viewport_pos);
+ gui.drag_mouse_over = section_root->gui.target_control;
+ if (gui.drag_mouse_over) {
+ if (!_gui_drop(gui.drag_mouse_over, gui.drag_mouse_over->get_local_mouse_position(), true)) {
+ gui.drag_mouse_over = nullptr;
}
- // Find control under at position.
- gui.drag_mouse_over = viewport_under->gui_find_control(viewport_pos);
if (gui.drag_mouse_over) {
- Transform2D localizer = gui.drag_mouse_over->get_global_transform_with_canvas().affine_inverse();
- gui.drag_mouse_over_pos = localizer.xform(viewport_pos);
-
- bool can_drop = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, true);
-
- if (!can_drop) {
- ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN;
- } else {
- ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP;
- }
+ ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP;
+ } else {
+ ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN;
}
-
- } else {
- gui.drag_mouse_over = nullptr;
}
}
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE) && !Object::cast_to<SubViewportContainer>(over)) {
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE) && (gui.dragging || (!section_root->gui.global_dragging && !Object::cast_to<SubViewportContainer>(over)))) {
+ // If dragging is active, then set the cursor shape only from the Viewport where dragging started.
+ // If dragging is inactive, then set the cursor shape only when not over a SubViewportContainer.
DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
}
}
@@ -2091,20 +2016,15 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Control *over = gui_find_control(pos);
if (over) {
gui.touch_focus[touch_index] = over->get_instance_id();
- bool stopped = false;
if (over->can_process()) {
touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
touch_event->set_position(pos);
- stopped = _gui_call_input(over, touch_event);
- }
- if (stopped) {
- set_input_as_handled();
+ _gui_call_input(over, touch_event);
}
return;
}
} else {
- bool stopped = false;
ObjectID control_id = gui.touch_focus[touch_index];
Control *over = control_id.is_valid() ? Object::cast_to<Control>(ObjectDB::get_instance(control_id)) : nullptr;
if (over && over->can_process()) {
@@ -2112,10 +2032,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
touch_event->set_position(pos);
- stopped = _gui_call_input(over, touch_event);
- }
- if (stopped) {
- set_input_as_handled();
+ _gui_call_input(over, touch_event);
}
gui.touch_focus.erase(touch_index);
return;
@@ -2124,23 +2041,17 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Ref<InputEventGesture> gesture_event = p_event;
if (gesture_event.is_valid()) {
- gui.key_event_accepted = false;
-
_gui_cancel_tooltip();
Size2 pos = gesture_event->get_position();
Control *over = gui_find_control(pos);
if (over) {
- bool stopped = false;
if (over->can_process()) {
gesture_event = gesture_event->xformed_by(Transform2D()); // Make a copy.
pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
gesture_event->set_position(pos);
- stopped = _gui_call_input(over, gesture_event);
- }
- if (stopped) {
- set_input_as_handled();
+ _gui_call_input(over, gesture_event);
}
return;
}
@@ -2155,7 +2066,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
over = gui_find_control(drag_event->get_position());
}
if (over) {
- bool stopped = false;
if (over->can_process()) {
Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse();
Size2 pos = localizer.xform(drag_event->get_position());
@@ -2168,24 +2078,21 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
drag_event->set_relative(rel);
drag_event->set_position(pos);
- stopped = _gui_call_input(over, drag_event);
+ _gui_call_input(over, drag_event);
}
- if (stopped) {
- set_input_as_handled();
- }
return;
}
}
if (mm.is_null() && mb.is_null() && p_event->is_action_type()) {
- if (gui.dragging && p_event->is_action_pressed("ui_cancel") && Input::get_singleton()->is_action_just_pressed("ui_cancel")) {
+ if (gui.dragging && p_event->is_action_pressed(SNAME("ui_cancel")) && Input::get_singleton()->is_action_just_pressed(SNAME("ui_cancel"))) {
_perform_drop();
set_input_as_handled();
return;
}
- if (p_event->is_action_pressed("ui_cancel")) {
+ if (p_event->is_action_pressed(SNAME("ui_cancel"))) {
// Cancel tooltip timer or hide tooltip when pressing Escape (this is standard behavior in most applications).
_gui_cancel_tooltip();
if (gui.tooltip_popup) {
@@ -2202,13 +2109,11 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (gui.key_focus) {
- gui.key_event_accepted = false;
if (gui.key_focus->can_process()) {
gui.key_focus->_call_gui_input(p_event);
}
- if (gui.key_event_accepted) {
- set_input_as_handled();
+ if (is_input_handled()) {
return;
}
}
@@ -2222,51 +2127,51 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (joypadmotion_event.is_valid()) {
Input *input = Input::get_singleton();
- if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) {
+ if (p_event->is_action_pressed(SNAME("ui_focus_next")) && input->is_action_just_pressed(SNAME("ui_focus_next"))) {
next = from->find_next_valid_focus();
}
- if (p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) {
+ if (p_event->is_action_pressed(SNAME("ui_focus_prev")) && input->is_action_just_pressed(SNAME("ui_focus_prev"))) {
next = from->find_prev_valid_focus();
}
- if (p_event->is_action_pressed("ui_up") && input->is_action_just_pressed("ui_up")) {
+ if (p_event->is_action_pressed(SNAME("ui_up")) && input->is_action_just_pressed(SNAME("ui_up"))) {
next = from->_get_focus_neighbor(SIDE_TOP);
}
- if (p_event->is_action_pressed("ui_left") && input->is_action_just_pressed("ui_left")) {
+ if (p_event->is_action_pressed(SNAME("ui_left")) && input->is_action_just_pressed(SNAME("ui_left"))) {
next = from->_get_focus_neighbor(SIDE_LEFT);
}
- if (p_event->is_action_pressed("ui_right") && input->is_action_just_pressed("ui_right")) {
+ if (p_event->is_action_pressed(SNAME("ui_right")) && input->is_action_just_pressed(SNAME("ui_right"))) {
next = from->_get_focus_neighbor(SIDE_RIGHT);
}
- if (p_event->is_action_pressed("ui_down") && input->is_action_just_pressed("ui_down")) {
+ if (p_event->is_action_pressed(SNAME("ui_down")) && input->is_action_just_pressed(SNAME("ui_down"))) {
next = from->_get_focus_neighbor(SIDE_BOTTOM);
}
} else {
- if (p_event->is_action_pressed("ui_focus_next", true, true)) {
+ if (p_event->is_action_pressed(SNAME("ui_focus_next"), true, true)) {
next = from->find_next_valid_focus();
}
- if (p_event->is_action_pressed("ui_focus_prev", true, true)) {
+ if (p_event->is_action_pressed(SNAME("ui_focus_prev"), true, true)) {
next = from->find_prev_valid_focus();
}
- if (p_event->is_action_pressed("ui_up", true, true)) {
+ if (p_event->is_action_pressed(SNAME("ui_up"), true, true)) {
next = from->_get_focus_neighbor(SIDE_TOP);
}
- if (p_event->is_action_pressed("ui_left", true, true)) {
+ if (p_event->is_action_pressed(SNAME("ui_left"), true, true)) {
next = from->_get_focus_neighbor(SIDE_LEFT);
}
- if (p_event->is_action_pressed("ui_right", true, true)) {
+ if (p_event->is_action_pressed(SNAME("ui_right"), true, true)) {
next = from->_get_focus_neighbor(SIDE_RIGHT);
}
- if (p_event->is_action_pressed("ui_down", true, true)) {
+ if (p_event->is_action_pressed(SNAME("ui_down"), true, true)) {
next = from->_get_focus_neighbor(SIDE_BOTTOM);
}
}
@@ -2278,10 +2183,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
-void Viewport::_perform_drop(Control *p_control, Point2 p_pos) {
+void Viewport::_perform_drop(Control *p_control) {
// Without any arguments, simply cancel Drag and Drop.
if (p_control) {
- gui.drag_successful = _gui_drop(p_control, p_pos, false);
+ gui.drag_successful = _gui_drop(p_control, p_control->get_local_mouse_position(), false);
} else {
gui.drag_successful = false;
}
@@ -2292,10 +2197,12 @@ void Viewport::_perform_drop(Control *p_control, Point2 p_pos) {
gui.drag_preview_id = ObjectID();
}
- gui.drag_data = Variant();
+ Viewport *section_root = get_section_root_viewport();
+ section_root->gui.drag_data = Variant();
gui.dragging = false;
+ section_root->gui.global_dragging = false;
gui.drag_mouse_over = nullptr;
- _propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
+ Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_END);
// Display the new cursor shape instantly.
update_mouse_cursor_state();
}
@@ -2325,14 +2232,16 @@ void Viewport::_gui_force_drag(Control *p_base, const Variant &p_data, Control *
ERR_FAIL_COND_MSG(p_data.get_type() == Variant::NIL, "Drag data must be a value.");
gui.dragging = true;
- gui.drag_data = p_data;
+ Viewport *section_root = get_section_root_viewport();
+ section_root->gui.global_dragging = true;
+ section_root->gui.drag_data = p_data;
gui.mouse_focus = nullptr;
gui.mouse_focus_mask.clear();
if (p_control) {
_gui_set_drag_preview(p_base, p_control);
}
- _propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN);
+ Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_BEGIN);
}
void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) {
@@ -2397,7 +2306,6 @@ void Viewport::_gui_hide_control(Control *p_control) {
void Viewport::_gui_remove_control(Control *p_control) {
if (gui.mouse_focus == p_control) {
gui.mouse_focus = nullptr;
- gui.forced_mouse_focus = false;
gui.mouse_focus_mask.clear();
}
if (gui.key_focus == p_control) {
@@ -2553,7 +2461,6 @@ void Viewport::_gui_control_grab_focus(Control *p_control) {
}
void Viewport::_gui_accept_event() {
- gui.key_event_accepted = true;
if (is_inside_tree()) {
set_input_as_handled();
}
@@ -2563,9 +2470,12 @@ void Viewport::_drop_mouse_focus() {
Control *c = gui.mouse_focus;
BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
gui.mouse_focus = nullptr;
- gui.forced_mouse_focus = false;
gui.mouse_focus_mask.clear();
+ if (!c) {
+ return;
+ }
+
for (int i = 0; i < 3; i++) {
if ((int)mask & (1 << i)) {
Ref<InputEventMouseButton> mb;
@@ -3014,6 +2924,7 @@ void Viewport::_update_mouse_over() {
}
void Viewport::_update_mouse_over(Vector2 p_pos) {
+ gui.last_mouse_pos = p_pos; // Necessary, because mouse cursor can be over Viewports that are not reached by the InputEvent.
// Look for embedded windows at mouse position.
if (is_embedding_subwindows()) {
for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
@@ -3065,6 +2976,7 @@ void Viewport::_update_mouse_over(Vector2 p_pos) {
// Look for Controls at mouse position.
Control *over = gui_find_control(p_pos);
+ get_section_root_viewport()->gui.target_control = over;
bool notify_embedded_viewports = false;
if (over != gui.mouse_over || (!over && !gui.mouse_over_hierarchy.is_empty())) {
// Find the common ancestor of `gui.mouse_over` and `over`.
@@ -3187,6 +3099,10 @@ void Viewport::_drop_mouse_over(Control *p_until_control) {
if (gui.mouse_over && gui.mouse_over->is_inside_tree()) {
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
}
+ Viewport *section_root = get_section_root_viewport();
+ if (section_root && section_root->gui.target_control == gui.mouse_over) {
+ section_root->gui.target_control = nullptr;
+ }
gui.mouse_over = nullptr;
// Send Mouse Exit notifications to children first. Don't send to p_until_control or above.
@@ -3395,7 +3311,7 @@ bool Viewport::is_input_disabled() const {
Variant Viewport::gui_get_drag_data() const {
ERR_READ_THREAD_GUARD_V(Variant());
- return gui.drag_data;
+ return get_section_root_viewport()->gui.drag_data;
}
PackedStringArray Viewport::get_configuration_warnings() const {
@@ -3589,7 +3505,7 @@ bool Viewport::is_snap_2d_vertices_to_pixel_enabled() const {
bool Viewport::gui_is_dragging() const {
ERR_READ_THREAD_GUARD_V(false);
- return gui.dragging;
+ return get_section_root_viewport()->gui.global_dragging;
}
bool Viewport::gui_is_drag_successful() const {
@@ -3892,23 +3808,6 @@ Rect2i Viewport::subwindow_get_popup_safe_rect(Window *p_window) const {
return gui.sub_windows[index].parent_safe_rect;
}
-void Viewport::pass_mouse_focus_to(Viewport *p_viewport, Control *p_control) {
- ERR_MAIN_THREAD_GUARD;
- ERR_FAIL_NULL(p_viewport);
- ERR_FAIL_NULL(p_control);
-
- if (gui.mouse_focus) {
- p_viewport->gui.mouse_focus = p_control;
- p_viewport->gui.mouse_focus_mask = gui.mouse_focus_mask;
- p_viewport->gui.key_focus = p_control;
- p_viewport->gui.forced_mouse_focus = true;
-
- gui.mouse_focus = nullptr;
- gui.forced_mouse_focus = false;
- gui.mouse_focus_mask.clear();
- }
-}
-
void Viewport::set_sdf_oversize(SDFOversize p_sdf_oversize) {
ERR_MAIN_THREAD_GUARD;
ERR_FAIL_INDEX(p_sdf_oversize, SDF_OVERSIZE_MAX);
@@ -4704,6 +4603,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Viewport::warp_mouse);
ClassDB::bind_method(D_METHOD("update_mouse_cursor_state"), &Viewport::update_mouse_cursor_state);
+ ClassDB::bind_method(D_METHOD("gui_cancel_drag"), &Viewport::gui_cancel_drag);
ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data);
ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging);
ClassDB::bind_method(D_METHOD("gui_is_drag_successful"), &Viewport::gui_is_drag_successful);
@@ -5024,6 +4924,13 @@ Viewport::Viewport() {
#endif // _3D_DISABLED
set_sdf_oversize(sdf_oversize); // Set to server.
+
+ // Physics interpolation mode for viewports is a special case.
+ // Typically viewports will be housed within Controls,
+ // and Controls default to PHYSICS_INTERPOLATION_MODE_OFF.
+ // Viewports can thus inherit physics interpolation OFF, which is unexpected.
+ // Setting to ON allows each viewport to have a fresh interpolation state.
+ set_physics_interpolation_mode(Node::PHYSICS_INTERPOLATION_MODE_ON);
}
Viewport::~Viewport() {
@@ -5031,6 +4938,9 @@ Viewport::~Viewport() {
for (ViewportTexture *E : viewport_textures) {
E->vp = nullptr;
}
+ if (world_2d.is_valid()) {
+ world_2d->remove_viewport(this);
+ }
ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(viewport);
}
@@ -5062,6 +4972,7 @@ void SubViewport::_internal_set_size(const Size2i &p_size, bool p_force) {
if (c) {
c->update_minimum_size();
+ c->queue_redraw();
}
}
@@ -5153,9 +5064,12 @@ Transform2D SubViewport::get_popup_base_transform() const {
return c->get_screen_transform() * container_transform * get_final_transform();
}
-bool SubViewport::is_directly_attached_to_screen() const {
- // SubViewports, that are used as Textures are not considered to be directly attached to screen.
- return Object::cast_to<SubViewportContainer>(get_parent()) && get_parent()->get_viewport() && get_parent()->get_viewport()->is_directly_attached_to_screen();
+Viewport *SubViewport::get_section_root_viewport() const {
+ if (Object::cast_to<SubViewportContainer>(get_parent()) && get_parent()->get_viewport()) {
+ return get_parent()->get_viewport()->get_section_root_viewport();
+ }
+ SubViewport *vp = const_cast<SubViewport *>(this);
+ return vp;
}
bool SubViewport::is_attached_in_viewport() const {
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 0d31c07e57..edacee2d88 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -281,7 +281,7 @@ private:
bool disable_3d = false;
- void _propagate_viewport_notification(Node *p_node, int p_what);
+ static void _propagate_drag_notification(Node *p_node, int p_what);
void _update_global_transform();
@@ -349,9 +349,7 @@ private:
Ref<Texture2D> vrs_texture;
struct GUI {
- bool forced_mouse_focus = false; //used for menu buttons
bool mouse_in_viewport = false;
- bool key_event_accepted = false;
HashMap<int, ObjectID> touch_focus;
Control *mouse_focus = nullptr;
Control *mouse_click_grabber = nullptr;
@@ -363,7 +361,6 @@ private:
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.
Control *drag_mouse_over = nullptr;
- Vector2 drag_mouse_over_pos;
Control *tooltip_control = nullptr;
Window *tooltip_popup = nullptr;
Label *tooltip_label = nullptr;
@@ -372,7 +369,7 @@ private:
Point2 last_mouse_pos;
Point2 drag_accum;
bool drag_attempted = false;
- Variant drag_data;
+ Variant drag_data; // Only used in root-Viewport and SubViewports, that are not children of a SubViewportContainer.
ObjectID drag_preview_id;
Ref<SceneTreeTimer> tooltip_timer;
double tooltip_delay = 0.0;
@@ -380,8 +377,10 @@ private:
List<Control *> roots;
HashSet<ObjectID> canvas_parents_with_dirty_order;
int canvas_sort_index = 0; //for sorting items with canvas as root
- bool dragging = false;
+ bool dragging = false; // Is true in the viewport in which dragging started while dragging is active.
+ bool global_dragging = false; // Is true while dragging is active. Only used in root-Viewport and SubViewports that are not children of a SubViewportContainer.
bool drag_successful = false;
+ Control *target_control = nullptr; // Control that the mouse is over in the innermost nested Viewport. Only used in root-Viewport and SubViewports, that are not children of a SubViewportContainer.
bool embed_subwindows_hint = false;
Window *subwindow_focused = nullptr;
@@ -402,14 +401,14 @@ private:
bool disable_input = false;
- bool _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input);
+ void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input);
void _gui_call_notification(Control *p_control, int p_what);
void _gui_sort_roots();
Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform);
void _gui_input_event(Ref<InputEvent> p_event);
- void _perform_drop(Control *p_control = nullptr, Point2 p_pos = Point2());
+ void _perform_drop(Control *p_control = nullptr);
void _gui_cleanup_internal_state(Ref<InputEvent> p_event);
void _push_unhandled_input_internal(const Ref<InputEvent> &p_event);
@@ -662,8 +661,6 @@ public:
Viewport *get_parent_viewport() const;
Window *get_base_window() const;
- void pass_mouse_focus_to(Viewport *p_viewport, Control *p_control);
-
void set_canvas_cull_mask(uint32_t p_layers);
uint32_t get_canvas_cull_mask() const;
@@ -675,9 +672,9 @@ public:
Transform2D get_screen_transform() const;
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const;
virtual Transform2D get_popup_base_transform() const { return Transform2D(); }
- virtual bool is_directly_attached_to_screen() const { return false; };
- virtual bool is_attached_in_viewport() const { return false; };
- virtual bool is_sub_viewport() const { return false; };
+ virtual Viewport *get_section_root_viewport() const { return nullptr; }
+ virtual bool is_attached_in_viewport() const { return false; }
+ virtual bool is_sub_viewport() const { return false; }
private:
// 2D audio, camera, and physics. (don't put World2D here because World2D is needed for Control nodes).
@@ -839,9 +836,9 @@ public:
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
virtual Transform2D get_popup_base_transform() const override;
- virtual bool is_directly_attached_to_screen() const override;
+ virtual Viewport *get_section_root_viewport() const override;
virtual bool is_attached_in_viewport() const override;
- virtual bool is_sub_viewport() const override { return true; };
+ virtual bool is_sub_viewport() const override { return true; }
void _validate_property(PropertyInfo &p_property) const;
SubViewport();
diff --git a/scene/main/window.compat.inc b/scene/main/window.compat.inc
deleted file mode 100644
index 0bba01bb1b..0000000000
--- a/scene/main/window.compat.inc
+++ /dev/null
@@ -1,48 +0,0 @@
-/**************************************************************************/
-/* window.compat.inc */
-/**************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/**************************************************************************/
-/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/**************************************************************************/
-
-#ifndef DISABLE_DEPRECATED
-
-void Window::_bind_compatibility_methods() {
- ClassDB::bind_compatibility_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Window::get_theme_icon, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Window::get_theme_stylebox, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("get_theme_font", "name", "theme_type"), &Window::get_theme_font, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Window::get_theme_font_size, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("get_theme_color", "name", "theme_type"), &Window::get_theme_color, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Window::get_theme_constant, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Window::has_theme_icon, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Window::has_theme_stylebox, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("has_theme_font", "name", "theme_type"), &Window::has_theme_font, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Window::has_theme_font_size, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL(""));
-}
-
-#endif
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index e5873f0e9a..803ce89bc9 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -29,12 +29,11 @@
/**************************************************************************/
#include "window.h"
-#include "window.compat.inc"
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/input/shortcut.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/variant/variant_parser.h"
#include "scene/gui/control.h"
#include "scene/theme/theme_db.h"
@@ -810,7 +809,7 @@ void Window::update_mouse_cursor_state() {
mm->set_position(pos);
mm->set_global_position(xform.xform(pos));
mm->set_device(InputEvent::DEVICE_ID_INTERNAL);
- push_input(mm);
+ push_input(mm, true);
}
void Window::show() {
@@ -1224,6 +1223,7 @@ void Window::_update_viewport_size() {
TS->font_set_global_oversampling(font_oversampling);
if (!ci_updated) {
update_canvas_items();
+ emit_signal(SNAME("size_changed"));
}
}
}
@@ -1602,7 +1602,7 @@ Size2 Window::_get_contents_minimum_size() const {
}
}
- return max;
+ return max * content_scale_factor;
}
void Window::child_controls_changed() {
@@ -2149,8 +2149,8 @@ Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName
return theme_icon_cache[p_theme_type][p_name];
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
Ref<Texture2D> icon = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types);
theme_icon_cache[p_theme_type][p_name] = icon;
return icon;
@@ -2173,8 +2173,8 @@ Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringN
return theme_style_cache[p_theme_type][p_name];
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
Ref<StyleBox> style = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
theme_style_cache[p_theme_type][p_name] = style;
return style;
@@ -2197,8 +2197,8 @@ Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_t
return theme_font_cache[p_theme_type][p_name];
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
Ref<Font> font = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types);
theme_font_cache[p_theme_type][p_name] = font;
return font;
@@ -2221,8 +2221,8 @@ int Window::get_theme_font_size(const StringName &p_name, const StringName &p_th
return theme_font_size_cache[p_theme_type][p_name];
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
int font_size = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
theme_font_size_cache[p_theme_type][p_name] = font_size;
return font_size;
@@ -2245,8 +2245,8 @@ Color Window::get_theme_color(const StringName &p_name, const StringName &p_them
return theme_color_cache[p_theme_type][p_name];
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
Color color = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types);
theme_color_cache[p_theme_type][p_name] = color;
return color;
@@ -2269,8 +2269,8 @@ int Window::get_theme_constant(const StringName &p_name, const StringName &p_the
return theme_constant_cache[p_theme_type][p_name];
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
int constant = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
theme_constant_cache[p_theme_type][p_name] = constant;
return constant;
@@ -2315,8 +2315,8 @@ bool Window::has_theme_icon(const StringName &p_name, const StringName &p_theme_
}
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types);
}
@@ -2332,8 +2332,8 @@ bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_th
}
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
}
@@ -2349,8 +2349,8 @@ bool Window::has_theme_font(const StringName &p_name, const StringName &p_theme_
}
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types);
}
@@ -2366,8 +2366,8 @@ bool Window::has_theme_font_size(const StringName &p_name, const StringName &p_t
}
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
}
@@ -2383,8 +2383,8 @@ bool Window::has_theme_color(const StringName &p_name, const StringName &p_theme
}
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types);
}
@@ -2400,8 +2400,8 @@ bool Window::has_theme_constant(const StringName &p_name, const StringName &p_th
}
}
- List<StringName> theme_types;
- theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
+ Vector<StringName> theme_types;
+ theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
}
@@ -2755,12 +2755,16 @@ Transform2D Window::get_popup_base_transform() const {
return popup_base_transform;
}
-bool Window::is_directly_attached_to_screen() const {
+Viewport *Window::get_section_root_viewport() const {
if (get_embedder()) {
- return get_embedder()->is_directly_attached_to_screen();
+ return get_embedder()->get_section_root_viewport();
}
- // Distinguish between the case that this is a native Window and not inside the tree.
- return is_inside_tree();
+ if (is_inside_tree()) {
+ // Native window.
+ return SceneTree::get_singleton()->get_root();
+ }
+ Window *vp = const_cast<Window *>(this);
+ return vp;
}
bool Window::is_attached_in_viewport() const {
diff --git a/scene/main/window.h b/scene/main/window.h
index 33d593711f..47aaf73728 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -247,10 +247,6 @@ protected:
void _notification(int p_what);
static void _bind_methods();
-#ifndef DISABLE_DEPRECATED
- static void _bind_compatibility_methods();
-#endif
-
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
@@ -473,7 +469,7 @@ public:
virtual Transform2D get_final_transform() const override;
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
virtual Transform2D get_popup_base_transform() const override;
- virtual bool is_directly_attached_to_screen() const override;
+ virtual Viewport *get_section_root_viewport() const override;
virtual bool is_attached_in_viewport() const override;
Rect2i get_parent_rect() const;