Browse Source

[RTL] Improve table cell selection.

bruvzg 3 years ago
parent
commit
9c61fa7ad8
2 changed files with 58 additions and 16 deletions
  1. 57 15
      scene/gui/rich_text_label.cpp
  2. 1 1
      scene/gui/rich_text_label.h

+ 57 - 15
scene/gui/rich_text_label.cpp

@@ -1330,14 +1330,22 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
 	}
 }
 
-float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char) {
+float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool p_table) {
 	Vector2 off;
 
 	int char_pos = -1;
 	Line &l = p_frame->lines.write[p_line];
 	bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
 	bool lrtl = is_layout_rtl();
+
+	// Table hit test results.
 	bool table_hit = false;
+	Vector2i table_range;
+	float table_offy = 0.f;
+	ItemFrame *table_click_frame = nullptr;
+	int table_click_line = -1;
+	Item *table_click_item = nullptr;
+	int table_click_char = -1;
 
 	for (int line = 0; line < l.text_buf->get_line_count(); line++) {
 		RID rid = l.text_buf->get_line_rid(line);
@@ -1381,7 +1389,8 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
 			Item *it = (Item *)(uint64_t)objects[i];
 			if (it != nullptr) {
 				Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
-				if (rect.has_point(p_click - p_ofs - off)) {
+				rect.position += p_ofs + off;
+				if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) {
 					switch (it->type) {
 						case ITEM_TABLE: {
 							int hseparation = get_theme_constant(SNAME("table_hseparation"));
@@ -1389,8 +1398,6 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
 
 							ItemTable *table = static_cast<ItemTable *>(it);
 
-							table_hit = true;
-
 							int idx = 0;
 							int col_count = table->columns.size();
 							int row_count = table->rows.size();
@@ -1406,7 +1413,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
 									if (rtl) {
 										coff.x = rect.size.width - table->columns[col].width - coff.x;
 									}
-									Rect2 crect = Rect2(p_ofs + off + rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size);
+									Rect2 crect = Rect2(rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size);
 									if (col == col_count - 1) {
 										if (rtl) {
 											crect.size.x = crect.position.x + crect.size.x;
@@ -1417,9 +1424,19 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
 									}
 									if (crect.has_point(p_click)) {
 										for (int j = 0; j < frame->lines.size(); j++) {
-											_find_click_in_line(frame, j, p_ofs + off + rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
-											if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
-												return off.y;
+											_find_click_in_line(frame, j, rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, &table_click_frame, &table_click_line, &table_click_item, &table_click_char, true);
+											if (table_click_frame && table_click_item) {
+												// Save cell detected cell hit data.
+												table_range = Vector2i(INT32_MAX, 0);
+												for (Item *F : table->subitems) {
+													ItemFrame *sub_frame = static_cast<ItemFrame *>(F);
+													for (int k = 0; k < sub_frame->lines.size(); k++) {
+														table_range.x = MIN(table_range.x, sub_frame->lines[k].char_offset);
+														table_range.y = MAX(table_range.y, sub_frame->lines[k].char_offset + sub_frame->lines[k].char_count);
+													}
+												}
+												table_offy = off.y;
+												table_hit = true;
 											}
 										}
 									}
@@ -1433,14 +1450,39 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
 				}
 			}
 		}
-		Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)), Size2(get_size().x, TS->shaped_text_get_size(rid).y));
+		Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)) - p_frame->padding.position, TS->shaped_text_get_size(rid) + p_frame->padding.position + p_frame->padding.size);
+		if (p_table) {
+			rect.size.y += get_theme_constant(SNAME("table_vseparation"));
+		}
 
-		if (rect.has_point(p_click) && !table_hit) {
+		if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) {
 			char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
 		}
+
+		// If table hit was detected, and line hit is in the table bounds use table hit.
+		if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || char_pos == -1)) {
+			if (r_click_frame != nullptr) {
+				*r_click_frame = table_click_frame;
+			}
+
+			if (r_click_line != nullptr) {
+				*r_click_line = table_click_line;
+			}
+
+			if (r_click_item != nullptr) {
+				*r_click_item = table_click_item;
+			}
+
+			if (r_click_char != nullptr) {
+				*r_click_char = table_click_char;
+			}
+			return table_offy;
+		}
+
 		off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom() + get_theme_constant(SNAME("line_separation"));
 	}
 
+	// Text line hit.
 	if (char_pos >= 0) {
 		// Find item.
 		if (r_click_item != nullptr) {
@@ -1914,12 +1956,12 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
 			selection.to_char = c_index;
 
 			bool swap = false;
-			if (selection.from_item->index > selection.to_item->index) {
-				swap = true;
-			} else if (selection.from_item->index == selection.to_item->index) {
-				if (selection.from_char > selection.to_char) {
+			if (selection.click_frame && c_frame) {
+				const Line &l1 = c_frame->lines[c_line];
+				const Line &l2 = selection.click_frame->lines[selection.click_line];
+				if (l1.char_offset + c_index < l2.char_offset + selection.click_char) {
 					swap = true;
-				} else if (selection.from_char == selection.to_char) {
+				} else if (l1.char_offset + c_index == l2.char_offset + selection.click_char) {
 					deselect();
 					return;
 				}

+ 1 - 1
scene/gui/rich_text_label.h

@@ -435,7 +435,7 @@ private:
 	void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
 	void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size);
 	int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs);
-	float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr);
+	float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false);
 
 	String _roman(int p_num, bool p_capitalize) const;
 	String _letters(int p_num, bool p_capitalize) const;