Index: ui/message_center/views/bounded_label.cc |
diff --git a/ui/message_center/views/bounded_label.cc b/ui/message_center/views/bounded_label.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f9c71b312a67d7f7e7a64cc4923a5209d117de6a |
--- /dev/null |
+++ b/ui/message_center/views/bounded_label.cc |
@@ -0,0 +1,157 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/message_center/views/bounded_label.h" |
+ |
+#include <limits> |
+ |
+#include "base/string_util.h" |
+#include "base/utf_string_conversions.h" |
+#include "ui/base/text/text_elider.h" |
+#include "ui/gfx/canvas.h" |
+ |
+namespace message_center { |
+ |
+BoundedLabel::BoundedLabel(string16 text, size_t max_lines) |
+ : views::Label(text, gfx::Font()) { |
+ Init(max_lines); |
+} |
+ |
+BoundedLabel::BoundedLabel(string16 text, gfx::Font font, size_t max_lines) |
+ : views::Label(text, font) { |
+ Init(max_lines); |
+} |
+ |
+BoundedLabel::~BoundedLabel() { |
+} |
+ |
+void BoundedLabel::SetMaxLines(size_t lines) { |
+ is_preferred_lines_valid_ = false; |
+ is_text_size_valid_ = false; |
+ max_lines_ = lines; |
+} |
+ |
+size_t BoundedLabel::GetMaxLines() { |
+ return max_lines_; |
+} |
+ |
+size_t BoundedLabel::GetPreferredLines() { |
+ if (!is_preferred_lines_valid_) { |
+ int wrap_width = width() - GetInsets().width(); |
+ int unlimited_lines = std::numeric_limits<size_t>::max(); |
+ preferred_lines_ = SplitLines(wrap_width, unlimited_lines).size(); |
+ is_preferred_lines_valid_ = true; |
+ } |
+ return preferred_lines_; |
+} |
+ |
+int BoundedLabel::GetHeightForWidth(int width) { |
+ gfx::Insets insets = GetInsets(); |
+ return GetTextHeightForWidth(width - insets.width()) + insets.height(); |
+} |
+ |
+gfx::Size BoundedLabel::GetTextSize() const { |
+ if (!is_text_size_valid_) { |
+ text_size_.set_width(width() - GetInsets().width()); |
+ text_size_.set_height(GetTextHeightForWidth(text_size_.width())); |
+ is_text_size_valid_ = true; |
+ } |
+ return text_size_; |
+} |
+ |
+void BoundedLabel::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
+ is_preferred_lines_valid_ = false; |
+ is_text_size_valid_ = false; |
+} |
+ |
+void BoundedLabel::PaintText(gfx::Canvas* canvas, |
+ const string16& paint_text, |
+ const gfx::Rect& text_bounds, |
+ int flags) { |
+ gfx::Insets insets = GetInsets(); |
+ gfx::Rect bounds(gfx::Point(insets.left(), insets.top()), GetTextSize()); |
+ string16 text = JoinString(SplitLines(bounds.width(), max_lines_), '\n'); |
+ views::Label::PaintText(canvas, text, bounds, GetTextFlags()); |
+} |
+ |
+void BoundedLabel::Init(size_t max_lines) { |
+ SetMultiLine(true); |
+ SetAllowCharacterBreak(true); |
+ SetElideBehavior(views::Label::ELIDE_AT_END); |
+ max_lines_ = max_lines; |
+ is_preferred_lines_valid_ = false; |
+ is_text_size_valid_ = false; |
+} |
+ |
+int BoundedLabel::GetTextFlags() const { |
+ int flags = gfx::Canvas::MULTI_LINE | gfx::Canvas::CHARACTER_BREAK; |
+ |
+ // We can't use subpixel rendering if the background is non-opaque. |
+ if (SkColorGetA(background_color()) != 0xFF) |
+ flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING; |
+ |
+ if (directionality_mode() == AUTO_DETECT_DIRECTIONALITY) { |
+ base::i18n::TextDirection direction = |
+ base::i18n::GetFirstStrongCharacterDirection(text()); |
+ if (direction == base::i18n::RIGHT_TO_LEFT) |
+ flags |= gfx::Canvas::FORCE_RTL_DIRECTIONALITY; |
+ else |
+ flags |= gfx::Canvas::FORCE_LTR_DIRECTIONALITY; |
+ } |
+ |
+ switch (horizontal_alignment()) { |
+ case gfx::ALIGN_LEFT: |
+ flags |= gfx::Canvas::TEXT_ALIGN_LEFT; |
+ break; |
+ case gfx::ALIGN_CENTER: |
+ flags |= gfx::Canvas::TEXT_ALIGN_CENTER; |
+ break; |
+ case gfx::ALIGN_RIGHT: |
+ flags |= gfx::Canvas::TEXT_ALIGN_RIGHT; |
+ break; |
+ } |
+ |
+ return flags; |
+} |
+ |
+int BoundedLabel::GetTextHeightForWidth(int width) const { |
+ int height = font().GetHeight(); |
+ width = std::max(width, 0); |
+ string16 text = JoinString(SplitLines(width, max_lines_), '\n'); |
+ gfx::Canvas::SizeStringInt(text, font(), &width, &height, GetTextFlags()); |
+ return height; |
+} |
+ |
+std::vector<string16> BoundedLabel::SplitLines(int width, |
+ size_t max_lines) const { |
+ // Adjust max_lines so (max_lines + 1) * line_height <= INT_MAX, then use the |
+ // adjusted max_lines to get a max_height of (max_lines + 1) * line_height. |
+ size_t max_height = std::numeric_limits<int>::max(); |
+ size_t line_height = std::max(font().GetHeight(), 2); // At least 2 pixels! |
+ max_lines = std::min(max_lines, max_height / line_height - 1); |
+ max_height = (max_lines + 1) * line_height; |
+ |
+ // Split. Do not use ui::WRAP_LONG_WORDS instead of ui::IGNORE_LONG_WORDS |
+ // below as this may cause ui::ElideRectangleText() to go into an infinite |
+ // loop for small width values. |
+ std::vector<string16> lines; |
+ ui::ElideRectangleText(text(), font(), width, max_height, |
+ ui::IGNORE_LONG_WORDS, &lines); |
+ |
+ // Elide if necessary. |
+ if (lines.size() > max_lines) { |
+ // Add an ellipsis to the last line. If this ellipsis makes the last line |
+ // too wide, that line will be further elided by the ui::ElideText below, |
+ // so for example "ABC" could become "ABC..." here and "AB..." below. |
+ string16 last = lines[max_lines - 1] + UTF8ToUTF16(ui::kEllipsis); |
+ lines.resize(max_lines - 1); |
+ lines.push_back((font().GetStringWidth(last) > width) ? |
+ ui::ElideText(last, font(), width, ui::ELIDE_AT_END) : |
+ last); |
+ } |
+ |
+ return lines; |
+} |
+ |
+} // namespace message_center |