Selaa lähdekoodia

Merge pull request #105994 from syntaxerror247/split_container

Add option for a touch-friendly drag handle in `SplitContainer`
Rémi Verschelde 1 kuukausi sitten
vanhempi
commit
b25f609eed

+ 2 - 2
doc/classes/EditorSettings.xml

@@ -1119,8 +1119,8 @@
 			If [code]true[/code], enable two finger pan and scale gestures on touchscreen devices.
 			If [code]true[/code], enable two finger pan and scale gestures on touchscreen devices.
 			[b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices.
 			[b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices.
 		</member>
 		</member>
-		<member name="interface/touchscreen/increase_scrollbar_touch_area" type="bool" setter="" getter="">
-			If [code]true[/code], increases the scrollbar touch area to improve usability on touchscreen devices.
+		<member name="interface/touchscreen/enable_touch_optimizations" type="bool" setter="" getter="">
+			If [code]true[/code], increases the scrollbar touch area and enables a larger dragger for split containers to improve usability on touchscreen devices
 			[b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices.
 			[b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices.
 		</member>
 		</member>
 		<member name="interface/touchscreen/scale_gizmo_handles" type="float" setter="" getter="">
 		<member name="interface/touchscreen/scale_gizmo_handles" type="float" setter="" getter="">

+ 13 - 1
doc/classes/SplitContainer.xml

@@ -53,6 +53,9 @@
 		<member name="split_offset" type="int" setter="set_split_offset" getter="get_split_offset" default="0">
 		<member name="split_offset" type="int" setter="set_split_offset" getter="get_split_offset" default="0">
 			The initial offset of the splitting between the two [Control]s, with [code]0[/code] being at the end of the first [Control].
 			The initial offset of the splitting between the two [Control]s, with [code]0[/code] being at the end of the first [Control].
 		</member>
 		</member>
+		<member name="touch_dragger_enabled" type="bool" setter="set_touch_dragger_enabled" getter="is_touch_dragger_enabled" default="false">
+			If [code]true[/code], a touch-friendly drag handle will be enabled for better usability on smaller screens. Unlike the standard grabber, this drag handle overlaps the [SplitContainer]'s children and does not affect their minimum separation. The standard grabber will no longer be drawn when this option is enabled.
+		</member>
 		<member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" default="false">
 		<member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" default="false">
 			If [code]true[/code], the [SplitContainer] will arrange its children vertically, rather than horizontally.
 			If [code]true[/code], the [SplitContainer] will arrange its children vertically, rather than horizontally.
 			Can't be changed when using [HSplitContainer] and [VSplitContainer].
 			Can't be changed when using [HSplitContainer] and [VSplitContainer].
@@ -102,14 +105,23 @@
 			[b]Note:[/b] To obtain [theme_item separation] values less than the size of the grabber icon, for example a [code]1 px[/code] hairline, set [theme_item h_grabber] or [theme_item v_grabber] to a new [ImageTexture], which effectively sets the grabber icon size to [code]0 px[/code].
 			[b]Note:[/b] To obtain [theme_item separation] values less than the size of the grabber icon, for example a [code]1 px[/code] hairline, set [theme_item h_grabber] or [theme_item v_grabber] to a new [ImageTexture], which effectively sets the grabber icon size to [code]0 px[/code].
 		</theme_item>
 		</theme_item>
 		<theme_item name="grabber" data_type="icon" type="Texture2D">
 		<theme_item name="grabber" data_type="icon" type="Texture2D">
-			The icon used for the grabber drawn in the middle area.
+			The icon used for the grabber drawn in the middle area. This is only used in [HSplitContainer] and [VSplitContainer]. For [SplitContainer], see [theme_item h_grabber] and [theme_item v_grabber] instead.
 		</theme_item>
 		</theme_item>
 		<theme_item name="h_grabber" data_type="icon" type="Texture2D">
 		<theme_item name="h_grabber" data_type="icon" type="Texture2D">
 			The icon used for the grabber drawn in the middle area when [member vertical] is [code]false[/code].
 			The icon used for the grabber drawn in the middle area when [member vertical] is [code]false[/code].
 		</theme_item>
 		</theme_item>
+		<theme_item name="h_touch_dragger" data_type="icon" type="Texture2D">
+			The icon used for the drag handle when [member touch_dragger_enabled] is [code]true[/code] and [member vertical] is [code]false[/code].
+		</theme_item>
+		<theme_item name="touch_dragger" data_type="icon" type="Texture2D">
+			The icon used for the drag handle when [member touch_dragger_enabled] is [code]true[/code]. This is only used in [HSplitContainer] and [VSplitContainer]. For [SplitContainer], see [theme_item h_touch_dragger] and [theme_item v_touch_dragger] instead.
+		</theme_item>
 		<theme_item name="v_grabber" data_type="icon" type="Texture2D">
 		<theme_item name="v_grabber" data_type="icon" type="Texture2D">
 			The icon used for the grabber drawn in the middle area when [member vertical] is [code]true[/code].
 			The icon used for the grabber drawn in the middle area when [member vertical] is [code]true[/code].
 		</theme_item>
 		</theme_item>
+		<theme_item name="v_touch_dragger" data_type="icon" type="Texture2D">
+			The icon used for the drag handle when [member touch_dragger_enabled] is [code]true[/code] and [member vertical] is [code]true[/code].
+		</theme_item>
 		<theme_item name="split_bar_background" data_type="style" type="StyleBox">
 		<theme_item name="split_bar_background" data_type="style" type="StyleBox">
 			Determines the background of the split bar if its thickness is greater than zero.
 			Determines the background of the split bar if its thickness is greater than zero.
 		</theme_item>
 		</theme_item>

+ 6 - 0
editor/editor_dock_manager.cpp

@@ -114,6 +114,12 @@ void DockSplitContainer::remove_child_notify(Node *p_child) {
 	_update_visibility();
 	_update_visibility();
 }
 }
 
 
+DockSplitContainer::DockSplitContainer() {
+	if (EDITOR_GET("interface/touchscreen/enable_touch_optimizations")) {
+		callable_mp((SplitContainer *)this, &SplitContainer::set_touch_dragger_enabled).call_deferred(true);
+	}
+}
+
 void EditorDockManager::_dock_split_dragged(int p_offset) {
 void EditorDockManager::_dock_split_dragged(int p_offset) {
 	EditorNode::get_singleton()->save_editor_layout_delayed();
 	EditorNode::get_singleton()->save_editor_layout_delayed();
 }
 }

+ 3 - 0
editor/editor_dock_manager.h

@@ -52,6 +52,9 @@ protected:
 
 
 	virtual void add_child_notify(Node *p_child) override;
 	virtual void add_child_notify(Node *p_child) override;
 	virtual void remove_child_notify(Node *p_child) override;
 	virtual void remove_child_notify(Node *p_child) override;
+
+public:
+	DockSplitContainer();
 };
 };
 
 
 class DockContextPopup;
 class DockContextPopup;

+ 8 - 8
editor/editor_settings.cpp

@@ -592,19 +592,19 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 
 
 	// Touchscreen
 	// Touchscreen
 	bool has_touchscreen_ui = DisplayServer::get_singleton()->is_touchscreen_available();
 	bool has_touchscreen_ui = DisplayServer::get_singleton()->is_touchscreen_available();
+	bool is_native_touchscreen = has_touchscreen_ui && !OS::get_singleton()->has_feature("xr_editor"); // Disable some touchscreen settings by default for the XR Editor.
+
+	EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_touch_optimizations", is_native_touchscreen, "")
+	set_restart_if_changed("interface/touchscreen/enable_touch_optimizations", true);
+	EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_long_press_as_right_click", is_native_touchscreen, "")
+	set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true);
+
 	EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_pan_and_scale_gestures", has_touchscreen_ui, "")
 	EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_pan_and_scale_gestures", has_touchscreen_ui, "")
 	set_restart_if_changed("interface/touchscreen/enable_pan_and_scale_gestures", true);
 	set_restart_if_changed("interface/touchscreen/enable_pan_and_scale_gestures", true);
 	EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/touchscreen/scale_gizmo_handles", has_touchscreen_ui ? 3 : 1, "1,5,1")
 	EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/touchscreen/scale_gizmo_handles", has_touchscreen_ui ? 3 : 1, "1,5,1")
 	set_restart_if_changed("interface/touchscreen/scale_gizmo_handles", true);
 	set_restart_if_changed("interface/touchscreen/scale_gizmo_handles", true);
 
 
-	// Disable some touchscreen settings by default for the XR Editor.
-	bool is_native_touchscreen = has_touchscreen_ui && !OS::get_singleton()->has_feature("xr_editor");
-	EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_long_press_as_right_click", is_native_touchscreen, "")
-	set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true);
-	EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/increase_scrollbar_touch_area", is_native_touchscreen, "")
-	set_restart_if_changed("interface/touchscreen/increase_scrollbar_touch_area", true);
-
-	// Only available in the Android editor.
+	// Only available in the Android/XR editor.
 	String touch_actions_panel_hints = "Disabled:0,Embedded Panel:1,Floating Panel:2";
 	String touch_actions_panel_hints = "Disabled:0,Embedded Panel:1,Floating Panel:2";
 	EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "interface/touchscreen/touch_actions_panel", 1, touch_actions_panel_hints)
 	EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "interface/touchscreen/touch_actions_panel", 1, touch_actions_panel_hints)
 
 

+ 4 - 0
editor/editor_settings_dialog.cpp

@@ -927,6 +927,10 @@ EditorSettingsDialog::EditorSettingsDialog() {
 	inspector->get_inspector()->connect("property_edited", callable_mp(this, &EditorSettingsDialog::_settings_property_edited));
 	inspector->get_inspector()->connect("property_edited", callable_mp(this, &EditorSettingsDialog::_settings_property_edited));
 	inspector->get_inspector()->connect("restart_requested", callable_mp(this, &EditorSettingsDialog::_editor_restart_request));
 	inspector->get_inspector()->connect("restart_requested", callable_mp(this, &EditorSettingsDialog::_editor_restart_request));
 
 
+	if (EDITOR_GET("interface/touchscreen/enable_touch_optimizations")) {
+		inspector->set_touch_dragger_enabled(true);
+	}
+
 	restart_container = memnew(PanelContainer);
 	restart_container = memnew(PanelContainer);
 	tab_general->add_child(restart_container);
 	tab_general->add_child(restart_container);
 	HBoxContainer *restart_hb = memnew(HBoxContainer);
 	HBoxContainer *restart_hb = memnew(HBoxContainer);

+ 3 - 0
editor/export/project_export.cpp

@@ -1439,6 +1439,9 @@ ProjectExportDialog::ProjectExportDialog() {
 	HSplitContainer *hbox = memnew(HSplitContainer);
 	HSplitContainer *hbox = memnew(HSplitContainer);
 	main_vb->add_child(hbox);
 	main_vb->add_child(hbox);
 	hbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	hbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+	if (EDITOR_GET("interface/touchscreen/enable_touch_optimizations")) {
+		hbox->set_touch_dragger_enabled(true);
+	}
 
 
 	// Presets list.
 	// Presets list.
 
 

+ 4 - 0
editor/project_settings_editor.cpp

@@ -765,6 +765,10 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
 	general_settings_inspector->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request));
 	general_settings_inspector->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request));
 	general_editor->add_child(general_settings_inspector);
 	general_editor->add_child(general_settings_inspector);
 
 
+	if (EDITOR_GET("interface/touchscreen/enable_touch_optimizations")) {
+		general_settings_inspector->set_touch_dragger_enabled(true);
+	}
+
 	restart_container = memnew(PanelContainer);
 	restart_container = memnew(PanelContainer);
 	general_editor->add_child(restart_container);
 	general_editor->add_child(restart_container);
 
 

+ 4 - 4
editor/themes/editor_theme_manager.cpp

@@ -73,7 +73,7 @@ uint32_t EditorThemeManager::ThemeConfiguration::hash() {
 	hash = hash_murmur3_one_float(relationship_line_opacity, hash);
 	hash = hash_murmur3_one_float(relationship_line_opacity, hash);
 	hash = hash_murmur3_one_32(thumb_size, hash);
 	hash = hash_murmur3_one_32(thumb_size, hash);
 	hash = hash_murmur3_one_32(class_icon_size, hash);
 	hash = hash_murmur3_one_32(class_icon_size, hash);
-	hash = hash_murmur3_one_32((int)increase_scrollbar_touch_area, hash);
+	hash = hash_murmur3_one_32((int)enable_touch_optimizations, hash);
 	hash = hash_murmur3_one_float(gizmo_handle_scale, hash);
 	hash = hash_murmur3_one_float(gizmo_handle_scale, hash);
 	hash = hash_murmur3_one_32(color_picker_button_height, hash);
 	hash = hash_murmur3_one_32(color_picker_button_height, hash);
 	hash = hash_murmur3_one_float(subresource_hue_tint, hash);
 	hash = hash_murmur3_one_float(subresource_hue_tint, hash);
@@ -246,7 +246,7 @@ EditorThemeManager::ThemeConfiguration EditorThemeManager::_create_theme_config(
 	config.relationship_line_opacity = EDITOR_GET("interface/theme/relationship_line_opacity");
 	config.relationship_line_opacity = EDITOR_GET("interface/theme/relationship_line_opacity");
 	config.thumb_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
 	config.thumb_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
 	config.class_icon_size = 16 * EDSCALE;
 	config.class_icon_size = 16 * EDSCALE;
-	config.increase_scrollbar_touch_area = EDITOR_GET("interface/touchscreen/increase_scrollbar_touch_area");
+	config.enable_touch_optimizations = EDITOR_GET("interface/touchscreen/enable_touch_optimizations");
 	config.gizmo_handle_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
 	config.gizmo_handle_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
 	config.color_picker_button_height = 28 * EDSCALE;
 	config.color_picker_button_height = 28 * EDSCALE;
 	config.subresource_hue_tint = EDITOR_GET("docks/property_editor/subresource_hue_tint");
 	config.subresource_hue_tint = EDITOR_GET("docks/property_editor/subresource_hue_tint");
@@ -1437,7 +1437,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
 
 
 		// HScrollBar.
 		// HScrollBar.
 
 
-		if (p_config.increase_scrollbar_touch_area) {
+		if (p_config.enable_touch_optimizations) {
 			p_theme->set_stylebox("scroll", "HScrollBar", make_line_stylebox(p_config.separator_color, 50));
 			p_theme->set_stylebox("scroll", "HScrollBar", make_line_stylebox(p_config.separator_color, 50));
 		} else {
 		} else {
 			p_theme->set_stylebox("scroll", "HScrollBar", make_stylebox(p_theme->get_icon(SNAME("GuiScrollBg"), EditorStringName(EditorIcons)), 5, 5, 5, 5, -5, 1, -5, 1));
 			p_theme->set_stylebox("scroll", "HScrollBar", make_stylebox(p_theme->get_icon(SNAME("GuiScrollBg"), EditorStringName(EditorIcons)), 5, 5, 5, 5, -5, 1, -5, 1));
@@ -1456,7 +1456,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
 
 
 		// VScrollBar.
 		// VScrollBar.
 
 
-		if (p_config.increase_scrollbar_touch_area) {
+		if (p_config.enable_touch_optimizations) {
 			p_theme->set_stylebox("scroll", "VScrollBar", make_line_stylebox(p_config.separator_color, 50, 1, 1, true));
 			p_theme->set_stylebox("scroll", "VScrollBar", make_line_stylebox(p_config.separator_color, 50, 1, 1, true));
 		} else {
 		} else {
 			p_theme->set_stylebox("scroll", "VScrollBar", make_stylebox(p_theme->get_icon(SNAME("GuiScrollBg"), EditorStringName(EditorIcons)), 5, 5, 5, 5, 1, -5, 1, -5));
 			p_theme->set_stylebox("scroll", "VScrollBar", make_stylebox(p_theme->get_icon(SNAME("GuiScrollBg"), EditorStringName(EditorIcons)), 5, 5, 5, 5, 1, -5, 1, -5));

+ 1 - 1
editor/themes/editor_theme_manager.h

@@ -69,7 +69,7 @@ class EditorThemeManager {
 		float relationship_line_opacity = 1.0;
 		float relationship_line_opacity = 1.0;
 		int thumb_size = 16;
 		int thumb_size = 16;
 		int class_icon_size = 16;
 		int class_icon_size = 16;
-		bool increase_scrollbar_touch_area = false;
+		bool enable_touch_optimizations = false;
 		float gizmo_handle_scale = 1.0;
 		float gizmo_handle_scale = 1.0;
 		int color_picker_button_height = 28;
 		int color_picker_button_height = 28;
 		float subresource_hue_tint = 0.0;
 		float subresource_hue_tint = 0.0;

+ 89 - 1
scene/gui/split_container.cpp

@@ -30,6 +30,7 @@
 
 
 #include "split_container.h"
 #include "split_container.h"
 
 
+#include "scene/gui/texture_rect.h"
 #include "scene/main/viewport.h"
 #include "scene/main/viewport.h"
 #include "scene/theme/theme_db.h"
 #include "scene/theme/theme_db.h"
 
 
@@ -161,7 +162,7 @@ void SplitContainerDragger::_notification(int p_what) {
 		case NOTIFICATION_DRAW: {
 		case NOTIFICATION_DRAW: {
 			SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
 			SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
 			draw_style_box(sc->theme_cache.split_bar_background, split_bar_rect);
 			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)) {
+			if (sc->dragger_visibility == sc->DRAGGER_VISIBLE && (dragging || mouse_inside || !sc->theme_cache.autohide) && !sc->touch_dragger_enabled) {
 				Ref<Texture2D> tex = sc->_get_grabber_icon();
 				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);
 				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.
 				if (available_size - sc->drag_area_margin_begin - sc->drag_area_margin_end > 0) { // Draw the grabber only if it fits.
@@ -208,10 +209,26 @@ Ref<Texture2D> SplitContainer::_get_grabber_icon() const {
 	}
 	}
 }
 }
 
 
+Ref<Texture2D> SplitContainer::_get_touch_dragger_icon() const {
+	if (is_fixed) {
+		return theme_cache.touch_dragger_icon;
+	} else {
+		if (vertical) {
+			return theme_cache.touch_dragger_icon_v;
+		} else {
+			return theme_cache.touch_dragger_icon_h;
+		}
+	}
+}
+
 int SplitContainer::_get_separation() const {
 int SplitContainer::_get_separation() const {
 	if (dragger_visibility == DRAGGER_HIDDEN_COLLAPSED) {
 	if (dragger_visibility == DRAGGER_HIDDEN_COLLAPSED) {
 		return 0;
 		return 0;
 	}
 	}
+
+	if (touch_dragger_enabled) {
+		return theme_cache.separation;
+	}
 	// DRAGGER_VISIBLE or DRAGGER_HIDDEN.
 	// DRAGGER_VISIBLE or DRAGGER_HIDDEN.
 	Ref<Texture2D> g = _get_grabber_icon();
 	Ref<Texture2D> g = _get_grabber_icon();
 	return MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width());
 	return MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width());
@@ -267,6 +284,9 @@ void SplitContainer::_resort() {
 		return;
 		return;
 	}
 	}
 	dragging_area_control->set_visible(!collapsed);
 	dragging_area_control->set_visible(!collapsed);
+	if (touch_dragger_enabled) {
+		touch_dragger->set_visible(dragging_enabled);
+	}
 
 
 	_compute_split_offset(false); // This recalculates and sets computed_split_offset.
 	_compute_split_offset(false); // This recalculates and sets computed_split_offset.
 
 
@@ -406,7 +426,15 @@ bool SplitContainer::is_collapsed() const {
 
 
 void SplitContainer::set_vertical(bool p_vertical) {
 void SplitContainer::set_vertical(bool p_vertical) {
 	ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + ".");
 	ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + ".");
+	if (vertical == p_vertical) {
+		return;
+	}
 	vertical = p_vertical;
 	vertical = p_vertical;
+	if (touch_dragger) {
+		touch_dragger->set_texture(_get_touch_dragger_icon());
+		touch_dragger->set_anchors_and_offsets_preset(Control::PRESET_CENTER);
+		touch_dragger->set_default_cursor_shape(vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT);
+	}
 	update_minimum_size();
 	update_minimum_size();
 	_resort();
 	_resort();
 }
 }
@@ -504,6 +532,59 @@ bool SplitContainer::is_show_drag_area_enabled() const {
 	return show_drag_area;
 	return show_drag_area;
 }
 }
 
 
+void SplitContainer::set_touch_dragger_enabled(bool p_enabled) {
+	if (touch_dragger_enabled == p_enabled) {
+		return;
+	}
+	touch_dragger_enabled = p_enabled;
+	if (p_enabled) {
+		touch_dragger = memnew(TextureRect);
+		touch_dragger->set_texture(_get_touch_dragger_icon());
+		touch_dragger->set_anchors_and_offsets_preset(Control::PRESET_CENTER);
+		touch_dragger->set_default_cursor_shape(vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT);
+		touch_dragger->set_modulate(Color(1, 1, 1, 0.3));
+		touch_dragger->connect(SceneStringName(gui_input), callable_mp(this, &SplitContainer::_touch_dragger_gui_input));
+		touch_dragger->connect(SceneStringName(mouse_exited), callable_mp(this, &SplitContainer::_touch_dragger_mouse_exited));
+		dragging_area_control->add_child(touch_dragger, false, Node::INTERNAL_MODE_FRONT);
+	} else {
+		if (touch_dragger) {
+			touch_dragger->queue_free();
+			touch_dragger = nullptr;
+		}
+	}
+	dragging_area_control->queue_redraw();
+}
+
+bool SplitContainer::is_touch_dragger_enabled() const {
+	return touch_dragger_enabled;
+}
+
+void SplitContainer::_touch_dragger_mouse_exited() {
+	if (!dragging_area_control->dragging) {
+		touch_dragger->set_modulate(Color(1, 1, 1, 0.3));
+	}
+}
+
+void SplitContainer::_touch_dragger_gui_input(const Ref<InputEvent> &p_event) {
+	if (!touch_dragger_enabled) {
+		return;
+	}
+	Ref<InputEventMouseMotion> mm = p_event;
+	Ref<InputEventMouseButton> mb = p_event;
+
+	if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
+		if (mb->is_pressed()) {
+			touch_dragger->set_modulate(Color(1, 1, 1, 1));
+		} else {
+			touch_dragger->set_modulate(Color(1, 1, 1, 0.3));
+		}
+	}
+
+	if (mm.is_valid() && !dragging_area_control->dragging) {
+		touch_dragger->set_modulate(Color(1, 1, 1, 0.6));
+	}
+}
+
 void SplitContainer::_bind_methods() {
 void SplitContainer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_split_offset", "offset"), &SplitContainer::set_split_offset);
 	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);
 	ClassDB::bind_method(D_METHOD("get_split_offset"), &SplitContainer::get_split_offset);
@@ -535,6 +616,9 @@ void SplitContainer::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("get_drag_area_control"), &SplitContainer::get_drag_area_control);
 	ClassDB::bind_method(D_METHOD("get_drag_area_control"), &SplitContainer::get_drag_area_control);
 
 
+	ClassDB::bind_method(D_METHOD("set_touch_dragger_enabled", "enabled"), &SplitContainer::set_touch_dragger_enabled);
+	ClassDB::bind_method(D_METHOD("is_touch_dragger_enabled"), &SplitContainer::is_touch_dragger_enabled);
+
 	ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset")));
 	ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset")));
 	ADD_SIGNAL(MethodInfo("drag_started"));
 	ADD_SIGNAL(MethodInfo("drag_started"));
 	ADD_SIGNAL(MethodInfo("drag_ended"));
 	ADD_SIGNAL(MethodInfo("drag_ended"));
@@ -544,6 +628,7 @@ void SplitContainer::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dragging_enabled"), "set_dragging_enabled", "is_dragging_enabled");
 	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::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_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "touch_dragger_enabled"), "set_touch_dragger_enabled", "is_touch_dragger_enabled");
 
 
 	ADD_GROUP("Drag Area", "drag_area_");
 	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_begin", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_margin_begin", "get_drag_area_margin_begin");
@@ -558,6 +643,9 @@ void SplitContainer::_bind_methods() {
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SplitContainer, separation);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SplitContainer, separation);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SplitContainer, minimum_grab_thickness);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SplitContainer, minimum_grab_thickness);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SplitContainer, autohide);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SplitContainer, autohide);
+	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, touch_dragger_icon, "touch_dragger");
+	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, touch_dragger_icon_h, "h_touch_dragger");
+	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, touch_dragger_icon_v, "v_touch_dragger");
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon, "grabber");
 	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_h, "h_grabber");
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon_v, "v_grabber");
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon_v, "v_grabber");

+ 14 - 0
scene/gui/split_container.h

@@ -32,6 +32,8 @@
 
 
 #include "scene/gui/container.h"
 #include "scene/gui/container.h"
 
 
+class TextureRect;
+
 class SplitContainerDragger : public Control {
 class SplitContainerDragger : public Control {
 	GDCLASS(SplitContainerDragger, Control);
 	GDCLASS(SplitContainerDragger, Control);
 	friend class SplitContainer;
 	friend class SplitContainer;
@@ -82,10 +84,16 @@ private:
 
 
 	SplitContainerDragger *dragging_area_control = nullptr;
 	SplitContainerDragger *dragging_area_control = nullptr;
 
 
+	bool touch_dragger_enabled = false;
+	TextureRect *touch_dragger = nullptr;
+
 	struct ThemeCache {
 	struct ThemeCache {
 		int separation = 0;
 		int separation = 0;
 		int minimum_grab_thickness = 0;
 		int minimum_grab_thickness = 0;
 		bool autohide = false;
 		bool autohide = false;
+		Ref<Texture2D> touch_dragger_icon;
+		Ref<Texture2D> touch_dragger_icon_h;
+		Ref<Texture2D> touch_dragger_icon_v;
 		Ref<Texture2D> grabber_icon;
 		Ref<Texture2D> grabber_icon;
 		Ref<Texture2D> grabber_icon_h;
 		Ref<Texture2D> grabber_icon_h;
 		Ref<Texture2D> grabber_icon_v;
 		Ref<Texture2D> grabber_icon_v;
@@ -94,6 +102,9 @@ private:
 	} theme_cache;
 	} theme_cache;
 
 
 	Ref<Texture2D> _get_grabber_icon() const;
 	Ref<Texture2D> _get_grabber_icon() const;
+	Ref<Texture2D> _get_touch_dragger_icon() const;
+	void _touch_dragger_mouse_exited();
+	void _touch_dragger_gui_input(const Ref<InputEvent> &p_event);
 	void _compute_split_offset(bool p_clamp);
 	void _compute_split_offset(bool p_clamp);
 	int _get_separation() const;
 	int _get_separation() const;
 	void _resort();
 	void _resort();
@@ -142,6 +153,9 @@ public:
 
 
 	Control *get_drag_area_control() { return dragging_area_control; }
 	Control *get_drag_area_control() { return dragging_area_control; }
 
 
+	void set_touch_dragger_enabled(bool p_enabled);
+	bool is_touch_dragger_enabled() const;
+
 	SplitContainer(bool p_vertical = false);
 	SplitContainer(bool p_vertical = false);
 };
 };
 
 

+ 4 - 0
scene/theme/default_theme.cpp

@@ -1192,6 +1192,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 
 
 	// Containers
 	// Containers
 
 
+	theme->set_icon("h_touch_dragger", "SplitContainer", icons["h_dragger"]);
+	theme->set_icon("v_touch_dragger", "SplitContainer", icons["v_dragger"]);
+	theme->set_icon("touch_dragger", "VSplitContainer", icons["v_dragger"]);
+	theme->set_icon("touch_dragger", "HSplitContainer", icons["h_dragger"]);
 	theme->set_icon("h_grabber", "SplitContainer", icons["hsplitter"]);
 	theme->set_icon("h_grabber", "SplitContainer", icons["hsplitter"]);
 	theme->set_icon("v_grabber", "SplitContainer", icons["vsplitter"]);
 	theme->set_icon("v_grabber", "SplitContainer", icons["vsplitter"]);
 	theme->set_icon("grabber", "VSplitContainer", icons["vsplitter"]);
 	theme->set_icon("grabber", "VSplitContainer", icons["vsplitter"]);

+ 1 - 0
scene/theme/icons/h_dragger.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="30" height="60"><rect width="28" height="58" x="1" y="1" fill="#e0e0e0" fill-opacity=".6" stroke="#000" stroke-width=".8" rx="7"/></svg>

+ 1 - 0
scene/theme/icons/v_dragger.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="60" height="30"><rect width="58" height="28" x="1" y="1" fill="#e0e0e0" fill-opacity=".6" stroke="#000" stroke-width=".8" rx="7"/></svg>