Repository: godotengine/godot (language: cpp)

Issue:
ScrollContainer doesn't scroll during drag & drop like Tree does
### Tested versions

4.5 master

### System information

Windows 10

### Issue description

While working on #103738, I noticed the SceneTree and FileSystem will scroll while drag & drop is held near the edge, but not the inspector, which is a ScrollContainer

### Steps to reproduce

Inspect some node and expand sections until it is scrollable.
Try dragging something like a file near the edges, it will not scroll.

### Minimal reproduction project (MRP)

N/A


Discussion / hints:

Fix patch:
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 4990a415d6db..d18cf896e629 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -1938,6 +1938,12 @@ void EditorInspectorSection::set_bg_color(const Color &p_bg_color) {
 	queue_redraw();
 }
 
+void EditorInspectorSection::reset_timer() {
+	if (dropping_for_unfold && !dropping_unfold_timer->is_stopped()) {
+		dropping_unfold_timer->start();
+	}
+}
+
 bool EditorInspectorSection::has_revertable_properties() const {
 	return !revertable_properties.is_empty();
 }
@@ -3478,6 +3484,7 @@ void EditorInspector::update_tree() {
 			if (!vbox_per_path[root_vbox].has(acc_path)) {
 				// If the section does not exists, create it.
 				EditorInspectorSection *section = memnew(EditorInspectorSection);
+				get_root_inspector()->get_v_scroll_bar()->connect(SceneStringName(value_changed), callable_mp(section, &EditorInspectorSection::reset_timer).unbind(1));
 				current_vbox->add_child(section);
 				sections.push_back(section);
 
@@ -3878,6 +3885,7 @@ void EditorInspector::update_tree() {
 				}
 
 				EditorInspectorSection *section = memnew(EditorInspectorSection);
+				get_root_inspector()->get_v_scroll_bar()->connect(SceneStringName(value_changed), callable_mp(section, &EditorInspectorSection::reset_timer).unbind(1));
 				favorites_groups_vbox->add_child(section);
 				parent_vbox = section->get_vbox();
 				section->setup("", section_name, object, sscolor, false);
@@ -3897,6 +3905,7 @@ void EditorInspector::update_tree() {
 					}
 
 					EditorInspectorSection *section = memnew(EditorInspectorSection);
+					get_root_inspector()->get_v_scroll_bar()->connect(SceneStringName(value_changed), callable_mp(section, &EditorInspectorSection::reset_timer).unbind(1));
 					vbox->add_child(section);
 					vbox = section->get_vbox();
 					section->setup("", section_name, object, sscolor, false);
@@ -5001,4 +5010,5 @@ EditorInspector::EditorInspector() {
 	set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
 
 	set_draw_focus_border(true);
+	set_scroll_on_drag_hover(true);
 }
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index ac7c3d03e76b..a3974c1132ad 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -357,6 +357,7 @@ class EditorInspectorSection : public Container {
 	void unfold();
 	void fold();
 	void set_bg_color(const Color &p_bg_color);
+	void reset_timer();
 
 	bool has_revertable_properties() const;
 	void property_can_revert_changed(const String &p_path, bool p_can_revert);
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index fdb134c5d0e4..ca7b730e1249 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -361,6 +361,10 @@ void ScrollContainer::_notification(int p_what) {
 		case NOTIFICATION_TRANSLATION_CHANGED: {
 			_updating_scrollbars = true;
 			callable_mp(this, is_ready() ? &ScrollContainer::_reposition_children : &ScrollContainer::_update_scrollbar_position).call_deferred();
+			if (p_what == NOTIFICATION_THEME_CHANGED) {
+				scroll_border = get_theme_constant(SNAME("scroll_border"), SNAME("Tree"));
+				scroll_speed = get_theme_constant(SNAME("scroll_speed"), SNAME("Tree"));
+			}
 		} break;
 
 		case NOTIFICATION_READY: {
@@ -387,6 +391,43 @@ void ScrollContainer::_notification(int p_what) {
 			}
 		} break;
 
+		case NOTIFICATION_DRAG_BEGIN: {
+			if (scroll_on_drag_hover && is_visible_in_tree()) {
+				set_process_internal(true);
+			}
+		} break;
+
+		case NOTIFICATION_DRAG_END: {
+			set_process_internal(false);
+		} break;
+
+		case NOTIFICATION_INTERNAL_PROCESS: {
+			if (scroll_on_drag_hover && get_viewport()->gui_is_dragging()) {
+				Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position();
+				Transform2D xform = get_transform();
+				if (Rect2(Point2(), xform.get_scale() * get_size()).grow(scroll_border).has_point(mouse_position)) {
+					Point2 point;
+
+					if ((Math::abs(mouse_position.x) < Math::abs(mouse_position.x - get_size().width)) && (Math::abs(mouse_position.x) < scroll_border)) {
+						point.x = mouse_position.x - scroll_border;
+					} else if (Math::abs(mouse_position.x - get_size().width) < scroll_border) {
+						point.x = mouse_position.x - (get_size().width - scroll_border);
+					}
+
+					if ((Math::abs(mouse_position.y) < Math::abs(mouse_position.y - get_size().height)) && (Math::abs(mouse_position.y) < scroll_border)) {
+						point.y = mouse_position.y - scroll_border;
+					} else if (Math::abs(mouse_position.y - get_size().height) < scroll_border) {
+						point.y = mouse_position.y - (get_size().height - scroll_border);
+					}
+
+					point *= scroll_speed * get_process_delta_time();
+					point += Point2(get_h_scroll(), get_v_scroll());
+					h_scroll->set_value(point.x);
+					v_scroll->set_value(point.y);
+				}
+			}
+		} break;
+
 		case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
 			if (drag_touching) {
 				if (drag_touching_deaccel) {
@@ -584,6 +625,10 @@ PackedStringArray ScrollContainer::get_configuration_warnings() const {
 	return warnings;
 }
 
+void ScrollContainer::set_scroll_on_drag_hover(bool p_scroll) {
+	scroll_on_drag_hover = p_scroll;
+}
+
 HScrollBar *ScrollContainer::get_h_scroll_bar() {
 	return h_scroll;
 }
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 887a8e3f1c35..7309c9ea6133 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -62,12 +62,15 @@ class ScrollContainer : public Container {
 	bool drag_touching = false;
 	bool drag_touching_deaccel = false;
 	bool beyond_deadzone = false;
+	bool scroll_on_drag_hover = false;
 
 	ScrollMode horizontal_scroll_mode = SCROLL_MODE_AUTO;
 	ScrollMode vertical_scroll_mode = SCROLL_MODE_AUTO;
 
 	int deadzone = 0;
 	bool follow_focus = false;
+	int scroll_border = 20;
+	int scroll_speed = 12;
 
 	struct ThemeCache {
 		Ref<StyleBox> panel_style;
@@ -123,6 +126,8 @@ class ScrollContainer : public Container {
 	bool is_following_focus() const;
 	void set_follow_focus(bool p_follow);
 
+	void set_scroll_on_drag_hover(bool p_scroll);
+
 	HScrollBar *get_h_scroll_bar();
 	VScrollBar *get_v_scroll_bar();
 	void ensure_control_visible(Control *p_control);


Test patch: