Chromium Code Reviews| 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 |
| 40 const char Label::kViewClassName[] = "Label"; | 39 const char Label::kViewClassName[] = "Label"; |
| 41 const int Label::kFocusBorderPadding = 1; | |
| 42 | 40 |
| 43 Label::Label() : Label(base::string16()) { | 41 Label::Label() : Label(base::string16()) { |
| 44 } | 42 } |
| 45 | 43 |
| 46 Label::Label(const base::string16& text) | 44 Label::Label(const base::string16& text) |
| 47 : Label(text, style::CONTEXT_LABEL, style::STYLE_PRIMARY) {} | 45 : Label(text, style::CONTEXT_LABEL, style::STYLE_PRIMARY) {} |
| 48 | 46 |
| 49 Label::Label(const base::string16& text, int text_context, int text_style) | 47 Label::Label(const base::string16& text, int text_context, int text_style) |
| 50 : context_menu_contents_(this) { | 48 : context_menu_contents_(this) { |
| 51 Init(text, style::GetFont(text_context, text_style)); | 49 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(); | 293 render_text->ClearSelection(); |
| 296 SchedulePaint(); | 294 SchedulePaint(); |
| 297 } | 295 } |
| 298 | 296 |
| 299 void Label::SelectRange(const gfx::Range& range) { | 297 void Label::SelectRange(const gfx::Range& range) { |
| 300 gfx::RenderText* render_text = GetRenderTextForSelectionController(); | 298 gfx::RenderText* render_text = GetRenderTextForSelectionController(); |
| 301 if (render_text && render_text->SelectRange(range)) | 299 if (render_text && render_text->SelectRange(range)) |
| 302 SchedulePaint(); | 300 SchedulePaint(); |
| 303 } | 301 } |
| 304 | 302 |
| 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 { | 303 int Label::GetBaseline() const { |
| 315 return GetInsets().top() + font_list().GetBaseline(); | 304 return GetInsets().top() + font_list().GetBaseline(); |
| 316 } | 305 } |
| 317 | 306 |
| 318 gfx::Size Label::GetPreferredSize() const { | 307 gfx::Size Label::GetPreferredSize() const { |
| 319 // Return a size of (0, 0) if the label is not visible and if the | 308 // Return a size of (0, 0) if the label is not visible and if the |
| 320 // |collapse_when_hidden_| flag is set. | 309 // |collapse_when_hidden_| flag is set. |
| 321 // TODO(munjal): This logic probably belongs to the View class. But for now, | 310 // 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 | 311 // put it here since putting it in View class means all inheriting classes |
| 323 // need to respect the |collapse_when_hidden_| flag. | 312 // 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); | 435 render_text->SetElideBehavior(elide_behavior); |
| 447 render_text->SetObscured(obscured()); | 436 render_text->SetObscured(obscured()); |
| 448 render_text->SetMinLineHeight(line_height()); | 437 render_text->SetMinLineHeight(line_height()); |
| 449 render_text->SetFontList(font_list()); | 438 render_text->SetFontList(font_list()); |
| 450 render_text->set_shadows(shadows()); | 439 render_text->set_shadows(shadows()); |
| 451 render_text->SetCursorEnabled(false); | 440 render_text->SetCursorEnabled(false); |
| 452 render_text->SetText(text); | 441 render_text->SetText(text); |
| 453 return render_text; | 442 return render_text; |
| 454 } | 443 } |
| 455 | 444 |
| 445 void Label::MaybePaintFocusRing(gfx::Canvas* canvas) const { | |
| 446 // No focus ring by default. | |
| 447 } | |
| 448 | |
| 449 gfx::Rect Label::GetFocusRingBounds() const { | |
| 450 MaybeBuildRenderTextLines(); | |
| 451 | |
| 452 gfx::Rect focus_bounds; | |
| 453 if (lines_.empty()) { | |
| 454 focus_bounds = gfx::Rect(GetTextSize()); | |
| 455 } else { | |
| 456 for (size_t i = 0; i < lines_.size(); ++i) { | |
| 457 gfx::Point origin; | |
| 458 origin += lines_[i]->GetLineOffset(0); | |
| 459 focus_bounds.Union(gfx::Rect(origin, lines_[i]->GetStringSize())); | |
| 460 } | |
| 461 } | |
| 462 | |
| 463 focus_bounds.Inset(-(GetInsets() - View::GetInsets())); | |
| 464 focus_bounds.Intersect(GetLocalBounds()); | |
| 465 return focus_bounds; | |
| 466 } | |
| 467 | |
| 456 void Label::PaintText(gfx::Canvas* canvas) { | 468 void Label::PaintText(gfx::Canvas* canvas) { |
| 457 MaybeBuildRenderTextLines(); | 469 MaybeBuildRenderTextLines(); |
| 458 | 470 |
| 459 for (size_t i = 0; i < lines_.size(); ++i) | 471 for (size_t i = 0; i < lines_.size(); ++i) |
| 460 lines_[i]->Draw(canvas); | 472 lines_[i]->Draw(canvas); |
| 461 | 473 |
| 462 #if DCHECK_IS_ON() | 474 #if DCHECK_IS_ON() |
| 463 // Attempt to ensure that if we're using subpixel rendering, we're painting | 475 // 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 | 476 // 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. | 477 // hierarchy that paints to a non-opaque layer. |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 492 // fixed. | 504 // fixed. |
| 493 tracked_objects::ScopedTracker tracking_profile( | 505 tracked_objects::ScopedTracker tracking_profile( |
| 494 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 First PaintText()")); | 506 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 First PaintText()")); |
| 495 | 507 |
| 496 is_first_paint_text_ = false; | 508 is_first_paint_text_ = false; |
| 497 PaintText(canvas); | 509 PaintText(canvas); |
| 498 } else { | 510 } else { |
| 499 PaintText(canvas); | 511 PaintText(canvas); |
| 500 } | 512 } |
| 501 | 513 |
| 502 // Check for IsAccessibilityFocusable() to prevent drawing a focus rect for | 514 if (HasFocus()) |
| 503 // non-focusable labels with selection, which are given focus explicitly in | 515 MaybePaintFocusRing(canvas); |
|
tapted
2017/04/19 12:28:45
views::Link are never selectable (Link::IsSelectio
| |
| 504 // OnMousePressed. | |
| 505 if (HasFocus() && !ui::MaterialDesignController::IsSecondaryUiMaterial() && | |
| 506 IsAccessibilityFocusable()) { | |
| 507 canvas->DrawFocusRect(GetFocusBounds()); | |
| 508 } | |
| 509 } | 516 } |
| 510 | 517 |
| 511 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) { | 518 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
| 512 UpdateColorsFromTheme(theme); | 519 UpdateColorsFromTheme(theme); |
| 513 } | 520 } |
| 514 | 521 |
| 515 gfx::NativeCursor Label::GetCursor(const ui::MouseEvent& event) { | 522 gfx::NativeCursor Label::GetCursor(const ui::MouseEvent& event) { |
| 516 return GetRenderTextForSelectionController() ? GetNativeIBeamCursor() | 523 return GetRenderTextForSelectionController() ? GetNativeIBeamCursor() |
| 517 : gfx::kNullCursor; | 524 : gfx::kNullCursor; |
| 518 } | 525 } |
| (...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 836 PreferredSizeChanged(); | 843 PreferredSizeChanged(); |
| 837 SchedulePaint(); | 844 SchedulePaint(); |
| 838 ClearRenderTextLines(); | 845 ClearRenderTextLines(); |
| 839 } | 846 } |
| 840 | 847 |
| 841 void Label::MaybeBuildRenderTextLines() const { | 848 void Label::MaybeBuildRenderTextLines() const { |
| 842 if (!lines_.empty()) | 849 if (!lines_.empty()) |
| 843 return; | 850 return; |
| 844 | 851 |
| 845 gfx::Rect rect = GetContentsBounds(); | 852 gfx::Rect rect = GetContentsBounds(); |
| 846 if (focus_behavior() != FocusBehavior::NEVER) | 853 rect.Inset(GetInsets() - View::GetInsets()); |
|
sky
2017/04/19 17:27:46
This is very subtle and worth a comment. In fact I
tapted
2017/04/20 11:50:17
Done.
| |
| 847 rect.Inset(kFocusBorderPadding, kFocusBorderPadding); | |
| 848 if (rect.IsEmpty()) | 854 if (rect.IsEmpty()) |
| 849 return; | 855 return; |
| 856 | |
| 850 rect.Inset(-gfx::ShadowValue::GetMargin(shadows())); | 857 rect.Inset(-gfx::ShadowValue::GetMargin(shadows())); |
| 851 | 858 |
| 852 gfx::HorizontalAlignment alignment = horizontal_alignment(); | 859 gfx::HorizontalAlignment alignment = horizontal_alignment(); |
| 853 gfx::DirectionalityMode directionality = render_text_->directionality_mode(); | 860 gfx::DirectionalityMode directionality = render_text_->directionality_mode(); |
| 854 if (multi_line()) { | 861 if (multi_line()) { |
| 855 // Force the directionality and alignment of the first line on other lines. | 862 // Force the directionality and alignment of the first line on other lines. |
| 856 bool rtl = | 863 bool rtl = |
| 857 render_text_->GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT; | 864 render_text_->GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT; |
| 858 if (alignment == gfx::ALIGN_TO_HEAD) | 865 if (alignment == gfx::ALIGN_TO_HEAD) |
| 859 alignment = rtl ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; | 866 alignment = rtl ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 895 } | 902 } |
| 896 // Append the remaining text to the last visible line. | 903 // Append the remaining text to the last visible line. |
| 897 for (size_t i = lines_.size(); i < lines.size(); ++i) | 904 for (size_t i = lines_.size(); i < lines.size(); ++i) |
| 898 lines_.back()->SetText(lines_.back()->text() + lines[i]); | 905 lines_.back()->SetText(lines_.back()->text() + lines[i]); |
| 899 } | 906 } |
| 900 | 907 |
| 901 stored_selection_range_ = gfx::Range::InvalidRange(); | 908 stored_selection_range_ = gfx::Range::InvalidRange(); |
| 902 ApplyTextColors(); | 909 ApplyTextColors(); |
| 903 } | 910 } |
| 904 | 911 |
| 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 { | 912 std::vector<base::string16> Label::GetLinesForWidth(int width) const { |
| 925 std::vector<base::string16> lines; | 913 std::vector<base::string16> lines; |
| 926 // |width| can be 0 when getting the default text size, in that case | 914 // |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. | 915 // the ideal lines (i.e. broken at newline characters) are wanted. |
| 928 if (width <= 0) { | 916 if (width <= 0) { |
| 929 lines = base::SplitString( | 917 lines = base::SplitString( |
| 930 render_text_->GetDisplayText(), base::string16(1, '\n'), | 918 render_text_->GetDisplayText(), base::string16(1, '\n'), |
| 931 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 919 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| 932 } else { | 920 } else { |
| 933 gfx::ElideRectangleText(render_text_->GetDisplayText(), font_list(), width, | 921 gfx::ElideRectangleText(render_text_->GetDisplayText(), font_list(), width, |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1059 .WriteText(GetSelectedText()); | 1047 .WriteText(GetSelectedText()); |
| 1060 } | 1048 } |
| 1061 | 1049 |
| 1062 void Label::BuildContextMenuContents() { | 1050 void Label::BuildContextMenuContents() { |
| 1063 context_menu_contents_.AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); | 1051 context_menu_contents_.AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); |
| 1064 context_menu_contents_.AddItemWithStringId(IDS_APP_SELECT_ALL, | 1052 context_menu_contents_.AddItemWithStringId(IDS_APP_SELECT_ALL, |
| 1065 IDS_APP_SELECT_ALL); | 1053 IDS_APP_SELECT_ALL); |
| 1066 } | 1054 } |
| 1067 | 1055 |
| 1068 } // namespace views | 1056 } // namespace views |
| OLD | NEW |