diff options
Diffstat (limited to 'views/controls/label.cc')
-rw-r--r-- | views/controls/label.cc | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/views/controls/label.cc b/views/controls/label.cc new file mode 100644 index 0000000..1dc0b54 --- /dev/null +++ b/views/controls/label.cc @@ -0,0 +1,444 @@ +// Copyright (c) 2006-2008 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 "views/controls/label.h" + +#include <math.h> + +#include "app/gfx/chrome_canvas.h" +#include "app/gfx/chrome_font.h" +#include "app/gfx/insets.h" +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "chrome/common/gfx/text_elider.h" +#include "views/background.h" + +namespace views { + +const char Label::kViewClassName[] = "views/Label"; + +static const SkColor kEnabledColor = SK_ColorBLACK; +static const SkColor kDisabledColor = SkColorSetRGB(161, 161, 146); +static const int kFocusBorderPadding = 1; + +Label::Label() { + Init(L"", GetDefaultFont()); +} + +Label::Label(const std::wstring& text) { + Init(text, GetDefaultFont()); +} + +Label::Label(const std::wstring& text, const ChromeFont& font) { + Init(text, font); +} + +void Label::Init(const std::wstring& text, const ChromeFont& font) { + contains_mouse_ = false; + font_ = font; + text_size_valid_ = false; + SetText(text); + url_set_ = false; + color_ = kEnabledColor; + horiz_alignment_ = ALIGN_CENTER; + is_multi_line_ = false; + allow_character_break_ = false; + collapse_when_hidden_ = false; + rtl_alignment_mode_ = USE_UI_ALIGNMENT; + paint_as_focused_ = false; + has_focus_border_ = false; +} + +Label::~Label() { +} + +gfx::Size Label::GetPreferredSize() { + gfx::Size prefsize; + + // Return a size of (0, 0) if the label is not visible and if the + // collapse_when_hidden_ flag is set. + // TODO(munjal): This logic probably belongs to the View class. But for now, + // put it here since putting it in View class means all inheriting classes + // need ot respect the collapse_when_hidden_ flag. + if (!IsVisible() && collapse_when_hidden_) + return prefsize; + + if (is_multi_line_) { + int w = width(), h = 0; + ChromeCanvas::SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags()); + prefsize.SetSize(w, h); + } else { + prefsize = GetTextSize(); + } + + gfx::Insets insets = GetInsets(); + prefsize.Enlarge(insets.width(), insets.height()); + return prefsize; +} + +int Label::ComputeMultiLineFlags() { + int flags = ChromeCanvas::MULTI_LINE; + if (allow_character_break_) + flags |= ChromeCanvas::CHARACTER_BREAK; + switch (horiz_alignment_) { + case ALIGN_LEFT: + flags |= ChromeCanvas::TEXT_ALIGN_LEFT; + break; + case ALIGN_CENTER: + flags |= ChromeCanvas::TEXT_ALIGN_CENTER; + break; + case ALIGN_RIGHT: + flags |= ChromeCanvas::TEXT_ALIGN_RIGHT; + break; + } + return flags; +} + +void Label::CalculateDrawStringParams( + std::wstring* paint_text, gfx::Rect* text_bounds, int* flags) { + DCHECK(paint_text && text_bounds && flags); + + if (url_set_) { + // TODO(jungshik) : Figure out how to get 'intl.accept_languages' + // preference and use it when calling ElideUrl. + *paint_text = gfx::ElideUrl(url_, font_, width(), std::wstring()); + + // An URLs is always treated as an LTR text and therefore we should + // explicitly mark it as such if the locale is RTL so that URLs containing + // Hebrew or Arabic characters are displayed correctly. + // + // Note that we don't check the View's UI layout setting in order to + // determine whether or not to insert the special Unicode formatting + // characters. We use the locale settings because an URL is always treated + // as an LTR string, even if its containing view does not use an RTL UI + // layout. + if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) + l10n_util::WrapStringWithLTRFormatting(paint_text); + } else { + *paint_text = text_; + } + + if (is_multi_line_) { + gfx::Insets insets = GetInsets(); + text_bounds->SetRect(insets.left(), + insets.top(), + width() - insets.width(), + height() - insets.height()); + *flags = ComputeMultiLineFlags(); + } else { + *text_bounds = GetTextBounds(); + *flags = 0; + } +} + +void Label::Paint(ChromeCanvas* canvas) { + PaintBackground(canvas); + std::wstring paint_text; + gfx::Rect text_bounds; + int flags = 0; + CalculateDrawStringParams(&paint_text, &text_bounds, &flags); + canvas->DrawStringInt(paint_text, + font_, + color_, + text_bounds.x(), + text_bounds.y(), + text_bounds.width(), + text_bounds.height(), + flags); + + // The focus border always hugs the text, regardless of the label's bounds. + if (HasFocus() || paint_as_focused_) { + int w = text_bounds.width(); + int h = 0; + // We explicitly OR in MULTI_LINE here since SizeStringInt seems to return + // an incorrect height for single line text when the MULTI_LINE flag isn't + // specified. o_O... + ChromeCanvas::SizeStringInt(paint_text, font_, &w, &h, + flags | ChromeCanvas::MULTI_LINE); + gfx::Rect focus_rect = text_bounds; + focus_rect.set_width(w); + focus_rect.set_height(h); + focus_rect.Inset(-kFocusBorderPadding, -kFocusBorderPadding); + canvas->DrawFocusRect(MirroredLeftPointForRect(focus_rect), focus_rect.y(), + focus_rect.width(), focus_rect.height()); + } +} + +void Label::PaintBackground(ChromeCanvas* canvas) { + const Background* bg = contains_mouse_ ? GetMouseOverBackground() : NULL; + if (!bg) + bg = background(); + if (bg) + bg->Paint(canvas, this); +} + +void Label::SetFont(const ChromeFont& font) { + font_ = font; + text_size_valid_ = false; + SchedulePaint(); +} + +ChromeFont Label::GetFont() const { + return font_; +} + +void Label::SetText(const std::wstring& text) { + text_ = text; + url_set_ = false; + text_size_valid_ = false; + SchedulePaint(); +} + +void Label::SetURL(const GURL& url) { + url_ = url; + text_ = UTF8ToWide(url_.spec()); + url_set_ = true; + text_size_valid_ = false; + SchedulePaint(); +} + +const std::wstring Label::GetText() const { + if (url_set_) + return UTF8ToWide(url_.spec()); + else + return text_; +} + +const GURL Label::GetURL() const { + if (url_set_) + return url_; + else + return GURL(WideToUTF8(text_)); +} + +gfx::Size Label::GetTextSize() { + if (!text_size_valid_) { + text_size_.SetSize(font_.GetStringWidth(text_), font_.height()); + text_size_valid_ = true; + } + + if (text_size_valid_) + return text_size_; + return gfx::Size(); +} + +int Label::GetHeightForWidth(int w) { + if (is_multi_line_) { + gfx::Insets insets = GetInsets(); + w = std::max<int>(0, w - insets.width()); + int h = 0; + ChromeCanvas cc(0, 0, true); + cc.SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags()); + return h + insets.height(); + } + + return View::GetHeightForWidth(w); +} + +std::string Label::GetClassName() const { + return kViewClassName; +} + +void Label::SetColor(const SkColor& color) { + color_ = color; +} + +const SkColor Label::GetColor() const { + return color_; +} + +void Label::SetHorizontalAlignment(Alignment a) { + // If the View's UI layout is right-to-left and rtl_alignment_mode_ is + // USE_UI_ALIGNMENT, we need to flip the alignment so that the alignment + // settings take into account the text directionality. + if (UILayoutIsRightToLeft() && rtl_alignment_mode_ == USE_UI_ALIGNMENT) { + if (a == ALIGN_LEFT) + a = ALIGN_RIGHT; + else if (a == ALIGN_RIGHT) + a = ALIGN_LEFT; + } + if (horiz_alignment_ != a) { + horiz_alignment_ = a; + SchedulePaint(); + } +} + +Label::Alignment Label::GetHorizontalAlignment() const { + return horiz_alignment_; +} + +void Label::SetRTLAlignmentMode(RTLAlignmentMode mode) { + rtl_alignment_mode_ = mode; +} + +Label::RTLAlignmentMode Label::GetRTLAlignmentMode() const { + return rtl_alignment_mode_; +} + +void Label::SetMultiLine(bool f) { + if (f != is_multi_line_) { + is_multi_line_ = f; + SchedulePaint(); + } +} + +void Label::SetAllowCharacterBreak(bool f) { + if (f != allow_character_break_) { + allow_character_break_ = f; + SchedulePaint(); + } +} + +bool Label::IsMultiLine() { + return is_multi_line_; +} + +void Label::SetTooltipText(const std::wstring& tooltip_text) { + tooltip_text_ = tooltip_text; +} + +bool Label::GetTooltipText(int x, int y, std::wstring* tooltip) { + DCHECK(tooltip); + + // If a tooltip has been explicitly set, use it. + if (!tooltip_text_.empty()) { + tooltip->assign(tooltip_text_); + return true; + } + + // Show the full text if the text does not fit. + if (!is_multi_line_ && font_.GetStringWidth(text_) > width()) { + *tooltip = text_; + return true; + } + return false; +} + +void Label::OnMouseMoved(const MouseEvent& e) { + UpdateContainsMouse(e); +} + +void Label::OnMouseEntered(const MouseEvent& event) { + UpdateContainsMouse(event); +} + +void Label::OnMouseExited(const MouseEvent& event) { + SetContainsMouse(false); +} + +void Label::SetMouseOverBackground(Background* background) { + mouse_over_background_.reset(background); +} + +const Background* Label::GetMouseOverBackground() const { + return mouse_over_background_.get(); +} + +void Label::SetEnabled(bool enabled) { + if (enabled == enabled_) + return; + View::SetEnabled(enabled); + SetColor(enabled ? kEnabledColor : kDisabledColor); +} + +gfx::Insets Label::GetInsets() const { + gfx::Insets insets = View::GetInsets(); + if (IsFocusable() || has_focus_border_) { + insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding, + kFocusBorderPadding, kFocusBorderPadding); + } + return insets; +} + +// static +ChromeFont Label::GetDefaultFont() { + return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); +} + +void Label::UpdateContainsMouse(const MouseEvent& event) { + SetContainsMouse(GetTextBounds().Contains(event.x(), event.y())); +} + +void Label::SetContainsMouse(bool contains_mouse) { + if (contains_mouse_ == contains_mouse) + return; + contains_mouse_ = contains_mouse; + if (GetMouseOverBackground()) + SchedulePaint(); +} + +gfx::Rect Label::GetTextBounds() { + gfx::Size text_size = GetTextSize(); + gfx::Insets insets = GetInsets(); + int avail_width = width() - insets.width(); + // Respect the size set by the owner view + text_size.set_width(std::min(avail_width, text_size.width())); + + int text_y = insets.top() + + (height() - text_size.height() - insets.height()) / 2; + int text_x; + switch (horiz_alignment_) { + case ALIGN_LEFT: + text_x = insets.left(); + break; + case ALIGN_CENTER: + // We put any extra margin pixel on the left rather than the right, since + // GetTextExtentPoint32() can report a value one too large on the right. + text_x = insets.left() + (avail_width + 1 - text_size.width()) / 2; + break; + case ALIGN_RIGHT: + text_x = width() - insets.right() - text_size.width(); + break; + default: + NOTREACHED(); + text_x = 0; + break; + } + return gfx::Rect(text_x, text_y, text_size.width(), text_size.height()); +} + +void Label::SizeToFit(int max_width) { + DCHECK(is_multi_line_); + + std::vector<std::wstring> lines; + SplitString(text_, L'\n', &lines); + + int label_width = 0; + for (std::vector<std::wstring>::const_iterator iter = lines.begin(); + iter != lines.end(); ++iter) { + label_width = std::max(label_width, font_.GetStringWidth(*iter)); + } + + gfx::Insets insets = GetInsets(); + label_width += insets.width(); + + if (max_width > 0) + label_width = std::min(label_width, max_width); + + SetBounds(x(), y(), label_width, 0); + SizeToPreferredSize(); +} + +bool Label::GetAccessibleRole(AccessibilityTypes::Role* role) { + DCHECK(role); + + *role = AccessibilityTypes::ROLE_TEXT; + return true; +} + +bool Label::GetAccessibleName(std::wstring* name) { + *name = GetText(); + return true; +} + +bool Label::GetAccessibleState(AccessibilityTypes::State* state) { + DCHECK(state); + + *state = AccessibilityTypes::STATE_READONLY; + return true; +} + +} // namespace views |