From b6ba69bae74093da0a40314dd46ce8682fdd6708 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Wed, 18 Apr 2018 18:04:52 -0700 Subject: [PATCH] libtxt: add boxes representing empty lines in GetRectsForRange (#5043) If the start/end range passed to GetRectsForRange includes lines of text that did not render any glyphs, then GetRectsForRange should add a placeholder for that line. In particular, the framework expects this so that it can position the cursor on an empty line when editing text. Fixes https://github.com/flutter/flutter/issues/16418 --- third_party/txt/src/txt/paragraph.cc | 47 ++++++++++++++++++++++------ third_party/txt/src/txt/paragraph.h | 8 +++-- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/third_party/txt/src/txt/paragraph.cc b/third_party/txt/src/txt/paragraph.cc index 515013d6e..bab302814 100644 --- a/third_party/txt/src/txt/paragraph.cc +++ b/third_party/txt/src/txt/paragraph.cc @@ -249,7 +249,7 @@ bool Paragraph::ComputeLineBreaks() { size_t block_size = block_end - block_start; if (block_size == 0) { - line_ranges_.emplace_back(block_start, block_start, true); + line_ranges_.emplace_back(block_start, block_end, block_end + 1, true); line_widths_.push_back(0); continue; } @@ -297,8 +297,13 @@ bool Paragraph::ComputeLineBreaks() { const int* breaks = breaker_.getBreaks(); for (size_t i = 0; i < breaks_count; ++i) { size_t break_start = (i > 0) ? breaks[i - 1] : 0; - line_ranges_.emplace_back(break_start + block_start, - breaks[i] + block_start, i == breaks_count - 1); + size_t line_start = break_start + block_start; + size_t line_end = breaks[i] + block_start; + bool hard_break = i == breaks_count - 1; + size_t line_end_including_newline = + (hard_break && line_end < text_.size()) ? line_end + 1 : line_end; + line_ranges_.emplace_back(line_start, line_end, + line_end_including_newline, hard_break); line_widths_.push_back(breaker_.getWidths()[i]); } @@ -437,9 +442,9 @@ void Paragraph::Layout(double width, bool force) { std::vector> words; double word_gap_width = 0; size_t word_index = 0; - bool justify_line = (paragraph_style_.text_align == TextAlign::justify && - line_number != line_limit - 1 && - !line_ranges_[line_number].hard_break); + bool justify_line = + (paragraph_style_.text_align == TextAlign::justify && + line_number != line_limit - 1 && !line_range.hard_break); FindWords(text_, line_range.start, line_range.end, &words); if (justify_line) { if (words.size() > 1) { @@ -986,7 +991,7 @@ void Paragraph::PaintDecorations(SkCanvas* canvas, const PaintRecord& record) { std::vector Paragraph::GetRectsForRange(size_t start, size_t end) const { - std::vector boxes; + std::map> line_boxes; for (const CodeUnitRun& run : code_unit_runs_) { if (run.code_units.start >= end) @@ -1014,10 +1019,34 @@ std::vector Paragraph::GetRectsForRange(size_t start, if (left == SK_ScalarMax || right == SK_ScalarMin) continue; } - boxes.emplace_back(SkRect::MakeLTRB(left, top, right, bottom), - run.direction); + line_boxes[run.line_number].emplace_back( + SkRect::MakeLTRB(left, top, right, bottom), run.direction); } + // Add empty rectangles representing any line within the range that did not + // render any glyphs. + for (size_t line_number = 0; line_number < line_ranges_.size(); ++line_number) { + const LineRange& line = line_ranges_[line_number]; + if (line.start >= end) + break; + if (line.end_including_newline <= start) + continue; + if (line_boxes.find(line_number) == line_boxes.end()) { + // If the range starts after the beginning of this line, then place the + // rectangle at the end of the line's width. This is intended to + // handle ranges encompassing the newline character at the end of a line. + SkScalar x = (start > line.start) ? line_widths_[line_number] : 0; + SkScalar top = (line_number > 0) ? line_heights_[line_number - 1] : 0; + SkScalar bottom = line_heights_[line_number]; + line_boxes[line_number].emplace_back( + SkRect::MakeLTRB(x, top, x, bottom), TextDirection::ltr); + } + } + + std::vector boxes; + for (const auto& kv : line_boxes) { + boxes.insert(boxes.end(), kv.second.begin(), kv.second.end()); + } return boxes; } diff --git a/third_party/txt/src/txt/paragraph.h b/third_party/txt/src/txt/paragraph.h index 4a40f89b3..3a3d9aa48 100644 --- a/third_party/txt/src/txt/paragraph.h +++ b/third_party/txt/src/txt/paragraph.h @@ -63,8 +63,8 @@ class Paragraph { }; struct TextBox { - const SkRect rect; - const TextDirection direction; + SkRect rect; + TextDirection direction; TextBox(SkRect r, TextDirection d) : rect(r), direction(d) {} }; @@ -187,8 +187,10 @@ class Paragraph { mutable std::unique_ptr word_breaker_; struct LineRange { - LineRange(size_t s, size_t e, bool h) : start(s), end(e), hard_break(h) {} + LineRange(size_t s, size_t e, size_t ewn, bool h) + : start(s), end(e), end_including_newline(ewn), hard_break(h) {} size_t start, end; + size_t end_including_newline; bool hard_break; }; std::vector line_ranges_; -- GitLab