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

Unified Diff: ui/gfx/render_text.cc

Issue 354963003: Move gfx::ElideText functionality to RenderText. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Reorder RenderText::Elide impl to match decl. Created 6 years, 6 months 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 c7871ee7a05ed4e1da574e2d6ffb1d6ca03e147a..7ed4edfe514ac023cf7af9ee0fd2e1774517dee4 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -22,7 +22,6 @@
#include "ui/gfx/scoped_canvas.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/switches.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"
@@ -873,6 +872,104 @@ SelectionModel RenderText::GetSelectionModelForSelectionStart() {
sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
}
+base::string16 RenderText::Elide(const base::string16& text,
+ float available_width,
+ ElideBehavior behavior) {
+ if (text.empty() || behavior == FADE_TAIL)
+ return text;
+ if (available_width <= 0)
+ return base::string16();
+ if (behavior == ELIDE_EMAIL)
+ return ElideEmail(text, available_width);
+
+ // Create a RenderText copy with attributes that affect the rendering width.
+ scoped_ptr<RenderText> render_text(CreateInstance());
+ render_text->SetFontList(font_list_);
+ render_text->SetDirectionalityMode(directionality_mode_);
+ render_text->SetCursorEnabled(cursor_enabled_);
+ render_text->styles_ = styles_;
+ render_text->colors_ = colors_;
+ render_text->SetText(text);
+ const int current_text_pixel_width = render_text->GetContentWidth();
+
+ const base::string16 ellipsis = base::string16(kEllipsisUTF16);
+ const bool insert_ellipsis = (behavior != TRUNCATE);
+ const bool elide_in_middle = (behavior == ELIDE_MIDDLE);
+ const bool elide_at_beginning = (behavior == ELIDE_HEAD);
+ StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning);
+
+ // 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())
+ return Elide(slicer.CutString(text.length() / 2, insert_ellipsis),
+ available_width, behavior);
+
+ if (current_text_pixel_width <= available_width)
+ return text;
+
+ render_text->SetText(ellipsis);
+ const int ellipsis_width = render_text->GetContentWidth();
+
+ if (insert_ellipsis && (ellipsis_width > available_width))
+ return base::string16();
+
+ // Use binary search to compute the elided text.
+ size_t lo = 0;
+ size_t hi = text.length() - 1;
+ const base::i18n::TextDirection text_direction = GetTextDirection();
+ for (size_t 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 new_text =
+ slicer.CutString(guess, insert_ellipsis && behavior != ELIDE_TAIL);
+ render_text->SetText(new_text);
+
+ // 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 && behavior == ELIDE_TAIL) {
+ // 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(new_text);
+ new_text.append(ellipsis);
+ if (trailing_text_direction != text_direction) {
+ if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT)
+ new_text += base::i18n::kLeftToRightMark;
+ else
+ new_text += base::i18n::kRightToLeftMark;
+ }
+ render_text->SetText(new_text);
+ }
+
+ // We check the width of the whole desired string at once to ensure we
+ // handle kerning/ligatures/etc. correctly.
+ const int guess_width = render_text->GetContentWidth();
+ if (guess_width == available_width)
+ break;
+ if (guess_width > available_width) {
+ hi = guess - 1;
+ // Move back if we are on loop terminating condition, and guess is wider
+ // than available.
+ if (hi < lo)
+ lo = hi;
+ } else {
+ lo = guess + 1;
+ }
+ }
+
+ return render_text->text();
+}
+
RenderText::RenderText()
: horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
directionality_mode_(DIRECTIONALITY_FROM_TEXT),
@@ -1169,101 +1266,66 @@ void RenderText::UpdateLayoutText() {
GetContentWidth() > display_rect_.width()) {
// This doesn't trim styles so ellipsis may get rendered as a different
// style than the preceding text. See crbug.com/327850.
- layout_text_.assign(ElideText(layout_text_));
+ layout_text_.assign(
+ Elide(layout_text_, display_rect_.width(), elide_behavior_));
}
ResetLayout();
}
-// TODO(skanuj): Fix code duplication with ElideText in ui/gfx/text_elider.cc
-// See crbug.com/327846
-base::string16 RenderText::ElideText(const base::string16& text) {
- const bool insert_ellipsis = (elide_behavior_ != TRUNCATE);
- // Create a RenderText copy with attributes that affect the rendering width.
- scoped_ptr<RenderText> render_text(CreateInstance());
- render_text->SetFontList(font_list_);
- render_text->SetDirectionalityMode(directionality_mode_);
- render_text->SetCursorEnabled(cursor_enabled_);
-
- render_text->styles_ = styles_;
- render_text->colors_ = colors_;
- render_text->SetText(text);
- const int current_text_pixel_width = render_text->GetContentWidth();
-
- const base::string16 ellipsis = base::string16(kEllipsisUTF16);
- const bool elide_in_middle = false;
- const bool elide_at_beginning = false;
- StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning);
-
- // 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())
- return ElideText(slicer.CutString(text.length() / 2, insert_ellipsis));
-
- if (current_text_pixel_width <= display_rect_.width())
- return text;
-
- render_text->SetText(base::string16());
- render_text->SetText(ellipsis);
- const int ellipsis_width = render_text->GetContentWidth();
-
- if (insert_ellipsis && (ellipsis_width >= display_rect_.width()))
- return base::string16();
-
- // Use binary search to compute the elided text.
- size_t lo = 0;
- size_t hi = text.length() - 1;
- const base::i18n::TextDirection text_direction = GetTextDirection();
- for (size_t 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 new_text = slicer.CutString(guess, false);
- render_text->SetText(new_text);
-
- // 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) {
- // 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(new_text);
- new_text.append(ellipsis);
- if (trailing_text_direction != text_direction) {
- if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT)
- new_text += base::i18n::kLeftToRightMark;
- else
- new_text += base::i18n::kRightToLeftMark;
- }
- render_text->SetText(new_text);
- }
-
- // We check the width of the whole desired string at once to ensure we
- // handle kerning/ligatures/etc. correctly.
- const int guess_width = render_text->GetContentWidth();
- if (guess_width == display_rect_.width())
- break;
- if (guess_width > display_rect_.width()) {
- hi = guess - 1;
- // Move back if we are on loop terminating condition, and guess is wider
- // than available.
- if (hi < lo)
- lo = hi;
- } else {
- lo = guess + 1;
- }
+base::string16 RenderText::ElideEmail(const base::string16& email,
+ float available_width) {
+ // The returned string will have at least one character besides the ellipses
+ // on either side of '@'; if that's impossible a single ellipsis is returned.
+ // If possible, only the username is elided. Otherwise, the domain is elided
+ // in the middle, splitting available width equally with the elided username.
+ // If the username is short enough that it doesn't need half the available
+ // width, the elided domain will occupy that extra width.
+
+ // Split the email into its local-part (username) and domain-part. The email
+ // spec allows for @ symbols in the username under some special requirements,
+ // but not in the domain part, so splitting at the last @ symbol is safe.
+ const size_t split_index = email.find_last_of('@');
+ DCHECK_NE(split_index, base::string16::npos);
+ base::string16 username = email.substr(0, split_index);
+ base::string16 domain = email.substr(split_index + 1);
+ DCHECK(!username.empty());
+ DCHECK(!domain.empty());
+
+ // Subtract the @ symbol from the available width as it is mandatory.
+ const base::string16 kAtSignUTF16 = base::ASCIIToUTF16("@");
+ available_width -= GetStringWidthF(kAtSignUTF16, font_list());
+
+ // Check whether eliding the domain is necessary: if eliding the username
+ // is sufficient, the domain will not be elided.
+ const float full_username_width = GetStringWidthF(username, font_list());
+ const float available_domain_width = available_width -
+ std::min(full_username_width,
+ GetStringWidthF(username.substr(0, 1) + kEllipsisUTF16, font_list()));
+ if (GetStringWidthF(domain, font_list()) > available_domain_width) {
+ // Elide the domain so that it only takes half of the available width.
+ // Should the username not need all the width available in its half, the
+ // domain will occupy the leftover width.
+ // If |desired_domain_width| is greater than |available_domain_width|: the
+ // minimal username elision allowed by the specifications will not fit; thus
+ // |desired_domain_width| must be <= |available_domain_width| at all cost.
+ const float desired_domain_width =
+ std::min<float>(available_domain_width,
+ std::max<float>(available_width - full_username_width,
+ available_width / 2));
+ domain = Elide(domain, desired_domain_width, ELIDE_MIDDLE);
+ // Failing to elide the domain such that at least one character remains
+ // (other than the ellipsis itself) remains: return a single ellipsis.
+ if (domain.length() <= 1U)
+ return base::string16(kEllipsisUTF16);
}
- return render_text->text();
+ // Fit the username in the remaining width (at this point the elided username
+ // is guaranteed to fit with at least one character remaining given all the
+ // precautions taken earlier).
+ available_width -= GetStringWidthF(domain, font_list());
+ username = Elide(username, available_width, ELIDE_TAIL);
+ return username + kAtSignUTF16 + domain;
}
void RenderText::UpdateCachedBoundsAndOffset() {
« ui/gfx/render_text.h ('K') | « ui/gfx/render_text.h ('k') | ui/gfx/text_elider.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698