| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/controls/label.h" | 5 #include "ui/views/controls/label.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <cmath> | 10 #include <cmath> |
| 11 #include <limits> | 11 #include <limits> |
| 12 #include <utility> | 12 #include <utility> |
| 13 #include <vector> | 13 #include <vector> |
| 14 | 14 |
| 15 #include "base/i18n/rtl.h" | 15 #include "base/i18n/rtl.h" |
| 16 #include "base/logging.h" | 16 #include "base/logging.h" |
| 17 #include "base/memory/ptr_util.h" | 17 #include "base/memory/ptr_util.h" |
| 18 #include "base/profiler/scoped_tracker.h" | 18 #include "base/profiler/scoped_tracker.h" |
| 19 #include "base/strings/string_split.h" | 19 #include "base/strings/string_split.h" |
| 20 #include "base/strings/utf_string_conversions.h" | 20 #include "base/strings/utf_string_conversions.h" |
| 21 #include "ui/accessibility/ax_node_data.h" | 21 #include "ui/accessibility/ax_node_data.h" |
| 22 #include "ui/base/clipboard/scoped_clipboard_writer.h" | 22 #include "ui/base/clipboard/scoped_clipboard_writer.h" |
| 23 #include "ui/base/cursor/cursor.h" | 23 #include "ui/base/cursor/cursor.h" |
| 24 #include "ui/base/default_style.h" | 24 #include "ui/base/default_style.h" |
| 25 #include "ui/base/material_design/material_design_controller.h" | |
| 26 #include "ui/gfx/canvas.h" | 25 #include "ui/gfx/canvas.h" |
| 27 #include "ui/gfx/color_utils.h" | 26 #include "ui/gfx/color_utils.h" |
| 28 #include "ui/gfx/geometry/insets.h" | 27 #include "ui/gfx/geometry/insets.h" |
| 29 #include "ui/gfx/text_elider.h" | 28 #include "ui/gfx/text_elider.h" |
| 30 #include "ui/native_theme/native_theme.h" | 29 #include "ui/native_theme/native_theme.h" |
| 31 #include "ui/strings/grit/ui_strings.h" | 30 #include "ui/strings/grit/ui_strings.h" |
| 32 #include "ui/views/background.h" | 31 #include "ui/views/background.h" |
| 33 #include "ui/views/controls/menu/menu_runner.h" | 32 #include "ui/views/controls/menu/menu_runner.h" |
| 34 #include "ui/views/focus/focus_manager.h" | 33 #include "ui/views/focus/focus_manager.h" |
| 35 #include "ui/views/native_cursor.h" | 34 #include "ui/views/native_cursor.h" |
| 36 #include "ui/views/selection_controller.h" | 35 #include "ui/views/selection_controller.h" |
| 37 | 36 |
| 38 namespace views { | 37 namespace views { |
| 39 // static | 38 namespace { |
| 39 // Returns additional Insets applied to |label->GetContentsBounds()| to obtain |
| 40 // the text bounds. GetContentsBounds() includes the Border, but not any |
| 41 // additional insets used by the Label (e.g. for a focus ring). |
| 42 gfx::Insets NonBorderInsets(const Label& label) { |
| 43 return label.GetInsets() - label.View::GetInsets(); |
| 44 } |
| 45 } // namespace |
| 46 |
| 40 const char Label::kViewClassName[] = "Label"; | 47 const char Label::kViewClassName[] = "Label"; |
| 41 const int Label::kFocusBorderPadding = 1; | |
| 42 | 48 |
| 43 Label::Label() : Label(base::string16()) { | 49 Label::Label() : Label(base::string16()) { |
| 44 } | 50 } |
| 45 | 51 |
| 46 Label::Label(const base::string16& text) | 52 Label::Label(const base::string16& text) |
| 47 : Label(text, style::CONTEXT_LABEL, style::STYLE_PRIMARY) {} | 53 : Label(text, style::CONTEXT_LABEL, style::STYLE_PRIMARY) {} |
| 48 | 54 |
| 49 Label::Label(const base::string16& text, int text_context, int text_style) | 55 Label::Label(const base::string16& text, int text_context, int text_style) |
| 50 : context_menu_contents_(this) { | 56 : context_menu_contents_(this) { |
| 51 Init(text, style::GetFont(text_context, text_style)); | 57 Init(text, style::GetFont(text_context, text_style)); |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 295 render_text->ClearSelection(); | 301 render_text->ClearSelection(); |
| 296 SchedulePaint(); | 302 SchedulePaint(); |
| 297 } | 303 } |
| 298 | 304 |
| 299 void Label::SelectRange(const gfx::Range& range) { | 305 void Label::SelectRange(const gfx::Range& range) { |
| 300 gfx::RenderText* render_text = GetRenderTextForSelectionController(); | 306 gfx::RenderText* render_text = GetRenderTextForSelectionController(); |
| 301 if (render_text && render_text->SelectRange(range)) | 307 if (render_text && render_text->SelectRange(range)) |
| 302 SchedulePaint(); | 308 SchedulePaint(); |
| 303 } | 309 } |
| 304 | 310 |
| 305 gfx::Insets Label::GetInsets() const { | |
| 306 gfx::Insets insets = View::GetInsets(); | |
| 307 if (focus_behavior() != FocusBehavior::NEVER) { | |
| 308 insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding, | |
| 309 kFocusBorderPadding, kFocusBorderPadding); | |
| 310 } | |
| 311 return insets; | |
| 312 } | |
| 313 | |
| 314 int Label::GetBaseline() const { | 311 int Label::GetBaseline() const { |
| 315 return GetInsets().top() + font_list().GetBaseline(); | 312 return GetInsets().top() + font_list().GetBaseline(); |
| 316 } | 313 } |
| 317 | 314 |
| 318 gfx::Size Label::GetPreferredSize() const { | 315 gfx::Size Label::GetPreferredSize() const { |
| 319 // Return a size of (0, 0) if the label is not visible and if the | 316 // Return a size of (0, 0) if the label is not visible and if the |
| 320 // |collapse_when_hidden_| flag is set. | 317 // |collapse_when_hidden_| flag is set. |
| 321 // TODO(munjal): This logic probably belongs to the View class. But for now, | 318 // TODO(munjal): This logic probably belongs to the View class. But for now, |
| 322 // put it here since putting it in View class means all inheriting classes | 319 // put it here since putting it in View class means all inheriting classes |
| 323 // need to respect the |collapse_when_hidden_| flag. | 320 // need to respect the |collapse_when_hidden_| flag. |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 446 render_text->SetElideBehavior(elide_behavior); | 443 render_text->SetElideBehavior(elide_behavior); |
| 447 render_text->SetObscured(obscured()); | 444 render_text->SetObscured(obscured()); |
| 448 render_text->SetMinLineHeight(line_height()); | 445 render_text->SetMinLineHeight(line_height()); |
| 449 render_text->SetFontList(font_list()); | 446 render_text->SetFontList(font_list()); |
| 450 render_text->set_shadows(shadows()); | 447 render_text->set_shadows(shadows()); |
| 451 render_text->SetCursorEnabled(false); | 448 render_text->SetCursorEnabled(false); |
| 452 render_text->SetText(text); | 449 render_text->SetText(text); |
| 453 return render_text; | 450 return render_text; |
| 454 } | 451 } |
| 455 | 452 |
| 453 void Label::PaintFocusRing(gfx::Canvas* canvas) const { |
| 454 // No focus ring by default. |
| 455 } |
| 456 |
| 457 gfx::Rect Label::GetFocusRingBounds() const { |
| 458 MaybeBuildRenderTextLines(); |
| 459 |
| 460 gfx::Rect focus_bounds; |
| 461 if (lines_.empty()) { |
| 462 focus_bounds = gfx::Rect(GetTextSize()); |
| 463 } else { |
| 464 for (size_t i = 0; i < lines_.size(); ++i) { |
| 465 gfx::Point origin; |
| 466 origin += lines_[i]->GetLineOffset(0); |
| 467 focus_bounds.Union(gfx::Rect(origin, lines_[i]->GetStringSize())); |
| 468 } |
| 469 } |
| 470 |
| 471 focus_bounds.Inset(-NonBorderInsets(*this)); |
| 472 focus_bounds.Intersect(GetLocalBounds()); |
| 473 return focus_bounds; |
| 474 } |
| 475 |
| 456 void Label::PaintText(gfx::Canvas* canvas) { | 476 void Label::PaintText(gfx::Canvas* canvas) { |
| 457 MaybeBuildRenderTextLines(); | 477 MaybeBuildRenderTextLines(); |
| 458 | 478 |
| 459 for (size_t i = 0; i < lines_.size(); ++i) | 479 for (size_t i = 0; i < lines_.size(); ++i) |
| 460 lines_[i]->Draw(canvas); | 480 lines_[i]->Draw(canvas); |
| 461 | 481 |
| 462 #if DCHECK_IS_ON() | 482 #if DCHECK_IS_ON() |
| 463 // Attempt to ensure that if we're using subpixel rendering, we're painting | 483 // Attempt to ensure that if we're using subpixel rendering, we're painting |
| 464 // to an opaque background. What we don't want to find is an ancestor in the | 484 // to an opaque background. What we don't want to find is an ancestor in the |
| 465 // hierarchy that paints to a non-opaque layer. | 485 // hierarchy that paints to a non-opaque layer. |
| (...skipping 26 matching lines...) Expand all Loading... |
| 492 // fixed. | 512 // fixed. |
| 493 tracked_objects::ScopedTracker tracking_profile( | 513 tracked_objects::ScopedTracker tracking_profile( |
| 494 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 First PaintText()")); | 514 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 First PaintText()")); |
| 495 | 515 |
| 496 is_first_paint_text_ = false; | 516 is_first_paint_text_ = false; |
| 497 PaintText(canvas); | 517 PaintText(canvas); |
| 498 } else { | 518 } else { |
| 499 PaintText(canvas); | 519 PaintText(canvas); |
| 500 } | 520 } |
| 501 | 521 |
| 502 // Check for IsAccessibilityFocusable() to prevent drawing a focus rect for | 522 if (HasFocus()) |
| 503 // non-focusable labels with selection, which are given focus explicitly in | 523 PaintFocusRing(canvas); |
| 504 // OnMousePressed. | |
| 505 if (HasFocus() && !ui::MaterialDesignController::IsSecondaryUiMaterial() && | |
| 506 IsAccessibilityFocusable()) { | |
| 507 canvas->DrawFocusRect(GetFocusBounds()); | |
| 508 } | |
| 509 } | 524 } |
| 510 | 525 |
| 511 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) { | 526 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
| 512 UpdateColorsFromTheme(theme); | 527 UpdateColorsFromTheme(theme); |
| 513 } | 528 } |
| 514 | 529 |
| 515 gfx::NativeCursor Label::GetCursor(const ui::MouseEvent& event) { | 530 gfx::NativeCursor Label::GetCursor(const ui::MouseEvent& event) { |
| 516 return GetRenderTextForSelectionController() ? GetNativeIBeamCursor() | 531 return GetRenderTextForSelectionController() ? GetNativeIBeamCursor() |
| 517 : gfx::kNullCursor; | 532 : gfx::kNullCursor; |
| 518 } | 533 } |
| (...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 836 PreferredSizeChanged(); | 851 PreferredSizeChanged(); |
| 837 SchedulePaint(); | 852 SchedulePaint(); |
| 838 ClearRenderTextLines(); | 853 ClearRenderTextLines(); |
| 839 } | 854 } |
| 840 | 855 |
| 841 void Label::MaybeBuildRenderTextLines() const { | 856 void Label::MaybeBuildRenderTextLines() const { |
| 842 if (!lines_.empty()) | 857 if (!lines_.empty()) |
| 843 return; | 858 return; |
| 844 | 859 |
| 845 gfx::Rect rect = GetContentsBounds(); | 860 gfx::Rect rect = GetContentsBounds(); |
| 846 if (focus_behavior() != FocusBehavior::NEVER) | 861 rect.Inset(NonBorderInsets(*this)); |
| 847 rect.Inset(kFocusBorderPadding, kFocusBorderPadding); | |
| 848 if (rect.IsEmpty()) | 862 if (rect.IsEmpty()) |
| 849 return; | 863 return; |
| 864 |
| 850 rect.Inset(-gfx::ShadowValue::GetMargin(shadows())); | 865 rect.Inset(-gfx::ShadowValue::GetMargin(shadows())); |
| 851 | 866 |
| 852 gfx::HorizontalAlignment alignment = horizontal_alignment(); | 867 gfx::HorizontalAlignment alignment = horizontal_alignment(); |
| 853 gfx::DirectionalityMode directionality = render_text_->directionality_mode(); | 868 gfx::DirectionalityMode directionality = render_text_->directionality_mode(); |
| 854 if (multi_line()) { | 869 if (multi_line()) { |
| 855 // Force the directionality and alignment of the first line on other lines. | 870 // Force the directionality and alignment of the first line on other lines. |
| 856 bool rtl = | 871 bool rtl = |
| 857 render_text_->GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT; | 872 render_text_->GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT; |
| 858 if (alignment == gfx::ALIGN_TO_HEAD) | 873 if (alignment == gfx::ALIGN_TO_HEAD) |
| 859 alignment = rtl ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; | 874 alignment = rtl ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 895 } | 910 } |
| 896 // Append the remaining text to the last visible line. | 911 // Append the remaining text to the last visible line. |
| 897 for (size_t i = lines_.size(); i < lines.size(); ++i) | 912 for (size_t i = lines_.size(); i < lines.size(); ++i) |
| 898 lines_.back()->SetText(lines_.back()->text() + lines[i]); | 913 lines_.back()->SetText(lines_.back()->text() + lines[i]); |
| 899 } | 914 } |
| 900 | 915 |
| 901 stored_selection_range_ = gfx::Range::InvalidRange(); | 916 stored_selection_range_ = gfx::Range::InvalidRange(); |
| 902 ApplyTextColors(); | 917 ApplyTextColors(); |
| 903 } | 918 } |
| 904 | 919 |
| 905 gfx::Rect Label::GetFocusBounds() const { | |
| 906 MaybeBuildRenderTextLines(); | |
| 907 | |
| 908 gfx::Rect focus_bounds; | |
| 909 if (lines_.empty()) { | |
| 910 focus_bounds = gfx::Rect(GetTextSize()); | |
| 911 } else { | |
| 912 for (size_t i = 0; i < lines_.size(); ++i) { | |
| 913 gfx::Point origin; | |
| 914 origin += lines_[i]->GetLineOffset(0); | |
| 915 focus_bounds.Union(gfx::Rect(origin, lines_[i]->GetStringSize())); | |
| 916 } | |
| 917 } | |
| 918 | |
| 919 focus_bounds.Inset(-kFocusBorderPadding, -kFocusBorderPadding); | |
| 920 focus_bounds.Intersect(GetLocalBounds()); | |
| 921 return focus_bounds; | |
| 922 } | |
| 923 | |
| 924 std::vector<base::string16> Label::GetLinesForWidth(int width) const { | 920 std::vector<base::string16> Label::GetLinesForWidth(int width) const { |
| 925 std::vector<base::string16> lines; | 921 std::vector<base::string16> lines; |
| 926 // |width| can be 0 when getting the default text size, in that case | 922 // |width| can be 0 when getting the default text size, in that case |
| 927 // the ideal lines (i.e. broken at newline characters) are wanted. | 923 // the ideal lines (i.e. broken at newline characters) are wanted. |
| 928 if (width <= 0) { | 924 if (width <= 0) { |
| 929 lines = base::SplitString( | 925 lines = base::SplitString( |
| 930 render_text_->GetDisplayText(), base::string16(1, '\n'), | 926 render_text_->GetDisplayText(), base::string16(1, '\n'), |
| 931 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 927 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| 932 } else { | 928 } else { |
| 933 gfx::ElideRectangleText(render_text_->GetDisplayText(), font_list(), width, | 929 gfx::ElideRectangleText(render_text_->GetDisplayText(), font_list(), width, |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1059 .WriteText(GetSelectedText()); | 1055 .WriteText(GetSelectedText()); |
| 1060 } | 1056 } |
| 1061 | 1057 |
| 1062 void Label::BuildContextMenuContents() { | 1058 void Label::BuildContextMenuContents() { |
| 1063 context_menu_contents_.AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); | 1059 context_menu_contents_.AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); |
| 1064 context_menu_contents_.AddItemWithStringId(IDS_APP_SELECT_ALL, | 1060 context_menu_contents_.AddItemWithStringId(IDS_APP_SELECT_ALL, |
| 1065 IDS_APP_SELECT_ALL); | 1061 IDS_APP_SELECT_ALL); |
| 1066 } | 1062 } |
| 1067 | 1063 |
| 1068 } // namespace views | 1064 } // namespace views |
| OLD | NEW |