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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: ui/views/controls/label.cc
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index 3533ea6f267d18a52a57a5c26999d270c3fd088b..19484ace6cde19ad0ba15ef3af19a2a845c5ce93 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -29,6 +29,8 @@
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/text_elider.h"
#include "ui/native_theme/native_theme.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/native_cursor.h"
#include "ui/views/selection_controller.h"
@@ -544,6 +546,58 @@ void Label::OnMouseCaptureLost() {
selection_controller_->OnMouseCaptureLost();
}
+bool Label::AcceleratorPressed(const ui::Accelerator& accelerator) {
+ // 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
+ // Chrome menu.
+ 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.
+ CopyToClipboard();
+ return true;
+ }
+ return false;
+}
+
+bool Label::CanHandleAccelerators() const {
+ // 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
+ return HasFocus() && GetRenderTextForSelectionController() &&
+ View::CanHandleAccelerators();
+}
+
+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
+ if (!GetRenderTextForSelectionController())
+ return false;
+
+ const bool shift = event.IsShiftDown();
+ const bool control = event.IsControlDown();
+ const bool alt = event.IsAltDown() || event.IsAltGrDown();
+
+ switch (event.key_code()) {
+ case ui::VKEY_C:
+ 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
+ CopyToClipboard();
+ return true;
+ }
+ break;
+ case ui::VKEY_INSERT:
+ if (control && !shift && HasSelection()) {
+ CopyToClipboard();
+ return true;
+ }
+ break;
+ case ui::VKEY_A:
+ if (control && !alt && !text().empty()) {
+ SelectAll();
+ 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.
+ 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.
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
void Label::OnDeviceScaleFactorChanged(float device_scale_factor) {
View::OnDeviceScaleFactorChanged(device_scale_factor);
// When the device scale factor is changed, some font rendering parameters is
@@ -557,6 +611,79 @@ void Label::VisibilityChanged(View* starting_from, bool is_visible) {
ClearRenderTextLines();
}
+bool Label::IsCommandIdChecked(int command_id) const {
+ return true;
+}
+
+bool Label::IsCommandIdEnabled(int command_id) const {
+ switch (command_id) {
+ case IDS_APP_COPY:
+ return HasSelection() && !obscured();
+ case IDS_APP_SELECT_ALL:
+ return GetRenderTextForSelectionController() && !text().empty();
+ }
+ return false;
+}
+
+void Label::ExecuteCommand(int command_id, int event_flags) {
+ switch (command_id) {
+ case IDS_APP_COPY:
+ CopyToClipboard();
+ break;
+ case IDS_APP_SELECT_ALL:
+ SelectAll();
+ if (HasSelection())
+ UpdateSelectionClipboard();
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+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
+ ui::Accelerator* accelerator) const {
+ switch (command_id) {
+ case IDS_APP_UNDO:
+ *accelerator = ui::Accelerator(ui::VKEY_Z, ui::EF_CONTROL_DOWN);
+ return true;
+
+ case IDS_APP_CUT:
+ *accelerator = ui::Accelerator(ui::VKEY_X, ui::EF_CONTROL_DOWN);
+ return true;
+
+ case IDS_APP_COPY:
+ *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN);
+ return true;
+
+ case IDS_APP_PASTE:
+ *accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_CONTROL_DOWN);
+ return true;
+
+ case IDS_APP_SELECT_ALL:
+ *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_CONTROL_DOWN);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void Label::ShowContextMenuForView(View* source,
+ const gfx::Point& point,
+ ui::MenuSourceType source_type) {
+ DCHECK(context_menu_contents_);
+ if (!GetRenderTextForSelectionController())
+ return;
+
+ 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.
+ MenuRunner::HAS_MNEMONICS |
+ MenuRunner::CONTEXT_MENU |
+ MenuRunner::ASYNC));
+ 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
+ GetWidget(), nullptr, gfx::Rect(point, gfx::Size()), MENU_ANCHOR_TOPLEFT,
+ source_type));
+}
+
gfx::RenderText* Label::GetRenderTextForSelectionController() {
return const_cast<gfx::RenderText*>(
static_cast<const Label*>(this)->GetRenderTextForSelectionController());
@@ -611,12 +738,8 @@ bool Label::PasteSelectionClipboard() {
void Label::UpdateSelectionClipboard() {
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
if (!obscured()) {
- const gfx::RenderText* render_text = GetRenderTextForSelectionController();
- DCHECK(render_text);
- const base::string16 selected_text =
- render_text->GetTextFromRange(render_text->selection());
ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION)
- .WriteText(selected_text);
+ .WriteText(GetSelectedText());
}
#endif
}
@@ -659,6 +782,15 @@ void Label::Init(const base::string16& text, const gfx::FontList& font_list) {
max_width_ = 0;
is_first_paint_text_ = true;
SetText(text);
+
+ // Only selectable labels will get requests to show the context menu, due to
+ // CanProcessEventsWithinSubtree.
+ BuildContextMenuContents();
+ set_context_menu_controller(this);
+
+ // 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.
+ // to the Label.
+ AddAccelerator(ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN));
}
void Label::ResetLayout() {
@@ -870,4 +1002,31 @@ void Label::ClearRenderTextLines() const {
lines_.clear();
}
+base::string16 Label::GetSelectedText() const {
+ const gfx::RenderText* render_text = GetRenderTextForSelectionController();
+ return render_text ? render_text->GetTextFromRange(render_text->selection())
+ : base::string16();
+}
+
+void Label::CopyToClipboard() {
+ if (!HasSelection() || obscured())
+ return;
+ ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE)
+ .WriteText(GetSelectedText());
+}
+
+void Label::BuildContextMenuContents() {
+ DCHECK(!context_menu_contents_);
+ 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.
+ context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
+ context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
+ context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
+ context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
+ context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
+ context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE);
+ context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
+ context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
+ IDS_APP_SELECT_ALL);
+}
+
} // namespace views

Powered by Google App Engine
This is Rietveld 408576698