diff options
author | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-21 16:11:13 +0000 |
---|---|---|
committer | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-21 16:11:13 +0000 |
commit | 5ef265defa58eee026b799e8ec0e15dc2e7e8113 (patch) | |
tree | 7ef249fcd4d3b802c5967cb444a2f8fb1a58f841 /ash/tooltips | |
parent | 8f91790d65a6228eeb71cad070b2ae5cd1fe6cbb (diff) | |
download | chromium_src-5ef265defa58eee026b799e8ec0e15dc2e7e8113.zip chromium_src-5ef265defa58eee026b799e8ec0e15dc2e7e8113.tar.gz chromium_src-5ef265defa58eee026b799e8ec0e15dc2e7e8113.tar.bz2 |
aura: Really long tooltips should be wrapped not truncated.
BUG=119083
TEST=manual
Review URL: http://codereview.chromium.org/9796004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@127975 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/tooltips')
-rw-r--r-- | ash/tooltips/tooltip_controller.cc | 137 | ||||
-rw-r--r-- | ash/tooltips/tooltip_controller.h | 10 | ||||
-rw-r--r-- | ash/tooltips/tooltip_controller_unittest.cc | 104 |
3 files changed, 206 insertions, 45 deletions
diff --git a/ash/tooltips/tooltip_controller.cc b/ash/tooltips/tooltip_controller.cc index 52049e7..d3efc1f 100644 --- a/ash/tooltips/tooltip_controller.cc +++ b/ash/tooltips/tooltip_controller.cc @@ -33,6 +33,14 @@ const SkColor kTooltipBackground = 0xFFFFFFCC; const SkColor kTooltipBorder = 0xFF646450; const int kTooltipBorderWidth = 1; const int kTooltipHorizontalPadding = 3; + +// Max visual tooltip width. If a tooltip is greater than this width, it will +// be wrapped. +const int kTooltipMaxWidthPixels = 400; + +// Maximum number of lines we allow in the tooltip. +const size_t kMaxLines = 10; + // TODO(derat): This padding is needed on Chrome OS devices but seems excessive // when running the same binary on a Linux workstation; presumably there's a // difference in font metrics. Rationalize this. @@ -46,9 +54,6 @@ const int kCursorOffsetY = 15; // Maximum number of characters we allow in a tooltip. const size_t kMaxTooltipLength = 1024; -// Maximum number of lines we allow in the tooltip. -const size_t kMaxLines = 6; - gfx::Font GetDefaultFont() { // TODO(varunjain): implementation duplicated in tooltip_manager_aura. Figure // out a way to merge. @@ -64,48 +69,6 @@ int GetMaxWidth(int x, int y) { return (monitor_bounds.width() + 1) / 2; } -// Trims the tooltip to fit, setting |text| to the clipped result, -// |max_width| to the width (in pixels) of the clipped text and |line_count| -// to the number of lines of text in the tooltip. |x| and |y| give the -// location of the tooltip in screen coordinates. -void TrimTooltipToFit(string16* text, - int* max_width, - int* line_count, - int x, - int y) { - *max_width = 0; - *line_count = 0; - - // Clamp the tooltip length to kMaxTooltipLength so that we don't - // accidentally DOS the user with a mega tooltip. - if (text->length() > kMaxTooltipLength) - *text = text->substr(0, kMaxTooltipLength); - - // Determine the available width for the tooltip. - int available_width = GetMaxWidth(x, y); - - // Split the string into at most kMaxLines lines. - std::vector<string16> lines; - base::SplitString(*text, '\n', &lines); - if (lines.size() > kMaxLines) - lines.resize(kMaxLines); - *line_count = static_cast<int>(lines.size()); - - // Format each line to fit. - gfx::Font font = GetDefaultFont(); - string16 result; - for (std::vector<string16>::iterator i = lines.begin(); i != lines.end(); - ++i) { - string16 elided_text = - ui::ElideText(*i, font, available_width, ui::ELIDE_AT_END); - *max_width = std::max(*max_width, font.GetStringWidth(elided_text)); - if (!result.empty()) - result.push_back('\n'); - result.append(elided_text); - } - *text = result; -} - // Creates a widget of type TYPE_TOOLTIP views::Widget* CreateTooltip() { views::Widget* widget = new views::Widget; @@ -320,6 +283,90 @@ void TooltipController::OnWindowDestroyed(aura::Window* window) { } } +//////////////////////////////////////////////////////////////////////////////// +// TooltipController private: + +// static +void TooltipController::TrimTooltipToFit(string16* text, + int* max_width, + int* line_count, + int x, + int y) { + *max_width = 0; + *line_count = 0; + + // Clamp the tooltip length to kMaxTooltipLength so that we don't + // accidentally DOS the user with a mega tooltip. + if (text->length() > kMaxTooltipLength) + *text = text->substr(0, kMaxTooltipLength); + + // Determine the available width for the tooltip. + int available_width = std::min(kTooltipMaxWidthPixels, GetMaxWidth(x, y)); + + std::vector<string16> lines; + base::SplitString(*text, '\n', &lines); + std::vector<string16> result_lines; + + // Format each line to fit. + gfx::Font font = GetDefaultFont(); + for (std::vector<string16>::iterator l = lines.begin(); l != lines.end(); + ++l) { + // We break the line at word boundaries, then stuff as many words as we can + // in the available width to the current line, and move the remaining words + // to a new line. + std::vector<string16> words; + base::SplitStringDontTrim(*l, ' ', &words); + int current_width = 0; + string16 line; + for (std::vector<string16>::iterator w = words.begin(); w != words.end(); + ++w) { + string16 word = *w; + if (w + 1 != words.end()) + word.push_back(' '); + int word_width = font.GetStringWidth(word); + if (current_width + word_width > available_width) { + // Current width will exceed the available width. Must start a new line. + if (!line.empty()) + result_lines.push_back(line); + current_width = 0; + line.clear(); + } + current_width += word_width; + line.append(word); + } + result_lines.push_back(line); + } + + // Clamp number of lines to |kMaxLines|. + if (result_lines.size() > kMaxLines) { + result_lines.resize(kMaxLines); + // Add ellipses character to last line. + result_lines[kMaxLines - 1] = ui::TruncateString( + result_lines.back(), result_lines.back().length() - 1); + } + *line_count = result_lines.size(); + + // Flatten the result. + string16 result; + for (std::vector<string16>::iterator l = result_lines.begin(); + l != result_lines.end(); ++l) { + if (!result.empty()) + result.push_back('\n'); + int line_width = font.GetStringWidth(*l); + // Since we only break at word boundaries, it could happen that due to some + // very long word, line_width is greater than the available_width. In such + // case, we simply truncate at available_width and add ellipses at the end. + if (line_width > available_width) { + *max_width = available_width; + result.append(ui::ElideText(*l, font, available_width, ui::ELIDE_AT_END)); + } else { + *max_width = std::max(*max_width, line_width); + result.append(*l); + } + } + *text = result; +} + void TooltipController::TooltipTimerFired() { UpdateIfRequired(); } diff --git a/ash/tooltips/tooltip_controller.h b/ash/tooltips/tooltip_controller.h index e2b8ed6..5e9d103 100644 --- a/ash/tooltips/tooltip_controller.h +++ b/ash/tooltips/tooltip_controller.h @@ -60,6 +60,16 @@ class ASH_EXPORT TooltipController : public aura::client::TooltipClient, class Tooltip; + // Trims the tooltip to fit, setting |text| to the clipped result, + // |max_width| to the width (in pixels) of the clipped text and |line_count| + // to the number of lines of text in the tooltip. |x| and |y| give the + // location of the tooltip in screen coordinates. + static void TrimTooltipToFit(string16* text, + int* max_width, + int* line_count, + int x, + int y); + void TooltipTimerFired(); // Updates the tooltip if required (if there is any change in the tooltip diff --git a/ash/tooltips/tooltip_controller_unittest.cc b/ash/tooltips/tooltip_controller_unittest.cc index f07dfd5..8a8343f 100644 --- a/ash/tooltips/tooltip_controller_unittest.cc +++ b/ash/tooltips/tooltip_controller_unittest.cc @@ -10,6 +10,9 @@ #include "ui/aura/root_window.h" #include "ui/aura/test/event_generator.h" #include "ui/aura/window.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/text/text_elider.h" +#include "ui/gfx/font.h" #include "ui/gfx/point.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" @@ -71,6 +74,11 @@ ash::internal::TooltipController* GetController() { aura::client::GetTooltipClient(Shell::GetRootWindow())); } +gfx::Font GetDefaultFont() { + return ui::ResourceBundle::GetSharedInstance().GetFont( + ui::ResourceBundle::BaseFont); +} + } // namespace class TooltipControllerTest : public AshTestBase { @@ -94,6 +102,15 @@ class TooltipControllerTest : public AshTestBase { return GetController()->IsTooltipVisible(); } + void TrimTooltipToFit(string16* text, + int* max_width, + int* line_count, + int x, + int y) { + ash::internal::TooltipController::TrimTooltipToFit(text, max_width, + line_count, x, y); + } + private: DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest); }; @@ -208,5 +225,92 @@ TEST_F(TooltipControllerTest, EnableOrDisableTooltips) { EXPECT_TRUE(IsTooltipVisible()); } +TEST_F(TooltipControllerTest, TrimTooltipToFitTests) { + string16 tooltip; + int max_width, line_count, expect_lines; + int max_pixel_width = 400; // copied from constants in tooltip_controller.cc + int max_lines = 10; // copied from constants in tooltip_controller.cc + gfx::Font font = GetDefaultFont(); + size_t tooltip_len; + + // Error in computed size vs. expected size should not be greater than the + // size of the longest word. + int error_in_pixel_width = font.GetStringWidth(ASCIIToUTF16("tooltip")); + + // Long tooltips should wrap to next line + tooltip.clear(); + max_width = line_count = -1; + expect_lines = 3; + for (; font.GetStringWidth(tooltip) <= (expect_lines - 1) * max_pixel_width;) + tooltip.append(ASCIIToUTF16("This is part of the tooltip")); + tooltip_len = tooltip.length(); + TrimTooltipToFit(&tooltip, &max_width, &line_count, 0, 0); + EXPECT_NEAR(max_pixel_width, max_width, error_in_pixel_width); + EXPECT_EQ(expect_lines, line_count); + EXPECT_EQ(tooltip_len + expect_lines - 1, tooltip.length()); + + // More than |max_lines| lines should get truncated at 10 lines. + tooltip.clear(); + max_width = line_count = -1; + expect_lines = 13; + for (; font.GetStringWidth(tooltip) <= (expect_lines - 1) * max_pixel_width;) + tooltip.append(ASCIIToUTF16("This is part of the tooltip")); + TrimTooltipToFit(&tooltip, &max_width, &line_count, 0, 0); + EXPECT_NEAR(max_pixel_width, max_width, error_in_pixel_width); + EXPECT_EQ(max_lines, line_count); + + // Long multi line tooltips should wrap individual lines. + tooltip.clear(); + max_width = line_count = -1; + expect_lines = 4; + for (; font.GetStringWidth(tooltip) <= (expect_lines - 2) * max_pixel_width;) + tooltip.append(ASCIIToUTF16("This is part of the tooltip")); + tooltip.insert(tooltip.length() / 2, ASCIIToUTF16("\n")); + tooltip_len = tooltip.length(); + TrimTooltipToFit(&tooltip, &max_width, &line_count, 0, 0); + EXPECT_NEAR(max_pixel_width, max_width, error_in_pixel_width); + EXPECT_EQ(expect_lines, line_count); + EXPECT_EQ(tooltip_len + expect_lines - 2, tooltip.length()); + + + // Tooltip with really long word gets elided. + tooltip.clear(); + max_width = line_count = -1; + tooltip = UTF8ToUTF16(std::string('a', max_pixel_width)); + TrimTooltipToFit(&tooltip, &max_width, &line_count, 0, 0); + EXPECT_NEAR(max_pixel_width, max_width, 5); + EXPECT_EQ(1, line_count); + EXPECT_EQ(ui::ElideText(tooltip, font, max_pixel_width, ui::ELIDE_AT_END), + tooltip); + + // Normal small tooltip should stay as is. + tooltip.clear(); + max_width = line_count = -1; + tooltip = ASCIIToUTF16("Small Tooltip"); + TrimTooltipToFit(&tooltip, &max_width, &line_count, 0, 0); + EXPECT_EQ(font.GetStringWidth(ASCIIToUTF16("Small Tooltip")), max_width); + EXPECT_EQ(1, line_count); + EXPECT_EQ(ASCIIToUTF16("Small Tooltip"), tooltip); + + // Normal small multi-line tooltip should stay as is. + tooltip.clear(); + max_width = line_count = -1; + tooltip = ASCIIToUTF16("Multi line\nTooltip"); + TrimTooltipToFit(&tooltip, &max_width, &line_count, 0, 0); + EXPECT_EQ(font.GetStringWidth(ASCIIToUTF16("Multi line\nTooltip")), + max_width); + EXPECT_EQ(2, line_count); + EXPECT_EQ(ASCIIToUTF16("Multi line\nTooltip"), tooltip); + + // Whitespaces in tooltips are preserved. + tooltip.clear(); + max_width = line_count = -1; + tooltip = ASCIIToUTF16("Small Tool t\tip"); + TrimTooltipToFit(&tooltip, &max_width, &line_count, 0, 0); + EXPECT_EQ(font.GetStringWidth(ASCIIToUTF16("Small Tool t\tip")), max_width); + EXPECT_EQ(1, line_count); + EXPECT_EQ(ASCIIToUTF16("Small Tool t\tip"), tooltip); +} + } // namespace test } // namespace ash |