Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Unified Diff: ui/gfx/render_text.cc

Issue 112063003: Implement eliding/truncating at end in RenderText (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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() {

Powered by Google App Engine
This is Rietveld 408576698