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> |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 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" | 25 #include "ui/base/material_design/material_design_controller.h" |
| 26 #include "ui/base/resource/resource_bundle.h" | 26 #include "ui/base/resource/resource_bundle.h" |
| 27 #include "ui/gfx/canvas.h" | 27 #include "ui/gfx/canvas.h" |
| 28 #include "ui/gfx/color_utils.h" | 28 #include "ui/gfx/color_utils.h" |
| 29 #include "ui/gfx/geometry/insets.h" | 29 #include "ui/gfx/geometry/insets.h" |
| 30 #include "ui/gfx/text_elider.h" | 30 #include "ui/gfx/text_elider.h" |
| 31 #include "ui/native_theme/native_theme.h" | 31 #include "ui/native_theme/native_theme.h" |
| 32 #include "ui/strings/grit/ui_strings.h" | |
| 33 #include "ui/views/controls/menu/menu_runner.h" | |
| 32 #include "ui/views/focus/focus_manager.h" | 34 #include "ui/views/focus/focus_manager.h" |
| 33 #include "ui/views/native_cursor.h" | 35 #include "ui/views/native_cursor.h" |
| 34 #include "ui/views/selection_controller.h" | 36 #include "ui/views/selection_controller.h" |
| 35 | 37 |
| 36 namespace views { | 38 namespace views { |
| 37 // static | 39 // static |
| 38 const char Label::kViewClassName[] = "Label"; | 40 const char Label::kViewClassName[] = "Label"; |
| 39 const int Label::kFocusBorderPadding = 1; | 41 const int Label::kFocusBorderPadding = 1; |
| 40 | 42 |
| 41 Label::Label() : Label(base::string16()) { | 43 Label::Label() : Label(base::string16()) { |
| 42 } | 44 } |
| 43 | 45 |
| 44 Label::Label(const base::string16& text) : Label(text, GetDefaultFontList()) { | 46 Label::Label(const base::string16& text) : Label(text, GetDefaultFontList()) { |
| 45 } | 47 } |
| 46 | 48 |
| 47 Label::Label(const base::string16& text, const gfx::FontList& font_list) { | 49 Label::Label(const base::string16& text, const gfx::FontList& font_list) |
| 50 : context_menu_contents_(this) { | |
| 48 Init(text, font_list); | 51 Init(text, font_list); |
| 49 } | 52 } |
| 50 | 53 |
| 51 Label::~Label() { | 54 Label::~Label() { |
| 52 } | 55 } |
| 53 | 56 |
| 54 // static | 57 // static |
| 55 const gfx::FontList& Label::GetDefaultFontList() { | 58 const gfx::FontList& Label::GetDefaultFontList() { |
| 56 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 59 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 57 return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta); | 60 return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta); |
| 58 } | 61 } |
| 59 | 62 |
| 60 void Label::SetFontList(const gfx::FontList& font_list) { | 63 void Label::SetFontList(const gfx::FontList& font_list) { |
| 61 is_first_paint_text_ = true; | 64 is_first_paint_text_ = true; |
| 62 render_text_->SetFontList(font_list); | 65 render_text_->SetFontList(font_list); |
| 63 ResetLayout(); | 66 ResetLayout(); |
| 64 } | 67 } |
| 65 | 68 |
| 66 void Label::SetText(const base::string16& new_text) { | 69 void Label::SetText(const base::string16& new_text) { |
| 67 if (new_text == text()) | 70 if (new_text == text()) |
| 68 return; | 71 return; |
| 69 is_first_paint_text_ = true; | 72 is_first_paint_text_ = true; |
| 70 render_text_->SetText(new_text); | 73 render_text_->SetText(new_text); |
| 71 ResetLayout(); | 74 ResetLayout(); |
| 75 | |
| 76 // Invalidate |stored_selection_range_|. | |
|
msw
2016/11/15 20:06:45
nit: this comment seems unnecessary (blank line ab
karandeepb
2016/11/16 07:48:28
Done.
| |
| 77 stored_selection_range_ = gfx::Range::InvalidRange(); | |
|
msw
2016/11/15 20:06:45
nit: maybe do this before ResetLayout?
karandeepb
2016/11/16 07:48:28
This needs to be done after ResetLayout. ResetLayo
| |
| 72 } | 78 } |
| 73 | 79 |
| 74 void Label::SetAutoColorReadabilityEnabled(bool enabled) { | 80 void Label::SetAutoColorReadabilityEnabled(bool enabled) { |
| 75 if (auto_color_readability_ == enabled) | 81 if (auto_color_readability_ == enabled) |
| 76 return; | 82 return; |
| 77 is_first_paint_text_ = true; | 83 is_first_paint_text_ = true; |
| 78 auto_color_readability_ = enabled; | 84 auto_color_readability_ = enabled; |
| 79 RecalculateColors(); | 85 RecalculateColors(); |
| 80 } | 86 } |
| 81 | 87 |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 246 bool Label::IsSelectionSupported() const { | 252 bool Label::IsSelectionSupported() const { |
| 247 return !multi_line() && render_text_->IsSelectionSupported(); | 253 return !multi_line() && render_text_->IsSelectionSupported(); |
| 248 } | 254 } |
| 249 | 255 |
| 250 bool Label::SetSelectable(bool value) { | 256 bool Label::SetSelectable(bool value) { |
| 251 if (value == selectable()) | 257 if (value == selectable()) |
| 252 return true; | 258 return true; |
| 253 | 259 |
| 254 if (!value) { | 260 if (!value) { |
| 255 ClearSelection(); | 261 ClearSelection(); |
| 262 stored_selection_range_ = gfx::Range::InvalidRange(); | |
| 256 selection_controller_.reset(); | 263 selection_controller_.reset(); |
| 257 return true; | 264 return true; |
| 258 } | 265 } |
| 259 | 266 |
| 267 DCHECK(!stored_selection_range_.IsValid()); | |
| 260 if (!IsSelectionSupported()) | 268 if (!IsSelectionSupported()) |
| 261 return false; | 269 return false; |
| 262 | 270 |
| 263 selection_controller_ = base::MakeUnique<SelectionController>(this); | 271 selection_controller_ = base::MakeUnique<SelectionController>(this); |
| 264 return true; | 272 return true; |
| 265 } | 273 } |
| 266 | 274 |
| 267 bool Label::HasSelection() const { | 275 bool Label::HasSelection() const { |
| 268 const gfx::RenderText* render_text = GetRenderTextForSelectionController(); | 276 const gfx::RenderText* render_text = GetRenderTextForSelectionController(); |
| 269 return render_text ? !render_text->selection().is_empty() : false; | 277 return render_text ? !render_text->selection().is_empty() : false; |
| (...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 537 selection_controller_->OnMouseReleased(event); | 545 selection_controller_->OnMouseReleased(event); |
| 538 } | 546 } |
| 539 | 547 |
| 540 void Label::OnMouseCaptureLost() { | 548 void Label::OnMouseCaptureLost() { |
| 541 if (!GetRenderTextForSelectionController()) | 549 if (!GetRenderTextForSelectionController()) |
| 542 return; | 550 return; |
| 543 | 551 |
| 544 selection_controller_->OnMouseCaptureLost(); | 552 selection_controller_->OnMouseCaptureLost(); |
| 545 } | 553 } |
| 546 | 554 |
| 555 bool Label::OnKeyPressed(const ui::KeyEvent& event) { | |
| 556 if (!GetRenderTextForSelectionController()) | |
| 557 return false; | |
| 558 | |
| 559 const bool shift = event.IsShiftDown(); | |
|
tapted
2016/11/16 02:45:19
This is probably orthogonal, but I feel like there
karandeepb
2016/11/16 07:48:28
So if I am understanding you correctly, the Simple
tapted
2016/11/16 08:39:07
It's pretty much exactly how Cocoa makes you do it
karandeepb
2016/11/16 23:47:13
Oh ok. Din't know this. As talked offline, this ca
| |
| 560 const bool control = event.IsControlDown(); | |
| 561 const bool alt = event.IsAltDown() || event.IsAltGrDown(); | |
| 562 | |
| 563 switch (event.key_code()) { | |
| 564 case ui::VKEY_C: | |
| 565 if (control && !alt && HasSelection()) { | |
| 566 CopyToClipboard(); | |
| 567 return true; | |
| 568 } | |
| 569 break; | |
| 570 case ui::VKEY_INSERT: | |
| 571 if (control && !shift && HasSelection()) { | |
| 572 CopyToClipboard(); | |
| 573 return true; | |
| 574 } | |
| 575 break; | |
| 576 case ui::VKEY_A: | |
| 577 if (control && !alt && !text().empty()) { | |
| 578 SelectAll(); | |
| 579 DCHECK(HasSelection()); | |
| 580 UpdateSelectionClipboard(); | |
| 581 return true; | |
| 582 } | |
| 583 break; | |
| 584 default: | |
| 585 break; | |
| 586 } | |
| 587 | |
| 588 return false; | |
| 589 } | |
| 590 | |
| 591 bool Label::AcceleratorPressed(const ui::Accelerator& accelerator) { | |
| 592 // The only accelerator a Label needs to handle is the Copy command from the | |
| 593 // Chrome menu. | |
|
tapted
2016/11/16 02:45:19
In the comment, say why? I had the same immediate
karandeepb
2016/11/16 07:48:28
Done.
| |
| 594 if (accelerator.key_code() == ui::VKEY_C && accelerator.IsCtrlDown()) { | |
| 595 CopyToClipboard(); | |
| 596 return true; | |
| 597 } | |
| 598 return false; | |
| 599 } | |
| 600 | |
| 601 bool Label::CanHandleAccelerators() const { | |
| 602 // Focus needs to be checked since the accelerator for the Copy command from | |
| 603 // the Chrome menu should only be handled when the current view has focus. See | |
| 604 // related comment in BrowserView::CutCopyPaste. | |
| 605 return HasFocus() && GetRenderTextForSelectionController() && | |
| 606 View::CanHandleAccelerators(); | |
| 607 } | |
| 608 | |
| 547 void Label::OnDeviceScaleFactorChanged(float device_scale_factor) { | 609 void Label::OnDeviceScaleFactorChanged(float device_scale_factor) { |
| 548 View::OnDeviceScaleFactorChanged(device_scale_factor); | 610 View::OnDeviceScaleFactorChanged(device_scale_factor); |
| 549 // When the device scale factor is changed, some font rendering parameters is | 611 // When the device scale factor is changed, some font rendering parameters is |
| 550 // changed (especially, hinting). The bounding box of the text has to be | 612 // changed (especially, hinting). The bounding box of the text has to be |
| 551 // re-computed based on the new parameters. See crbug.com/441439 | 613 // re-computed based on the new parameters. See crbug.com/441439 |
| 552 ResetLayout(); | 614 ResetLayout(); |
| 553 } | 615 } |
| 554 | 616 |
| 555 void Label::VisibilityChanged(View* starting_from, bool is_visible) { | 617 void Label::VisibilityChanged(View* starting_from, bool is_visible) { |
| 556 if (!is_visible) | 618 if (!is_visible) |
| 557 ClearRenderTextLines(); | 619 ClearRenderTextLines(); |
| 558 } | 620 } |
| 559 | 621 |
| 622 void Label::ShowContextMenuForView(View* source, | |
| 623 const gfx::Point& point, | |
| 624 ui::MenuSourceType source_type) { | |
| 625 if (!GetRenderTextForSelectionController()) | |
| 626 return; | |
| 627 | |
| 628 context_menu_runner_.reset( | |
| 629 new MenuRunner(&context_menu_contents_, MenuRunner::HAS_MNEMONICS | | |
| 630 MenuRunner::CONTEXT_MENU | | |
| 631 MenuRunner::ASYNC)); | |
| 632 ignore_result(context_menu_runner_->RunMenuAt( | |
| 633 GetWidget(), nullptr, gfx::Rect(point, gfx::Size()), MENU_ANCHOR_TOPLEFT, | |
| 634 source_type)); | |
| 635 } | |
| 636 | |
| 560 gfx::RenderText* Label::GetRenderTextForSelectionController() { | 637 gfx::RenderText* Label::GetRenderTextForSelectionController() { |
| 561 return const_cast<gfx::RenderText*>( | 638 return const_cast<gfx::RenderText*>( |
| 562 static_cast<const Label*>(this)->GetRenderTextForSelectionController()); | 639 static_cast<const Label*>(this)->GetRenderTextForSelectionController()); |
| 563 } | 640 } |
| 564 | 641 |
| 565 bool Label::IsReadOnly() const { | 642 bool Label::IsReadOnly() const { |
| 566 return true; | 643 return true; |
| 567 } | 644 } |
| 568 | 645 |
| 569 bool Label::SupportsDrag() const { | 646 bool Label::SupportsDrag() const { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 604 } | 681 } |
| 605 | 682 |
| 606 bool Label::PasteSelectionClipboard() { | 683 bool Label::PasteSelectionClipboard() { |
| 607 NOTREACHED(); | 684 NOTREACHED(); |
| 608 return false; | 685 return false; |
| 609 } | 686 } |
| 610 | 687 |
| 611 void Label::UpdateSelectionClipboard() { | 688 void Label::UpdateSelectionClipboard() { |
| 612 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) | 689 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| 613 if (!obscured()) { | 690 if (!obscured()) { |
| 614 const gfx::RenderText* render_text = GetRenderTextForSelectionController(); | |
| 615 DCHECK(render_text); | |
| 616 const base::string16 selected_text = | |
| 617 render_text->GetTextFromRange(render_text->selection()); | |
| 618 ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION) | 691 ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION) |
| 619 .WriteText(selected_text); | 692 .WriteText(GetSelectedText()); |
| 620 } | 693 } |
| 621 #endif | 694 #endif |
| 622 } | 695 } |
| 623 | 696 |
| 697 bool Label::IsCommandIdChecked(int command_id) const { | |
| 698 return true; | |
| 699 } | |
| 700 | |
| 701 bool Label::IsCommandIdEnabled(int command_id) const { | |
| 702 switch (command_id) { | |
| 703 case IDS_APP_COPY: | |
| 704 return HasSelection() && !obscured(); | |
| 705 case IDS_APP_SELECT_ALL: | |
| 706 return GetRenderTextForSelectionController() && !text().empty(); | |
| 707 } | |
| 708 return false; | |
| 709 } | |
| 710 | |
| 711 void Label::ExecuteCommand(int command_id, int event_flags) { | |
| 712 switch (command_id) { | |
| 713 case IDS_APP_COPY: | |
| 714 CopyToClipboard(); | |
| 715 break; | |
| 716 case IDS_APP_SELECT_ALL: | |
| 717 SelectAll(); | |
| 718 DCHECK(HasSelection()); | |
| 719 UpdateSelectionClipboard(); | |
| 720 break; | |
| 721 default: | |
| 722 NOTREACHED(); | |
| 723 } | |
| 724 } | |
| 725 | |
| 726 bool Label::GetAcceleratorForCommandId(int command_id, | |
| 727 ui::Accelerator* accelerator) const { | |
| 728 switch (command_id) { | |
| 729 case IDS_APP_COPY: | |
| 730 *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN); | |
| 731 return true; | |
| 732 | |
| 733 case IDS_APP_SELECT_ALL: | |
| 734 *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_CONTROL_DOWN); | |
| 735 return true; | |
| 736 | |
| 737 default: | |
| 738 return false; | |
| 739 } | |
| 740 } | |
| 741 | |
| 624 const gfx::RenderText* Label::GetRenderTextForSelectionController() const { | 742 const gfx::RenderText* Label::GetRenderTextForSelectionController() const { |
| 625 if (!selectable()) | 743 if (!selectable()) |
| 626 return nullptr; | 744 return nullptr; |
| 627 MaybeBuildRenderTextLines(); | 745 MaybeBuildRenderTextLines(); |
| 628 | 746 |
| 629 // This may happen when the content bounds of the view are empty. | 747 // This may happen when the content bounds of the view are empty. |
| 630 if (lines_.empty()) | 748 if (lines_.empty()) |
| 631 return nullptr; | 749 return nullptr; |
| 632 | 750 |
| 633 DCHECK_EQ(1u, lines_.size()); | 751 DCHECK_EQ(1u, lines_.size()); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 652 subpixel_rendering_enabled_ = true; | 770 subpixel_rendering_enabled_ = true; |
| 653 auto_color_readability_ = true; | 771 auto_color_readability_ = true; |
| 654 multi_line_ = false; | 772 multi_line_ = false; |
| 655 UpdateColorsFromTheme(GetNativeTheme()); | 773 UpdateColorsFromTheme(GetNativeTheme()); |
| 656 handles_tooltips_ = true; | 774 handles_tooltips_ = true; |
| 657 collapse_when_hidden_ = false; | 775 collapse_when_hidden_ = false; |
| 658 fixed_width_ = 0; | 776 fixed_width_ = 0; |
| 659 max_width_ = 0; | 777 max_width_ = 0; |
| 660 is_first_paint_text_ = true; | 778 is_first_paint_text_ = true; |
| 661 SetText(text); | 779 SetText(text); |
| 780 | |
| 781 // Only selectable labels will get requests to show the context menu, due to | |
| 782 // CanProcessEventsWithinSubtree. | |
|
tapted
2016/11/16 02:45:19
nit: CanProcessEventsWithinSubtree().
karandeepb
2016/11/16 07:48:28
Done.
| |
| 783 BuildContextMenuContents(); | |
| 784 set_context_menu_controller(this); | |
| 785 | |
| 786 // This allows the BrowserView to pass the copy command from the Chrome menu | |
| 787 // to the Label. | |
| 788 AddAccelerator(ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN)); | |
| 662 } | 789 } |
| 663 | 790 |
| 664 void Label::ResetLayout() { | 791 void Label::ResetLayout() { |
| 665 InvalidateLayout(); | 792 InvalidateLayout(); |
| 666 PreferredSizeChanged(); | 793 PreferredSizeChanged(); |
| 667 SchedulePaint(); | 794 SchedulePaint(); |
| 668 ClearRenderTextLines(); | 795 ClearRenderTextLines(); |
| 669 } | 796 } |
| 670 | 797 |
| 671 void Label::MaybeBuildRenderTextLines() const { | 798 void Label::MaybeBuildRenderTextLines() const { |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 855 } | 982 } |
| 856 | 983 |
| 857 bool Label::ShouldShowDefaultTooltip() const { | 984 bool Label::ShouldShowDefaultTooltip() const { |
| 858 const gfx::Size text_size = GetTextSize(); | 985 const gfx::Size text_size = GetTextSize(); |
| 859 const gfx::Size size = GetContentsBounds().size(); | 986 const gfx::Size size = GetContentsBounds().size(); |
| 860 return !obscured() && (text_size.width() > size.width() || | 987 return !obscured() && (text_size.width() > size.width() || |
| 861 (multi_line() && text_size.height() > size.height())); | 988 (multi_line() && text_size.height() > size.height())); |
| 862 } | 989 } |
| 863 | 990 |
| 864 void Label::ClearRenderTextLines() const { | 991 void Label::ClearRenderTextLines() const { |
| 992 if(lines_.empty()) | |
|
karandeepb
2016/11/15 10:54:32
This is because we follow this codepath ClearRende
msw
2016/11/15 20:06:45
Add a quick explanatory comment; and run git cl fo
karandeepb
2016/11/16 07:48:28
Done.
| |
| 993 return; | |
| 994 | |
| 865 // Persist the selection range if there is an active selection. | 995 // Persist the selection range if there is an active selection. |
| 866 if (HasSelection()) { | 996 if (HasSelection()) { |
| 867 stored_selection_range_ = | 997 stored_selection_range_ = |
| 868 GetRenderTextForSelectionController()->selection(); | 998 GetRenderTextForSelectionController()->selection(); |
| 869 } | 999 } |
| 870 lines_.clear(); | 1000 lines_.clear(); |
| 871 } | 1001 } |
| 872 | 1002 |
| 1003 base::string16 Label::GetSelectedText() const { | |
| 1004 const gfx::RenderText* render_text = GetRenderTextForSelectionController(); | |
| 1005 return render_text ? render_text->GetTextFromRange(render_text->selection()) | |
| 1006 : base::string16(); | |
| 1007 } | |
| 1008 | |
| 1009 void Label::CopyToClipboard() { | |
| 1010 if (!HasSelection() || obscured()) | |
| 1011 return; | |
| 1012 ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE) | |
| 1013 .WriteText(GetSelectedText()); | |
| 1014 } | |
| 1015 | |
| 1016 void Label::BuildContextMenuContents() { | |
| 1017 context_menu_contents_.AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); | |
| 1018 context_menu_contents_.AddItemWithStringId(IDS_APP_SELECT_ALL, | |
| 1019 IDS_APP_SELECT_ALL); | |
| 1020 } | |
| 1021 | |
| 873 } // namespace views | 1022 } // namespace views |
| OLD | NEW |