소스 검색

Merge pull request #50146 from reduz/fix-tree

Clean up Tree
Rémi Verschelde 4 년 전
부모
커밋
6bf26fb74c

+ 44 - 0
doc/classes/Tree.xml

@@ -96,6 +96,14 @@
 				Returns the column index at [code]position[/code], or -1 if no item is there.
 			</description>
 		</method>
+		<method name="get_column_expand_ratio" qualifiers="const">
+			<return type="int">
+			</return>
+			<argument index="0" name="column" type="int">
+			</argument>
+			<description>
+			</description>
+		</method>
 		<method name="get_column_title" qualifiers="const">
 			<return type="String">
 			</return>
@@ -264,6 +272,22 @@
 				To tell whether a column of an item is selected, use [method TreeItem.is_selected].
 			</description>
 		</method>
+		<method name="is_column_clipping_content" qualifiers="const">
+			<return type="bool">
+			</return>
+			<argument index="0" name="column" type="int">
+			</argument>
+			<description>
+			</description>
+		</method>
+		<method name="is_column_expanding" qualifiers="const">
+			<return type="bool">
+			</return>
+			<argument index="0" name="column" type="int">
+			</argument>
+			<description>
+			</description>
+		</method>
 		<method name="scroll_to_item">
 			<return type="void">
 			</return>
@@ -272,6 +296,16 @@
 			<description>
 			</description>
 		</method>
+		<method name="set_column_clip_content">
+			<return type="void">
+			</return>
+			<argument index="0" name="column" type="int">
+			</argument>
+			<argument index="1" name="enable" type="bool">
+			</argument>
+			<description>
+			</description>
+		</method>
 		<method name="set_column_custom_minimum_width">
 			<return type="void">
 			</return>
@@ -294,6 +328,16 @@
 				If [code]true[/code], the column will have the "Expand" flag of [Control]. Columns that have the "Expand" flag will use their "min_width" in a similar fashion to [member Control.size_flags_stretch_ratio].
 			</description>
 		</method>
+		<method name="set_column_expand_ratio">
+			<return type="void">
+			</return>
+			<argument index="0" name="column" type="int">
+			</argument>
+			<argument index="1" name="ratio" type="int">
+			</argument>
+			<description>
+			</description>
+		</method>
 		<method name="set_column_title">
 			<return type="void">
 			</return>

+ 1 - 0
editor/action_map_editor.cpp

@@ -1123,6 +1123,7 @@ ActionMapEditor::ActionMapEditor() {
 	action_tree->set_hide_root(true);
 	action_tree->set_column_titles_visible(true);
 	action_tree->set_column_title(0, TTR("Action"));
+	action_tree->set_column_clip_content(0, true);
 	action_tree->set_column_title(1, TTR("Deadzone"));
 	action_tree->set_column_expand(1, false);
 	action_tree->set_column_custom_minimum_width(1, 80 * EDSCALE);

+ 5 - 0
editor/debugger/editor_network_profiler.cpp

@@ -178,18 +178,23 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
 	counters_display->set_column_titles_visible(true);
 	counters_display->set_column_title(0, TTR("Node"));
 	counters_display->set_column_expand(0, true);
+	counters_display->set_column_clip_content(0, true);
 	counters_display->set_column_custom_minimum_width(0, 60 * EDSCALE);
 	counters_display->set_column_title(1, TTR("Incoming RPC"));
 	counters_display->set_column_expand(1, false);
+	counters_display->set_column_clip_content(1, true);
 	counters_display->set_column_custom_minimum_width(1, 120 * EDSCALE);
 	counters_display->set_column_title(2, TTR("Incoming RSET"));
 	counters_display->set_column_expand(2, false);
+	counters_display->set_column_clip_content(2, true);
 	counters_display->set_column_custom_minimum_width(2, 120 * EDSCALE);
 	counters_display->set_column_title(3, TTR("Outgoing RPC"));
 	counters_display->set_column_expand(3, false);
+	counters_display->set_column_clip_content(3, true);
 	counters_display->set_column_custom_minimum_width(3, 120 * EDSCALE);
 	counters_display->set_column_title(4, TTR("Outgoing RSET"));
 	counters_display->set_column_expand(4, false);
+	counters_display->set_column_clip_content(4, true);
 	counters_display->set_column_custom_minimum_width(4, 120 * EDSCALE);
 	add_child(counters_display);
 

+ 6 - 3
editor/debugger/editor_profiler.cpp

@@ -631,13 +631,16 @@ EditorProfiler::EditorProfiler() {
 	variables->set_column_titles_visible(true);
 	variables->set_column_title(0, TTR("Name"));
 	variables->set_column_expand(0, true);
-	variables->set_column_custom_minimum_width(0, 60 * EDSCALE);
+	variables->set_column_clip_content(0, true);
+	variables->set_column_expand_ratio(0, 60);
 	variables->set_column_title(1, TTR("Time"));
 	variables->set_column_expand(1, false);
-	variables->set_column_custom_minimum_width(1, 100 * EDSCALE);
+	variables->set_column_clip_content(1, true);
+	variables->set_column_expand_ratio(1, 100);
 	variables->set_column_title(2, TTR("Calls"));
 	variables->set_column_expand(2, false);
-	variables->set_column_custom_minimum_width(2, 60 * EDSCALE);
+	variables->set_column_clip_content(2, true);
+	variables->set_column_expand_ratio(2, 60);
 	variables->connect("item_edited", callable_mp(this, &EditorProfiler::_item_edited));
 
 	graph = memnew(TextureRect);

+ 3 - 0
editor/debugger/editor_visual_profiler.cpp

@@ -773,12 +773,15 @@ EditorVisualProfiler::EditorVisualProfiler() {
 	variables->set_column_titles_visible(true);
 	variables->set_column_title(0, TTR("Name"));
 	variables->set_column_expand(0, true);
+	variables->set_column_clip_content(0, true);
 	variables->set_column_custom_minimum_width(0, 60);
 	variables->set_column_title(1, TTR("CPU"));
 	variables->set_column_expand(1, false);
+	variables->set_column_clip_content(1, true);
 	variables->set_column_custom_minimum_width(1, 60 * EDSCALE);
 	variables->set_column_title(2, TTR("GPU"));
 	variables->set_column_expand(2, false);
+	variables->set_column_clip_content(2, true);
 	variables->set_column_custom_minimum_width(2, 60 * EDSCALE);
 	variables->connect("cell_selected", callable_mp(this, &EditorVisualProfiler::_item_selected));
 

+ 2 - 0
editor/debugger/script_editor_debugger.cpp

@@ -1644,8 +1644,10 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
 
 		error_tree->set_column_expand(0, false);
 		error_tree->set_column_custom_minimum_width(0, 140);
+		error_tree->set_column_clip_content(0, true);
 
 		error_tree->set_column_expand(1, true);
+		error_tree->set_column_clip_content(1, true);
 
 		error_tree->set_select_mode(Tree::SELECT_ROW);
 		error_tree->set_hide_root(true);

+ 7 - 1
editor/dependency_editor.cpp

@@ -226,7 +226,11 @@ DependencyEditor::DependencyEditor() {
 	tree->set_columns(2);
 	tree->set_column_titles_visible(true);
 	tree->set_column_title(0, TTR("Resource"));
+	tree->set_column_clip_content(0, true);
+	tree->set_column_expand_ratio(0, 2);
 	tree->set_column_title(1, TTR("Path"));
+	tree->set_column_clip_content(1, true);
+	tree->set_column_expand_ratio(1, 1);
 	tree->set_hide_root(true);
 	tree->connect("button_pressed", callable_mp(this, &DependencyEditor::_load_pressed));
 
@@ -769,9 +773,11 @@ OrphanResourcesDialog::OrphanResourcesDialog() {
 	files = memnew(Tree);
 	files->set_columns(2);
 	files->set_column_titles_visible(true);
-	files->set_column_custom_minimum_width(1, 100);
+	files->set_column_custom_minimum_width(1, 100 * EDSCALE);
 	files->set_column_expand(0, true);
+	files->set_column_clip_content(0, true);
 	files->set_column_expand(1, false);
+	files->set_column_clip_content(1, true);
 	files->set_column_title(0, TTR("Resource"));
 	files->set_column_title(1, TTR("Owns"));
 	files->set_hide_root(true);

+ 3 - 5
editor/editor_autoload_settings.cpp

@@ -882,19 +882,17 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
 
 	tree->set_column_title(0, TTR("Name"));
 	tree->set_column_expand(0, true);
-	tree->set_column_custom_minimum_width(0, 100 * EDSCALE);
+	tree->set_column_expand_ratio(0, 1);
 
 	tree->set_column_title(1, TTR("Path"));
 	tree->set_column_expand(1, true);
-	tree->set_column_custom_minimum_width(1, 100 * EDSCALE);
+	tree->set_column_clip_content(1, true);
+	tree->set_column_expand_ratio(1, 2);
 
 	tree->set_column_title(2, TTR("Global Variable"));
 	tree->set_column_expand(2, false);
-	// Reserve enough space for translations of "Global Variable" which may be longer.
-	tree->set_column_custom_minimum_width(2, 150 * EDSCALE);
 
 	tree->set_column_expand(3, false);
-	tree->set_column_custom_minimum_width(3, 120 * EDSCALE);
 
 	tree->connect("cell_selected", callable_mp(this, &EditorAutoloadSettings::_autoload_selected));
 	tree->connect("item_edited", callable_mp(this, &EditorAutoloadSettings::_autoload_edited));

+ 2 - 0
editor/editor_help_search.cpp

@@ -237,9 +237,11 @@ EditorHelpSearch::EditorHelpSearch() {
 	results_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	results_tree->set_columns(2);
 	results_tree->set_column_title(0, TTR("Name"));
+	results_tree->set_column_clip_content(0, true);
 	results_tree->set_column_title(1, TTR("Member Type"));
 	results_tree->set_column_expand(1, false);
 	results_tree->set_column_custom_minimum_width(1, 150 * EDSCALE);
+	results_tree->set_column_clip_content(1, true);
 	results_tree->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
 	results_tree->set_hide_root(true);
 	results_tree->set_select_mode(Tree::SELECT_ROW);

+ 5 - 0
editor/editor_plugin_settings.cpp

@@ -212,10 +212,15 @@ EditorPluginSettings::EditorPluginSettings() {
 	plugin_list->set_column_title(3, TTR("Status:"));
 	plugin_list->set_column_title(4, TTR("Edit:"));
 	plugin_list->set_column_expand(0, true);
+	plugin_list->set_column_clip_content(0, true);
 	plugin_list->set_column_expand(1, false);
+	plugin_list->set_column_clip_content(1, true);
 	plugin_list->set_column_expand(2, false);
+	plugin_list->set_column_clip_content(2, true);
 	plugin_list->set_column_expand(3, false);
+	plugin_list->set_column_clip_content(3, true);
 	plugin_list->set_column_expand(4, false);
+	plugin_list->set_column_clip_content(4, true);
 	plugin_list->set_column_custom_minimum_width(1, 100 * EDSCALE);
 	plugin_list->set_column_custom_minimum_width(2, 250 * EDSCALE);
 	plugin_list->set_column_custom_minimum_width(3, 80 * EDSCALE);

+ 2 - 0
editor/localization_editor.cpp

@@ -728,7 +728,9 @@ LocalizationEditor::LocalizationEditor() {
 		translation_remap_options->set_column_title(1, TTR("Locale"));
 		translation_remap_options->set_column_titles_visible(true);
 		translation_remap_options->set_column_expand(0, true);
+		translation_remap_options->set_column_clip_content(0, true);
 		translation_remap_options->set_column_expand(1, false);
+		translation_remap_options->set_column_clip_content(1, true);
 		translation_remap_options->set_column_custom_minimum_width(1, 200);
 		translation_remap_options->connect("item_edited", callable_mp(this, &LocalizationEditor::_translation_res_option_changed));
 		translation_remap_options->connect("button_pressed", callable_mp(this, &LocalizationEditor::_translation_res_option_delete));

+ 4 - 2
editor/plugins/animation_player_editor_plugin.cpp

@@ -569,8 +569,10 @@ void AnimationPlayerEditor::_animation_blend() {
 	blend_editor.dialog->popup_centered(Size2(400, 400) * EDSCALE);
 
 	blend_editor.tree->set_hide_root(true);
-	blend_editor.tree->set_column_custom_minimum_width(0, 10);
-	blend_editor.tree->set_column_custom_minimum_width(1, 3);
+	blend_editor.tree->set_column_expand_ratio(0, 10);
+	blend_editor.tree->set_column_clip_content(0, true);
+	blend_editor.tree->set_column_expand_ratio(1, 3);
+	blend_editor.tree->set_column_clip_content(1, true);
 
 	List<StringName> anims;
 	player->get_animation_list(&anims);

+ 4 - 2
editor/plugins/resource_preloader_editor_plugin.cpp

@@ -367,8 +367,10 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() {
 	tree = memnew(Tree);
 	tree->connect("button_pressed", callable_mp(this, &ResourcePreloaderEditor::_cell_button_pressed));
 	tree->set_columns(2);
-	tree->set_column_custom_minimum_width(0, 2);
-	tree->set_column_custom_minimum_width(1, 3);
+	tree->set_column_expand_ratio(0, 2);
+	tree->set_column_clip_content(0, true);
+	tree->set_column_expand_ratio(1, 3);
+	tree->set_column_clip_content(1, true);
 	tree->set_column_expand(0, true);
 	tree->set_column_expand(1, true);
 	tree->set_v_size_flags(SIZE_EXPAND_FILL);

+ 3 - 0
editor/plugins/theme_editor_plugin.cpp

@@ -930,11 +930,14 @@ ThemeItemImportTree::ThemeItemImportTree() {
 	import_items_tree->set_column_title(IMPORT_ITEM, TTR("Import"));
 	import_items_tree->set_column_title(IMPORT_ITEM_DATA, TTR("With Data"));
 	import_items_tree->set_column_expand(0, true);
+	import_items_tree->set_column_clip_content(0, true);
 	import_items_tree->set_column_expand(IMPORT_ITEM, false);
 	import_items_tree->set_column_expand(IMPORT_ITEM_DATA, false);
 	import_items_tree->set_column_custom_minimum_width(0, 160 * EDSCALE);
 	import_items_tree->set_column_custom_minimum_width(IMPORT_ITEM, 80 * EDSCALE);
 	import_items_tree->set_column_custom_minimum_width(IMPORT_ITEM_DATA, 80 * EDSCALE);
+	import_items_tree->set_column_clip_content(1, true);
+	import_items_tree->set_column_clip_content(2, true);
 
 	ScrollContainer *import_bulk_sc = memnew(ScrollContainer);
 	import_bulk_sc->set_custom_minimum_size(Size2(260.0, 0.0) * EDSCALE);

+ 119 - 29
scene/gui/tree.cpp

@@ -975,6 +975,9 @@ Size2 TreeItem::get_minimum_size(int p_column) {
 	}
 
 	// Icon.
+	if (cell.mode == CELL_MODE_CHECK) {
+		size.width += tree->cache.checked->get_width() + tree->cache.hseparation;
+	}
 	if (cell.icon.is_valid()) {
 		Size2i icon_size = cell.get_icon_size();
 		if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) {
@@ -1249,6 +1252,9 @@ void Tree::update_cache() {
 	cache.item_margin = get_theme_constant("item_margin");
 	cache.button_margin = get_theme_constant("button_margin");
 
+	cache.font_outline_color = get_theme_color("font_outline_color");
+	cache.font_outline_size = get_theme_constant("outline_size");
+
 	cache.draw_guides = get_theme_constant("draw_guides");
 	cache.guide_color = get_theme_color("guide_color");
 	cache.draw_relationship_lines = get_theme_constant("draw_relationship_lines");
@@ -1510,7 +1516,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
 	int htotal = 0;
 
 	int label_h = compute_item_height(p_item);
-	bool rtl = is_layout_rtl();
+	bool rtl = cache.rtl;
 
 	/* Calculate height of the label part */
 	label_h += cache.vseparation;
@@ -1556,6 +1562,20 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
 				}
 			}
 
+			if (!rtl && p_item->cells[i].buttons.size()) {
+				int button_w = 0;
+				for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) {
+					Ref<Texture2D> b = p_item->cells[i].buttons[j].texture;
+					button_w += b->get_size().width + cache.button_pressed->get_minimum_size().width + cache.button_margin;
+				}
+
+				int total_ofs = ofs - cache.offset.x;
+
+				if (total_ofs + w > p_draw_size.width) {
+					w = MAX(button_w, p_draw_size.width - total_ofs);
+				}
+			}
+
 			int bw = 0;
 			for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) {
 				Ref<Texture2D> b = p_item->cells[i].buttons[j].texture;
@@ -1680,8 +1700,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
 			}
 
 			Color col = p_item->cells[i].custom_color ? p_item->cells[i].color : get_theme_color(p_item->cells[i].selected ? "font_selected_color" : "font_color");
-			Color font_outline_color = get_theme_color("font_outline_color");
-			int outline_size = get_theme_constant("outline_size");
+			Color font_outline_color = cache.font_outline_color;
+			int outline_size = cache.font_outline_size;
 			Color icon_col = p_item->cells[i].icon_color;
 
 			if (p_item->cells[i].dirty) {
@@ -2139,9 +2159,16 @@ void Tree::_range_click_timeout() {
 		Ref<InputEventMouseButton> mb;
 		mb.instantiate();
 
+		int x_limit = get_size().width - cache.bg->get_minimum_size().width;
+		if (h_scroll->is_visible()) {
+			x_limit -= h_scroll->get_minimum_size().width;
+		}
+
+		cache.rtl = is_layout_rtl();
+
 		propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case)
 		blocked++;
-		propagate_mouse_event(pos + cache.offset, 0, 0, false, root, MOUSE_BUTTON_LEFT, mb);
+		propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, false, root, MOUSE_BUTTON_LEFT, mb);
 		blocked--;
 
 		if (range_click_timer->is_one_shot()) {
@@ -2164,7 +2191,7 @@ void Tree::_range_click_timeout() {
 	}
 }
 
-int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod) {
+int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod) {
 	int item_h = compute_item_height(p_item) + cache.vseparation;
 
 	bool skip = (p_item == root && hide_root);
@@ -2189,6 +2216,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
 		int col = -1;
 		int col_ofs = 0;
 		int col_width = 0;
+
+		int limit_w = x_limit;
+
 		for (int i = 0; i < columns.size(); i++) {
 			col_width = get_column_width(i);
 
@@ -2204,6 +2234,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
 			if (x > col_width) {
 				col_ofs += col_width;
 				x -= col_width;
+				limit_w -= col_width;
 				continue;
 			}
 
@@ -2217,10 +2248,12 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
 			int margin = x_ofs + cache.item_margin; //-cache.hseparation;
 			//int lm = cache.bg->get_margin(SIDE_LEFT);
 			col_width -= margin;
+			limit_w -= margin;
 			col_ofs += margin;
 			x -= margin;
 		} else {
 			col_width -= cache.hseparation;
+			limit_w -= cache.hseparation;
 			x -= cache.hseparation;
 		}
 
@@ -2234,6 +2267,16 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
 		bool already_selected = c.selected;
 		bool already_cursor = (p_item == selected_item) && col == selected_col;
 
+		if (!cache.rtl && p_item->cells[col].buttons.size()) {
+			int button_w = 0;
+			for (int j = p_item->cells[col].buttons.size() - 1; j >= 0; j--) {
+				Ref<Texture2D> b = p_item->cells[col].buttons[j].texture;
+				button_w += b->get_size().width + cache.button_pressed->get_minimum_size().width + cache.button_margin;
+			}
+
+			col_width = MAX(button_w, MIN(limit_w, col_width));
+		}
+
 		for (int j = c.buttons.size() - 1; j >= 0; j--) {
 			Ref<Texture2D> b = c.buttons[j].texture;
 			int w = b->get_size().width + cache.button_pressed->get_minimum_size().width;
@@ -2255,6 +2298,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
 				//emit_signal("button_pressed");
 				return -1;
 			}
+
 			col_width -= w + cache.button_margin;
 		}
 
@@ -2465,7 +2509,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
 			TreeItem *c = p_item->first_child;
 
 			while (c) {
-				int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, p_double_click, c, p_button, p_mod);
+				int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, x_limit, p_double_click, c, p_button, p_mod);
 
 				if (child_h < 0) {
 					return -1; // break, stop propagating, no need to anymore
@@ -3143,8 +3187,14 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
 				pressing_for_editor = false;
 				propagate_mouse_activated = false;
 
+				int x_limit = get_size().width - cache.bg->get_minimum_size().width;
+				if (h_scroll->is_visible()) {
+					x_limit -= h_scroll->get_minimum_size().width;
+				}
+
+				cache.rtl = is_layout_rtl();
 				blocked++;
-				propagate_mouse_event(pos + cache.offset, 0, 0, b->is_double_click(), root, b->get_button_index(), b);
+				propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, b->is_double_click(), root, b->get_button_index(), b);
 				blocked--;
 
 				if (pressing_for_editor) {
@@ -3496,6 +3546,9 @@ void Tree::_notification(int p_what) {
 		Point2 draw_ofs;
 		draw_ofs += bg->get_offset();
 		Size2 draw_size = get_size() - bg->get_minimum_size();
+		if (h_scroll->is_visible()) {
+			draw_size.width -= h_scroll->get_minimum_size().width;
+		}
 
 		bg->draw(ci, Rect2(Point2(), get_size()));
 
@@ -3504,6 +3557,8 @@ void Tree::_notification(int p_what) {
 		draw_ofs.y += tbh;
 		draw_size.y -= tbh;
 
+		cache.rtl = is_layout_rtl();
+
 		if (root && get_size().x > 0 && get_size().y > 0) {
 			draw_item(Point2(), draw_ofs, draw_size, root);
 		}
@@ -3515,7 +3570,7 @@ void Tree::_notification(int p_what) {
 				Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button);
 				Ref<Font> f = cache.tb_font;
 				Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh);
-				if (is_layout_rtl()) {
+				if (cache.rtl) {
 					tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x;
 				}
 				sb->draw(ci, tbrect);
@@ -3761,6 +3816,36 @@ void Tree::set_column_expand(int p_column, bool p_expand) {
 	update();
 }
 
+void Tree::set_column_expand_ratio(int p_column, int p_ratio) {
+	ERR_FAIL_INDEX(p_column, columns.size());
+	columns.write[p_column].expand_ratio = p_ratio;
+	update();
+}
+
+void Tree::set_column_clip_content(int p_column, bool p_fit) {
+	ERR_FAIL_INDEX(p_column, columns.size());
+
+	columns.write[p_column].clip_content = p_fit;
+	update();
+}
+
+bool Tree::is_column_expanding(int p_column) const {
+	ERR_FAIL_INDEX_V(p_column, columns.size(), false);
+
+	return columns[p_column].expand;
+}
+int Tree::get_column_expand_ratio(int p_column) const {
+	ERR_FAIL_INDEX_V(p_column, columns.size(), 1);
+
+	return columns[p_column].expand_ratio;
+}
+
+bool Tree::is_column_clipping_content(int p_column) const {
+	ERR_FAIL_INDEX_V(p_column, columns.size(), false);
+
+	return columns[p_column].clip_content;
+}
+
 TreeItem *Tree::get_selected() const {
 	return selected_item;
 }
@@ -3820,11 +3905,14 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) {
 int Tree::get_column_minimum_width(int p_column) const {
 	ERR_FAIL_INDEX_V(p_column, columns.size(), -1);
 
-	if (columns[p_column].custom_min_width != 0) {
-		return columns[p_column].custom_min_width;
-	} else {
+	int min_width = columns[p_column].custom_min_width;
+
+	if (show_column_titles) {
+		min_width = MAX(cache.font->get_string_size(columns[p_column].title).width, min_width);
+	}
+
+	if (!columns[p_column].clip_content) {
 		int depth = 0;
-		int min_width = 0;
 		TreeItem *next;
 		for (TreeItem *item = get_root(); item; item = next) {
 			next = item->get_next_visible();
@@ -3848,13 +3936,16 @@ int Tree::get_column_minimum_width(int p_column) const {
 			}
 			min_width = MAX(min_width, item_size.width);
 		}
-		return min_width;
 	}
+
+	return min_width;
 }
 
 int Tree::get_column_width(int p_column) const {
 	ERR_FAIL_INDEX_V(p_column, columns.size(), -1);
 
+	int column_width = get_column_minimum_width(p_column);
+
 	if (columns[p_column].expand) {
 		int expand_area = get_size().width;
 
@@ -3868,31 +3959,24 @@ int Tree::get_column_width(int p_column) const {
 			expand_area -= v_scroll->get_combined_minimum_size().width;
 		}
 
-		int expanding_columns = 0;
 		int expanding_total = 0;
 
 		for (int i = 0; i < columns.size(); i++) {
-			if (!columns[i].expand) {
-				expand_area -= get_column_minimum_width(i);
-			} else {
-				expanding_total += get_column_minimum_width(i);
-				expanding_columns++;
+			expand_area -= get_column_minimum_width(i);
+			if (columns[i].expand) {
+				expanding_total += columns[i].expand_ratio;
 			}
 		}
 
-		if (expand_area < expanding_total) {
-			return get_column_minimum_width(p_column);
+		if (expand_area >= expanding_total && expanding_total > 0) {
+			column_width += expand_area * columns[p_column].expand_ratio / expanding_total;
 		}
+	}
 
-		ERR_FAIL_COND_V(expanding_columns == 0, -1); // shouldn't happen
-		if (expanding_total == 0) {
-			return 0;
-		} else {
-			return expand_area * get_column_minimum_width(p_column) / expanding_total;
-		}
-	} else {
-		return get_column_minimum_width(p_column);
+	if (p_column < columns.size() - 1) {
+		column_width += cache.hseparation;
 	}
+	return column_width;
 }
 
 void Tree::propagate_set_columns(TreeItem *p_item) {
@@ -4549,6 +4633,12 @@ void Tree::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root);
 	ClassDB::bind_method(D_METHOD("set_column_custom_minimum_width", "column", "min_width"), &Tree::set_column_custom_minimum_width);
 	ClassDB::bind_method(D_METHOD("set_column_expand", "column", "expand"), &Tree::set_column_expand);
+	ClassDB::bind_method(D_METHOD("set_column_expand_ratio", "column", "ratio"), &Tree::set_column_expand_ratio);
+	ClassDB::bind_method(D_METHOD("set_column_clip_content", "column", "enable"), &Tree::set_column_clip_content);
+	ClassDB::bind_method(D_METHOD("is_column_expanding", "column"), &Tree::is_column_expanding);
+	ClassDB::bind_method(D_METHOD("is_column_clipping_content", "column"), &Tree::is_column_clipping_content);
+	ClassDB::bind_method(D_METHOD("get_column_expand_ratio", "column"), &Tree::get_column_expand_ratio);
+
 	ClassDB::bind_method(D_METHOD("get_column_width", "column"), &Tree::get_column_width);
 
 	ClassDB::bind_method(D_METHOD("set_hide_root", "enable"), &Tree::set_hide_root);

+ 14 - 1
scene/gui/tree.h

@@ -411,7 +411,9 @@ private:
 
 	struct ColumnInfo {
 		int custom_min_width = 0;
+		int expand_ratio = 1;
 		bool expand = true;
+		bool clip_content = false;
 		String title;
 		Ref<TextLine> text_buf;
 		Dictionary opentype_features;
@@ -450,7 +452,7 @@ private:
 	void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color);
 	int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item);
 	void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false);
-	int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod);
+	int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod);
 	void _text_editor_submit(String p_text);
 	void _text_editor_modal_close();
 	void value_editor_changed(double p_value);
@@ -504,6 +506,7 @@ private:
 		Color parent_hl_line_color;
 		Color children_hl_line_color;
 		Color custom_button_font_highlight;
+		Color font_outline_color;
 
 		int hseparation = 0;
 		int vseparation = 0;
@@ -518,6 +521,7 @@ private:
 		int draw_guides = 0;
 		int scroll_border = 0;
 		int scroll_speed = 0;
+		int font_outline_size = 0;
 
 		enum ClickType {
 			CLICK_NONE,
@@ -540,6 +544,8 @@ private:
 
 		Point2i text_editor_position;
 
+		bool rtl = false;
+
 	} cache;
 
 	int _get_title_button_height() const;
@@ -547,6 +553,7 @@ private:
 	void _scroll_moved(float p_value);
 	HScrollBar *h_scroll;
 	VScrollBar *v_scroll;
+
 	bool h_scroll_enabled = true;
 	bool v_scroll_enabled = true;
 
@@ -632,8 +639,14 @@ public:
 
 	void set_column_custom_minimum_width(int p_column, int p_min_width);
 	void set_column_expand(int p_column, bool p_expand);
+	void set_column_expand_ratio(int p_column, int p_ratio);
+	void set_column_clip_content(int p_column, bool p_fit);
 	int get_column_minimum_width(int p_column) const;
 	int get_column_width(int p_column) const;
+	int get_column_expand_ratio(int p_column) const;
+
+	bool is_column_expanding(int p_column) const;
+	bool is_column_clipping_content(int p_column) const;
 
 	void set_hide_root(bool p_enabled);
 	bool is_root_hidden() const;