Browse Source

Merge pull request #55739 from Paulb23/text-edit-base-performance

Rémi Verschelde 3 years ago
parent
commit
7e32a27879
4 changed files with 93 additions and 50 deletions
  1. 1 1
      scene/gui/control.cpp
  2. 1 1
      scene/gui/control.h
  3. 88 46
      scene/gui/text_edit.cpp
  4. 3 2
      scene/gui/text_edit.h

+ 1 - 1
scene/gui/control.cpp

@@ -2597,7 +2597,7 @@ bool Control::is_text_field() const {
 	return false;
 	return false;
 }
 }
 
 
-Array Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const {
+Array Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String &p_text) const {
 	Array ret;
 	Array ret;
 	switch (p_theme_type) {
 	switch (p_theme_type) {
 		case STRUCTURED_TEXT_URI: {
 		case STRUCTURED_TEXT_URI: {

+ 1 - 1
scene/gui/control.h

@@ -279,7 +279,7 @@ protected:
 
 
 	//virtual void _window_gui_input(InputEvent p_event);
 	//virtual void _window_gui_input(InputEvent p_event);
 
 
-	virtual Array structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const;
+	virtual Array structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String &p_text) const;
 
 
 	bool _set(const StringName &p_name, const Variant &p_value);
 	bool _set(const StringName &p_name, const Variant &p_value);
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	bool _get(const StringName &p_name, Variant &r_ret) const;

+ 88 - 46
scene/gui/text_edit.cpp

@@ -155,30 +155,30 @@ _FORCE_INLINE_ const String &TextEdit::Text::operator[](int p_line) const {
 
 
 void TextEdit::Text::_calculate_line_height() {
 void TextEdit::Text::_calculate_line_height() {
 	int height = 0;
 	int height = 0;
-	for (int i = 0; i < text.size(); i++) {
+	for (const Line &l : text) {
 		// Found another line with the same height...nothing to update.
 		// Found another line with the same height...nothing to update.
-		if (text[i].height == line_height) {
+		if (l.height == line_height) {
 			height = line_height;
 			height = line_height;
 			break;
 			break;
 		}
 		}
-		height = MAX(height, text[i].height);
+		height = MAX(height, l.height);
 	}
 	}
 	line_height = height;
 	line_height = height;
 }
 }
 
 
 void TextEdit::Text::_calculate_max_line_width() {
 void TextEdit::Text::_calculate_max_line_width() {
 	int width = 0;
 	int width = 0;
-	for (int i = 0; i < text.size(); i++) {
-		if (is_hidden(i)) {
+	for (const Line &l : text) {
+		if (l.hidden) {
 			continue;
 			continue;
 		}
 		}
 
 
 		// Found another line with the same width...nothing to update.
 		// Found another line with the same width...nothing to update.
-		if (text[i].width == max_width) {
+		if (l.width == max_width) {
 			width = max_width;
 			width = max_width;
 			break;
 			break;
 		}
 		}
-		width = MAX(width, text[i].width);
+		width = MAX(width, l.width);
 	}
 	}
 	max_width = width;
 	max_width = width;
 }
 }
@@ -216,7 +216,7 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_
 	// Update height.
 	// Update height.
 	const int old_height = text.write[p_line].height;
 	const int old_height = text.write[p_line].height;
 	const int wrap_amount = get_line_wrap_amount(p_line);
 	const int wrap_amount = get_line_wrap_amount(p_line);
-	int height = font->get_height(font_size);
+	int height = font_height;
 	for (int i = 0; i <= wrap_amount; i++) {
 	for (int i = 0; i <= wrap_amount; i++) {
 		height = MAX(height, text[p_line].data_buf->get_line_size(i).y);
 		height = MAX(height, text[p_line].data_buf->get_line_size(i).y);
 	}
 	}
@@ -267,6 +267,13 @@ void TextEdit::Text::invalidate_all() {
 		return;
 		return;
 	}
 	}
 
 
+	max_width = -1;
+	line_height = -1;
+
+	if (!font.is_null() && font_size > 0) {
+		font_height = font->get_height(font_size);
+	}
+
 	for (int i = 0; i < text.size(); i++) {
 	for (int i = 0; i < text.size(); i++) {
 		invalidate_cache(i);
 		invalidate_cache(i);
 	}
 	}
@@ -275,7 +282,15 @@ void TextEdit::Text::invalidate_all() {
 
 
 void TextEdit::Text::clear() {
 void TextEdit::Text::clear() {
 	text.clear();
 	text.clear();
-	insert(0, "", Array());
+
+	max_width = -1;
+	line_height = -1;
+
+	Line line;
+	line.gutters.resize(gutter_count);
+	line.data = "";
+	text.insert(0, line);
+	invalidate_cache(0);
 }
 }
 
 
 int TextEdit::Text::get_max_width() const {
 int TextEdit::Text::get_max_width() const {
@@ -290,30 +305,64 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_o
 	invalidate_cache(p_line);
 	invalidate_cache(p_line);
 }
 }
 
 
-void TextEdit::Text::insert(int p_at, const String &p_text, const Array &p_bidi_override) {
-	Line line;
-	line.gutters.resize(gutter_count);
-	line.hidden = false;
-	line.data = p_text;
-	line.bidi_override = p_bidi_override;
-	text.insert(p_at, line);
+void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override) {
+	int new_line_count = p_text.size() - 1;
+	if (new_line_count > 0) {
+		text.resize(text.size() + new_line_count);
+		for (int i = (text.size() - 1); i > p_at; i--) {
+			if ((i - new_line_count) <= 0) {
+				break;
+			}
+			text.write[i] = text[i - new_line_count];
+		}
+	}
 
 
-	invalidate_cache(p_at);
+	for (int i = 0; i < p_text.size(); i++) {
+		if (i == 0) {
+			set(p_at + i, p_text[i], p_bidi_override[i]);
+			continue;
+		}
+		Line line;
+		line.gutters.resize(gutter_count);
+		line.data = p_text[i];
+		line.bidi_override = p_bidi_override[i];
+		text.write[p_at + i] = line;
+		invalidate_cache(p_at + i);
+	}
 }
 }
 
 
-void TextEdit::Text::remove_at(int p_index) {
-	int height = text[p_index].height;
-	int width = text[p_index].width;
+void TextEdit::Text::remove_range(int p_from_line, int p_to_line) {
+	if (p_from_line == p_to_line) {
+		return;
+	}
 
 
-	text.remove_at(p_index);
+	bool dirty_height = false;
+	bool dirty_width = false;
+	for (int i = p_from_line; i < p_to_line; i++) {
+		if (!dirty_height && text[i].height == line_height) {
+			dirty_height = true;
+		}
+
+		if (!dirty_width && text[i].width == max_width) {
+			dirty_width = true;
+		}
+
+		if (dirty_height && dirty_width) {
+			break;
+		}
+	}
+
+	int diff = (p_to_line - p_from_line);
+	for (int i = p_to_line; i < text.size() - 1; i++) {
+		text.write[(i - diff) + 1] = text[i + 1];
+	}
+	text.resize(text.size() - diff);
 
 
-	// If this is the tallest line, we need to get the next tallest.
-	if (height == line_height) {
+	if (dirty_height) {
 		_calculate_line_height();
 		_calculate_line_height();
 	}
 	}
 
 
-	// If this is the longest line, we need to get the next longest.
-	if (width == max_width) {
+	if (dirty_width) {
 		_calculate_max_line_width();
 		_calculate_max_line_width();
 	}
 	}
 }
 }
@@ -6289,11 +6338,11 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
 	ERR_FAIL_COND(p_char < 0);
 	ERR_FAIL_COND(p_char < 0);
 
 
 	/* STEP 1: Remove \r from source text and separate in substrings. */
 	/* STEP 1: Remove \r from source text and separate in substrings. */
-
-	Vector<String> substrings = p_text.replace("\r", "").split("\n");
+	const String text_to_insert = p_text.replace("\r", "");
+	Vector<String> substrings = text_to_insert.split("\n");
 
 
 	// Is this just a new empty line?
 	// Is this just a new empty line?
-	bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n";
+	bool shift_first_line = p_char == 0 && substrings.size() == 2 && text_to_insert == "\n";
 
 
 	/* STEP 2: Add spaces if the char is greater than the end of the line. */
 	/* STEP 2: Add spaces if the char is greater than the end of the line. */
 	while (p_char > text[p_line].length()) {
 	while (p_char > text[p_line].length()) {
@@ -6301,24 +6350,19 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
 	}
 	}
 
 
 	/* STEP 3: Separate dest string in pre and post text. */
 	/* STEP 3: Separate dest string in pre and post text. */
-
-	String preinsert_text = text[p_line].substr(0, p_char);
 	String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
 	String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
 
 
-	for (int j = 0; j < substrings.size(); j++) {
-		// Insert the substrings.
-
-		if (j == 0) {
-			text.set(p_line, preinsert_text + substrings[j], structured_text_parser(st_parser, st_args, preinsert_text + substrings[j]));
-		} else {
-			text.insert(p_line + j, substrings[j], structured_text_parser(st_parser, st_args, substrings[j]));
-		}
+	substrings.write[0] = text[p_line].substr(0, p_char) + substrings[0];
+	substrings.write[substrings.size() - 1] += postinsert_text;
 
 
-		if (j == substrings.size() - 1) {
-			text.set(p_line + j, text[p_line + j] + postinsert_text, structured_text_parser(st_parser, st_args, text[p_line + j] + postinsert_text));
-		}
+	Vector<Array> bidi_override;
+	bidi_override.resize(substrings.size());
+	for (int i = 0; i < substrings.size(); i++) {
+		bidi_override.write[i] = structured_text_parser(st_parser, st_args, substrings[i]);
 	}
 	}
 
 
+	text.insert(p_line, substrings, bidi_override);
+
 	if (shift_first_line) {
 	if (shift_first_line) {
 		text.move_gutters(p_line, p_line + 1);
 		text.move_gutters(p_line, p_line + 1);
 		text.set_hidden(p_line + 1, text.is_hidden(p_line));
 		text.set_hidden(p_line + 1, text.is_hidden(p_line));
@@ -6351,7 +6395,7 @@ String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_lin
 	ERR_FAIL_COND_V(p_to_line < p_from_line, String()); // 'from > to'.
 	ERR_FAIL_COND_V(p_to_line < p_from_line, String()); // 'from > to'.
 	ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column < p_from_column, String()); // 'from > to'.
 	ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column < p_from_column, String()); // 'from > to'.
 
 
-	String ret;
+	StringBuilder ret;
 
 
 	for (int i = p_from_line; i <= p_to_line; i++) {
 	for (int i = p_from_line; i <= p_to_line; i++) {
 		int begin = (i == p_from_line) ? p_from_column : 0;
 		int begin = (i == p_from_line) ? p_from_column : 0;
@@ -6363,7 +6407,7 @@ String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_lin
 		ret += text[i].substr(begin, end - begin);
 		ret += text[i].substr(begin, end - begin);
 	}
 	}
 
 
-	return ret;
+	return ret.as_string();
 }
 }
 
 
 void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
 void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
@@ -6377,9 +6421,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
 	String pre_text = text[p_from_line].substr(0, p_from_column);
 	String pre_text = text[p_from_line].substr(0, p_from_column);
 	String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
 	String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
 
 
-	for (int i = p_from_line; i < p_to_line; i++) {
-		text.remove_at(p_from_line + 1);
-	}
+	text.remove_range(p_from_line, p_to_line);
 	text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text));
 	text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text));
 
 
 	if (!text_changed_dirty && !setting_text) {
 	if (!text_changed_dirty && !setting_text) {

+ 3 - 2
scene/gui/text_edit.h

@@ -159,6 +159,7 @@ private:
 		mutable Vector<Line> text;
 		mutable Vector<Line> text;
 		Ref<Font> font;
 		Ref<Font> font;
 		int font_size = -1;
 		int font_size = -1;
+		int font_height = 0;
 
 
 		Dictionary opentype_features;
 		Dictionary opentype_features;
 		String language;
 		String language;
@@ -204,8 +205,8 @@ private:
 			}
 			}
 		}
 		}
 		bool is_hidden(int p_line) const { return text[p_line].hidden; }
 		bool is_hidden(int p_line) const { return text[p_line].hidden; }
-		void insert(int p_at, const String &p_text, const Array &p_bidi_override);
-		void remove_at(int p_index);
+		void insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override);
+		void remove_range(int p_from_line, int p_to_line);
 		int size() const { return text.size(); }
 		int size() const { return text.size(); }
 		void clear();
 		void clear();