From 7d5a8ea0e9c22b5e9b513de6ceb49f34fe120cc3 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Thu, 17 Dec 2020 23:38:55 +0200 Subject: [PATCH 1/4] Always include space characters (including tabs and other space-like chars) into selection rectangles. --- servers/text_server.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 30dfa60ba330..68864980e21b 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -905,7 +905,7 @@ Vector TextServer::shaped_text_get_selection(RID p_shaped, int p_start, float off = 0.0f; for (int i = 0; i < v_size; i++) { for (int k = 0; k < glyphs[i].repeat; k++) { - if (glyphs[i].count > 0 && glyphs[i].index != 0) { + if ((glyphs[i].count > 0) && ((glyphs[i].index != 0) || ((glyphs[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE))) { if (glyphs[i].start < end && glyphs[i].end > start) { // Grapheme fully in selection range. if (glyphs[i].start >= start && glyphs[i].end <= end) { @@ -962,6 +962,10 @@ Vector TextServer::shaped_text_get_selection(RID p_shaped, int p_start, // Merge intersecting ranges. int i = 0; + while (i < ranges.size()) { + i++; + } + i = 0; while (i < ranges.size()) { int j = i + 1; while (j < ranges.size()) { From 1adea98d070783c8b5a560eeb1f9ca636db28514 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Thu, 17 Dec 2020 13:22:04 +0200 Subject: [PATCH 2/4] Use integer text position in scroll container, TextEdit and canvas editor, to ensure sharp text rendering. Use integer font align/advance with any font scaling, to ensure sharp text rendering. --- editor/plugins/canvas_item_editor_plugin.cpp | 4 ++-- modules/text_server_adv/dynamic_font_adv.cpp | 4 ++-- modules/text_server_fb/dynamic_font_fb.cpp | 4 ++-- scene/gui/progress_bar.cpp | 2 +- scene/gui/scroll_container.cpp | 1 + scene/gui/text_edit.cpp | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 2a4cc691c3f6..ac037f829f1c 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2935,7 +2935,7 @@ void CanvasItemEditor::_draw_rulers() { // Draw top ruler viewport->draw_rect(Rect2(Point2(RULER_WIDTH, 0), Size2(viewport->get_size().x, RULER_WIDTH)), bg_color); for (int i = Math::ceil(first.x); i < last.x; i++) { - Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)); + Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)).round(); if (i % (major_subdivision * minor_subdivision) == 0) { viewport->draw_line(Point2(position.x, 0), Point2(position.x, RULER_WIDTH), graduation_color, Math::round(EDSCALE)); float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)).x; @@ -2952,7 +2952,7 @@ void CanvasItemEditor::_draw_rulers() { // Draw left ruler viewport->draw_rect(Rect2(Point2(0, RULER_WIDTH), Size2(RULER_WIDTH, viewport->get_size().y)), bg_color); for (int i = Math::ceil(first.y); i < last.y; i++) { - Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)); + Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).round(); if (i % (major_subdivision * minor_subdivision) == 0) { viewport->draw_line(Point2(0, position.y), Point2(RULER_WIDTH, position.y), graduation_color, Math::round(EDSCALE)); float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).y; diff --git a/modules/text_server_adv/dynamic_font_adv.cpp b/modules/text_server_adv/dynamic_font_adv.cpp index 08c4ad2727be..99d78a5299ae 100644 --- a/modules/text_server_adv/dynamic_font_adv.cpp +++ b/modules/text_server_adv/dynamic_font_adv.cpp @@ -432,8 +432,8 @@ DynamicFontDataAdvanced::Character DynamicFontDataAdvanced::bitmap_to_character( } Character chr; - chr.align = Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling; - chr.advance = advance * p_data->scale_color_font / oversampling; + chr.align = (Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling).round(); + chr.advance = (advance * p_data->scale_color_font / oversampling).round(); chr.texture_idx = tex_pos.index; chr.found = true; diff --git a/modules/text_server_fb/dynamic_font_fb.cpp b/modules/text_server_fb/dynamic_font_fb.cpp index 6731870e8f1e..ca9e5b580b86 100644 --- a/modules/text_server_fb/dynamic_font_fb.cpp +++ b/modules/text_server_fb/dynamic_font_fb.cpp @@ -317,8 +317,8 @@ DynamicFontDataFallback::Character DynamicFontDataFallback::bitmap_to_character( } Character chr; - chr.align = Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling; - chr.advance = advance * p_data->scale_color_font / oversampling; + chr.align = (Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling).round(); + chr.advance = (advance * p_data->scale_color_font / oversampling).round(); chr.texture_idx = tex_pos.index; chr.found = true; diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 1344d010aec1..c111ddff5802 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -74,7 +74,7 @@ void ProgressBar::_notification(int p_what) { if (percent_visible) { String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign(); TextLine tl = TextLine(txt, font, font_size); - tl.draw(get_canvas_item(), Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2, font_color); + tl.draw(get_canvas_item(), (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(), font_color); } } } diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 3a54ac744306..4110a971c88d 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -328,6 +328,7 @@ void ScrollContainer::_notification(int p_what) { if (rtl && v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { r.position.x += v_scroll->get_minimum_size().x; } + r.position = r.position.floor(); fit_child_in_rect(c, r); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index f3569f9ce3b7..bf7e82e87c65 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1245,7 +1245,7 @@ void TextEdit::_notification(int p_what) { int gl_size = visual.size(); ofs_y += ldata->get_line_ascent(line_wrap_index); - float char_ofs = 0.f; + int char_ofs = 0; for (int j = 0; j < gl_size; j++) { if (color_map.has(glyphs[j].start)) { current_color = color_map[glyphs[j].start].get("color"); From 784f869f0f36a9e4d600fca2523a6200b648fbbd Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Fri, 18 Dec 2020 08:55:19 +0200 Subject: [PATCH 3/4] Fix RichTextLabel content height and scrollbar calculations. --- scene/gui/rich_text_label.cpp | 27 +++++++++++++-------------- scene/gui/rich_text_label.h | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 244d90ca6b79..8fd9dc7ddb29 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -606,11 +606,11 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref } } -float RichTextLabel::_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_color_shadow, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) { +void RichTextLabel::_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_color_shadow, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) { Vector2 off; - ERR_FAIL_COND_V(p_frame == nullptr, off.y); - ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), off.y); + ERR_FAIL_COND(p_frame == nullptr); + ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); Line &l = p_frame->lines.write[p_line]; @@ -619,7 +619,7 @@ float RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p Variant meta; if (it_from == nullptr) { - return off.y; + return; } RID ci = get_canvas_item(); @@ -1040,8 +1040,6 @@ float RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p } off.y += TS->shaped_text_get_descent(rid); } - - return off.y; } void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool *r_outside) { @@ -1065,7 +1063,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item //TODO, change to binary search ? while (from_line < main->lines.size()) { - if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + _get_text_rect().get_position().y >= vofs) { + if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) { break; } from_line++; @@ -1349,7 +1347,7 @@ void RichTextLabel::_notification(int p_what) { //TODO, change to binary search ? while (from_line < main->lines.size()) { - if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + _get_text_rect().get_position().y >= vofs) { + if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) { break; } from_line++; @@ -1372,7 +1370,8 @@ void RichTextLabel::_notification(int p_what) { Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); while (ofs.y < size.height && from_line < main->lines.size()) { visible_line_count++; - ofs.y += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_color_shadow, use_outline, shadow_ofs); + _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_color_shadow, use_outline, shadow_ofs); + ofs.y += main->lines[from_line].text_buf->get_size().y; from_line++; } } break; @@ -2062,14 +2061,14 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { int total_height = 0; if (p_frame->lines.size()) { - total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + get_theme_stylebox("normal")->get_minimum_size().height; + total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y; } p_frame->first_resized_line = p_frame->lines.size(); updating_scroll = true; vscroll->set_max(total_height); - vscroll->set_page(size.height); + vscroll->set_page(text_rect.size.height); if (scroll_follow && scroll_following) { vscroll->set_value(total_height - size.height); } @@ -2098,7 +2097,7 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { int total_height = 0; if (p_frame->lines.size()) { - total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + get_theme_stylebox("normal")->get_minimum_size().height; + total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y; } p_frame->first_invalid_line = p_frame->lines.size(); @@ -2106,7 +2105,7 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { updating_scroll = true; vscroll->set_max(total_height); - vscroll->set_page(size.height); + vscroll->set_page(text_rect.size.height); if (scroll_follow && scroll_following) { vscroll->set_value(total_height - size.height); } @@ -3649,7 +3648,7 @@ void RichTextLabel::install_effect(const Variant effect) { int RichTextLabel::get_content_height() const { int total_height = 0; if (main->lines.size()) { - total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y + get_theme_stylebox("normal")->get_minimum_size().height; + total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y; } return total_height; } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 93e48dd449fd..f1dac69dce8c 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -378,7 +378,7 @@ class RichTextLabel : public Control { void _shape_line(ItemFrame *p_frame, int p_line, const Ref &p_base_font, int p_base_font_size, int p_width, int *r_char_offset); void _resize_line(ItemFrame *p_frame, int p_line, const Ref &p_base_font, int p_base_font_size, int p_width); - float _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_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs); + void _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_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs); 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); String _roman(int p_num, bool p_capitalize) const; From cc4d6eb7f6be529f6fabc1242e363a460e39abdc Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Mon, 21 Dec 2020 11:57:35 +0200 Subject: [PATCH 4/4] Improve fill aligned text hit testing. --- servers/text_server.cpp | 45 +++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 68864980e21b..fa39185ac094 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -1038,31 +1038,36 @@ int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) cons float off = 0.0f; for (int i = 0; i < v_size; i++) { - for (int k = 0; k < glyphs[i].repeat; k++) { - if (glyphs[i].count > 0) { - float advance = 0.f; - for (int j = 0; j < glyphs[i].count; j++) { - advance += glyphs[i + j].advance; + if (glyphs[i].count > 0) { + float advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance * glyphs[i + j].repeat; + } + if (((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) && (p_coords >= off && p_coords < off + advance)) { + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + return glyphs[i].end; + } else { + return glyphs[i].start; } - // Place caret to the left of clicked grapheme. - if (p_coords >= off && p_coords < off + advance / 2) { - if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { - return glyphs[i].end; - } else { - return glyphs[i].start; - } + } + // Place caret to the left of clicked grapheme. + if (p_coords >= off && p_coords < off + advance / 2) { + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + return glyphs[i].end; + } else { + return glyphs[i].start; } - // Place caret to the right of clicked grapheme. - if (p_coords >= off + advance / 2 && p_coords < off + advance) { - if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { - return glyphs[i].start; - } else { - return glyphs[i].end; - } + } + // Place caret to the right of clicked grapheme. + if (p_coords >= off + advance / 2 && p_coords < off + advance) { + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + return glyphs[i].start; + } else { + return glyphs[i].end; } } - off += glyphs[i].advance; } + off += glyphs[i].advance * glyphs[i].repeat; } return 0; }