Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(477)

Side by Side Diff: ui/views/controls/label.cc

Issue 2422993002: views::Label: Implement context menu, keyboard shortcuts for copy/select all. (Closed)
Patch Set: -- Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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()) {
(...skipping 495 matching lines...) Expand 10 before | Expand all | Expand 10 after
537 selection_controller_->OnMouseReleased(event); 539 selection_controller_->OnMouseReleased(event);
538 } 540 }
539 541
540 void Label::OnMouseCaptureLost() { 542 void Label::OnMouseCaptureLost() {
541 if (!GetRenderTextForSelectionController()) 543 if (!GetRenderTextForSelectionController())
542 return; 544 return;
543 545
544 selection_controller_->OnMouseCaptureLost(); 546 selection_controller_->OnMouseCaptureLost();
545 } 547 }
546 548
549 bool Label::AcceleratorPressed(const ui::Accelerator& accelerator) {
550 // The only accelerator a Label needs to handle is the Copy command from the
msw 2016/11/09 18:32:52 Not CTRL+A for select all?
karandeepb 2016/11/15 10:54:32 My understanding of accelerators is that they are
msw 2016/11/15 20:06:44 Acknowledged, thanks for the explanation. 'Chrome
551 // Chrome menu.
552 if(accelerator.key_code() == ui::VKEY_C && accelerator.IsCtrlDown()) {
msw 2016/11/09 18:32:52 nit: space after if, git cl format
karandeepb 2016/11/15 10:54:32 Done.
553 CopyToClipboard();
554 return true;
555 }
556 return false;
557 }
558
559 bool Label::CanHandleAccelerators() const {
560 // See related comment in BrowserView::CutCopyPaste.
msw 2016/11/09 18:32:53 nit: please explain a bit more here; I'm not sure
karandeepb 2016/11/15 10:54:32 Done. I was referring to the following comment in
561 return HasFocus() && GetRenderTextForSelectionController() &&
562 View::CanHandleAccelerators();
563 }
564
565 bool Label::OnKeyPressed(const ui::KeyEvent& event) {
msw 2016/11/09 18:32:52 Should this use Textfield's GetCommandForKeyEvent
karandeepb 2016/11/15 10:54:32 Currently the TextEditCommand enum is only being u
msw 2016/11/15 20:06:44 Hmm, this is okay for now; worth considering some
566 if (!GetRenderTextForSelectionController())
567 return false;
568
569 const bool shift = event.IsShiftDown();
570 const bool control = event.IsControlDown();
571 const bool alt = event.IsAltDown() || event.IsAltGrDown();
572
573 switch (event.key_code()) {
574 case ui::VKEY_C:
575 if (control && !alt && HasSelection()) {
msw 2016/11/09 18:32:53 nit && !obscured()? here and below?
karandeepb 2016/11/15 10:54:32 But shouldn't we consume the event (by returning t
msw 2016/11/15 20:06:44 Okay, that's fair, and the CopyToClipboard's early
576 CopyToClipboard();
577 return true;
578 }
579 break;
580 case ui::VKEY_INSERT:
581 if (control && !shift && HasSelection()) {
582 CopyToClipboard();
583 return true;
584 }
585 break;
586 case ui::VKEY_A:
587 if (control && !alt && !text().empty()) {
588 SelectAll();
589 if (HasSelection())
msw 2016/11/09 18:32:53 Should this be a DCHECK given the !text().empty()
karandeepb 2016/11/15 10:54:32 Oh yeah, done.
590 UpdateSelectionClipboard();
msw 2016/11/09 18:32:53 Should this be a part of SelectAll?
karandeepb 2016/11/15 10:54:32 Don't think so. SelectAll is a part of the public
msw 2016/11/15 20:06:44 Acknowledged.
591 return true;
592 }
593 break;
594 default:
595 break;
596 }
597
598 return false;
599 }
600
547 void Label::OnDeviceScaleFactorChanged(float device_scale_factor) { 601 void Label::OnDeviceScaleFactorChanged(float device_scale_factor) {
548 View::OnDeviceScaleFactorChanged(device_scale_factor); 602 View::OnDeviceScaleFactorChanged(device_scale_factor);
549 // When the device scale factor is changed, some font rendering parameters is 603 // 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 604 // changed (especially, hinting). The bounding box of the text has to be
551 // re-computed based on the new parameters. See crbug.com/441439 605 // re-computed based on the new parameters. See crbug.com/441439
552 ResetLayout(); 606 ResetLayout();
553 } 607 }
554 608
555 void Label::VisibilityChanged(View* starting_from, bool is_visible) { 609 void Label::VisibilityChanged(View* starting_from, bool is_visible) {
556 if (!is_visible) 610 if (!is_visible)
557 ClearRenderTextLines(); 611 ClearRenderTextLines();
558 } 612 }
559 613
614 bool Label::IsCommandIdChecked(int command_id) const {
615 return true;
616 }
617
618 bool Label::IsCommandIdEnabled(int command_id) const {
619 switch (command_id) {
620 case IDS_APP_COPY:
621 return HasSelection() && !obscured();
622 case IDS_APP_SELECT_ALL:
623 return GetRenderTextForSelectionController() && !text().empty();
624 }
625 return false;
626 }
627
628 void Label::ExecuteCommand(int command_id, int event_flags) {
629 switch (command_id) {
630 case IDS_APP_COPY:
631 CopyToClipboard();
632 break;
633 case IDS_APP_SELECT_ALL:
634 SelectAll();
635 if (HasSelection())
636 UpdateSelectionClipboard();
637 break;
638 default:
639 NOTREACHED();
640 }
641 }
642
643 bool Label::GetAcceleratorForCommandId(int command_id,
msw 2016/11/09 18:32:52 nit: Share a helper with textfield if feasible.
karandeepb 2016/11/15 10:54:32 Do you think it's still necessary now that we are
msw 2016/11/15 20:06:44 Nope, the simpler inlined impl here makes sense fo
644 ui::Accelerator* accelerator) const {
645 switch (command_id) {
646 case IDS_APP_UNDO:
647 *accelerator = ui::Accelerator(ui::VKEY_Z, ui::EF_CONTROL_DOWN);
648 return true;
649
650 case IDS_APP_CUT:
651 *accelerator = ui::Accelerator(ui::VKEY_X, ui::EF_CONTROL_DOWN);
652 return true;
653
654 case IDS_APP_COPY:
655 *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN);
656 return true;
657
658 case IDS_APP_PASTE:
659 *accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_CONTROL_DOWN);
660 return true;
661
662 case IDS_APP_SELECT_ALL:
663 *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_CONTROL_DOWN);
664 return true;
665
666 default:
667 return false;
668 }
669 }
670
671 void Label::ShowContextMenuForView(View* source,
672 const gfx::Point& point,
673 ui::MenuSourceType source_type) {
674 DCHECK(context_menu_contents_);
675 if (!GetRenderTextForSelectionController())
676 return;
677
678 context_menu_runner_.reset(new MenuRunner(context_menu_contents_.get(),
msw 2016/11/09 18:32:52 Would it suffice to initialize the runner once in/
karandeepb 2016/11/15 10:54:32 Although the MenuRunner documentation does not sta
msw 2016/11/15 20:06:44 Acknowledged.
679 MenuRunner::HAS_MNEMONICS |
680 MenuRunner::CONTEXT_MENU |
681 MenuRunner::ASYNC));
682 ignore_result(context_menu_runner_->RunMenuAt(
msw 2016/11/09 18:32:53 Perhaps we should check that the result isn't MENU
karandeepb 2016/11/15 10:54:32 But what to do if it is MENU_DELETED?
msw 2016/11/15 20:06:44 Right, it doesn't actually matter now, since the f
683 GetWidget(), nullptr, gfx::Rect(point, gfx::Size()), MENU_ANCHOR_TOPLEFT,
684 source_type));
685 }
686
560 gfx::RenderText* Label::GetRenderTextForSelectionController() { 687 gfx::RenderText* Label::GetRenderTextForSelectionController() {
561 return const_cast<gfx::RenderText*>( 688 return const_cast<gfx::RenderText*>(
562 static_cast<const Label*>(this)->GetRenderTextForSelectionController()); 689 static_cast<const Label*>(this)->GetRenderTextForSelectionController());
563 } 690 }
564 691
565 bool Label::IsReadOnly() const { 692 bool Label::IsReadOnly() const {
566 return true; 693 return true;
567 } 694 }
568 695
569 bool Label::SupportsDrag() const { 696 bool Label::SupportsDrag() const {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
604 } 731 }
605 732
606 bool Label::PasteSelectionClipboard() { 733 bool Label::PasteSelectionClipboard() {
607 NOTREACHED(); 734 NOTREACHED();
608 return false; 735 return false;
609 } 736 }
610 737
611 void Label::UpdateSelectionClipboard() { 738 void Label::UpdateSelectionClipboard() {
612 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) 739 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
613 if (!obscured()) { 740 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) 741 ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION)
619 .WriteText(selected_text); 742 .WriteText(GetSelectedText());
620 } 743 }
621 #endif 744 #endif
622 } 745 }
623 746
624 const gfx::RenderText* Label::GetRenderTextForSelectionController() const { 747 const gfx::RenderText* Label::GetRenderTextForSelectionController() const {
625 if (!selectable()) 748 if (!selectable())
626 return nullptr; 749 return nullptr;
627 MaybeBuildRenderTextLines(); 750 MaybeBuildRenderTextLines();
628 751
629 // This may happen when the content bounds of the view are empty. 752 // This may happen when the content bounds of the view are empty.
(...skipping 22 matching lines...) Expand all
652 subpixel_rendering_enabled_ = true; 775 subpixel_rendering_enabled_ = true;
653 auto_color_readability_ = true; 776 auto_color_readability_ = true;
654 multi_line_ = false; 777 multi_line_ = false;
655 UpdateColorsFromTheme(GetNativeTheme()); 778 UpdateColorsFromTheme(GetNativeTheme());
656 handles_tooltips_ = true; 779 handles_tooltips_ = true;
657 collapse_when_hidden_ = false; 780 collapse_when_hidden_ = false;
658 fixed_width_ = 0; 781 fixed_width_ = 0;
659 max_width_ = 0; 782 max_width_ = 0;
660 is_first_paint_text_ = true; 783 is_first_paint_text_ = true;
661 SetText(text); 784 SetText(text);
785
786 // Only selectable labels will get requests to show the context menu, due to
787 // CanProcessEventsWithinSubtree.
788 BuildContextMenuContents();
789 set_context_menu_controller(this);
790
791 // This allows the BrowserView to pass the copy command from the Chrome menu
msw 2016/11/09 18:32:53 Should we also add CTRL+A and Ctrl+Insert? It seem
karandeepb 2016/11/15 10:54:32 See my comment above for "Not CTRL+A for select al
msw 2016/11/15 20:06:44 Acknowledged.
792 // to the Label.
793 AddAccelerator(ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN));
662 } 794 }
663 795
664 void Label::ResetLayout() { 796 void Label::ResetLayout() {
665 InvalidateLayout(); 797 InvalidateLayout();
666 PreferredSizeChanged(); 798 PreferredSizeChanged();
667 SchedulePaint(); 799 SchedulePaint();
668 ClearRenderTextLines(); 800 ClearRenderTextLines();
669 } 801 }
670 802
671 void Label::MaybeBuildRenderTextLines() const { 803 void Label::MaybeBuildRenderTextLines() const {
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
863 995
864 void Label::ClearRenderTextLines() const { 996 void Label::ClearRenderTextLines() const {
865 // Persist the selection range if there is an active selection. 997 // Persist the selection range if there is an active selection.
866 if (HasSelection()) { 998 if (HasSelection()) {
867 stored_selection_range_ = 999 stored_selection_range_ =
868 GetRenderTextForSelectionController()->selection(); 1000 GetRenderTextForSelectionController()->selection();
869 } 1001 }
870 lines_.clear(); 1002 lines_.clear();
871 } 1003 }
872 1004
1005 base::string16 Label::GetSelectedText() const {
1006 const gfx::RenderText* render_text = GetRenderTextForSelectionController();
1007 return render_text ? render_text->GetTextFromRange(render_text->selection())
1008 : base::string16();
1009 }
1010
1011 void Label::CopyToClipboard() {
1012 if (!HasSelection() || obscured())
1013 return;
1014 ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE)
1015 .WriteText(GetSelectedText());
1016 }
1017
1018 void Label::BuildContextMenuContents() {
1019 DCHECK(!context_menu_contents_);
1020 context_menu_contents_.reset(new ui::SimpleMenuModel(this));
karandeepb 2016/11/09 10:20:00 Do we want to show only Copy/SelectAll in the cont
msw 2016/11/09 18:32:52 Yeah, I think we should only show copy and select
karandeepb 2016/11/15 10:54:32 Done.
1021 context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
1022 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1023 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
1024 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
1025 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
1026 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE);
1027 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1028 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
1029 IDS_APP_SELECT_ALL);
1030 }
1031
873 } // namespace views 1032 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698