Commit 4f024812 authored by Andrew Xu's avatar Andrew Xu Committed by Chromium LUCI CQ

[Multipaste] Add the multipaste option to the textfield context menu

This CL adds the multipaste option to the textfield's context menu
under CrOS. In detail, this CL does the following jobs:

(1) It creates a class called ViewsTextServicesContextMenuChromeos to
support the text context menu under CrOS.

(2) Because ViewsTextServicesContextMenuChromeos lives under ui/views,
it cannot access the Ash side functions such as those related to the
multipaste menu directly. To solve such a problem, a new class called
ViewsTextServicesContextMenuImpl is created under ash/public/cpp
and the function to build this new class is injected into the ui/views
side from the ash side.

Bug: 1157669
Change-Id: Ia5a26ab3e89637c4e55a4d8a86235ae2c21d3075
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2595203
Commit-Queue: Andrew Xu <andrewxu@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841345}
parent cb7da9e4
...@@ -287,6 +287,8 @@ component("cpp") { ...@@ -287,6 +287,8 @@ component("cpp") {
"update_types.h", "update_types.h",
"view_shadow.cc", "view_shadow.cc",
"view_shadow.h", "view_shadow.h",
"views_text_services_context_menu_impl.cc",
"views_text_services_context_menu_impl.h",
"vm_camera_mic_constants.cc", "vm_camera_mic_constants.cc",
"vm_camera_mic_constants.h", "vm_camera_mic_constants.h",
"wallpaper_controller.cc", "wallpaper_controller.cc",
......
// Copyright 2021 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 "ash/public/cpp/views_text_services_context_menu_impl.h"
#include "ash/public/cpp/clipboard_history_controller.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/pointer/touch_editing_controller.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/controls/textfield/textfield.h"
namespace ash {
ViewsTextServicesContextMenuImpl::ViewsTextServicesContextMenuImpl(
ui::SimpleMenuModel* menu,
views::Textfield* client)
: views::ViewsTextServicesContextMenuBase(menu, client) {
if (chromeos::features::IsClipboardHistoryEnabled())
AddClipboardHistoryMenuOption(menu);
}
ViewsTextServicesContextMenuImpl::~ViewsTextServicesContextMenuImpl() = default;
bool ViewsTextServicesContextMenuImpl::GetAcceleratorForCommandId(
int command_id,
ui::Accelerator* accelerator) const {
if (command_id == IDS_APP_SHOW_CLIPBOARD_HISTORY) {
*accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_COMMAND_DOWN);
return true;
}
return ViewsTextServicesContextMenuBase::GetAcceleratorForCommandId(
command_id, accelerator);
}
bool ViewsTextServicesContextMenuImpl::IsCommandIdChecked(
int command_id) const {
if (command_id == IDS_APP_SHOW_CLIPBOARD_HISTORY)
return true;
return ViewsTextServicesContextMenuBase::IsCommandIdChecked(command_id);
}
bool ViewsTextServicesContextMenuImpl::IsCommandIdEnabled(
int command_id) const {
if (command_id == IDS_APP_SHOW_CLIPBOARD_HISTORY)
return ClipboardHistoryController::Get()->CanShowMenu();
return ViewsTextServicesContextMenuBase::IsCommandIdEnabled(command_id);
}
void ViewsTextServicesContextMenuImpl::ExecuteCommand(int command_id,
int event_flags) {
if (command_id == IDS_APP_SHOW_CLIPBOARD_HISTORY) {
auto* clipboard_history_controller = ClipboardHistoryController::Get();
// Calculate the menu source type from `event_flags`.
ui::MenuSourceType source_type;
if (event_flags & ui::EF_LEFT_MOUSE_BUTTON)
source_type = ui::MENU_SOURCE_MOUSE;
else if (event_flags & ui::EF_FROM_TOUCH)
source_type = ui::MENU_SOURCE_TOUCH;
else
source_type = ui::MENU_SOURCE_KEYBOARD;
clipboard_history_controller->ShowMenu(client()->GetCaretBounds(),
source_type);
return;
}
ViewsTextServicesContextMenuBase::ExecuteCommand(command_id, event_flags);
}
bool ViewsTextServicesContextMenuImpl::SupportsCommand(int command_id) const {
if (command_id == IDS_APP_SHOW_CLIPBOARD_HISTORY)
return true;
return ViewsTextServicesContextMenuBase::SupportsCommand(command_id);
}
void ViewsTextServicesContextMenuImpl::AddClipboardHistoryMenuOption(
ui::SimpleMenuModel* menu) {
const int index_of_paste =
menu->GetIndexOfCommandId(ui::TouchEditable::kPaste);
// Only add the clipboard history menu option when having the menu option
// for paste.
if (index_of_paste == -1)
return;
const int target_index = index_of_paste + 1;
menu->InsertItemAt(target_index, IDS_APP_SHOW_CLIPBOARD_HISTORY,
l10n_util::GetStringUTF16(IDS_APP_SHOW_CLIPBOARD_HISTORY));
}
} // namespace ash
// Copyright 2021 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 ASH_PUBLIC_CPP_VIEWS_TEXT_SERVICES_CONTEXT_MENU_IMPL_H_
#define ASH_PUBLIC_CPP_VIEWS_TEXT_SERVICES_CONTEXT_MENU_IMPL_H_
#include "ash/public/cpp/ash_public_export.h"
#include "ui/views/controls/views_text_services_context_menu_base.h"
namespace views {
class Textfield;
}
namespace ash {
// This class supports the text context menu with the exclusive functions under
// the CrOS environment.
class ASH_PUBLIC_EXPORT ViewsTextServicesContextMenuImpl
: public views::ViewsTextServicesContextMenuBase {
public:
ViewsTextServicesContextMenuImpl(ui::SimpleMenuModel* menu,
views::Textfield* client);
ViewsTextServicesContextMenuImpl(const ViewsTextServicesContextMenuImpl&) =
delete;
ViewsTextServicesContextMenuImpl& operator=(
const ViewsTextServicesContextMenuImpl&) = delete;
~ViewsTextServicesContextMenuImpl() override;
// ViewsTextServicesContextMenuBase:
bool GetAcceleratorForCommandId(int command_id,
ui::Accelerator* accelerator) const override;
bool IsCommandIdChecked(int command_id) const override;
bool IsCommandIdEnabled(int command_id) const override;
void ExecuteCommand(int command_id, int event_flags) override;
bool SupportsCommand(int command_id) const override;
private:
// Adds the menu option which shows the clipboard history menu after
// activation.
void AddClipboardHistoryMenuOption(ui::SimpleMenuModel* menu);
};
} // namespace ash
#endif // ASH_PUBLIC_CPP_VIEWS_TEXT_SERVICES_CONTEXT_MENU_IMPL_H_
...@@ -84,6 +84,7 @@ ...@@ -84,6 +84,7 @@
#include "ash/public/cpp/shelf_config.h" #include "ash/public/cpp/shelf_config.h"
#include "ash/public/cpp/shelf_model.h" #include "ash/public/cpp/shelf_model.h"
#include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/views_text_services_context_menu_impl.h"
#include "ash/quick_answers/quick_answers_controller_impl.h" #include "ash/quick_answers/quick_answers_controller_impl.h"
#include "ash/root_window_controller.h" #include "ash/root_window_controller.h"
#include "ash/screenshot_delegate.h" #include "ash/screenshot_delegate.h"
...@@ -210,6 +211,7 @@ ...@@ -210,6 +211,7 @@
#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia.h"
#include "ui/message_center/message_center.h" #include "ui/message_center/message_center.h"
#include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/ozone_platform.h"
#include "ui/views/controls/views_text_services_context_menu_chromeos.h"
#include "ui/views/corewm/tooltip_aura.h" #include "ui/views/corewm/tooltip_aura.h"
#include "ui/views/corewm/tooltip_controller.h" #include "ui/views/corewm/tooltip_controller.h"
#include "ui/views/focus/focus_manager_factory.h" #include "ui/views/focus/focus_manager_factory.h"
...@@ -640,6 +642,10 @@ Shell::~Shell() { ...@@ -640,6 +642,10 @@ Shell::~Shell() {
RemovePreTargetHandler(modality_filter_.get()); RemovePreTargetHandler(modality_filter_.get());
RemovePreTargetHandler(tooltip_controller_.get()); RemovePreTargetHandler(tooltip_controller_.get());
// Resets the text context menu implementation factory.
views::ViewsTextServicesContextMenuChromeos::SetImplFactory(
base::NullCallback());
event_rewriter_controller_.reset(); event_rewriter_controller_.reset();
screen_orientation_controller_.reset(); screen_orientation_controller_.reset();
...@@ -1253,6 +1259,16 @@ void Shell::Init( ...@@ -1253,6 +1259,16 @@ void Shell::Init(
std::make_unique<DisplayAlignmentController>(); std::make_unique<DisplayAlignmentController>();
} }
// Injects the factory which fulfills the implementation of the text context
// menu exclusive to CrOS.
views::ViewsTextServicesContextMenuChromeos::SetImplFactory(
base::BindRepeating(
[](ui::SimpleMenuModel* menu_model, views::Textfield* textfield)
-> std::unique_ptr<views::ViewsTextServicesContextMenu> {
return std::make_unique<ViewsTextServicesContextMenuImpl>(
menu_model, textfield);
}));
for (auto& observer : shell_observers_) for (auto& observer : shell_observers_)
observer.OnShellInitialized(); observer.OnShellInitialized();
......
...@@ -620,6 +620,11 @@ need to be translated for each locale.--> ...@@ -620,6 +620,11 @@ need to be translated for each locale.-->
Emoji &amp;&amp; Symbols Emoji &amp;&amp; Symbols
</message> </message>
</if> </if>
<if expr="chromeos">
<message name="IDS_APP_SHOW_CLIPBOARD_HISTORY" desc="The context menu item to show the clipboard history menu.">
Clipboard
</message>
</if>
<!-- Generic terms --> <!-- Generic terms -->
<message name="IDS_APP_OK" desc="Used for Ok on buttons"> <message name="IDS_APP_OK" desc="Used for Ok on buttons">
......
5ab94be1bbbebc839acf4bb0b07c6b5f8ce60b99
\ No newline at end of file
...@@ -180,6 +180,7 @@ component("views") { ...@@ -180,6 +180,7 @@ component("views") {
"controls/tree/tree_view_controller.h", "controls/tree/tree_view_controller.h",
"controls/tree/tree_view_drawing_provider.h", "controls/tree/tree_view_drawing_provider.h",
"controls/views_text_services_context_menu.h", "controls/views_text_services_context_menu.h",
"controls/views_text_services_context_menu_base.h",
"debug_utils.h", "debug_utils.h",
"drag_controller.h", "drag_controller.h",
"drag_utils.h", "drag_utils.h",
...@@ -386,7 +387,6 @@ component("views") { ...@@ -386,7 +387,6 @@ 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_base.cc", "controls/views_text_services_context_menu_base.cc",
"controls/views_text_services_context_menu_base.h",
"debug_utils.cc", "debug_utils.cc",
"drag_utils.cc", "drag_utils.cc",
"focus/external_focus_tracker.cc", "focus/external_focus_tracker.cc",
...@@ -562,6 +562,10 @@ component("views") { ...@@ -562,6 +562,10 @@ component("views") {
if (is_chromeos_ash || is_chromeos_lacros) { if (is_chromeos_ash || is_chromeos_lacros) {
sources += [ "controls/menu/menu_config_chromeos.cc" ] sources += [ "controls/menu/menu_config_chromeos.cc" ]
if (!is_chromeos_lacros) {
public += [ "controls/views_text_services_context_menu_chromeos.h" ]
sources += [ "controls/views_text_services_context_menu_chromeos.cc" ]
}
} }
if (is_mac) { if (is_mac) {
......
...@@ -85,7 +85,7 @@ bool ViewsTextServicesContextMenuBase::SupportsCommand(int command_id) const { ...@@ -85,7 +85,7 @@ bool ViewsTextServicesContextMenuBase::SupportsCommand(int command_id) const {
return command_id == IDS_CONTENT_CONTEXT_EMOJI; return command_id == IDS_CONTENT_CONTEXT_EMOJI;
} }
#if !defined(OS_APPLE) #if !defined(OS_APPLE) && !BUILDFLAG(IS_CHROMEOS_ASH)
// static // static
std::unique_ptr<ViewsTextServicesContextMenu> std::unique_ptr<ViewsTextServicesContextMenu>
ViewsTextServicesContextMenu::Create(ui::SimpleMenuModel* menu, ViewsTextServicesContextMenu::Create(ui::SimpleMenuModel* menu,
......
...@@ -7,12 +7,14 @@ ...@@ -7,12 +7,14 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "ui/views/controls/views_text_services_context_menu.h" #include "ui/views/controls/views_text_services_context_menu.h"
#include "ui/views/views_export.h"
namespace views { namespace views {
// This base class is used to add and handle text service items in the textfield // This base class is used to add and handle text service items in the textfield
// context menu. Specific platforms may subclass and add additional items. // context menu. Specific platforms may subclass and add additional items.
class ViewsTextServicesContextMenuBase : public ViewsTextServicesContextMenu { class VIEWS_EXPORT ViewsTextServicesContextMenuBase
: public ViewsTextServicesContextMenu {
public: public:
ViewsTextServicesContextMenuBase(ui::SimpleMenuModel* menu, ViewsTextServicesContextMenuBase(ui::SimpleMenuModel* menu,
Textfield* client); Textfield* client);
...@@ -31,7 +33,7 @@ class ViewsTextServicesContextMenuBase : public ViewsTextServicesContextMenu { ...@@ -31,7 +33,7 @@ class ViewsTextServicesContextMenuBase : public ViewsTextServicesContextMenu {
bool SupportsCommand(int command_id) const override; bool SupportsCommand(int command_id) const override;
protected: protected:
#if defined(OS_APPLE) #if defined(OS_APPLE) || defined(OS_CHROMEOS)
Textfield* client() { return client_; } Textfield* client() { return client_; }
const Textfield* client() const { return client_; } const Textfield* client() const { return client_; }
#endif #endif
......
// Copyright 2021 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_chromeos.h"
#include <utility>
#include "base/no_destructor.h"
#include "ui/views/controls/views_text_services_context_menu_base.h"
namespace views {
namespace {
using ImplFactory = ViewsTextServicesContextMenuChromeos::ImplFactory;
ImplFactory& GetImplFactory() {
static base::NoDestructor<ImplFactory> impl_factory;
return *impl_factory;
}
} // namespace
// static
void ViewsTextServicesContextMenuChromeos::SetImplFactory(
ImplFactory impl_factory) {
GetImplFactory() = std::move(impl_factory);
}
ViewsTextServicesContextMenuChromeos::ViewsTextServicesContextMenuChromeos(
ui::SimpleMenuModel* menu,
Textfield* client) {
auto& impl_factory = GetImplFactory();
// In unit tests, `impl_factory` may not be set. Use
// `ViewTextServicesContextMenuBase` in that case.
if (impl_factory)
impl_ = impl_factory.Run(menu, client);
else
impl_ = std::make_unique<ViewsTextServicesContextMenuBase>(menu, client);
}
ViewsTextServicesContextMenuChromeos::~ViewsTextServicesContextMenuChromeos() =
default;
bool ViewsTextServicesContextMenuChromeos::GetAcceleratorForCommandId(
int command_id,
ui::Accelerator* accelerator) const {
return impl_->GetAcceleratorForCommandId(command_id, accelerator);
}
bool ViewsTextServicesContextMenuChromeos::IsCommandIdChecked(
int command_id) const {
return impl_->IsCommandIdChecked(command_id);
}
bool ViewsTextServicesContextMenuChromeos::IsCommandIdEnabled(
int command_id) const {
return impl_->IsCommandIdEnabled(command_id);
}
void ViewsTextServicesContextMenuChromeos::ExecuteCommand(int command_id,
int event_flags) {
return impl_->ExecuteCommand(command_id, event_flags);
}
bool ViewsTextServicesContextMenuChromeos::SupportsCommand(
int command_id) const {
return impl_->IsCommandIdEnabled(command_id);
}
// static
std::unique_ptr<ViewsTextServicesContextMenu>
ViewsTextServicesContextMenu::Create(ui::SimpleMenuModel* menu,
Textfield* client) {
return std::make_unique<ViewsTextServicesContextMenuChromeos>(menu, client);
}
} // namespace views
// Copyright 2021 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_CHROMEOS_H_
#define UI_VIEWS_CONTROLS_VIEWS_TEXT_SERVICES_CONTEXT_MENU_CHROMEOS_H_
#include <memory>
#include "ui/views/controls/views_text_services_context_menu.h"
#include "ui/views/views_export.h"
namespace views {
// This class is used to add and handle text service items in the text context
// menu under the CrOS environment.
class VIEWS_EXPORT ViewsTextServicesContextMenuChromeos
: public ViewsTextServicesContextMenu {
public:
using ImplFactory = base::RepeatingCallback<std::unique_ptr<
ViewsTextServicesContextMenu>(ui::SimpleMenuModel*, Textfield*)>;
// Injects the method to construct `impl_`.
static void SetImplFactory(ImplFactory factory);
ViewsTextServicesContextMenuChromeos(ui::SimpleMenuModel* menu,
Textfield* client);
ViewsTextServicesContextMenuChromeos(
const ViewsTextServicesContextMenuChromeos&) = delete;
ViewsTextServicesContextMenuChromeos& operator=(
const ViewsTextServicesContextMenuChromeos&) = delete;
~ViewsTextServicesContextMenuChromeos() override;
// ViewsTextServicesContextMenu:
bool GetAcceleratorForCommandId(int command_id,
ui::Accelerator* accelerator) const override;
bool IsCommandIdChecked(int command_id) const override;
bool IsCommandIdEnabled(int command_id) const override;
void ExecuteCommand(int command_id, int event_flags) override;
bool SupportsCommand(int command_id) const override;
private:
// CrOS functionality must be provided by the embedder, so requests are
// forwarded to this concrete object, whose construction can be controlled by
// `SetImplFactory()`.
std::unique_ptr<ViewsTextServicesContextMenu> impl_;
};
} // namespace views
#endif // UI_VIEWS_CONTROLS_VIEWS_TEXT_SERVICES_CONTEXT_MENU_CHROMEOS_H_
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