Commit 9903451e authored by Mathieu Perreault's avatar Mathieu Perreault Committed by Commit Bot

[Emoji Panel] Add emoji option to context menu for native Views

This is a complement to
https://chromium-review.googlesource.com/c/chromium/src/+/1045990
which added support for this panel to the render view context menu.

Bug: 842014
Change-Id: I2bb3bdd8c4a65871c34ad642cbe2de16c791c267
Reviewed-on: https://chromium-review.googlesource.com/1067656
Commit-Queue: Mathieu Perreault <mathp@chromium.org>
Reviewed-by: default avatarTrent Apted <tapted@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561459}
parent 056a01b6
...@@ -370,6 +370,8 @@ jumbo_component("views") { ...@@ -370,6 +370,8 @@ jumbo_component("views") {
"controls/tree/tree_view_controller.cc", "controls/tree/tree_view_controller.cc",
"controls/tree/tree_view_drawing_provider.cc", "controls/tree/tree_view_drawing_provider.cc",
"controls/views_text_services_context_menu.cc", "controls/views_text_services_context_menu.cc",
"controls/views_text_services_context_menu_base.cc",
"controls/views_text_services_context_menu_base.h",
"controls/views_text_services_context_menu_mac.mm", "controls/views_text_services_context_menu_mac.mm",
"debug_utils.cc", "debug_utils.cc",
"drag_utils.cc", "drag_utils.cc",
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/ime/input_method_base.h" #include "ui/base/ime/input_method_base.h"
#include "ui/base/ime/input_method_delegate.h" #include "ui/base/ime/input_method_delegate.h"
#include "ui/base/ime/input_method_factory.h" #include "ui/base/ime/input_method_factory.h"
...@@ -704,9 +705,11 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { ...@@ -704,9 +705,11 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController {
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* LOOK UP */)); EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* LOOK UP */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */)); EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
} }
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* EMOJI */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
#endif #endif
if (ui::IsEmojiPanelSupported()) {
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* EMOJI */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
}
EXPECT_EQ(can_undo, menu->IsEnabledAt(menu_index++ /* UNDO */)); EXPECT_EQ(can_undo, menu->IsEnabledAt(menu_index++ /* UNDO */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */)); EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
...@@ -3432,6 +3435,69 @@ TEST_F(TextfieldTest, SendingDeletePreservesShiftFlag) { ...@@ -3432,6 +3435,69 @@ TEST_F(TextfieldTest, SendingDeletePreservesShiftFlag) {
EXPECT_EQ(ui::EF_SHIFT_DOWN, textfield_->event_flags()); EXPECT_EQ(ui::EF_SHIFT_DOWN, textfield_->event_flags());
} }
TEST_F(TextfieldTest, EmojiItem_EmptyField) {
InitTextfield();
EXPECT_TRUE(textfield_->context_menu_controller());
// Enable the emoji feature.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kEnableEmojiContextMenu);
// A normal empty field may show the Emoji option (if supported).
ui::MenuModel* context_menu = GetContextMenuModel();
EXPECT_TRUE(context_menu);
EXPECT_GT(context_menu->GetItemCount(), 0);
// Not all OS/versions support the emoji menu.
EXPECT_EQ(ui::IsEmojiPanelSupported(),
context_menu->GetLabelAt(0) ==
l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_EMOJI));
}
TEST_F(TextfieldTest, EmojiItem_ReadonlyField) {
InitTextfield();
EXPECT_TRUE(textfield_->context_menu_controller());
// Enable the emoji feature.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kEnableEmojiContextMenu);
textfield_->SetReadOnly(true);
// In no case is the emoji option showing on a read-only field.
ui::MenuModel* context_menu = GetContextMenuModel();
EXPECT_TRUE(context_menu);
EXPECT_GT(context_menu->GetItemCount(), 0);
EXPECT_NE(context_menu->GetLabelAt(0),
l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_EMOJI));
}
TEST_F(TextfieldTest, EmojiItem_FieldWithText) {
InitTextfield();
EXPECT_TRUE(textfield_->context_menu_controller());
#if defined(OS_MACOSX)
// On Mac, when there is text, the "Look up" item (+ separator) takes the top
// position, and emoji comes after.
constexpr int kExpectedEmojiIndex = 2;
#else
constexpr int kExpectedEmojiIndex = 0;
#endif
// Enable the emoji feature.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kEnableEmojiContextMenu);
// A field with text may still show the Emoji option (if supported).
textfield_->SetText(base::ASCIIToUTF16("some text"));
textfield_->SelectAll(false);
ui::MenuModel* context_menu = GetContextMenuModel();
EXPECT_TRUE(context_menu);
EXPECT_GT(context_menu->GetItemCount(), 0);
// Not all OS/versions support the emoji menu.
EXPECT_EQ(ui::IsEmojiPanelSupported(),
context_menu->GetLabelAt(kExpectedEmojiIndex) ==
l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_EMOJI));
}
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
// Tests to see if the BiDi submenu items are updated correctly when the // Tests to see if the BiDi submenu items are updated correctly when the
// textfield's text direction is changed. // textfield's text direction is changed.
...@@ -3470,6 +3536,10 @@ TEST_F(TextfieldTest, LookUpItemUpdate) { ...@@ -3470,6 +3536,10 @@ TEST_F(TextfieldTest, LookUpItemUpdate) {
InitTextfield(); InitTextfield();
EXPECT_TRUE(textfield_->context_menu_controller()); EXPECT_TRUE(textfield_->context_menu_controller());
// Make sure the Emoji feature is disabled for this test.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kEnableEmojiContextMenu);
const base::string16 kTextOne = ASCIIToUTF16("crake"); const base::string16 kTextOne = ASCIIToUTF16("crake");
textfield_->SetText(kTextOne); textfield_->SetText(kTextOne);
textfield_->SelectAll(false); textfield_->SelectAll(false);
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
#include "ui/views/controls/views_text_services_context_menu.h" #include "ui/views/controls/views_text_services_context_menu.h"
#include <memory>
#include "base/logging.h" #include "base/logging.h"
#include "ui/views/controls/views_text_services_context_menu_base.h"
namespace views { namespace views {
...@@ -12,7 +15,7 @@ namespace views { ...@@ -12,7 +15,7 @@ namespace views {
std::unique_ptr<ViewsTextServicesContextMenu> std::unique_ptr<ViewsTextServicesContextMenu>
ViewsTextServicesContextMenu::Create(ui::SimpleMenuModel* menu, ViewsTextServicesContextMenu::Create(ui::SimpleMenuModel* menu,
Textfield* client) { Textfield* client) {
return nullptr; return std::make_unique<ViewsTextServicesContextMenuBase>(menu, client);
} }
bool ViewsTextServicesContextMenu::IsTextDirectionCheckedForTesting( bool ViewsTextServicesContextMenu::IsTextDirectionCheckedForTesting(
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/controls/views_text_services_context_menu_base.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/resources/grit/ui_resources.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/controls/textfield/textfield.h"
namespace views {
ViewsTextServicesContextMenuBase::ViewsTextServicesContextMenuBase(
ui::SimpleMenuModel* menu,
Textfield* client)
: client_(client) {
DCHECK(client);
DCHECK(menu);
// Not inserted on read-only fields or if the OS/version doesn't support it.
if (!client_->read_only() && ui::IsEmojiPanelSupported()) {
menu->InsertSeparatorAt(0, ui::NORMAL_SEPARATOR);
menu->InsertItemWithStringIdAt(0, IDS_CONTENT_CONTEXT_EMOJI,
IDS_CONTENT_CONTEXT_EMOJI);
}
}
ViewsTextServicesContextMenuBase::~ViewsTextServicesContextMenuBase() {}
bool ViewsTextServicesContextMenuBase::SupportsCommand(int command_id) const {
return command_id == IDS_CONTENT_CONTEXT_EMOJI;
}
bool ViewsTextServicesContextMenuBase::IsCommandIdChecked(
int command_id) const {
return false;
}
bool ViewsTextServicesContextMenuBase::IsCommandIdEnabled(
int command_id) const {
if (command_id == IDS_CONTENT_CONTEXT_EMOJI)
return true;
return false;
}
void ViewsTextServicesContextMenuBase::ExecuteCommand(int command_id) {
if (command_id == IDS_CONTENT_CONTEXT_EMOJI)
ui::ShowEmojiPanel();
}
} // namespace views
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_VIEWS_CONTROLS_VIEWS_TEXT_SERVICES_CONTEXT_MENU_BASE_H_
#define UI_VIEWS_CONTROLS_VIEWS_TEXT_SERVICES_CONTEXT_MENU_BASE_H_
#include "base/macros.h"
#include "ui/views/controls/views_text_services_context_menu.h"
namespace views {
// This base class is used to add and handle text service items in the text
// context menu. Specific platforms may subclass and add additional items.
class ViewsTextServicesContextMenuBase : public ViewsTextServicesContextMenu {
public:
ViewsTextServicesContextMenuBase(ui::SimpleMenuModel* menu,
Textfield* client);
~ViewsTextServicesContextMenuBase() override;
// Returns true if the given |command_id| is handled by the menu.
bool SupportsCommand(int command_id) const override;
// Methods associated with SimpleMenuModel::Delegate.
bool IsCommandIdChecked(int command_id) const override;
bool IsCommandIdEnabled(int command_id) const override;
void ExecuteCommand(int command_id) override;
protected:
Textfield* client() const { return client_; }
private:
// The view associated with the menu. Weak. Owns |this|.
Textfield* client_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(ViewsTextServicesContextMenuBase);
};
} // namespace views
#endif // UI_VIEWS_CONTROLS_VIEWS_TEXT_SERVICES_CONTEXT_MENU_BASE_H_
...@@ -6,19 +6,16 @@ ...@@ -6,19 +6,16 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include "base/feature_list.h"
#include "ui/base/cocoa/text_services_context_menu.h" #include "ui/base/cocoa/text_services_context_menu.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/simple_menu_model.h" #include "ui/base/models/simple_menu_model.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/decorated_text.h" #include "ui/gfx/decorated_text.h"
#import "ui/gfx/decorated_text_mac.h" #import "ui/gfx/decorated_text_mac.h"
#include "ui/resources/grit/ui_resources.h" #include "ui/resources/grit/ui_resources.h"
#include "ui/strings/grit/ui_strings.h" #include "ui/strings/grit/ui_strings.h"
#include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield.h"
#include "ui/views/view.h" #include "ui/views/controls/views_text_services_context_menu_base.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
namespace views { namespace views {
...@@ -29,28 +26,21 @@ namespace { ...@@ -29,28 +26,21 @@ namespace {
// text service items in the context menu. The items include Speech, Look Up // text service items in the context menu. The items include Speech, Look Up
// and BiDi. // and BiDi.
class ViewsTextServicesContextMenuMac class ViewsTextServicesContextMenuMac
: public ViewsTextServicesContextMenu, : public ViewsTextServicesContextMenuBase,
public ui::TextServicesContextMenu::Delegate { public ui::TextServicesContextMenu::Delegate {
public: public:
ViewsTextServicesContextMenuMac(ui::SimpleMenuModel* menu, Textfield* client) ViewsTextServicesContextMenuMac(ui::SimpleMenuModel* menu, Textfield* client)
: text_services_menu_(this), client_(client) { : ViewsTextServicesContextMenuBase(menu, client),
// The index to use when inserting items into the menu. text_services_menu_(this) {
int index = 0; // Insert the "Look up" item in the first position.
base::string16 text = GetSelectedText(); base::string16 text = GetSelectedText();
if (!text.empty()) { if (!text.empty()) {
menu->InsertSeparatorAt(0, ui::NORMAL_SEPARATOR);
menu->InsertItemAt( menu->InsertItemAt(
index++, IDS_CONTENT_CONTEXT_LOOK_UP, 0, IDS_CONTENT_CONTEXT_LOOK_UP,
l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, text)); l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, text));
menu->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
}
// TODO(crbug.com/827404): Move this to the cross-platform context menu
// code.
if (ui::IsEmojiPanelSupported()) {
menu->InsertItemWithStringIdAt(index++, IDS_CONTENT_CONTEXT_EMOJI,
IDS_CONTENT_CONTEXT_EMOJI);
menu->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
} }
text_services_menu_.AppendToContextMenu(menu); text_services_menu_.AppendToContextMenu(menu);
text_services_menu_.AppendEditableItems(menu); text_services_menu_.AppendEditableItems(menu);
} }
...@@ -60,13 +50,8 @@ class ViewsTextServicesContextMenuMac ...@@ -60,13 +50,8 @@ class ViewsTextServicesContextMenuMac
// ViewsTextServicesContextMenu: // ViewsTextServicesContextMenu:
bool SupportsCommand(int command_id) const override { bool SupportsCommand(int command_id) const override {
return text_services_menu_.SupportsCommand(command_id) || return text_services_menu_.SupportsCommand(command_id) ||
command_id == IDS_CONTENT_CONTEXT_EMOJI || command_id == IDS_CONTENT_CONTEXT_LOOK_UP ||
command_id == IDS_CONTENT_CONTEXT_LOOK_UP; ViewsTextServicesContextMenuBase::SupportsCommand(command_id);
}
bool IsCommandIdChecked(int command_id) const override {
DCHECK_EQ(IDS_CONTENT_CONTEXT_LOOK_UP, command_id);
return false;
} }
bool IsCommandIdEnabled(int command_id) const override { bool IsCommandIdEnabled(int command_id) const override {
...@@ -74,35 +59,32 @@ class ViewsTextServicesContextMenuMac ...@@ -74,35 +59,32 @@ class ViewsTextServicesContextMenuMac
return text_services_menu_.IsCommandIdEnabled(command_id); return text_services_menu_.IsCommandIdEnabled(command_id);
switch (command_id) { switch (command_id) {
case IDS_CONTENT_CONTEXT_EMOJI:
return true;
case IDS_CONTENT_CONTEXT_LOOK_UP: case IDS_CONTENT_CONTEXT_LOOK_UP:
return true; return true;
default: default:
return false; return ViewsTextServicesContextMenuBase::IsCommandIdEnabled(command_id);
} }
} }
void ExecuteCommand(int command_id) override { void ExecuteCommand(int command_id) override {
switch (command_id) { switch (command_id) {
case IDS_CONTENT_CONTEXT_EMOJI:
ui::ShowEmojiPanel();
break;
case IDS_CONTENT_CONTEXT_LOOK_UP: case IDS_CONTENT_CONTEXT_LOOK_UP:
LookUpInDictionary(); LookUpInDictionary();
break; break;
default:
ViewsTextServicesContextMenuBase::ExecuteCommand(command_id);
break;
} }
} }
// TextServicesContextMenu::Delegate: // TextServicesContextMenu::Delegate:
base::string16 GetSelectedText() const override { base::string16 GetSelectedText() const override {
if (client_->GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD) if (client()->GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD)
return base::string16(); return base::string16();
return client_->GetSelectedText(); return client()->GetSelectedText();
} }
bool IsTextDirectionEnabled( bool IsTextDirectionEnabled(
...@@ -113,7 +95,7 @@ class ViewsTextServicesContextMenuMac ...@@ -113,7 +95,7 @@ class ViewsTextServicesContextMenuMac
bool IsTextDirectionChecked( bool IsTextDirectionChecked(
base::i18n::TextDirection direction) const override { base::i18n::TextDirection direction) const override {
return direction != base::i18n::UNKNOWN_DIRECTION && return direction != base::i18n::UNKNOWN_DIRECTION &&
client_->GetTextDirection() == direction; client()->GetTextDirection() == direction;
} }
void UpdateTextDirection(base::i18n::TextDirection direction) override { void UpdateTextDirection(base::i18n::TextDirection direction) override {
...@@ -122,7 +104,7 @@ class ViewsTextServicesContextMenuMac ...@@ -122,7 +104,7 @@ class ViewsTextServicesContextMenuMac
base::i18n::TextDirection text_direction = base::i18n::TextDirection text_direction =
direction == base::i18n::LEFT_TO_RIGHT ? base::i18n::LEFT_TO_RIGHT direction == base::i18n::LEFT_TO_RIGHT ? base::i18n::LEFT_TO_RIGHT
: base::i18n::RIGHT_TO_LEFT; : base::i18n::RIGHT_TO_LEFT;
client_->ChangeTextDirectionAndLayoutAlignment(text_direction); client()->ChangeTextDirectionAndLayoutAlignment(text_direction);
} }
private: private:
...@@ -130,10 +112,10 @@ class ViewsTextServicesContextMenuMac ...@@ -130,10 +112,10 @@ class ViewsTextServicesContextMenuMac
void LookUpInDictionary() { void LookUpInDictionary() {
gfx::Point baseline_point; gfx::Point baseline_point;
gfx::DecoratedText text; gfx::DecoratedText text;
if (client_->GetWordLookupDataFromSelection(&text, &baseline_point)) { if (client()->GetWordLookupDataFromSelection(&text, &baseline_point)) {
Widget* widget = client_->GetWidget(); Widget* widget = client()->GetWidget();
gfx::NativeView view = widget->GetNativeView(); gfx::NativeView view = widget->GetNativeView();
views::View::ConvertPointToTarget(client_, widget->GetRootView(), views::View::ConvertPointToTarget(client(), widget->GetRootView(),
&baseline_point); &baseline_point);
NSPoint lookup_point = NSMakePoint( NSPoint lookup_point = NSMakePoint(
...@@ -147,9 +129,6 @@ class ViewsTextServicesContextMenuMac ...@@ -147,9 +129,6 @@ class ViewsTextServicesContextMenuMac
// Appends and handles the text service menu. // Appends and handles the text service menu.
ui::TextServicesContextMenu text_services_menu_; ui::TextServicesContextMenu text_services_menu_;
// The view associated with the menu. Weak. Owns |this|.
Textfield* client_;
DISALLOW_COPY_AND_ASSIGN(ViewsTextServicesContextMenuMac); DISALLOW_COPY_AND_ASSIGN(ViewsTextServicesContextMenuMac);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment