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") {
"controls/tree/tree_view_controller.cc",
"controls/tree/tree_view_drawing_provider.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",
"debug_utils.cc",
"drag_utils.cc",
......
......@@ -26,6 +26,7 @@
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.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_delegate.h"
#include "ui/base/ime/input_method_factory.h"
......@@ -704,9 +705,11 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController {
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* LOOK UP */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
}
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* EMOJI */));
EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
#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_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */));
......@@ -3432,6 +3435,69 @@ TEST_F(TextfieldTest, SendingDeletePreservesShiftFlag) {
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)
// Tests to see if the BiDi submenu items are updated correctly when the
// textfield's text direction is changed.
......@@ -3470,6 +3536,10 @@ TEST_F(TextfieldTest, LookUpItemUpdate) {
InitTextfield();
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");
textfield_->SetText(kTextOne);
textfield_->SelectAll(false);
......
......@@ -4,7 +4,10 @@
#include "ui/views/controls/views_text_services_context_menu.h"
#include <memory>
#include "base/logging.h"
#include "ui/views/controls/views_text_services_context_menu_base.h"
namespace views {
......@@ -12,7 +15,7 @@ namespace views {
std::unique_ptr<ViewsTextServicesContextMenu>
ViewsTextServicesContextMenu::Create(ui::SimpleMenuModel* menu,
Textfield* client) {
return nullptr;
return std::make_unique<ViewsTextServicesContextMenuBase>(menu, client);
}
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 @@
#import <Cocoa/Cocoa.h>
#include "base/feature_list.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/models/simple_menu_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/decorated_text.h"
#import "ui/gfx/decorated_text_mac.h"
#include "ui/resources/grit/ui_resources.h"
#include "ui/strings/grit/ui_strings.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"
namespace views {
......@@ -29,28 +26,21 @@ namespace {
// text service items in the context menu. The items include Speech, Look Up
// and BiDi.
class ViewsTextServicesContextMenuMac
: public ViewsTextServicesContextMenu,
: public ViewsTextServicesContextMenuBase,
public ui::TextServicesContextMenu::Delegate {
public:
ViewsTextServicesContextMenuMac(ui::SimpleMenuModel* menu, Textfield* client)
: text_services_menu_(this), client_(client) {
// The index to use when inserting items into the menu.
int index = 0;
: ViewsTextServicesContextMenuBase(menu, client),
text_services_menu_(this) {
// Insert the "Look up" item in the first position.
base::string16 text = GetSelectedText();
if (!text.empty()) {
menu->InsertSeparatorAt(0, ui::NORMAL_SEPARATOR);
menu->InsertItemAt(
index++, IDS_CONTENT_CONTEXT_LOOK_UP,
0, IDS_CONTENT_CONTEXT_LOOK_UP,
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_.AppendEditableItems(menu);
}
......@@ -60,13 +50,8 @@ class ViewsTextServicesContextMenuMac
// ViewsTextServicesContextMenu:
bool SupportsCommand(int command_id) const override {
return text_services_menu_.SupportsCommand(command_id) ||
command_id == IDS_CONTENT_CONTEXT_EMOJI ||
command_id == IDS_CONTENT_CONTEXT_LOOK_UP;
}
bool IsCommandIdChecked(int command_id) const override {
DCHECK_EQ(IDS_CONTENT_CONTEXT_LOOK_UP, command_id);
return false;
command_id == IDS_CONTENT_CONTEXT_LOOK_UP ||
ViewsTextServicesContextMenuBase::SupportsCommand(command_id);
}
bool IsCommandIdEnabled(int command_id) const override {
......@@ -74,35 +59,32 @@ class ViewsTextServicesContextMenuMac
return text_services_menu_.IsCommandIdEnabled(command_id);
switch (command_id) {
case IDS_CONTENT_CONTEXT_EMOJI:
return true;
case IDS_CONTENT_CONTEXT_LOOK_UP:
return true;
default:
return false;
return ViewsTextServicesContextMenuBase::IsCommandIdEnabled(command_id);
}
}
void ExecuteCommand(int command_id) override {
switch (command_id) {
case IDS_CONTENT_CONTEXT_EMOJI:
ui::ShowEmojiPanel();
break;
case IDS_CONTENT_CONTEXT_LOOK_UP:
LookUpInDictionary();
break;
default:
ViewsTextServicesContextMenuBase::ExecuteCommand(command_id);
break;
}
}
// TextServicesContextMenu::Delegate:
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 client_->GetSelectedText();
return client()->GetSelectedText();
}
bool IsTextDirectionEnabled(
......@@ -113,7 +95,7 @@ class ViewsTextServicesContextMenuMac
bool IsTextDirectionChecked(
base::i18n::TextDirection direction) const override {
return direction != base::i18n::UNKNOWN_DIRECTION &&
client_->GetTextDirection() == direction;
client()->GetTextDirection() == direction;
}
void UpdateTextDirection(base::i18n::TextDirection direction) override {
......@@ -122,7 +104,7 @@ class ViewsTextServicesContextMenuMac
base::i18n::TextDirection text_direction =
direction == base::i18n::LEFT_TO_RIGHT ? base::i18n::LEFT_TO_RIGHT
: base::i18n::RIGHT_TO_LEFT;
client_->ChangeTextDirectionAndLayoutAlignment(text_direction);
client()->ChangeTextDirectionAndLayoutAlignment(text_direction);
}
private:
......@@ -130,10 +112,10 @@ class ViewsTextServicesContextMenuMac
void LookUpInDictionary() {
gfx::Point baseline_point;
gfx::DecoratedText text;
if (client_->GetWordLookupDataFromSelection(&text, &baseline_point)) {
Widget* widget = client_->GetWidget();
if (client()->GetWordLookupDataFromSelection(&text, &baseline_point)) {
Widget* widget = client()->GetWidget();
gfx::NativeView view = widget->GetNativeView();
views::View::ConvertPointToTarget(client_, widget->GetRootView(),
views::View::ConvertPointToTarget(client(), widget->GetRootView(),
&baseline_point);
NSPoint lookup_point = NSMakePoint(
......@@ -147,9 +129,6 @@ class ViewsTextServicesContextMenuMac
// Appends and handles the text service menu.
ui::TextServicesContextMenu text_services_menu_;
// The view associated with the menu. Weak. Owns |this|.
Textfield* client_;
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