From a144f17aeede2c4d139e3d7f43451a4e70b4e214 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Thu, 18 Apr 2019 09:08:41 -0700 Subject: [PATCH] Tight Paragraph Width (#8530) Calculate and expose paragraph tightWidth to the framework to allow drawing Text based on this. --- lib/ui/text.dart | 6 +++++ lib/ui/text/paragraph.cc | 5 ++++ lib/ui/text/paragraph.h | 1 + lib/ui/text/paragraph_impl.h | 2 ++ lib/ui/text/paragraph_impl_txt.cc | 4 ++++ lib/ui/text/paragraph_impl_txt.h | 1 + third_party/txt/src/txt/paragraph.cc | 6 +++++ third_party/txt/src/txt/paragraph.h | 7 ++++++ third_party/txt/tests/paragraph_unittests.cc | 24 +++++++++++++++++++- 9 files changed, 55 insertions(+), 1 deletion(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 87bb5571631..4475551922a 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1315,6 +1315,12 @@ class Paragraph extends NativeFieldWrapperClass2 { /// Valid only after [layout] has been called. double get height native 'Paragraph_height'; + /// The distance from the left edge of the leftmost glyph to the right edge of + /// the rightmost glyph in the paragraph. + /// + /// Valid only after [layout] has been called. + double get tightWidth native 'Paragraph_tightWidth'; + /// The minimum width that this paragraph could be without failing to paint /// its contents within itself. /// diff --git a/lib/ui/text/paragraph.cc b/lib/ui/text/paragraph.cc index 8d776076077..fd517d58073 100644 --- a/lib/ui/text/paragraph.cc +++ b/lib/ui/text/paragraph.cc @@ -22,6 +22,7 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, Paragraph); #define FOR_EACH_BINDING(V) \ V(Paragraph, width) \ V(Paragraph, height) \ + V(Paragraph, tightWidth) \ V(Paragraph, minIntrinsicWidth) \ V(Paragraph, maxIntrinsicWidth) \ V(Paragraph, alphabeticBaseline) \ @@ -56,6 +57,10 @@ double Paragraph::height() { return m_paragraphImpl->height(); } +double Paragraph::tightWidth() { + return m_paragraphImpl->tightWidth(); +} + double Paragraph::minIntrinsicWidth() { return m_paragraphImpl->minIntrinsicWidth(); } diff --git a/lib/ui/text/paragraph.h b/lib/ui/text/paragraph.h index a96f6fdfeff..a348cdf674c 100644 --- a/lib/ui/text/paragraph.h +++ b/lib/ui/text/paragraph.h @@ -33,6 +33,7 @@ class Paragraph : public RefCountedDartWrappable { double width(); double height(); + double tightWidth(); double minIntrinsicWidth(); double maxIntrinsicWidth(); double alphabeticBaseline(); diff --git a/lib/ui/text/paragraph_impl.h b/lib/ui/text/paragraph_impl.h index 357fbd3dffd..f81cb36d39a 100644 --- a/lib/ui/text/paragraph_impl.h +++ b/lib/ui/text/paragraph_impl.h @@ -19,6 +19,8 @@ class ParagraphImpl { virtual double height() = 0; + virtual double tightWidth() = 0; + virtual double minIntrinsicWidth() = 0; virtual double maxIntrinsicWidth() = 0; diff --git a/lib/ui/text/paragraph_impl_txt.cc b/lib/ui/text/paragraph_impl_txt.cc index a875cd9a51a..e488f38f334 100644 --- a/lib/ui/text/paragraph_impl_txt.cc +++ b/lib/ui/text/paragraph_impl_txt.cc @@ -29,6 +29,10 @@ double ParagraphImplTxt::height() { return m_paragraph->GetHeight(); } +double ParagraphImplTxt::tightWidth() { + return m_paragraph->GetTightWidth(); +} + double ParagraphImplTxt::minIntrinsicWidth() { return m_paragraph->GetMinIntrinsicWidth(); } diff --git a/lib/ui/text/paragraph_impl_txt.h b/lib/ui/text/paragraph_impl_txt.h index ad44d7235b0..53479a45a38 100644 --- a/lib/ui/text/paragraph_impl_txt.h +++ b/lib/ui/text/paragraph_impl_txt.h @@ -19,6 +19,7 @@ class ParagraphImplTxt : public ParagraphImpl { double width() override; double height() override; + double tightWidth() override; double minIntrinsicWidth() override; double maxIntrinsicWidth() override; double alphabeticBaseline() override; diff --git a/third_party/txt/src/txt/paragraph.cc b/third_party/txt/src/txt/paragraph.cc index 2e84f6fe49a..393b28be4f3 100644 --- a/third_party/txt/src/txt/paragraph.cc +++ b/third_party/txt/src/txt/paragraph.cc @@ -919,6 +919,8 @@ void Paragraph::Layout(double width, bool force) { [](const CodeUnitRun& a, const CodeUnitRun& b) { return a.code_units.start < b.code_units.start; }); + + tight_width_ = max_right_ - min_left_; } double Paragraph::GetLineXOffset(double line_total_advance) { @@ -970,6 +972,10 @@ double Paragraph::GetMaxWidth() const { return width_; } +double Paragraph::GetTightWidth() const { + return tight_width_; +} + void Paragraph::SetParagraphStyle(const ParagraphStyle& style) { needs_layout_ = true; paragraph_style_ = style; diff --git a/third_party/txt/src/txt/paragraph.h b/third_party/txt/src/txt/paragraph.h index 0518bac0332..c537871451a 100644 --- a/third_party/txt/src/txt/paragraph.h +++ b/third_party/txt/src/txt/paragraph.h @@ -155,6 +155,12 @@ class Paragraph { // GetMaxWidth() >= GetLayoutWidth(). double GetMaxWidth() const; + // Returns the tight width found in Layout(), which is defined as the + // horizontal distance from the left edge of the leftmost glyph to the right + // edge of the rightmost glyph. We expect that GetTightWidth() <= + // GetMaxWidth(). + double GetTightWidth() const; + // Distance from top of paragraph to the Alphabetic baseline of the first // line. Used for alphabetic fonts (A-Z, a-z, greek, etc.) double GetAlphabeticBaseline() const; @@ -348,6 +354,7 @@ class Paragraph { // The max width of the paragraph as provided in the most recent Layout() // call. double width_ = -1.0f; + double tight_width_ = -1.0f; double max_intrinsic_width_ = 0; double min_intrinsic_width_ = 0; double alphabetic_baseline_ = FLT_MAX; diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index 62f984af8c6..bab5430abc4 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -264,6 +264,19 @@ TEST_F(ParagraphTest, BoldParagraph) { ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style)); ASSERT_EQ(paragraph->records_[0].style().color, text_style.color); ASSERT_TRUE(Snapshot()); + + // width_ takes the full available space, but tight_width_ is only the width + // of the text, which is less than one line. + ASSERT_DOUBLE_EQ(paragraph->width_, GetTestCanvasWidth()); + ASSERT_TRUE(paragraph->tight_width_ < paragraph->width_); + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + std::vector boxes = paragraph->GetRectsForRange( + 0, strlen(text), rect_height_style, rect_width_style); + ASSERT_DOUBLE_EQ(paragraph->tight_width_, + boxes[boxes.size() - 1].rect.right() - boxes[0].rect.left()); } TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(LeftAlignParagraph)) { @@ -407,7 +420,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(RightAlignParagraph)) { builder.Pop(); auto paragraph = builder.Build(); - paragraph->Layout(GetTestCanvasWidth() - 100); + int available_width = GetTestCanvasWidth() - 100; + paragraph->Layout(available_width); paragraph->Paint(GetCanvas(), 0, 0); @@ -432,6 +446,14 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(RightAlignParagraph)) { paragraph->breaker_.getWidths()[paragraph->records_[0].line()], 2.0); + // width_ takes the full available space, while tight_width_ wraps the glyphs + // as tightly as possible. Even though this text is more than one line long, + // no line perfectly spans the width of the full line, so tight_width_ is less + // than width_. + ASSERT_DOUBLE_EQ(paragraph->width_, available_width); + ASSERT_TRUE(paragraph->tight_width_ < available_width); + ASSERT_DOUBLE_EQ(paragraph->tight_width_, 893.19921875); + ASSERT_TRUE(paragraph->records_[2].style().equals(text_style)); ASSERT_DOUBLE_EQ(paragraph->records_[2].offset().y(), expected_y); expected_y += 30;