Browse Source

Merge pull request #50200 from akien-mga/lineedit-max_length-truncate

LineEdit: Respect `max_length` by truncating text to append
Rémi Verschelde 4 years ago
parent
commit
30f379df6a
4 changed files with 43 additions and 27 deletions
  1. 25 1
      doc/classes/LineEdit.xml
  2. 18 14
      scene/gui/line_edit.cpp
  3. 0 8
      scene/gui/text_edit.cpp
  4. 0 4
      scene/gui/text_edit.h

+ 25 - 1
doc/classes/LineEdit.xml

@@ -196,6 +196,28 @@
 		</member>
 		<member name="max_length" type="int" setter="set_max_length" getter="get_max_length" default="0">
 			Maximum amount of characters that can be entered inside the [LineEdit]. If [code]0[/code], there is no limit.
+			When a limit is defined, characters that would exceed [member max_length] are truncated. This happens both for existing [member text] contents when setting the max length, or for new text inserted in the [LineEdit], including pasting. If any input text is truncated, the [signal text_change_rejected] signal is emitted with the truncated substring as parameter.
+			[b]Example:[/b]
+			[codeblocks]
+			[gdscript]
+			text = "Hello world"
+			max_length = 5
+			# `text` becomes "Hello".
+			max_length = 10
+			text += " goodbye"
+			# `text` becomes "Hello good".
+			# `text_change_rejected` is emitted with "bye" as parameter.
+			[/gdscript]
+			[csharp]
+			Text = "Hello world";
+			MaxLength = 5;
+			// `Text` becomes "Hello".
+			MaxLength = 10;
+			Text += " goodbye";
+			// `Text` becomes "Hello good".
+			// `text_change_rejected` is emitted with "bye" as parameter.
+			[/csharp]
+			[/codeblocks]
 		</member>
 		<member name="mouse_default_cursor_shape" type="int" setter="set_default_cursor_shape" getter="get_default_cursor_shape" override="true" enum="Control.CursorShape" default="1" />
 		<member name="placeholder_alpha" type="float" setter="set_placeholder_alpha" getter="get_placeholder_alpha" default="0.6">
@@ -238,8 +260,10 @@
 	</members>
 	<signals>
 		<signal name="text_change_rejected">
+			<argument index="0" name="rejected_substring" type="String">
+			</argument>
 			<description>
-				Emitted when trying to append text that would overflow the [member max_length].
+				Emitted when appending text that overflows the [member max_length]. The appended text is truncated to fit [member max_length], and the part that couldn't fit is passed as the [code]rejected_substring[/code] argument.
 			</description>
 		</signal>
 		<signal name="text_changed">

+ 18 - 14
scene/gui/line_edit.cpp

@@ -1470,19 +1470,23 @@ int LineEdit::get_scroll_offset() const {
 }
 
 void LineEdit::insert_text_at_caret(String p_text) {
-	if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) {
-		String pre = text.substr(0, caret_column);
-		String post = text.substr(caret_column, text.length() - caret_column);
-		text = pre + p_text + post;
-		_shape();
-		TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length());
-		if (dir != TextServer::DIRECTION_AUTO) {
-			input_direction = (TextDirection)dir;
+	if (max_length > 0) {
+		// Truncate text to append to fit in max_length, if needed.
+		int available_chars = max_length - text.length();
+		if (p_text.length() > available_chars) {
+			emit_signal("text_change_rejected", p_text.substr(available_chars));
+			p_text = p_text.substr(0, available_chars);
 		}
-		set_caret_column(caret_column + p_text.length());
-	} else {
-		emit_signal("text_change_rejected");
 	}
+	String pre = text.substr(0, caret_column);
+	String post = text.substr(caret_column, text.length() - caret_column);
+	text = pre + p_text + post;
+	_shape();
+	TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length());
+	if (dir != TextServer::DIRECTION_AUTO) {
+		input_direction = (TextDirection)dir;
+	}
+	set_caret_column(caret_column + p_text.length());
 }
 
 void LineEdit::clear_internal() {
@@ -2158,7 +2162,7 @@ void LineEdit::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon);
 
 	ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text")));
-	ADD_SIGNAL(MethodInfo("text_change_rejected"));
+	ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring")));
 	ADD_SIGNAL(MethodInfo("text_submitted", PropertyInfo(Variant::STRING, "new_text")));
 
 	BIND_ENUM_CONSTANT(ALIGN_LEFT);
@@ -2198,7 +2202,7 @@ void LineEdit::_bind_methods() {
 
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length"), "set_max_length", "get_max_length");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_max_length", "get_max_length");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character");
@@ -2221,7 +2225,7 @@ void LineEdit::_bind_methods() {
 	ADD_GROUP("Caret", "caret_");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column"), "set_caret_column", "get_caret_column");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_caret_column", "get_caret_column");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled");
 }

+ 0 - 8
scene/gui/text_edit.cpp

@@ -4008,14 +4008,6 @@ bool TextEdit::is_wrap_enabled() const {
 	return wrap_enabled;
 }
 
-void TextEdit::set_max_chars(int p_max_chars) {
-	max_chars = p_max_chars;
-}
-
-int TextEdit::get_max_chars() const {
-	return max_chars;
-}
-
 void TextEdit::_reset_caret_blink_timer() {
 	if (caret_blink_enabled) {
 		draw_caret = true;

+ 0 - 4
scene/gui/text_edit.h

@@ -258,7 +258,6 @@ private:
 	uint32_t version = 0;
 	uint32_t saved_version = 0;
 
-	int max_chars = 0;
 	bool readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly.
 
 	Timer *caret_blink_timer;
@@ -678,9 +677,6 @@ public:
 	void set_readonly(bool p_readonly);
 	bool is_readonly() const;
 
-	void set_max_chars(int p_max_chars);
-	int get_max_chars() const;
-
 	void set_wrap_enabled(bool p_wrap_enabled);
 	bool is_wrap_enabled() const;
 	bool line_wraps(int line) const;