diff options
Diffstat (limited to 'scene')
-rw-r--r-- | scene/3d/physical_bone_simulator_3d.cpp | 8 | ||||
-rw-r--r-- | scene/3d/physical_bone_simulator_3d.h | 1 | ||||
-rw-r--r-- | scene/3d/physics/vehicle_body_3d.cpp | 6 | ||||
-rw-r--r-- | scene/3d/skeleton_3d.cpp | 65 | ||||
-rw-r--r-- | scene/3d/skeleton_3d.h | 9 | ||||
-rw-r--r-- | scene/3d/soft_body_3d.compat.inc | 41 | ||||
-rw-r--r-- | scene/3d/soft_body_3d.cpp | 28 | ||||
-rw-r--r-- | scene/3d/soft_body_3d.h | 9 | ||||
-rw-r--r-- | scene/animation/animation_player.cpp | 2 | ||||
-rw-r--r-- | scene/gui/base_button.cpp | 10 | ||||
-rw-r--r-- | scene/gui/line_edit.cpp | 2 | ||||
-rw-r--r-- | scene/gui/slider.cpp | 2 | ||||
-rw-r--r-- | scene/gui/split_container.cpp | 244 | ||||
-rw-r--r-- | scene/gui/split_container.h | 31 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 2 | ||||
-rw-r--r-- | scene/main/node.cpp | 3 | ||||
-rw-r--r-- | scene/main/node.h | 4 | ||||
-rw-r--r-- | scene/main/viewport.cpp | 261 | ||||
-rw-r--r-- | scene/main/viewport.h | 24 | ||||
-rw-r--r-- | scene/main/window.cpp | 12 | ||||
-rw-r--r-- | scene/main/window.h | 2 | ||||
-rw-r--r-- | scene/resources/packed_scene.cpp | 4 | ||||
-rw-r--r-- | scene/theme/default_theme.cpp | 3 |
23 files changed, 470 insertions, 303 deletions
diff --git a/scene/3d/physical_bone_simulator_3d.cpp b/scene/3d/physical_bone_simulator_3d.cpp index 510d887c83..8874c9cfc6 100644 --- a/scene/3d/physical_bone_simulator_3d.cpp +++ b/scene/3d/physical_bone_simulator_3d.cpp @@ -73,10 +73,15 @@ void PhysicalBoneSimulator3D::_pose_updated() { } ERR_FAIL_COND(skeleton->get_bone_count() != bones.size()); for (int i = 0; i < skeleton->get_bone_count(); i++) { - bones.write[i].global_pose = skeleton->get_bone_global_pose(i); + _bone_pose_updated(skeleton, i); } } +void PhysicalBoneSimulator3D::_bone_pose_updated(Skeleton3D *p_skeleton, int p_bone_id) { + ERR_FAIL_INDEX(p_bone_id, bones.size()); + bones.write[p_bone_id].global_pose = p_skeleton->get_bone_global_pose(p_bone_id); +} + void PhysicalBoneSimulator3D::_set_active(bool p_active) { if (!Engine::get_singleton()->is_editor_hint()) { _reset_physical_bones_state(); @@ -363,6 +368,7 @@ void PhysicalBoneSimulator3D::_process_modification() { continue; } if (bones[i].physical_bone->is_simulating_physics() == false) { + _bone_pose_updated(skeleton, i); bones[i].physical_bone->reset_to_rest_position(); } else if (simulating) { skeleton->set_bone_global_pose(i, bones[i].global_pose); diff --git a/scene/3d/physical_bone_simulator_3d.h b/scene/3d/physical_bone_simulator_3d.h index ddc9cd569a..24136be2b8 100644 --- a/scene/3d/physical_bone_simulator_3d.h +++ b/scene/3d/physical_bone_simulator_3d.h @@ -73,6 +73,7 @@ protected: void _bone_list_changed(); void _pose_updated(); + void _bone_pose_updated(Skeleton3D *skeleton, int p_bone_id); virtual void _process_modification() override; diff --git a/scene/3d/physics/vehicle_body_3d.cpp b/scene/3d/physics/vehicle_body_3d.cpp index 5073705145..981e872af2 100644 --- a/scene/3d/physics/vehicle_body_3d.cpp +++ b/scene/3d/physics/vehicle_body_3d.cpp @@ -297,11 +297,11 @@ void VehicleWheel3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_friction_slip"), "set_friction_slip", "get_friction_slip"); ADD_GROUP("Suspension", "suspension_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_travel", PROPERTY_HINT_NONE, "suffix:m"), "set_suspension_travel", "get_suspension_travel"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness"), "set_suspension_stiffness", "get_suspension_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness", PROPERTY_HINT_NONE, U"suffix:N/mm"), "set_suspension_stiffness", "get_suspension_stiffness"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_max_force", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m/s\u00B2 (N)"), "set_suspension_max_force", "get_suspension_max_force"); ADD_GROUP("Damping", "damping_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression"), "set_damping_compression", "get_damping_compression"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation"), "set_damping_relaxation", "get_damping_relaxation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_compression", "get_damping_compression"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_relaxation", "get_damping_relaxation"); } void VehicleWheel3D::set_engine_force(real_t p_engine_force) { diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index c6ece84cdd..db9c4db30d 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -103,6 +103,8 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { set_bone_pose_rotation(which, p_value); } else if (what == "scale") { set_bone_pose_scale(which, p_value); + } else if (what == "bone_meta") { + set_bone_meta(which, path.get_slicec('/', 3), p_value); #ifndef DISABLE_DEPRECATED } else if (what == "pose" || what == "bound_children") { // Kept for compatibility from 3.x to 4.x. @@ -170,6 +172,8 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { r_ret = get_bone_pose_rotation(which); } else if (what == "scale") { r_ret = get_bone_pose_scale(which); + } else if (what == "bone_meta") { + r_ret = get_bone_meta(which, path.get_slicec('/', 3)); } else { return false; } @@ -187,6 +191,11 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("position"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + PNAME("rotation"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + + for (const KeyValue<StringName, Variant> &K : bones[i].metadata) { + PropertyInfo pi = PropertyInfo(bones[i].metadata[K.key].get_type(), prep + PNAME("bone_meta/") + K.key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); + p_list->push_back(pi); + } } for (PropertyInfo &E : *p_list) { @@ -531,6 +540,57 @@ void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { version++; } +Variant Skeleton3D::get_bone_meta(int p_bone, const StringName &p_key) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Variant()); + + if (!bones[p_bone].metadata.has(p_key)) { + return Variant(); + } + return bones[p_bone].metadata[p_key]; +} + +TypedArray<StringName> Skeleton3D::_get_bone_meta_list_bind(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, TypedArray<StringName>()); + + TypedArray<StringName> _metaret; + for (const KeyValue<StringName, Variant> &K : bones[p_bone].metadata) { + _metaret.push_back(K.key); + } + return _metaret; +} + +void Skeleton3D::get_bone_meta_list(int p_bone, List<StringName> *p_list) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + for (const KeyValue<StringName, Variant> &K : bones[p_bone].metadata) { + p_list->push_back(K.key); + } +} + +bool Skeleton3D::has_bone_meta(int p_bone, const StringName &p_key) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, false); + + return bones[p_bone].metadata.has(p_key); +} + +void Skeleton3D::set_bone_meta(int p_bone, const StringName &p_key, const Variant &p_value) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + if (p_value.get_type() == Variant::NIL) { + if (bones.write[p_bone].metadata.has(p_key)) { + bones.write[p_bone].metadata.erase(p_key); + } + return; + } + + bones.write[p_bone].metadata.insert(p_key, p_value, false); +} + bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const { int parent_of_bone = get_bone_parent(p_bone); @@ -1014,6 +1074,11 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton3D::get_bone_name); ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "name"), &Skeleton3D::set_bone_name); + ClassDB::bind_method(D_METHOD("get_bone_meta", "bone_idx", "key"), &Skeleton3D::get_bone_meta); + ClassDB::bind_method(D_METHOD("get_bone_meta_list", "bone_idx"), &Skeleton3D::_get_bone_meta_list_bind); + ClassDB::bind_method(D_METHOD("has_bone_meta", "bone_idx", "key"), &Skeleton3D::has_bone_meta); + ClassDB::bind_method(D_METHOD("set_bone_meta", "bone_idx", "key", "value"), &Skeleton3D::set_bone_meta); + ClassDB::bind_method(D_METHOD("get_concatenated_bone_names"), &Skeleton3D::get_concatenated_bone_names); ClassDB::bind_method(D_METHOD("get_bone_parent", "bone_idx"), &Skeleton3D::get_bone_parent); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index a009383f45..07bdeccf2f 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -116,6 +116,8 @@ private: } } + HashMap<StringName, Variant> metadata; + #ifndef DISABLE_DEPRECATED Transform3D pose_global_no_override; real_t global_pose_override_amount = 0.0; @@ -193,6 +195,7 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); + TypedArray<StringName> _get_bone_meta_list_bind(int p_bone) const; static void _bind_methods(); virtual void add_child_notify(Node *p_child) override; @@ -238,6 +241,12 @@ public: void set_motion_scale(float p_motion_scale); float get_motion_scale() const; + // bone metadata + Variant get_bone_meta(int p_bone, const StringName &p_key) const; + void get_bone_meta_list(int p_bone, List<StringName> *p_list) const; + bool has_bone_meta(int p_bone, const StringName &p_key) const; + void set_bone_meta(int p_bone, const StringName &p_key, const Variant &p_value); + // Posing API Transform3D get_bone_pose(int p_bone) const; Vector3 get_bone_pose_position(int p_bone) const; diff --git a/scene/3d/soft_body_3d.compat.inc b/scene/3d/soft_body_3d.compat.inc new file mode 100644 index 0000000000..0b01bfeb1f --- /dev/null +++ b/scene/3d/soft_body_3d.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* soft_body_3d.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 SoftBody3D::_pin_point_bind_compat_94684(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { + pin_point(p_point_index, pin, p_spatial_attachment_path, -1); +} + +void SoftBody3D::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::_pin_point_bind_compat_94684, DEFVAL(NodePath())); +} + +#endif // DISABLE_DEPRECATED diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 7f67bde0cf..b0fc94d75f 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "soft_body_3d.h" +#include "soft_body_3d.compat.inc" #include "scene/3d/physics/physics_body_3d.h" @@ -200,12 +201,18 @@ bool SoftBody3D::_set_property_pinned_points_indices(const Array &p_indices) { int point_index; for (int i = 0; i < p_indices_size; ++i) { point_index = p_indices.get(i); - if (w[i].point_index != point_index) { - if (-1 != w[i].point_index) { + if (w[i].point_index != point_index || pinned_points.size() < p_indices_size) { + bool insert = false; + if (w[i].point_index != -1 && p_indices.find(w[i].point_index) == -1) { pin_point(w[i].point_index, false); + insert = true; } w[i].point_index = point_index; - pin_point(w[i].point_index, true); + if (insert) { + pin_point(w[i].point_index, true, NodePath(), i); + } else { + pin_point(w[i].point_index, true); + } } } return true; @@ -356,7 +363,7 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform); - ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::pin_point, DEFVAL(NodePath())); + ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path", "insert_at"), &SoftBody3D::pin_point, DEFVAL(NodePath()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftBody3D::is_point_pinned); ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody3D::set_ray_pickable); @@ -668,10 +675,11 @@ void SoftBody3D::pin_point_toggle(int p_point_index) { pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index))); } -void SoftBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { +void SoftBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path, int p_insert_at) { + ERR_FAIL_COND_MSG(p_insert_at < -1 || p_insert_at >= pinned_points.size(), "Invalid index for pin point insertion position."); _pin_point_on_physics_server(p_point_index, pin); if (pin) { - _add_pinned_point(p_point_index, p_spatial_attachment_path); + _add_pinned_point(p_point_index, p_spatial_attachment_path, p_insert_at); } else { _remove_pinned_point(p_point_index); } @@ -730,7 +738,7 @@ void SoftBody3D::_pin_point_on_physics_server(int p_point_index, bool pin) { PhysicsServer3D::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin); } -void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { +void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path, int p_insert_at) { SoftBody3D::PinnedPoint *pinned_point; if (-1 == _get_pinned_point(p_point_index, pinned_point)) { // Create new @@ -743,7 +751,11 @@ void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_ pp.offset = (pp.spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer3D::get_singleton()->soft_body_get_point_global_position(physics_rid, pp.point_index)); } - pinned_points.push_back(pp); + if (p_insert_at != -1) { + pinned_points.insert(p_insert_at, pp); + } else { + pinned_points.push_back(pp); + } } else { pinned_point->point_index = p_point_index; diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h index ab30f7e654..b01d462d9f 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -126,6 +126,11 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + void _pin_point_bind_compat_94684(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath()); + static void _bind_compatibility_methods(); +#endif + PackedStringArray get_configuration_warnings() const override; public: @@ -177,7 +182,7 @@ public: Vector3 get_point_transform(int p_point_index); void pin_point_toggle(int p_point_index); - void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath()); + void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath(), int p_insert_at = -1); bool is_point_pinned(int p_point_index) const; void _pin_point_deferred(int p_point_index, bool pin, const NodePath p_spatial_attachment_path); @@ -193,7 +198,7 @@ private: void _update_cache_pin_points_datas(); void _pin_point_on_physics_server(int p_point_index, bool pin); - void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path); + void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path, int p_insert_at = -1); void _reset_points_offsets(); void _remove_pinned_point(int p_point_index); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 24b777e2eb..a4aa383a9d 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -904,7 +904,7 @@ void AnimationPlayer::_bind_methods() { ADD_GROUP("Playback Options", "playback_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_auto_capture"), "set_auto_capture", "is_auto_capture"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_auto_capture_duration", PROPERTY_HINT_NONE, "suffix:s"), "set_auto_capture_duration", "get_auto_capture_duration"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_auto_capture_transition_type", PROPERTY_HINT_ENUM, "Linear,Sine,Quint,Quart,Expo,Elastic,Cubic,Circ,Bounce,Back,Spring"), "set_auto_capture_transition_type", "get_auto_capture_transition_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_auto_capture_transition_type", PROPERTY_HINT_ENUM, "Linear,Sine,Quint,Quart,Quad,Expo,Elastic,Cubic,Circ,Bounce,Back,Spring"), "set_auto_capture_transition_type", "get_auto_capture_transition_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_auto_capture_ease_type", PROPERTY_HINT_ENUM, "In,Out,InOut,OutIn"), "set_auto_capture_ease_type", "get_auto_capture_ease_type"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:s"), "set_default_blend_time", "get_default_blend_time"); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index ed7e0de0e2..72299f788d 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -144,7 +144,9 @@ void BaseButton::_toggled(bool p_pressed) { } void BaseButton::on_action_event(Ref<InputEvent> p_event) { - if (p_event->is_pressed()) { + Ref<InputEventMouseButton> mouse_button = p_event; + + if (p_event->is_pressed() && (mouse_button.is_null() || status.hovering)) { status.press_attempt = true; status.pressing_inside = true; emit_signal(SNAME("button_down")); @@ -174,12 +176,6 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) { } if (!p_event->is_pressed()) { - Ref<InputEventMouseButton> mouse_button = p_event; - if (mouse_button.is_valid()) { - if (!has_point(mouse_button->get_position())) { - status.hovering = false; - } - } status.press_attempt = false; status.pressing_inside = false; emit_signal(SNAME("button_up")); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 1a066b0728..a08fb96e71 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -103,7 +103,7 @@ void LineEdit::_close_ime_window() { void LineEdit::_update_ime_window_position() { DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; - if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { + if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME) || !DisplayServer::get_singleton()->window_is_focused(wid)) { return; } DisplayServer::get_singleton()->window_set_ime_active(true, wid); diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index f984d781d3..6098548d32 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -275,7 +275,7 @@ void Slider::_notification(int p_what) { double areasize = size.height - (theme_cache.center_grabber ? 0 : grabber->get_height()); int grabber_shift = theme_cache.center_grabber ? grabber->get_height() / 2 : 0; style->draw(ci, Rect2i(Point2i(size.width / 2 - widget_width / 2, 0), Size2i(widget_width, size.height))); - grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_height() / 2 + grabber_shift), Size2i(widget_width, areasize * ratio + grabber->get_height() / 2 - grabber_shift))); + grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, Math::round(size.height - areasize * ratio - grabber->get_height() / 2 + grabber_shift)), Size2i(widget_width, Math::round(areasize * ratio + grabber->get_height() / 2 - grabber_shift)))); if (ticks > 1) { int grabber_offset = (grabber->get_height() / 2 - tick->get_height() / 2); diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index 8ab9b4c1cd..be03db526d 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -32,6 +32,7 @@ #include "scene/gui/label.h" #include "scene/gui/margin_container.h" +#include "scene/main/window.h" #include "scene/theme/theme_db.h" void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) { @@ -39,7 +40,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) { SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); - if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || sc->dragger_visibility != SplitContainer::DRAGGER_VISIBLE) { + if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || !sc->dragging_enabled) { return; } @@ -48,8 +49,9 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { - sc->_compute_middle_sep(true); + sc->_compute_split_offset(true); dragging = true; + sc->emit_signal(SNAME("drag_started")); drag_ofs = sc->split_offset; if (sc->vertical) { drag_from = get_transform().xform(mb->get_position()).y; @@ -59,6 +61,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) { } else { dragging = false; queue_redraw(); + sc->emit_signal(SNAME("drag_ended")); } } } @@ -76,7 +79,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) { } else { sc->split_offset = drag_ofs + ((sc->vertical ? in_parent_pos.y : in_parent_pos.x) - drag_from); } - sc->_compute_middle_sep(true); + sc->_compute_split_offset(true); sc->queue_sort(); sc->emit_signal(SNAME("dragged"), sc->get_split_offset()); } @@ -84,11 +87,9 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) { Control::CursorShape SplitContainerDragger::get_cursor_shape(const Point2 &p_pos) const { SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); - - if (!sc->collapsed && sc->dragger_visibility == SplitContainer::DRAGGER_VISIBLE) { + if (!sc->collapsed && sc->dragging_enabled) { return (sc->vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT); } - return Control::get_cursor_shape(p_pos); } @@ -101,7 +102,6 @@ void SplitContainerDragger::_notification(int p_what) { queue_redraw(); } } break; - case NOTIFICATION_MOUSE_EXIT: { mouse_inside = false; SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); @@ -109,22 +109,25 @@ void SplitContainerDragger::_notification(int p_what) { queue_redraw(); } } break; - case NOTIFICATION_DRAW: { SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); - if (!dragging && !mouse_inside && sc->theme_cache.autohide) { - return; + draw_style_box(sc->theme_cache.split_bar_background, split_bar_rect); + if (sc->dragger_visibility == sc->DRAGGER_VISIBLE && (dragging || mouse_inside || !sc->theme_cache.autohide)) { + Ref<Texture2D> tex = sc->_get_grabber_icon(); + float available_size = sc->vertical ? (sc->get_size().x - tex->get_size().x) : (sc->get_size().y - tex->get_size().y); + if (available_size - sc->drag_area_margin_begin - sc->drag_area_margin_end > 0) { // Draw the grabber only if it fits. + draw_texture(tex, (split_bar_rect.get_position() + (split_bar_rect.get_size() - tex->get_size()) * 0.5)); + } + } + if (sc->show_drag_area && Engine::get_singleton()->is_editor_hint()) { + draw_rect(Rect2(Vector2(0, 0), get_size()), sc->dragging_enabled ? Color(1, 1, 0, 0.3) : Color(1, 0, 0, 0.3)); } - - Ref<Texture2D> tex = sc->_get_grabber_icon(); - draw_texture(tex, (get_size() - tex->get_size()) / 2); } break; } } Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode) const { int idx = 0; - for (int i = 0; i < get_child_count(false); i++) { Control *c = as_sortable_control(get_child(i, false), p_visibility_mode); if (!c) { @@ -137,7 +140,6 @@ Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisbilityMode p_ idx++; } - return nullptr; } @@ -153,45 +155,48 @@ Ref<Texture2D> SplitContainer::_get_grabber_icon() const { } } -void SplitContainer::_compute_middle_sep(bool p_clamp) { +int SplitContainer::_get_separation() const { + if (dragger_visibility == DRAGGER_HIDDEN_COLLAPSED) { + return 0; + } + // DRAGGER_VISIBLE or DRAGGER_HIDDEN. + Ref<Texture2D> g = _get_grabber_icon(); + return MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()); +} + +void SplitContainer::_compute_split_offset(bool p_clamp) { Control *first = _get_sortable_child(0); Control *second = _get_sortable_child(1); + int axis_index = vertical ? 1 : 0; + int size = get_size()[axis_index]; + int sep = _get_separation(); - // Determine expanded children. - bool first_expanded = (vertical ? first->get_v_size_flags() : first->get_h_size_flags()) & SIZE_EXPAND; - bool second_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND; - - // Compute the minimum size. - int axis = vertical ? 1 : 0; - int size = get_size()[axis]; - int ms_first = first->get_combined_minimum_size()[axis]; - int ms_second = second->get_combined_minimum_size()[axis]; - - // Determine the separation between items. - Ref<Texture2D> g = _get_grabber_icon(); - int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0; - - // Compute the wished separation_point. - int wished_middle_sep = 0; + // Compute the wished size. + int wished_size = 0; int split_offset_with_collapse = 0; if (!collapsed) { split_offset_with_collapse = split_offset; } - if (first_expanded && second_expanded) { + bool first_is_expanded = (vertical ? first->get_v_size_flags() : first->get_h_size_flags()) & SIZE_EXPAND; + bool second_is_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND; + + if (first_is_expanded && second_is_expanded) { float ratio = first->get_stretch_ratio() / (first->get_stretch_ratio() + second->get_stretch_ratio()); - wished_middle_sep = size * ratio - sep / 2 + split_offset_with_collapse; - } else if (first_expanded) { - wished_middle_sep = size - sep + split_offset_with_collapse; + wished_size = size * ratio - sep * 0.5 + split_offset_with_collapse; + } else if (first_is_expanded) { + wished_size = size - sep + split_offset_with_collapse; } else { - wished_middle_sep = split_offset_with_collapse; + wished_size = split_offset_with_collapse; } - // Clamp the middle sep to acceptatble values. - middle_sep = CLAMP(wished_middle_sep, ms_first, size - sep - ms_second); + // Clamp the split offset to acceptable values. + int first_min_size = first->get_combined_minimum_size()[axis_index]; + int second_min_size = second->get_combined_minimum_size()[axis_index]; + computed_split_offset = CLAMP(wished_size, first_min_size, size - sep - second_min_size); // Clamp the split_offset if requested. if (p_clamp) { - split_offset -= wished_middle_sep - middle_sep; + split_offset -= wished_size - computed_split_offset; } } @@ -199,8 +204,7 @@ void SplitContainer::_resort() { Control *first = _get_sortable_child(0); Control *second = _get_sortable_child(1); - // If we have only one element. - if (!first || !second) { + if (!first || !second) { // Only one child. if (first) { fit_child_in_rect(first, Rect2(Point2(), get_size())); } else if (second) { @@ -209,53 +213,50 @@ void SplitContainer::_resort() { dragging_area_control->hide(); return; } + dragging_area_control->set_visible(!collapsed); - // If we have more that one. - _compute_middle_sep(false); + _compute_split_offset(false); // This recalculates and sets computed_split_offset. - // Determine the separation between items. - Ref<Texture2D> g = _get_grabber_icon(); - int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0; + int sep = _get_separation(); + bool is_rtl = is_layout_rtl(); - // Move the children, including the dragger. + // Move the children. if (vertical) { - fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(get_size().width, middle_sep))); - int sofs = middle_sep + sep; + fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(get_size().width, computed_split_offset))); + int sofs = computed_split_offset + sep; fit_child_in_rect(second, Rect2(Point2(0, sofs), Size2(get_size().width, get_size().height - sofs))); } else { - if (is_layout_rtl()) { - middle_sep = get_size().width - middle_sep - sep; - fit_child_in_rect(second, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height))); - int sofs = middle_sep + sep; + if (is_rtl) { + computed_split_offset = get_size().width - computed_split_offset - sep; + fit_child_in_rect(second, Rect2(Point2(0, 0), Size2(computed_split_offset, get_size().height))); + int sofs = computed_split_offset + sep; fit_child_in_rect(first, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height))); } else { - fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height))); - int sofs = middle_sep + sep; + fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(computed_split_offset, get_size().height))); + int sofs = computed_split_offset + sep; fit_child_in_rect(second, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height))); } } - // Handle the dragger visibility and position. - if (dragger_visibility == DRAGGER_VISIBLE && !collapsed) { - dragging_area_control->show(); - - int dragger_ctrl_size = MAX(sep, theme_cache.minimum_grab_thickness); - if (vertical) { - dragging_area_control->set_rect(Rect2(Point2(0, middle_sep - (dragger_ctrl_size - sep) / 2), Size2(get_size().width, dragger_ctrl_size))); - } else { - dragging_area_control->set_rect(Rect2(Point2(middle_sep - (dragger_ctrl_size - sep) / 2, 0), Size2(dragger_ctrl_size, get_size().height))); - } - - dragging_area_control->queue_redraw(); + dragging_area_control->set_mouse_filter(dragging_enabled ? MOUSE_FILTER_STOP : MOUSE_FILTER_IGNORE); + const int dragger_ctrl_size = MAX(sep, theme_cache.minimum_grab_thickness); + float split_bar_offset = (dragger_ctrl_size - sep) * 0.5; + if (vertical) { + Rect2 split_bar_rect = Rect2(is_rtl ? drag_area_margin_end : drag_area_margin_begin, computed_split_offset, get_size().width - drag_area_margin_begin - drag_area_margin_end, sep); + dragging_area_control->set_rect(Rect2(split_bar_rect.position.x, split_bar_rect.position.y - split_bar_offset + drag_area_offset, split_bar_rect.size.x, dragger_ctrl_size)); + dragging_area_control->split_bar_rect = Rect2(Vector2(0.0, int(split_bar_offset) - drag_area_offset), split_bar_rect.size); } else { - dragging_area_control->hide(); + Rect2 split_bar_rect = Rect2(computed_split_offset, drag_area_margin_begin, sep, get_size().height - drag_area_margin_begin - drag_area_margin_end); + dragging_area_control->set_rect(Rect2(split_bar_rect.position.x - split_bar_offset + drag_area_offset * (is_rtl ? -1 : 1), split_bar_rect.position.y, dragger_ctrl_size, split_bar_rect.size.y)); + dragging_area_control->split_bar_rect = Rect2(Vector2(int(split_bar_offset) - drag_area_offset * (is_rtl ? -1 : 1), 0.0), split_bar_rect.size); } + queue_redraw(); + dragging_area_control->queue_redraw(); } Size2 SplitContainer::get_minimum_size() const { Size2i minimum; - Ref<Texture2D> g = _get_grabber_icon(); - int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0; + int sep = _get_separation(); for (int i = 0; i < 2; i++) { Control *child = _get_sortable_child(i, SortableVisbilityMode::VISIBLE); @@ -297,11 +298,9 @@ void SplitContainer::_notification(int p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { queue_sort(); } break; - case NOTIFICATION_SORT_CHILDREN: { _resort(); } break; - case NOTIFICATION_THEME_CHANGED: { update_minimum_size(); } break; @@ -312,9 +311,7 @@ void SplitContainer::set_split_offset(int p_offset) { if (split_offset == p_offset) { return; } - split_offset = p_offset; - queue_sort(); } @@ -326,8 +323,7 @@ void SplitContainer::clamp_split_offset() { if (!_get_sortable_child(0) || !_get_sortable_child(1)) { return; } - - _compute_middle_sep(true); + _compute_split_offset(true); queue_sort(); } @@ -335,7 +331,6 @@ void SplitContainer::set_collapsed(bool p_collapsed) { if (collapsed == p_collapsed) { return; } - collapsed = p_collapsed; queue_sort(); } @@ -344,7 +339,6 @@ void SplitContainer::set_dragger_visibility(DraggerVisibility p_visibility) { if (dragger_visibility == p_visibility) { return; } - dragger_visibility = p_visibility; queue_sort(); } @@ -368,6 +362,26 @@ bool SplitContainer::is_vertical() const { return vertical; } +void SplitContainer::set_dragging_enabled(bool p_enabled) { + if (dragging_enabled == p_enabled) { + return; + } + dragging_enabled = p_enabled; + if (!dragging_enabled && dragging_area_control->dragging) { + dragging_area_control->dragging = false; + // queue_redraw() is called by _resort(). + emit_signal(SNAME("drag_ended")); + } + if (get_viewport()) { + get_viewport()->update_mouse_cursor_state(); + } + _resort(); +} + +bool SplitContainer::is_dragging_enabled() const { + return dragging_enabled; +} + Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const { Vector<int> flags; flags.append(SIZE_FILL); @@ -392,6 +406,51 @@ Vector<int> SplitContainer::get_allowed_size_flags_vertical() const { return flags; } +void SplitContainer::set_drag_area_margin_begin(int p_margin) { + if (drag_area_margin_begin == p_margin) { + return; + } + drag_area_margin_begin = p_margin; + queue_sort(); +} + +int SplitContainer::get_drag_area_margin_begin() const { + return drag_area_margin_begin; +} + +void SplitContainer::set_drag_area_margin_end(int p_margin) { + if (drag_area_margin_end == p_margin) { + return; + } + drag_area_margin_end = p_margin; + queue_sort(); +} + +int SplitContainer::get_drag_area_margin_end() const { + return drag_area_margin_end; +} + +void SplitContainer::set_drag_area_offset(int p_offset) { + if (drag_area_offset == p_offset) { + return; + } + drag_area_offset = p_offset; + queue_sort(); +} + +int SplitContainer::get_drag_area_offset() const { + return drag_area_offset; +} + +void SplitContainer::set_show_drag_area_enabled(bool p_enabled) { + show_drag_area = p_enabled; + dragging_area_control->queue_redraw(); +} + +bool SplitContainer::is_show_drag_area_enabled() const { + return show_drag_area; +} + void SplitContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_split_offset", "offset"), &SplitContainer::set_split_offset); ClassDB::bind_method(D_METHOD("get_split_offset"), &SplitContainer::get_split_offset); @@ -406,13 +465,39 @@ void SplitContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &SplitContainer::set_vertical); ClassDB::bind_method(D_METHOD("is_vertical"), &SplitContainer::is_vertical); + ClassDB::bind_method(D_METHOD("set_dragging_enabled", "dragging_enabled"), &SplitContainer::set_dragging_enabled); + ClassDB::bind_method(D_METHOD("is_dragging_enabled"), &SplitContainer::is_dragging_enabled); + + ClassDB::bind_method(D_METHOD("set_drag_area_margin_begin", "margin"), &SplitContainer::set_drag_area_margin_begin); + ClassDB::bind_method(D_METHOD("get_drag_area_margin_begin"), &SplitContainer::get_drag_area_margin_begin); + + ClassDB::bind_method(D_METHOD("set_drag_area_margin_end", "margin"), &SplitContainer::set_drag_area_margin_end); + ClassDB::bind_method(D_METHOD("get_drag_area_margin_end"), &SplitContainer::get_drag_area_margin_end); + + ClassDB::bind_method(D_METHOD("set_drag_area_offset", "offset"), &SplitContainer::set_drag_area_offset); + ClassDB::bind_method(D_METHOD("get_drag_area_offset"), &SplitContainer::get_drag_area_offset); + + ClassDB::bind_method(D_METHOD("set_drag_area_highlight_in_editor", "drag_area_highlight_in_editor"), &SplitContainer::set_show_drag_area_enabled); + ClassDB::bind_method(D_METHOD("is_drag_area_highlight_in_editor_enabled"), &SplitContainer::is_show_drag_area_enabled); + + ClassDB::bind_method(D_METHOD("get_drag_area_control"), &SplitContainer::get_drag_area_control); + ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset"))); + ADD_SIGNAL(MethodInfo("drag_started")); + ADD_SIGNAL(MethodInfo("drag_ended")); ADD_PROPERTY(PropertyInfo(Variant::INT, "split_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_split_offset", "get_split_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dragging_enabled"), "set_dragging_enabled", "is_dragging_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden and Collapsed"), "set_dragger_visibility", "get_dragger_visibility"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); + ADD_GROUP("Drag Area", "drag_area_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_margin_begin", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_margin_begin", "get_drag_area_margin_begin"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_margin_end", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_margin_end", "get_drag_area_margin_end"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_offset", "get_drag_area_offset"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_area_highlight_in_editor"), "set_drag_area_highlight_in_editor", "is_drag_area_highlight_in_editor_enabled"); + BIND_ENUM_CONSTANT(DRAGGER_VISIBLE); BIND_ENUM_CONSTANT(DRAGGER_HIDDEN); BIND_ENUM_CONSTANT(DRAGGER_HIDDEN_COLLAPSED); @@ -423,6 +508,7 @@ void SplitContainer::_bind_methods() { BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon, "grabber"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon_h, "h_grabber"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon_v, "v_grabber"); + BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SplitContainer, split_bar_background, "split_bar_background"); } SplitContainer::SplitContainer(bool p_vertical) { diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h index db870554c2..2bba96b4b8 100644 --- a/scene/gui/split_container.h +++ b/scene/gui/split_container.h @@ -35,6 +35,8 @@ class SplitContainerDragger : public Control { GDCLASS(SplitContainerDragger, Control); + friend class SplitContainer; + Rect2 split_bar_rect; protected: void _notification(int p_what); @@ -62,11 +64,16 @@ public: }; private: + int show_drag_area = false; + int drag_area_margin_begin = 0; + int drag_area_margin_end = 0; + int drag_area_offset = 0; int split_offset = 0; - int middle_sep = 0; + int computed_split_offset = 0; bool vertical = false; bool collapsed = false; DraggerVisibility dragger_visibility = DRAGGER_VISIBLE; + bool dragging_enabled = true; SplitContainerDragger *dragging_area_control = nullptr; @@ -77,10 +84,13 @@ private: Ref<Texture2D> grabber_icon; Ref<Texture2D> grabber_icon_h; Ref<Texture2D> grabber_icon_v; + float base_scale = 1.0; + Ref<StyleBox> split_bar_background; } theme_cache; Ref<Texture2D> _get_grabber_icon() const; - void _compute_middle_sep(bool p_clamp); + void _compute_split_offset(bool p_clamp); + int _get_separation() const; void _resort(); Control *_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode = SortableVisbilityMode::VISIBLE_IN_TREE) const; @@ -105,11 +115,28 @@ public: void set_vertical(bool p_vertical); bool is_vertical() const; + void set_dragging_enabled(bool p_enabled); + bool is_dragging_enabled() const; + virtual Size2 get_minimum_size() const override; virtual Vector<int> get_allowed_size_flags_horizontal() const override; virtual Vector<int> get_allowed_size_flags_vertical() const override; + void set_drag_area_margin_begin(int p_margin); + int get_drag_area_margin_begin() const; + + void set_drag_area_margin_end(int p_margin); + int get_drag_area_margin_end() const; + + void set_drag_area_offset(int p_offset); + int get_drag_area_offset() const; + + void set_show_drag_area_enabled(bool p_enabled); + bool is_show_drag_area_enabled() const; + + Control *get_drag_area_control() { return dragging_area_control; } + SplitContainer(bool p_vertical = false); }; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index d7799588ea..bf9c479c69 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2958,7 +2958,7 @@ void TextEdit::_close_ime_window() { void TextEdit::_update_ime_window_position() { DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; - if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { + if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME) || !DisplayServer::get_singleton()->window_is_focused(wid)) { return; } DisplayServer::get_singleton()->window_set_ime_active(true, wid); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 50f5923e3c..858fc2246b 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -757,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; } @@ -3638,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); diff --git a/scene/main/node.h b/scene/main/node.h index e412459105..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; @@ -717,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/viewport.cpp b/scene/main/viewport.cpp index 20c4889e0d..acf4f67673 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1236,14 +1236,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); } } @@ -1346,7 +1348,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; @@ -1549,8 +1551,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. @@ -1574,19 +1575,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; } @@ -1597,7 +1594,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) { @@ -1702,14 +1698,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; @@ -1737,8 +1734,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()); @@ -1781,7 +1776,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; @@ -1800,20 +1797,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. @@ -1836,20 +1832,19 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.mouse_focus = nullptr; } - 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) { @@ -1858,11 +1853,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.mouse_focus_mask.clear(); + gui.dragging = true; break; } else { Control *drag_preview = _gui_get_drag_preview(); @@ -1871,7 +1867,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) { @@ -1889,7 +1885,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); } } } @@ -1980,112 +1976,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; + 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; } - } 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); - } - // 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); } } @@ -2098,20 +2017,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()) { @@ -2119,10 +2033,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; @@ -2131,23 +2042,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; } @@ -2162,7 +2067,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()); @@ -2175,24 +2079,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) { @@ -2209,13 +2110,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; } } @@ -2229,51 +2128,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); } } @@ -2285,10 +2184,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; } @@ -2299,10 +2198,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(); } @@ -2332,14 +2233,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) { @@ -2559,7 +2462,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(); } @@ -3023,6 +2925,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--) { @@ -3074,6 +2977,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`. @@ -3196,6 +3100,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. @@ -3404,7 +3312,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 { @@ -3598,7 +3506,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 { @@ -5157,9 +5065,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 16e963698b..a18dc1f6f0 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -282,7 +282,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(); @@ -351,7 +351,6 @@ private: struct GUI { 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 +362,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 +370,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 +378,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 +402,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); @@ -673,9 +673,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). @@ -837,9 +837,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.cpp b/scene/main/window.cpp index 6b299eab6e..803ce89bc9 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -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 84d2febe51..47aaf73728 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -469,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; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 69dc71e414..d6fe4385c4 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -527,7 +527,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { bool valid; Array array = dnp.base->get(dnp.property, &valid); - ERR_CONTINUE(!valid); + ERR_CONTINUE_EDMSG(!valid, vformat("Failed to get property '%s' from node '%s'.", dnp.property, dnp.base->get_name())); array = array.duplicate(); array.resize(paths.size()); @@ -540,7 +540,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { bool valid; Dictionary dict = dnp.base->get(dnp.property, &valid); - ERR_CONTINUE(!valid); + ERR_CONTINUE_EDMSG(!valid, vformat("Failed to get property '%s' from node '%s'.", dnp.property, dnp.base->get_name())); dict = dict.duplicate(); bool convert_key = dict.get_typed_key_builtin() == Variant::OBJECT && ClassDB::is_parent_class(dict.get_typed_key_class_name(), "Node"); diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp index 749d4e3530..0c211be9f9 100644 --- a/scene/theme/default_theme.cpp +++ b/scene/theme/default_theme.cpp @@ -1211,6 +1211,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("v_separation", "VFlowContainer", Math::round(4 * scale)); theme->set_stylebox(SceneStringName(panel), "PanelContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0)); + theme->set_stylebox("split_bar_background", "SplitContainer", make_empty_stylebox(0, 0, 0, 0)); + theme->set_stylebox("split_bar_background", "VSplitContainer", make_empty_stylebox(0, 0, 0, 0)); + theme->set_stylebox("split_bar_background", "HSplitContainer", make_empty_stylebox(0, 0, 0, 0)); theme->set_icon("zoom_out", "GraphEdit", icons["zoom_less"]); theme->set_icon("zoom_in", "GraphEdit", icons["zoom_more"]); |