Browse Source

Merge pull request #96038 from kitbdev/fix-line-edit-caret-word-mode

Fix LineEdit word mode when there are no more words
Rémi Verschelde 11 months ago
parent
commit
f69de1c522
3 changed files with 92 additions and 23 deletions
  1. 36 16
      scene/gui/line_edit.cpp
  2. 12 7
      scene/gui/text_edit.cpp
  3. 44 0
      tests/scene/test_text_edit.h

+ 36 - 16
scene/gui/line_edit.cpp

@@ -68,10 +68,15 @@ void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
 		int cc = caret_column;
 
 		PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
-		for (int i = words.size() - 2; i >= 0; i = i - 2) {
-			if (words[i] < cc) {
-				cc = words[i];
-				break;
+		if (words.is_empty() || cc <= words[0]) {
+			// Move to the start when there are no more words.
+			cc = 0;
+		} else {
+			for (int i = words.size() - 2; i >= 0; i = i - 2) {
+				if (words[i] < cc) {
+					cc = words[i];
+					break;
+				}
 			}
 		}
 
@@ -101,10 +106,15 @@ void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
 		int cc = caret_column;
 
 		PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
-		for (int i = 1; i < words.size(); i = i + 2) {
-			if (words[i] > cc) {
-				cc = words[i];
-				break;
+		if (words.is_empty() || cc >= words[words.size() - 1]) {
+			// Move to the end when there are no more words.
+			cc = text.length();
+		} else {
+			for (int i = 1; i < words.size(); i = i + 2) {
+				if (words[i] > cc) {
+					cc = words[i];
+					break;
+				}
 			}
 		}
 
@@ -159,10 +169,15 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
 		int cc = caret_column;
 
 		PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
-		for (int i = words.size() - 2; i >= 0; i = i - 2) {
-			if (words[i] < cc) {
-				cc = words[i];
-				break;
+		if (words.is_empty() || cc <= words[0]) {
+			// Delete to the start when there are no more words.
+			cc = 0;
+		} else {
+			for (int i = words.size() - 2; i >= 0; i = i - 2) {
+				if (words[i] < cc) {
+					cc = words[i];
+					break;
+				}
 			}
 		}
 
@@ -198,10 +213,15 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) {
 	if (p_word) {
 		int cc = caret_column;
 		PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
-		for (int i = 1; i < words.size(); i = i + 2) {
-			if (words[i] > cc) {
-				cc = words[i];
-				break;
+		if (words.is_empty() || cc >= words[words.size() - 1]) {
+			// Delete to the end when there are no more words.
+			cc = text.length();
+		} else {
+			for (int i = 1; i < words.size(); i = i + 2) {
+				if (words[i] > cc) {
+					cc = words[i];
+					break;
+				}
 			}
 		}
 

+ 12 - 7
scene/gui/text_edit.cpp

@@ -2393,7 +2393,7 @@ void TextEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
 			} else {
 				PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(get_caret_line(i))->get_rid());
 				if (words.is_empty() || cc <= words[0]) {
-					// This solves the scenario where there are no words but glyfs that can be ignored.
+					// Move to the start when there are no more words.
 					cc = 0;
 				} else {
 					for (int j = words.size() - 2; j >= 0; j = j - 2) {
@@ -2450,7 +2450,7 @@ void TextEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
 			} else {
 				PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(get_caret_line(i))->get_rid());
 				if (words.is_empty() || cc >= words[words.size() - 1]) {
-					// This solves the scenario where there are no words but glyfs that can be ignored.
+					// Move to the end when there are no more words.
 					cc = text[get_caret_line(i)].length();
 				} else {
 					for (int j = 1; j < words.size(); j = j + 2) {
@@ -2666,7 +2666,7 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) {
 			// Get a list with the indices of the word bounds of the given text line.
 			const PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(get_caret_line(caret_index))->get_rid());
 			if (words.is_empty() || column <= words[0]) {
-				// If "words" is empty, meaning no words are left, we can remove everything until the beginning of the line.
+				// Delete to the start when there are no more words.
 				column = 0;
 			} else {
 				// Otherwise search for the first word break that is smaller than the index from we're currently deleting.
@@ -2731,10 +2731,15 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) {
 			int column = get_caret_column(caret_index);
 
 			PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
-			for (int j = 1; j < words.size(); j = j + 2) {
-				if (words[j] > column) {
-					column = words[j];
-					break;
+			if (words.is_empty() || column >= words[words.size() - 1]) {
+				// Delete to the end when there are no more words.
+				column = text[get_caret_line(i)].length();
+			} else {
+				for (int j = 1; j < words.size(); j = j + 2) {
+					if (words[j] > column) {
+						column = words[j];
+						break;
+					}
 				}
 			}
 

+ 44 - 0
tests/scene/test_text_edit.h

@@ -4232,6 +4232,18 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
 			CHECK(text_edit->get_caret_line(0) == 0);
 			CHECK(text_edit->get_caret_column(0) == 4);
 			text_edit->remove_secondary_carets();
+
+			// Remove when there are no words, only symbols.
+			text_edit->set_text("#{}");
+			text_edit->set_caret_line(0);
+			text_edit->set_caret_column(3);
+
+			SEND_GUI_ACTION("ui_text_backspace_word");
+			CHECK(text_edit->get_viewport()->is_input_handled());
+			CHECK_FALSE(text_edit->has_selection());
+			CHECK(text_edit->get_text() == "");
+			CHECK(text_edit->get_caret_line(0) == 0);
+			CHECK(text_edit->get_caret_column(0) == 0);
 		}
 
 		SUBCASE("[TextEdit] ui_text_backspace_word same line") {
@@ -4891,6 +4903,18 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
 			CHECK(text_edit->get_caret_line(0) == 0);
 			CHECK(text_edit->get_caret_column(0) == 2);
 			text_edit->remove_secondary_carets();
+
+			// Remove when there are no words, only symbols.
+			text_edit->set_text("#{}");
+			text_edit->set_caret_line(0);
+			text_edit->set_caret_column(0);
+
+			SEND_GUI_ACTION("ui_text_delete_word");
+			CHECK(text_edit->get_viewport()->is_input_handled());
+			CHECK_FALSE(text_edit->has_selection());
+			CHECK(text_edit->get_text() == "");
+			CHECK(text_edit->get_caret_line(0) == 0);
+			CHECK(text_edit->get_caret_column(0) == 0);
 		}
 
 		SUBCASE("[TextEdit] ui_text_delete_word same line") {
@@ -5301,6 +5325,16 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
 			SIGNAL_CHECK("caret_changed", empty_signal_args);
 			SIGNAL_CHECK_FALSE("text_changed");
 			SIGNAL_CHECK_FALSE("lines_edited_from");
+
+			// Move when there are no words, only symbols.
+			text_edit->set_text("#{}");
+			text_edit->set_caret_line(0);
+			text_edit->set_caret_column(3);
+
+			SEND_GUI_ACTION("ui_text_caret_word_left");
+			CHECK(text_edit->get_viewport()->is_input_handled());
+			CHECK(text_edit->get_caret_line(0) == 0);
+			CHECK(text_edit->get_caret_column(0) == 0);
 		}
 
 		SUBCASE("[TextEdit] ui_text_caret_left") {
@@ -5563,6 +5597,16 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
 			SIGNAL_CHECK("caret_changed", empty_signal_args);
 			SIGNAL_CHECK_FALSE("text_changed");
 			SIGNAL_CHECK_FALSE("lines_edited_from");
+
+			// Move when there are no words, only symbols.
+			text_edit->set_text("#{}");
+			text_edit->set_caret_line(0);
+			text_edit->set_caret_column(0);
+
+			SEND_GUI_ACTION("ui_text_caret_word_right");
+			CHECK(text_edit->get_viewport()->is_input_handled());
+			CHECK(text_edit->get_caret_line(0) == 0);
+			CHECK(text_edit->get_caret_column(0) == 3);
 		}
 
 		SUBCASE("[TextEdit] ui_text_caret_right") {