Browse Source

Fix TextEdit mouse interactions when the last line is hidden

Paulb23 3 years ago
parent
commit
2a302f7097
4 changed files with 78 additions and 44 deletions
  1. 10 1
      doc/classes/TextEdit.xml
  2. 13 7
      scene/gui/code_edit.cpp
  3. 53 35
      scene/gui/text_edit.cpp
  4. 2 1
      scene/gui/text_edit.h

+ 10 - 1
doc/classes/TextEdit.xml

@@ -233,8 +233,9 @@
 		<method name="get_line_column_at_pos" qualifiers="const">
 		<method name="get_line_column_at_pos" qualifiers="const">
 			<return type="Vector2i" />
 			<return type="Vector2i" />
 			<argument index="0" name="position" type="Vector2i" />
 			<argument index="0" name="position" type="Vector2i" />
+			<argument index="1" name="allow_out_of_bounds" type="bool" default="true" />
 			<description>
 			<description>
-				Returns the line and column at the given position. In the returned vector, [code]x[/code] is the column, [code]y[/code] is the line.
+				Returns the line and column at the given position. In the returned vector, [code]x[/code] is the column, [code]y[/code] is the line. If [code]allow_out_of_bounds[/code] is [code]false[/code] and the position is not over the text, both vector values will be set to [code]-1[/code].
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="get_line_count" qualifiers="const">
 		<method name="get_line_count" qualifiers="const">
@@ -453,6 +454,14 @@
 				Returns the number of visible lines, including wrapped text.
 				Returns the number of visible lines, including wrapped text.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="get_visible_line_count_in_range" qualifiers="const">
+			<return type="int" />
+			<argument index="0" name="from_line" type="int" />
+			<argument index="1" name="to_line" type="int" />
+			<description>
+				Returns the total number of visible + wrapped lines between the two lines.
+			</description>
+		</method>
 		<method name="get_word_at_pos" qualifiers="const">
 		<method name="get_word_at_pos" qualifiers="const">
 			<return type="String" />
 			<return type="String" />
 			<argument index="0" name="position" type="Vector2" />
 			<argument index="0" name="position" type="Vector2" />

+ 13 - 7
scene/gui/code_edit.cpp

@@ -296,11 +296,11 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
 				mpos.x = get_size().x - mpos.x;
 				mpos.x = get_size().x - mpos.x;
 			}
 			}
 
 
-			Point2i pos = get_line_column_at_pos(mpos);
+			Point2i pos = get_line_column_at_pos(mpos, false);
 			int line = pos.y;
 			int line = pos.y;
 			int col = pos.x;
 			int col = pos.x;
 
 
-			if (mb->get_button_index() == MouseButton::LEFT) {
+			if (line != -1 && mb->get_button_index() == MouseButton::LEFT) {
 				if (is_line_folded(line)) {
 				if (is_line_folded(line)) {
 					int wrap_index = get_line_wrap_index_at_column(line, col);
 					int wrap_index = get_line_wrap_index_at_column(line, col);
 					if (wrap_index == get_line_wrap_count(line)) {
 					if (wrap_index == get_line_wrap_count(line)) {
@@ -321,11 +321,13 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
 						mpos.x = get_size().x - mpos.x;
 						mpos.x = get_size().x - mpos.x;
 					}
 					}
 
 
-					Point2i pos = get_line_column_at_pos(mpos);
+					Point2i pos = get_line_column_at_pos(mpos, false);
 					int line = pos.y;
 					int line = pos.y;
 					int col = pos.x;
 					int col = pos.x;
 
 
-					emit_signal(SNAME("symbol_lookup"), symbol_lookup_word, line, col);
+					if (line != -1) {
+						emit_signal(SNAME("symbol_lookup"), symbol_lookup_word, line, col);
+					}
 					return;
 					return;
 				}
 				}
 			}
 			}
@@ -536,11 +538,11 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
 		return CURSOR_ARROW;
 		return CURSOR_ARROW;
 	}
 	}
 
 
-	Point2i pos = get_line_column_at_pos(p_pos);
+	Point2i pos = get_line_column_at_pos(p_pos, false);
 	int line = pos.y;
 	int line = pos.y;
 	int col = pos.x;
 	int col = pos.x;
 
 
-	if (is_line_folded(line)) {
+	if (line != -1 && is_line_folded(line)) {
 		int wrap_index = get_line_wrap_index_at_column(line, col);
 		int wrap_index = get_line_wrap_index_at_column(line, col);
 		if (wrap_index == get_line_wrap_count(line)) {
 		if (wrap_index == get_line_wrap_count(line)) {
 			int eol_icon_width = folded_eol_icon->get_width();
 			int eol_icon_width = folded_eol_icon->get_width();
@@ -2016,10 +2018,14 @@ bool CodeEdit::is_symbol_lookup_on_click_enabled() const {
 String CodeEdit::get_text_for_symbol_lookup() {
 String CodeEdit::get_text_for_symbol_lookup() {
 	Point2i mp = get_local_mouse_pos();
 	Point2i mp = get_local_mouse_pos();
 
 
-	Point2i pos = get_line_column_at_pos(mp);
+	Point2i pos = get_line_column_at_pos(mp, false);
 	int line = pos.y;
 	int line = pos.y;
 	int col = pos.x;
 	int col = pos.x;
 
 
+	if (line == -1) {
+		return String();
+	}
+
 	StringBuilder lookup_text;
 	StringBuilder lookup_text;
 	const int text_size = get_line_count();
 	const int text_size = get_line_count();
 	for (int i = 0; i < text_size; i++) {
 	for (int i = 0; i < text_size; i++) {

+ 53 - 35
scene/gui/text_edit.cpp

@@ -2878,6 +2878,16 @@ Point2i TextEdit::get_next_visible_line_index_offset_from(int p_line_from, int p
 			}
 			}
 		}
 		}
 		wrap_index = get_line_wrap_count(MIN(i, text.size() - 1)) - MAX(0, num_visible - p_visible_amount);
 		wrap_index = get_line_wrap_count(MIN(i, text.size() - 1)) - MAX(0, num_visible - p_visible_amount);
+
+		// If we are a hidden line, then we are the last line as we cannot reach "p_visible_amount".
+		// This means we need to backtrack to get last visible line.
+		// Currently, line 0 cannot be hidden so this should always be valid.
+		int line = (p_line_from + num_total) - 1;
+		if (_is_line_hidden(line)) {
+			Point2i backtrack = get_next_visible_line_index_offset_from(line, 0, -1);
+			num_total = num_total - (backtrack.x - 1);
+			wrap_index = backtrack.y;
+		}
 	} else {
 	} else {
 		p_visible_amount = ABS(p_visible_amount);
 		p_visible_amount = ABS(p_visible_amount);
 		int i;
 		int i;
@@ -3386,7 +3396,7 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
 	return String();
 	return String();
 }
 }
 
 
-Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos) const {
+Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_of_bounds) const {
 	float rows = p_pos.y;
 	float rows = p_pos.y;
 	rows -= style_normal->get_margin(SIDE_TOP);
 	rows -= style_normal->get_margin(SIDE_TOP);
 	rows /= get_line_height();
 	rows /= get_line_height();
@@ -3398,6 +3408,7 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos) const {
 	if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE || _is_hiding_enabled()) {
 	if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE || _is_hiding_enabled()) {
 		Point2i f_ofs = get_next_visible_line_index_offset_from(first_vis_line, caret.wrap_ofs, rows + (1 * SIGN(rows)));
 		Point2i f_ofs = get_next_visible_line_index_offset_from(first_vis_line, caret.wrap_ofs, rows + (1 * SIGN(rows)));
 		wrap_index = f_ofs.y;
 		wrap_index = f_ofs.y;
+
 		if (rows < 0) {
 		if (rows < 0) {
 			row = first_vis_line - (f_ofs.x - 1);
 			row = first_vis_line - (f_ofs.x - 1);
 		} else {
 		} else {
@@ -3409,33 +3420,39 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos) const {
 		row = 0;
 		row = 0;
 	}
 	}
 
 
-	int col = 0;
-
 	if (row >= text.size()) {
 	if (row >= text.size()) {
 		row = text.size() - 1;
 		row = text.size() - 1;
-		col = text[row].size();
-	} else {
-		int colx = p_pos.x - (style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding);
-		colx += caret.x_ofs;
-		col = _get_char_pos_for_line(colx, row, wrap_index);
-		if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) {
-			// Move back one if we are at the end of the row.
-			Vector<String> rows2 = get_line_wrapped_text(row);
-			int row_end_col = 0;
-			for (int i = 0; i < wrap_index + 1; i++) {
-				row_end_col += rows2[i].length();
-			}
-			if (col >= row_end_col) {
-				col -= 1;
-			}
+	}
+
+	int visible_lines = get_visible_line_count_in_range(first_vis_line, row);
+	if (rows > visible_lines) {
+		if (!p_allow_out_of_bounds) {
+			return Point2i(-1, -1);
 		}
 		}
+		return Point2i(text[row].size(), row);
+	}
 
 
-		RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index);
-		if (is_layout_rtl()) {
-			colx = TS->shaped_text_get_size(text_rid).x - colx;
+	int col = 0;
+	int colx = p_pos.x - (style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding);
+	colx += caret.x_ofs;
+	col = _get_char_pos_for_line(colx, row, wrap_index);
+	if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) {
+		// Move back one if we are at the end of the row.
+		Vector<String> rows2 = get_line_wrapped_text(row);
+		int row_end_col = 0;
+		for (int i = 0; i < wrap_index + 1; i++) {
+			row_end_col += rows2[i].length();
 		}
 		}
-		col = TS->shaped_text_hit_test_position(text_rid, colx);
+		if (col >= row_end_col) {
+			col -= 1;
+		}
+	}
+
+	RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index);
+	if (is_layout_rtl()) {
+		colx = TS->shaped_text_get_size(text_rid).x - colx;
 	}
 	}
+	col = TS->shaped_text_hit_test_position(text_rid, colx);
 
 
 	return Point2i(col, row);
 	return Point2i(col, row);
 }
 }
@@ -4012,15 +4029,7 @@ double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
 		return p_line;
 		return p_line;
 	}
 	}
 
 
-	// Count the number of visible lines up to this line.
-	double new_line_scroll_pos = 0.0;
-	int to = CLAMP(p_line, 0, text.size() - 1);
-	for (int i = 0; i < to; i++) {
-		if (!text.is_hidden(i)) {
-			new_line_scroll_pos++;
-			new_line_scroll_pos += get_line_wrap_count(i);
-		}
-	}
+	double new_line_scroll_pos = get_visible_line_count_in_range(0, CLAMP(p_line, 0, text.size() - 1));
 	new_line_scroll_pos += p_wrap_index;
 	new_line_scroll_pos += p_wrap_index;
 	return new_line_scroll_pos;
 	return new_line_scroll_pos;
 }
 }
@@ -4077,14 +4086,18 @@ int TextEdit::get_visible_line_count() const {
 	return _get_control_height() / get_line_height();
 	return _get_control_height() / get_line_height();
 }
 }
 
 
-int TextEdit::get_total_visible_line_count() const {
+int TextEdit::get_visible_line_count_in_range(int p_from_line, int p_to_line) const {
+	ERR_FAIL_INDEX_V(p_from_line, text.size(), 0);
+	ERR_FAIL_INDEX_V(p_to_line, text.size(), 0);
+	ERR_FAIL_COND_V(p_from_line > p_to_line, 0);
+
 	/* Returns the total number of (lines + wrapped - hidden). */
 	/* Returns the total number of (lines + wrapped - hidden). */
 	if (!_is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
 	if (!_is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
-		return text.size();
+		return p_to_line - p_from_line;
 	}
 	}
 
 
 	int total_rows = 0;
 	int total_rows = 0;
-	for (int i = 0; i < text.size(); i++) {
+	for (int i = p_from_line; i <= p_to_line; i++) {
 		if (!text.is_hidden(i)) {
 		if (!text.is_hidden(i)) {
 			total_rows++;
 			total_rows++;
 			total_rows += get_line_wrap_count(i);
 			total_rows += get_line_wrap_count(i);
@@ -4093,6 +4106,10 @@ int TextEdit::get_total_visible_line_count() const {
 	return total_rows;
 	return total_rows;
 }
 }
 
 
+int TextEdit::get_total_visible_line_count() const {
+	return get_visible_line_count_in_range(0, text.size() - 1);
+}
+
 // Auto adjust
 // Auto adjust
 void TextEdit::adjust_viewport_to_caret() {
 void TextEdit::adjust_viewport_to_caret() {
 	// Make sure Caret is visible on the screen.
 	// Make sure Caret is visible on the screen.
@@ -4683,7 +4700,7 @@ void TextEdit::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("get_word_at_pos", "position"), &TextEdit::get_word_at_pos);
 	ClassDB::bind_method(D_METHOD("get_word_at_pos", "position"), &TextEdit::get_word_at_pos);
 
 
-	ClassDB::bind_method(D_METHOD("get_line_column_at_pos", "position"), &TextEdit::get_line_column_at_pos);
+	ClassDB::bind_method(D_METHOD("get_line_column_at_pos", "position", "allow_out_of_bounds"), &TextEdit::get_line_column_at_pos, DEFVAL(true));
 	ClassDB::bind_method(D_METHOD("get_minimap_line_at_pos", "position"), &TextEdit::get_minimap_line_at_pos);
 	ClassDB::bind_method(D_METHOD("get_minimap_line_at_pos", "position"), &TextEdit::get_minimap_line_at_pos);
 
 
 	ClassDB::bind_method(D_METHOD("is_dragging_cursor"), &TextEdit::is_dragging_cursor);
 	ClassDB::bind_method(D_METHOD("is_dragging_cursor"), &TextEdit::is_dragging_cursor);
@@ -4807,6 +4824,7 @@ void TextEdit::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_last_full_visible_line_wrap_index"), &TextEdit::get_last_full_visible_line_wrap_index);
 	ClassDB::bind_method(D_METHOD("get_last_full_visible_line_wrap_index"), &TextEdit::get_last_full_visible_line_wrap_index);
 
 
 	ClassDB::bind_method(D_METHOD("get_visible_line_count"), &TextEdit::get_visible_line_count);
 	ClassDB::bind_method(D_METHOD("get_visible_line_count"), &TextEdit::get_visible_line_count);
+	ClassDB::bind_method(D_METHOD("get_visible_line_count_in_range", "from_line", "to_line"), &TextEdit::get_visible_line_count_in_range);
 	ClassDB::bind_method(D_METHOD("get_total_visible_line_count"), &TextEdit::get_total_visible_line_count);
 	ClassDB::bind_method(D_METHOD("get_total_visible_line_count"), &TextEdit::get_total_visible_line_count);
 
 
 	// Auto adjust
 	// Auto adjust

+ 2 - 1
scene/gui/text_edit.h

@@ -716,7 +716,7 @@ public:
 
 
 	String get_word_at_pos(const Vector2 &p_pos) const;
 	String get_word_at_pos(const Vector2 &p_pos) const;
 
 
-	Point2i get_line_column_at_pos(const Point2i &p_pos) const;
+	Point2i get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_of_bounds = true) const;
 	int get_minimap_line_at_pos(const Point2i &p_pos) const;
 	int get_minimap_line_at_pos(const Point2i &p_pos) const;
 
 
 	bool is_dragging_cursor() const;
 	bool is_dragging_cursor() const;
@@ -822,6 +822,7 @@ public:
 	int get_last_full_visible_line_wrap_index() const;
 	int get_last_full_visible_line_wrap_index() const;
 
 
 	int get_visible_line_count() const;
 	int get_visible_line_count() const;
+	int get_visible_line_count_in_range(int p_from, int p_to) const;
 	int get_total_visible_line_count() const;
 	int get_total_visible_line_count() const;
 
 
 	// Auto Adjust
 	// Auto Adjust