Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / views / controls / label.cc
index 7145323..9d71a98 100644 (file)
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_view_state.h"
-#include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/insets.h"
-#include "ui/gfx/shadow_value.h"
 #include "ui/gfx/text_elider.h"
 #include "ui/gfx/text_utils.h"
+#include "ui/gfx/utf16_indexing.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/background.h"
 
 namespace {
 
 const int kCachedSizeLimit = 10;
+const base::char16 kPasswordReplacementChar = '*';
 
 }  // namespace
 
@@ -60,9 +60,21 @@ void Label::SetFontList(const gfx::FontList& font_list) {
 }
 
 void Label::SetText(const base::string16& text) {
-  if (text == text_)
-    return;
+  if (text != text_)
+    SetTextInternal(text);
+}
+
+void Label::SetTextInternal(const base::string16& text) {
   text_ = text;
+
+  if (obscured_) {
+    size_t obscured_text_length =
+        static_cast<size_t>(gfx::UTF16IndexToOffset(text_, 0, text_.length()));
+    layout_text_.assign(obscured_text_length, kPasswordReplacementChar);
+  } else {
+    layout_text_ = text_;
+  }
+
   ResetCachedSize();
   PreferredSizeChanged();
   SchedulePaint();
@@ -91,26 +103,19 @@ void Label::SetBackgroundColor(SkColor color) {
   RecalculateColors();
 }
 
-void Label::SetShadowColors(SkColor enabled_color, SkColor disabled_color) {
-  enabled_shadow_color_ = enabled_color;
-  disabled_shadow_color_ = disabled_color;
-  has_shadow_ = true;
-}
-
-void Label::SetShadowOffset(int x, int y) {
-  shadow_offset_.SetPoint(x, y);
+void Label::SetShadows(const gfx::ShadowValues& shadows) {
+  shadows_ = shadows;
+  text_size_valid_ = false;
 }
 
-void Label::ClearEmbellishing() {
-  has_shadow_ = false;
+void Label::SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled) {
+  subpixel_rendering_enabled_ = subpixel_rendering_enabled;
 }
 
 void Label::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
-  // If the View's UI layout is right-to-left and directionality_mode_ is
-  // USE_UI_DIRECTIONALITY, we need to flip the alignment so that the alignment
-  // settings take into account the text directionality.
-  if (base::i18n::IsRTL() && (directionality_mode_ == USE_UI_DIRECTIONALITY) &&
-      (alignment != gfx::ALIGN_CENTER)) {
+  // If the UI layout is right-to-left, flip the alignment direction.
+  if (base::i18n::IsRTL() &&
+      (alignment == gfx::ALIGN_LEFT || alignment == gfx::ALIGN_RIGHT)) {
     alignment = (alignment == gfx::ALIGN_LEFT) ?
         gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT;
   }
@@ -120,6 +125,15 @@ void Label::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
   }
 }
 
+gfx::HorizontalAlignment Label::GetHorizontalAlignment() const {
+  if (horizontal_alignment_ != gfx::ALIGN_TO_HEAD)
+    return horizontal_alignment_;
+
+  const base::i18n::TextDirection dir =
+      base::i18n::GetFirstStrongCharacterDirection(layout_text_);
+  return dir == base::i18n::RIGHT_TO_LEFT ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT;
+}
+
 void Label::SetLineHeight(int height) {
   if (height != line_height_) {
     line_height_ = height;
@@ -130,16 +144,23 @@ void Label::SetLineHeight(int height) {
 }
 
 void Label::SetMultiLine(bool multi_line) {
-  DCHECK(!multi_line || (elide_behavior_ != ELIDE_IN_MIDDLE &&
-      elide_behavior_ != ELIDE_AT_BEGINNING));
-  if (multi_line != is_multi_line_) {
-    is_multi_line_ = multi_line;
+  DCHECK(!multi_line || (elide_behavior_ == gfx::ELIDE_TAIL ||
+                         elide_behavior_ == gfx::NO_ELIDE));
+  if (multi_line != multi_line_) {
+    multi_line_ = multi_line;
     ResetCachedSize();
     PreferredSizeChanged();
     SchedulePaint();
   }
 }
 
+void Label::SetObscured(bool obscured) {
+  if (obscured != obscured_) {
+    obscured_ = obscured;
+    SetTextInternal(text_);
+  }
+}
+
 void Label::SetAllowCharacterBreak(bool allow_character_break) {
   if (allow_character_break != allow_character_break_) {
     allow_character_break_ = allow_character_break;
@@ -149,8 +170,9 @@ void Label::SetAllowCharacterBreak(bool allow_character_break) {
   }
 }
 
-void Label::SetElideBehavior(ElideBehavior elide_behavior) {
-  DCHECK(elide_behavior != ELIDE_IN_MIDDLE || !is_multi_line_);
+void Label::SetElideBehavior(gfx::ElideBehavior elide_behavior) {
+  DCHECK(!multi_line_ || (elide_behavior_ == gfx::ELIDE_TAIL ||
+                          elide_behavior_ == gfx::NO_ELIDE));
   if (elide_behavior != elide_behavior_) {
     elide_behavior_ = elide_behavior;
     ResetCachedSize();
@@ -160,14 +182,19 @@ void Label::SetElideBehavior(ElideBehavior elide_behavior) {
 }
 
 void Label::SetTooltipText(const base::string16& tooltip_text) {
+  DCHECK(handles_tooltips_);
   tooltip_text_ = tooltip_text;
 }
 
+void Label::SetHandlesTooltips(bool enabled) {
+  handles_tooltips_ = enabled;
+}
+
 void Label::SizeToFit(int max_width) {
-  DCHECK(is_multi_line_);
+  DCHECK(multi_line_);
 
   std::vector<base::string16> lines;
-  base::SplitString(text_, '\n', &lines);
+  base::SplitString(layout_text_, '\n', &lines);
 
   int label_width = 0;
   for (std::vector<base::string16>::const_iterator iter = lines.begin();
@@ -184,6 +211,10 @@ void Label::SizeToFit(int max_width) {
   SizeToPreferredSize();
 }
 
+const base::string16& Label::GetLayoutTextForTesting() const {
+  return layout_text_;
+}
+
 gfx::Insets Label::GetInsets() const {
   gfx::Insets insets = View::GetInsets();
   if (focusable()) {
@@ -197,7 +228,7 @@ int Label::GetBaseline() const {
   return GetInsets().top() + font_list_.GetBaseline();
 }
 
-gfx::Size Label::GetPreferredSize() {
+gfx::Size Label::GetPreferredSize() const {
   // 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,
@@ -212,7 +243,7 @@ gfx::Size Label::GetPreferredSize() {
   return size;
 }
 
-gfx::Size Label::GetMinimumSize() {
+gfx::Size Label::GetMinimumSize() const {
   gfx::Size text_size(GetTextSize());
   if ((!visible() && collapse_when_hidden_) || text_size.IsEmpty())
     return gfx::Size();
@@ -226,8 +257,8 @@ gfx::Size Label::GetMinimumSize() {
   return size;
 }
 
-int Label::GetHeightForWidth(int w) {
-  if (!is_multi_line_)
+int Label::GetHeightForWidth(int w) const {
+  if (!multi_line_)
     return View::GetHeightForWidth(w);
 
   w = std::max(0, w - GetInsets().width());
@@ -242,7 +273,8 @@ int Label::GetHeightForWidth(int w) {
 
   int h = font_list_.GetHeight();
   const int flags = ComputeDrawStringFlags();
-  gfx::Canvas::SizeStringInt(text_, font_list_, &w, &h, line_height_, flags);
+  gfx::Canvas::SizeStringInt(
+      layout_text_, font_list_, &w, &h, line_height_, flags);
   cached_heights_[cached_heights_cursor_] = gfx::Size(cache_width, h);
   cached_heights_cursor_ = (cached_heights_cursor_ + 1) % kCachedSizeLimit;
   return h + GetInsets().height();
@@ -253,59 +285,52 @@ const char* Label::GetClassName() const {
 }
 
 View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) {
-  // Bail out if the label does not contain the point.
-  // Note that HitTestPoint() cannot be used here as it uses
-  // Label::HitTestRect() to determine if the point hits the label; and
-  // Label::HitTestRect() always fails. Instead, default HitTestRect()
-  // implementation should be used.
-  if (!View::HitTestRect(gfx::Rect(point, gfx::Size(1, 1))))
+  if (!handles_tooltips_ ||
+      (tooltip_text_.empty() && !ShouldShowDefaultTooltip()))
     return NULL;
 
-  if (tooltip_text_.empty() && !ShouldShowDefaultTooltip())
-    return NULL;
-
-  return this;
+  return HitTestPoint(point) ? this : NULL;
 }
 
-bool Label::HitTestRect(const gfx::Rect& rect) const {
+bool Label::CanProcessEventsWithinSubtree() const {
+  // Send events to the parent view for handling.
   return false;
 }
 
+void Label::GetAccessibleState(ui::AXViewState* state) {
+  state->role = ui::AX_ROLE_STATIC_TEXT;
+  state->AddStateFlag(ui::AX_STATE_READ_ONLY);
+  state->name = layout_text_;
+}
+
 bool Label::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const {
-  DCHECK(tooltip);
+  if (!handles_tooltips_)
+    return false;
 
-  // 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 (ShouldShowDefaultTooltip()) {
-    *tooltip = text_;
+    *tooltip = layout_text_;
     return true;
   }
 
   return false;
 }
 
-void Label::GetAccessibleState(ui::AXViewState* state) {
-  state->role = ui::AX_ROLE_STATIC_TEXT;
-  state->AddStateFlag(ui::AX_STATE_READ_ONLY);
-  state->name = text_;
-}
-
 void Label::PaintText(gfx::Canvas* canvas,
                       const base::string16& text,
                       const gfx::Rect& text_bounds,
                       int flags) {
-  gfx::ShadowValues shadows;
-  if (has_shadow_)
-    shadows.push_back(gfx::ShadowValue(shadow_offset_, 0,
-        enabled() ? enabled_shadow_color_ : disabled_shadow_color_));
-  canvas->DrawStringRectWithShadows(text, font_list_,
-      enabled() ? actual_enabled_color_ : actual_disabled_color_,
-      text_bounds, line_height_, flags, shadows);
+  SkColor color = enabled() ? actual_enabled_color_ : actual_disabled_color_;
+  if (elide_behavior_ == gfx::FADE_TAIL) {
+    canvas->DrawFadedString(text, font_list_, color, text_bounds, flags);
+  } else {
+    canvas->DrawStringRectWithShadows(text, font_list_, color, text_bounds,
+                                      line_height_, flags, shadows_);
+  }
 
   if (HasFocus()) {
     gfx::Rect focus_bounds = text_bounds;
@@ -320,16 +345,19 @@ gfx::Size Label::GetTextSize() const {
     // while adding NO_ELLIPSIS to the flags works on Windows for forcing
     // SizeStringInt() to calculate the desired width, it doesn't seem to work
     // on Linux.
-    int w = is_multi_line_ ?
+    int w = multi_line_ ?
         GetAvailableRect().width() : std::numeric_limits<int>::max();
     int h = font_list_.GetHeight();
     // For single-line strings, ignore the available width and calculate how
     // wide the text wants to be.
     int flags = ComputeDrawStringFlags();
-    if (!is_multi_line_)
+    if (!multi_line_)
       flags |= gfx::Canvas::NO_ELLIPSIS;
-    gfx::Canvas::SizeStringInt(text_, font_list_, &w, &h, line_height_, flags);
+    gfx::Canvas::SizeStringInt(
+        layout_text_, font_list_, &w, &h, line_height_, flags);
     text_size_.SetSize(w, h);
+    const gfx::Insets shadow_margin = -gfx::ShadowValue::GetMargin(shadows_);
+    text_size_.Enlarge(shadow_margin.width(), shadow_margin.height());
     text_size_valid_ = true;
   }
 
@@ -337,7 +365,7 @@ gfx::Size Label::GetTextSize() const {
 }
 
 void Label::OnBoundsChanged(const gfx::Rect& previous_bounds) {
-  text_size_valid_ &= !is_multi_line_;
+  text_size_valid_ &= !multi_line_;
 }
 
 void Label::OnPaint(gfx::Canvas* canvas) {
@@ -361,19 +389,17 @@ void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) {
 void Label::Init(const base::string16& text, const gfx::FontList& font_list) {
   font_list_ = font_list;
   enabled_color_set_ = disabled_color_set_ = background_color_set_ = false;
+  subpixel_rendering_enabled_ = true;
   auto_color_readability_ = true;
   UpdateColorsFromTheme(ui::NativeTheme::instance());
   horizontal_alignment_ = gfx::ALIGN_CENTER;
   line_height_ = 0;
-  is_multi_line_ = false;
+  multi_line_ = false;
+  obscured_ = false;
   allow_character_break_ = false;
-  elide_behavior_ = ELIDE_AT_END;
+  elide_behavior_ = gfx::ELIDE_TAIL;
+  handles_tooltips_ = true;
   collapse_when_hidden_ = false;
-  directionality_mode_ = USE_UI_DIRECTIONALITY;
-  enabled_shadow_color_ = 0;
-  disabled_shadow_color_ = 0;
-  shadow_offset_.SetPoint(1, 1);
-  has_shadow_ = false;
   cached_heights_.resize(kCachedSizeLimit);
   ResetCachedSize();
 
@@ -392,52 +418,47 @@ void Label::RecalculateColors() {
 }
 
 gfx::Rect Label::GetTextBounds() const {
-  gfx::Rect available_rect(GetAvailableRect());
+  gfx::Rect available(GetAvailableRect());
   gfx::Size text_size(GetTextSize());
-  text_size.set_width(std::min(available_rect.width(), text_size.width()));
-
-  gfx::Insets insets = GetInsets();
-  gfx::Point text_origin(insets.left(), insets.top());
-  switch (horizontal_alignment_) {
+  text_size.set_width(std::min(available.width(), text_size.width()));
+  gfx::Point origin(GetInsets().left(), GetInsets().top());
+  switch (GetHorizontalAlignment()) {
     case gfx::ALIGN_LEFT:
       break;
     case gfx::ALIGN_CENTER:
-      // We put any extra margin pixel on the left rather than the right.  We
-      // used to do this because measurement on Windows used
-      // GetTextExtentPoint32(), which could report a value one too large on the
-      // right; we now use DrawText(), and who knows if it can also do this.
-      text_origin.Offset((available_rect.width() + 1 - text_size.width()) / 2,
-                         0);
+      // Put any extra margin pixel on the left to match the legacy behavior
+      // from the use of GetTextExtentPoint32() on Windows.
+      origin.Offset((available.width() + 1 - text_size.width()) / 2, 0);
       break;
     case gfx::ALIGN_RIGHT:
-      text_origin.set_x(available_rect.right() - text_size.width());
+      origin.set_x(available.right() - text_size.width());
       break;
     default:
       NOTREACHED();
       break;
   }
-  text_origin.Offset(0,
-      std::max(0, (available_rect.height() - text_size.height())) / 2);
-  return gfx::Rect(text_origin, text_size);
+  if (!multi_line_)
+    text_size.set_height(available.height());
+  // Support vertical centering of multi-line labels: http://crbug.com/429595
+  origin.Offset(0, std::max(0, (available.height() - text_size.height())) / 2);
+  return gfx::Rect(origin, text_size);
 }
 
 int Label::ComputeDrawStringFlags() const {
   int flags = 0;
 
   // We can't use subpixel rendering if the background is non-opaque.
-  if (SkColorGetA(background_color_) != 0xFF)
+  if (SkColorGetA(background_color_) != 0xFF || !subpixel_rendering_enabled_)
     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;
-  }
+  base::i18n::TextDirection direction =
+      base::i18n::GetFirstStrongCharacterDirection(layout_text_);
+  if (direction == base::i18n::RIGHT_TO_LEFT)
+    flags |= gfx::Canvas::FORCE_RTL_DIRECTIONALITY;
+  else
+    flags |= gfx::Canvas::FORCE_LTR_DIRECTIONALITY;
 
-  switch (horizontal_alignment_) {
+  switch (GetHorizontalAlignment()) {
     case gfx::ALIGN_LEFT:
       flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
       break;
@@ -447,9 +468,12 @@ int Label::ComputeDrawStringFlags() const {
     case gfx::ALIGN_RIGHT:
       flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
       break;
+    default:
+      NOTREACHED();
+      break;
   }
 
-  if (!is_multi_line_)
+  if (!multi_line_)
     return flags;
 
   flags |= gfx::Canvas::MULTI_LINE;
@@ -478,29 +502,20 @@ void Label::CalculateDrawStringParams(base::string16* paint_text,
                                       int* flags) const {
   DCHECK(paint_text && text_bounds && flags);
 
-  // TODO(msw): Use ElideRectangleText to support eliding multi-line text.  Once
-  // this is done, we can set NO_ELLIPSIS unconditionally at the bottom.
-  if (is_multi_line_ || (elide_behavior_ == NO_ELIDE)) {
-    *paint_text = text_;
-  } else if (elide_behavior_ == ELIDE_AT_BEGINNING) {
-    *paint_text = gfx::ElideText(text_, font_list_, GetAvailableRect().width(),
-                                 gfx::ELIDE_AT_BEGINNING);
-  } else if (elide_behavior_ == ELIDE_IN_MIDDLE) {
-    *paint_text = gfx::ElideText(text_, font_list_, GetAvailableRect().width(),
-                                 gfx::ELIDE_IN_MIDDLE);
-  } else if (elide_behavior_ == ELIDE_AT_END) {
-    *paint_text = gfx::ElideText(text_, font_list_, GetAvailableRect().width(),
-                                 gfx::ELIDE_AT_END);
+  const bool forbid_ellipsis = elide_behavior_ == gfx::NO_ELIDE ||
+                               elide_behavior_ == gfx::FADE_TAIL;
+  if (multi_line_ || forbid_ellipsis) {
+    *paint_text = layout_text_;
   } else {
-    DCHECK_EQ(ELIDE_AS_EMAIL, elide_behavior_);
-    *paint_text = gfx::ElideEmail(text_, font_list_,
-                                  GetAvailableRect().width());
+    *paint_text = gfx::ElideText(layout_text_, font_list_,
+                                 GetAvailableRect().width(), elide_behavior_);
   }
 
   *text_bounds = GetTextBounds();
   *flags = ComputeDrawStringFlags();
-  if (!is_multi_line_ || (elide_behavior_ == NO_ELIDE))
-     *flags |= gfx::Canvas::NO_ELLIPSIS;
+  // TODO(msw): Elide multi-line text with ElideRectangleText instead.
+  if (!multi_line_ || forbid_ellipsis)
+    *flags |= gfx::Canvas::NO_ELLIPSIS;
 }
 
 void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) {
@@ -527,8 +542,10 @@ void Label::ResetCachedSize() {
 }
 
 bool Label::ShouldShowDefaultTooltip() const {
-  return !is_multi_line_ &&
-      gfx::GetStringWidth(text_, font_list_) > GetAvailableRect().width();
+  const gfx::Size text_size = GetTextSize();
+  const gfx::Size size = GetContentsBounds().size();
+  return !obscured() && (text_size.width() > size.width() ||
+                         (multi_line_ && text_size.height() > size.height()));
 }
 
 }  // namespace views