2
0
Эх сурвалжийг харах

Merge pull request #74813 from bruvzg/text_edit_autowrap

[TextEdit] Expose all auto-wrap modes.
Rémi Verschelde 2 жил өмнө
parent
commit
95a9089fdd

+ 3 - 0
doc/classes/EditorSettings.xml

@@ -746,6 +746,9 @@
 		<member name="text_editor/appearance/gutters/show_line_numbers" type="bool" setter="" getter="">
 			If [code]true[/code], displays line numbers in a gutter at the left.
 		</member>
+		<member name="text_editor/appearance/lines/autowrap_mode" type="int" setter="" getter="">
+			If [member text_editor/appearance/lines/word_wrap] is set to [code]1[/code], sets text wrapping mode. To see how each mode behaves, see [enum TextServer.AutowrapMode].
+		</member>
 		<member name="text_editor/appearance/lines/code_folding" type="bool" setter="" getter="">
 			If [code]true[/code], displays the folding arrows next to indented code sections and allows code folding. If [code]false[/code], hides the folding arrows next to indented code sections and disallows code folding.
 		</member>

+ 3 - 0
doc/classes/TextEdit.xml

@@ -1088,6 +1088,9 @@
 		</method>
 	</methods>
 	<members>
+		<member name="autowrap_mode" type="int" setter="set_autowrap_mode" getter="get_autowrap_mode" enum="TextServer.AutowrapMode" default="3">
+			If [member wrap_mode] is set to [constant LINE_WRAPPING_BOUNDARY], sets text wrapping mode. To see how each mode behaves, see [enum TextServer.AutowrapMode].
+		</member>
 		<member name="caret_blink" type="bool" setter="set_caret_blink_enabled" getter="is_caret_blink_enabled" default="false">
 			If [code]true[/code], makes the caret blink.
 		</member>

+ 1 - 0
editor/code_editor.cpp

@@ -1035,6 +1035,7 @@ void CodeTextEditor::update_editor_settings() {
 	text_editor->set_line_folding_enabled(EDITOR_GET("text_editor/appearance/lines/code_folding"));
 	text_editor->set_draw_fold_gutter(EDITOR_GET("text_editor/appearance/lines/code_folding"));
 	text_editor->set_line_wrapping_mode((TextEdit::LineWrappingMode)EDITOR_GET("text_editor/appearance/lines/word_wrap").operator int());
+	text_editor->set_autowrap_mode((TextServer::AutowrapMode)EDITOR_GET("text_editor/appearance/lines/autowrap_mode").operator int());
 
 	// Appearance: Whitespace
 	text_editor->set_draw_tabs(EDITOR_GET("text_editor/appearance/whitespace/draw_tabs"));

+ 1 - 0
editor/editor_settings.cpp

@@ -562,6 +562,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	// Appearance: Lines
 	_initial_set("text_editor/appearance/lines/code_folding", true);
 	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/appearance/lines/word_wrap", 0, "None,Boundary")
+	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/appearance/lines/autowrap_mode", 3, "Arbitrary:1,Word:2,Word (Smart):3")
 
 	// Appearance: Whitespace
 	_initial_set("text_editor/appearance/whitespace/draw_tabs", true);

+ 72 - 32
scene/gui/text_edit.cpp

@@ -112,6 +112,14 @@ float TextEdit::Text::get_width() const {
 	return width;
 }
 
+void TextEdit::Text::set_brk_flags(BitField<TextServer::LineBreakFlag> p_flags) {
+	brk_flags = p_flags;
+}
+
+BitField<TextServer::LineBreakFlag> TextEdit::Text::get_brk_flags() const {
+	return brk_flags;
+}
+
 int TextEdit::Text::get_line_wrap_amount(int p_line) const {
 	ERR_FAIL_INDEX_V(p_line, text.size(), 0);
 
@@ -180,6 +188,7 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_chan
 
 	text.write[p_line].data_buf->set_width(width);
 	text.write[p_line].data_buf->set_direction((TextServer::Direction)direction);
+	text.write[p_line].data_buf->set_break_flags(brk_flags);
 	text.write[p_line].data_buf->set_preserve_control(draw_control_chars);
 	if (p_ime_text.length() > 0) {
 		if (p_text_changed) {
@@ -247,21 +256,19 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_chan
 void TextEdit::Text::invalidate_all_lines() {
 	for (int i = 0; i < text.size(); i++) {
 		text.write[i].data_buf->set_width(width);
+		text.write[i].data_buf->set_break_flags(brk_flags);
 		if (tab_size_dirty) {
 			if (tab_size > 0) {
 				Vector<float> tabs;
 				tabs.push_back(font->get_char_size(' ', font_size).width * tab_size);
 				text.write[i].data_buf->tab_align(tabs);
 			}
-			// Tabs have changes, force width update.
-			text.write[i].width = get_line_width(i);
 		}
+		text.write[i].width = get_line_width(i);
 	}
+	tab_size_dirty = false;
 
-	if (tab_size_dirty) {
-		_calculate_max_line_width();
-		tab_size_dirty = false;
-	}
+	_calculate_max_line_width();
 }
 
 void TextEdit::Text::invalidate_font() {
@@ -2935,6 +2942,7 @@ void TextEdit::_update_placeholder() {
 	// Placeholder is generally smaller then text documents, and updates less so this should be fast enough for now.
 	placeholder_data_buf->clear();
 	placeholder_data_buf->set_width(text.get_width());
+	placeholder_data_buf->set_break_flags(text.get_brk_flags());
 	placeholder_data_buf->set_direction((TextServer::Direction)text_direction);
 	placeholder_data_buf->set_preserve_control(draw_control_chars);
 	placeholder_data_buf->add_string(placeholder_text, theme_cache.font, theme_cache.font_size, language);
@@ -5256,6 +5264,7 @@ void TextEdit::set_line_wrapping_mode(LineWrappingMode p_wrapping_mode) {
 	if (line_wrapping_mode != p_wrapping_mode) {
 		line_wrapping_mode = p_wrapping_mode;
 		_update_wrap_at_column(true);
+		queue_redraw();
 	}
 }
 
@@ -5263,6 +5272,22 @@ TextEdit::LineWrappingMode TextEdit::get_line_wrapping_mode() const {
 	return line_wrapping_mode;
 }
 
+void TextEdit::set_autowrap_mode(TextServer::AutowrapMode p_mode) {
+	if (autowrap_mode == p_mode) {
+		return;
+	}
+
+	autowrap_mode = p_mode;
+	if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE) {
+		_update_wrap_at_column(true);
+		queue_redraw();
+	}
+}
+
+TextServer::AutowrapMode TextEdit::get_autowrap_mode() const {
+	return autowrap_mode;
+}
+
 bool TextEdit::is_line_wrapped(int p_line) const {
 	ERR_FAIL_INDEX_V(p_line, text.size(), 0);
 	if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
@@ -5540,37 +5565,32 @@ void TextEdit::adjust_viewport_to_caret(int p_caret) {
 	}
 	visible_width -= 20; // Give it a little more space.
 
-	if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
-		// Adjust x offset.
-		Vector2i caret_pos;
+	Vector2i caret_pos;
 
-		// Get position of the start of caret.
-		if (ime_text.length() != 0 && ime_selection.x != 0) {
-			caret_pos.x = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x, get_caret_line(p_caret), get_caret_column(p_caret));
-		} else {
-			caret_pos.x = _get_column_x_offset_for_line(get_caret_column(p_caret), get_caret_line(p_caret), get_caret_column(p_caret));
-		}
+	// Get position of the start of caret.
+	if (ime_text.length() != 0 && ime_selection.x != 0) {
+		caret_pos.x = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x, get_caret_line(p_caret), get_caret_column(p_caret));
+	} else {
+		caret_pos.x = _get_column_x_offset_for_line(get_caret_column(p_caret), get_caret_line(p_caret), get_caret_column(p_caret));
+	}
 
-		// Get position of the end of caret.
-		if (ime_text.length() != 0) {
-			if (ime_selection.y != 0) {
-				caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x + ime_selection.y, get_caret_line(p_caret), get_caret_column(p_caret));
-			} else {
-				caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_text.size(), get_caret_line(p_caret), get_caret_column(p_caret));
-			}
+	// Get position of the end of caret.
+	if (ime_text.length() != 0) {
+		if (ime_selection.y != 0) {
+			caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x + ime_selection.y, get_caret_line(p_caret), get_caret_column(p_caret));
 		} else {
-			caret_pos.y = caret_pos.x;
+			caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_text.size(), get_caret_line(p_caret), get_caret_column(p_caret));
 		}
+	} else {
+		caret_pos.y = caret_pos.x;
+	}
 
-		if (MAX(caret_pos.x, caret_pos.y) > (first_visible_col + visible_width)) {
-			first_visible_col = MAX(caret_pos.x, caret_pos.y) - visible_width + 1;
-		}
+	if (MAX(caret_pos.x, caret_pos.y) > (first_visible_col + visible_width)) {
+		first_visible_col = MAX(caret_pos.x, caret_pos.y) - visible_width + 1;
+	}
 
-		if (MIN(caret_pos.x, caret_pos.y) < first_visible_col) {
-			first_visible_col = MIN(caret_pos.x, caret_pos.y);
-		}
-	} else {
-		first_visible_col = 0;
+	if (MIN(caret_pos.x, caret_pos.y) < first_visible_col) {
+		first_visible_col = MIN(caret_pos.x, caret_pos.y);
 	}
 	h_scroll->set_value(first_visible_col);
 
@@ -6280,6 +6300,9 @@ void TextEdit::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_line_wrapping_mode", "mode"), &TextEdit::set_line_wrapping_mode);
 	ClassDB::bind_method(D_METHOD("get_line_wrapping_mode"), &TextEdit::get_line_wrapping_mode);
 
+	ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &TextEdit::set_autowrap_mode);
+	ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &TextEdit::get_autowrap_mode);
+
 	ClassDB::bind_method(D_METHOD("is_line_wrapped", "line"), &TextEdit::is_line_wrapped);
 	ClassDB::bind_method(D_METHOD("get_line_wrap_count", "line"), &TextEdit::get_line_wrap_count);
 	ClassDB::bind_method(D_METHOD("get_line_wrap_index_at_column", "line", "column"), &TextEdit::get_line_wrap_index_at_column);
@@ -6415,6 +6438,7 @@ void TextEdit::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Arbitrary:1,Word:2,Word (Smart):3"), "set_autowrap_mode", "get_autowrap_mode");
 
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled");
@@ -7222,10 +7246,26 @@ void TextEdit::_update_wrap_at_column(bool p_force) {
 	if ((wrap_at_column != new_wrap_at) || p_force) {
 		wrap_at_column = new_wrap_at;
 		if (line_wrapping_mode) {
+			BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY;
+			switch (autowrap_mode) {
+				case TextServer::AUTOWRAP_WORD_SMART:
+					autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY;
+					break;
+				case TextServer::AUTOWRAP_WORD:
+					autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
+					break;
+				case TextServer::AUTOWRAP_ARBITRARY:
+					autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;
+					break;
+				case TextServer::AUTOWRAP_OFF:
+					break;
+			}
+			text.set_brk_flags(autowrap_flags);
 			text.set_width(wrap_at_column);
 		} else {
 			text.set_width(-1);
 		}
+
 		text.invalidate_all_lines();
 		_update_placeholder();
 	}
@@ -7293,7 +7333,7 @@ void TextEdit::_update_scrollbars() {
 		v_scroll->hide();
 	}
 
-	if (total_width > visible_width && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
+	if (total_width > visible_width) {
 		h_scroll->show();
 		h_scroll->set_max(total_width);
 		h_scroll->set_page(visible_width);

+ 7 - 0
scene/gui/text_edit.h

@@ -172,6 +172,7 @@ private:
 
 		String language;
 		TextServer::Direction direction = TextServer::DIRECTION_AUTO;
+		BitField<TextServer::LineBreakFlag> brk_flags = TextServer::BREAK_MANDATORY;
 		bool draw_control_chars = false;
 
 		int line_height = -1;
@@ -198,6 +199,8 @@ private:
 
 		void set_width(float p_width);
 		float get_width() const;
+		void set_brk_flags(BitField<TextServer::LineBreakFlag> p_flags);
+		BitField<TextServer::LineBreakFlag> get_brk_flags() const;
 		int get_line_wrap_amount(int p_line) const;
 
 		Vector<Vector2i> get_line_wrap_ranges(int p_line) const;
@@ -460,6 +463,7 @@ private:
 
 	/* Line wrapping. */
 	LineWrappingMode line_wrapping_mode = LineWrappingMode::LINE_WRAPPING_NONE;
+	TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_WORD_SMART;
 
 	int wrap_at_column = 0;
 	int wrap_right_offset = 10;
@@ -894,6 +898,9 @@ public:
 	void set_line_wrapping_mode(LineWrappingMode p_wrapping_mode);
 	LineWrappingMode get_line_wrapping_mode() const;
 
+	void set_autowrap_mode(TextServer::AutowrapMode p_mode);
+	TextServer::AutowrapMode get_autowrap_mode() const;
+
 	bool is_line_wrapped(int p_line) const;
 	int get_line_wrap_count(int p_line) const;
 	int get_line_wrap_index_at_column(int p_line, int p_column) const;

+ 4 - 4
tests/scene/test_text_edit.h

@@ -3827,10 +3827,10 @@ TEST_CASE("[SceneTree][TextEdit] viewport") {
 	text_edit->adjust_viewport_to_caret();
 	MessageQueue::get_singleton()->flush();
 
-	CHECK(text_edit->get_first_visible_line() == (visible_lines / 2) + 4);
-	CHECK(text_edit->get_v_scroll() == (visible_lines + (visible_lines / 2)) - 1);
-	CHECK(text_edit->get_last_full_visible_line() == (visible_lines) + 3);
-	CHECK(text_edit->get_last_full_visible_line_wrap_index() == 1);
+	CHECK(text_edit->get_first_visible_line() == (visible_lines / 2) + 6);
+	CHECK(text_edit->get_v_scroll() == (visible_lines + (visible_lines / 2)) + 1);
+	CHECK(text_edit->get_last_full_visible_line() == (visible_lines) + 5);
+	CHECK(text_edit->get_last_full_visible_line_wrap_index() == 0);
 	CHECK(text_edit->get_caret_wrap_index() == 1);
 
 	text_edit->center_viewport_to_caret();