Chromium Code Reviews| Index: ui/gfx/render_text.cc |
| diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc |
| index e8e3436f11c0508303fe1515447752d7ce341936..201aff194f56f52a2847f0e7dd881314b106fe9c 100644 |
| --- a/ui/gfx/render_text.cc |
| +++ b/ui/gfx/render_text.cc |
| @@ -19,6 +19,7 @@ |
| #include "ui/gfx/skia_util.h" |
| #include "ui/gfx/text_constants.h" |
| #include "ui/gfx/text_elider.h" |
| +#include "ui/gfx/text_utils.h" |
| #include "ui/gfx/utf16_indexing.h" |
| namespace gfx { |
| @@ -433,6 +434,14 @@ void RenderText::SetMultiline(bool multiline) { |
| } |
| } |
| +void RenderText::Elide(float available_pixel_width, |
| + ElideBehavior elide_behavior) { |
| + available_pixel_width_ = available_pixel_width; |
| + elide_behavior_ = elide_behavior; |
| + UpdateLayoutText(); |
| + ResetLayout(); |
|
msw
2013/12/11 08:10:14
nit: Would you mind adding a ResetLayout() call to
Anuj
2013/12/12 08:08:41
Done.
msw
2013/12/12 19:28:29
Thanks!
Anuj
2013/12/13 01:23:50
Done.
|
| +} |
| + |
| void RenderText::SetDisplayRect(const Rect& r) { |
| display_rect_ = r; |
| baseline_ = kInvalidBaseline; |
| @@ -830,6 +839,8 @@ RenderText::RenderText() |
| obscured_(false), |
| obscured_reveal_index_(-1), |
| truncate_length_(0), |
| + available_pixel_width_(0.0), |
| + elide_behavior_(NO_ELISION), |
| multiline_(false), |
| fade_head_(false), |
| fade_tail_(false), |
| @@ -1116,6 +1127,111 @@ void RenderText::UpdateLayoutText() { |
| iter.setIndex32(truncate_length_ - 1); |
| layout_text_.assign(text.substr(0, iter.getIndex()) + gfx::kEllipsisUTF16); |
| } |
| + |
| + int content_width = GetContentWidth(); |
| + if (elide_behavior_ != NO_ELISION && available_pixel_width_ > 0 && |
| + content_width > available_pixel_width_) { |
| + base::string16 elided_text = ElideText(text); |
|
msw
2013/12/11 08:10:14
This disregards the obscuring and truncation done
Anuj
2013/12/12 08:08:41
Done.
|
| + |
| + // This doesn't trim styles so ellipsis may get rendered as a different |
|
msw
2013/12/11 08:10:14
I suppose it'll be tough to temporarily apply styl
Anuj
2013/12/12 08:08:41
Filed crbug.com/327850
|
| + // style than the preceding text. |
| + // DISCUSS WITH REVIEWER. |
| + layout_text_.assign(elided_text); |
| + } |
| +} |
| + |
| +base::string16 RenderText::ElideText(const base::string16& text) { |
|
msw
2013/12/11 08:10:14
This is quite similar to ElideText from text_elide
Anuj
2013/12/12 08:08:41
Done.
|
| + if (text.empty()) |
| + return text; |
| + |
| + const float current_text_pixel_width = GetStringSizeF().width(); |
|
msw
2013/12/11 08:10:14
GetStringSizeF will actually EnsureLayout and calc
Anuj
2013/12/12 08:08:41
I can't forgo the argument because of the recursiv
msw
2013/12/12 19:28:29
Hmm, nice!
Anuj
2013/12/13 01:23:50
Done.
|
| + const bool elide_in_middle = false; |
| + const bool insert_ellipsis = (elide_behavior_ != TRUNCATE_AT_END); |
| + |
| + const base::string16 ellipsis = base::string16(kEllipsisUTF16); |
| + StringSlicer slicer(text, ellipsis, elide_in_middle); |
| + |
| + // Pango will return 0 width for absurdly long strings. Cut the string in |
| + // half and try again. |
| + // This is caused by an int overflow in Pango (specifically, in |
| + // pango_glyph_string_extents_range). It's actually more subtle than just |
| + // returning 0, since on super absurdly long strings, the int can wrap and |
| + // return positive numbers again. Detecting that is probably not worth it |
| + // (eliding way too much from a ridiculous string is probably still |
| + // ridiculous), but we should check other widths for bogus values as well. |
| + if (current_text_pixel_width <= 0 && !text.empty()) { |
| + const base::string16 cut = slicer.CutString(text.length() / 2, false); |
|
msw
2013/12/11 08:10:14
Shouldn't this actually insert an ellipsis if |ins
Anuj
2013/12/12 08:08:41
Done.
|
| + return ElideText(cut); |
| + } |
| + |
| + if (current_text_pixel_width <= available_pixel_width_) |
| + return text; |
| + |
| + if (insert_ellipsis && |
| + GetStringWidthF(ellipsis, font_list_) > available_pixel_width_) |
| + return base::string16(); |
| + |
| + // Use binary search to compute the elided text. |
| + size_t lo = 0; |
| + size_t hi = text.length() - 1; |
| + size_t guess; |
|
msw
2013/12/11 08:10:14
nit: declare guess in the for loop's control.
Anuj
2013/12/12 08:08:41
Done.
|
| + |
| + // Create a copy render text. We copy the attributes which affect the |
| + // rendering width. |
| + scoped_ptr<RenderText> render_text(CreateInstance()); |
| + render_text->SetFontList(font_list_); |
| + render_text->SetDirectionalityMode(directionality_mode_); |
| + render_text->styles_ = styles_; |
| + render_text->colors_ = colors_; |
| + render_text->SetText(text); |
| + for (guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { |
| + // Restore styles and colors. They will be truncated to size by SetText. |
| + render_text->styles_ = styles_; |
| + render_text->colors_ = colors_; |
| + base::string16 newtext = slicer.CutString(guess, false); |
|
msw
2013/12/11 08:10:14
nit: use the unix_hacker naming convention |new_te
Anuj
2013/12/12 08:08:41
Done.
|
| + render_text->SetText(newtext); |
| + |
| + // This has to be an additional step so that the ellipsis is rendered with |
| + // same style as trailing part of the text. |
| + if (insert_ellipsis) { |
| + newtext += ellipsis; |
| + |
| + // When ellipsis follows text whose directionality is not the same as that |
| + // of the whole text, it will be rendered with the directionality of the |
| + // whole text. Since we want ellipsis to indicate continuation of the |
| + // preceding text, we force the directionality of ellipsis to be same as |
| + // the preceding text using LTR or RTL markers. |
| + base::i18n::TextDirection trailing_text_direction = |
| + base::i18n::GetLastStrongCharacterDirection(newtext); |
|
msw
2013/12/11 08:10:14
nit: indent two more spaces.
Anuj
2013/12/12 08:08:41
Done.
|
| + if (trailing_text_direction != render_text->GetTextDirection()) { |
| + if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT) |
| + newtext += base::i18n::kLeftToRightMark; |
|
msw
2013/12/11 08:10:14
I think you should use WrapStringWithLTRFormatting
Anuj
2013/12/11 18:41:32
See implicit markers here - http://www.iamcal.com/
msw
2013/12/11 19:39:57
Cool, using that mark seems correct, thanks for th
Anuj
2013/12/12 08:08:41
I suggested an extra param because existing users
msw
2013/12/12 19:28:29
I suggest making the default behavior add the dire
Anuj
2013/12/13 01:23:50
Adding a TODO, because ellipsis in middle also nee
|
| + else |
| + newtext += base::i18n::kRightToLeftMark; |
| + } |
| + render_text->SetText(newtext); |
| + } |
| + |
| + // We check the length of the whole desired string at once to ensure we |
|
msw
2013/12/11 08:10:14
nit: s/length/width/, s/guess_length/guess_width/;
Anuj
2013/12/12 08:08:41
Done.
|
| + // handle kerning/ligatures/etc. correctly. |
| + const float guess_length = render_text->GetStringSizeF().width(); |
| + // Check again that we didn't hit a Pango width overflow. If so, cut the |
|
msw
2013/12/11 08:10:14
This shouldn't be necessary here or in the text_el
Anuj
2013/12/12 08:08:41
I thought adding ellipsis can cause that behavior.
|
| + // current string in half and start over. |
| + if (guess_length <= 0) { |
|
msw
2013/12/11 08:10:14
nit: remove unnecessary curly braces.
Anuj
2013/12/12 08:08:41
Done.
|
| + return ElideText(slicer.CutString(guess / 2, false)); |
| + } |
| + if (guess_length > available_pixel_width_) { |
|
msw
2013/12/11 08:10:14
Shouldn't this break if guess_length == available_
Anuj
2013/12/12 08:08:41
Done.
|
| + // Move back if we are on loop terminating condition, and guess is longer |
| + // than available. |
| + if (hi == lo) |
|
msw
2013/12/11 08:10:14
Sorry, I'm failing to understand how this can happ
Anuj
2013/12/11 18:41:32
I had a scenario where the following happened
avai
msw
2013/12/11 19:39:57
Yeah, I worked through a few conceptual examples a
Anuj
2013/12/12 08:08:41
I got a new interview question ;)
msw
2013/12/12 19:28:29
I thought binary searches were supposed to be easy
Anuj
2013/12/13 01:23:50
Done.
|
| + lo = guess - 1; |
| + hi = guess - 1; |
| + } else { |
| + lo = guess + 1; |
| + } |
| + } |
| + |
| + return render_text->text(); |
| } |
| void RenderText::UpdateCachedBoundsAndOffset() { |