0

Reland "[GTK] Support Emacs bindings on GTK4"

This is a reland of commit 50a0f5cfa8

Original change's description:
> [GTK] Support Emacs bindings on GTK4
>
> This change replaces the old vector‐based API
> GetTextEditCommandsForEvent with a function that returns a single
> ui::TextEditCommand. INVALID_COMMAND is returned when no valid command
> applies. The command mapping no longer relies on heap-allocated data
> (vector and string), so can now be a compile time constant.
>
> The old code used a complex signal handling approach that simulated a
> keypress in a fake window and widget to observe the resulting text edit
> command. However, the only "key theme" GTK ever supported was emacs
> bindings before key themes were removed in GTK4. This CL simplifies the
> approach by directly mapping key events to edit commands. It also
> enables emacs bindings on GTK4 and (eventually, when this code is moved
> out of //ui/gtk since it no longer has any GTK dependencies) QT.
>
> Change-Id: If4856d33aa8ec45f2e397aa517032ddc22b63def
> Bug: None
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6314265
> Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
> Reviewed-by: Jonathan Ross <jonross@chromium.org>
> Reviewed-by: Peter Kasting <pkasting@chromium.org>
> Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1428340}

Bug: None
Change-Id: I8127e65b0423953904de2188886df67a5131f825
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6332668
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
Auto-Submit: Thomas Anderson <thomasanderson@chromium.org>
Reviewed-by: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1429612}
This commit is contained in:
Tom Anderson
2025-03-07 10:44:04 -08:00
committed by Chromium LUCI CQ
parent d80e3fbdbc
commit 9d6ab0ac1e
26 changed files with 193 additions and 660 deletions

@ -1197,15 +1197,9 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
~TextDeleteDelegate() override = default;
bool GetTextEditCommandsForEvent(
const ui::Event& event,
int text_flags,
std::vector<ui::TextEditCommandAuraLinux>* commands) override {
if (commands) {
commands->emplace_back(ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE,
"");
}
return true;
ui::TextEditCommand GetTextEditCommandForEvent(const ui::Event& event,
int text_flags) override {
return ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE;
}
};

@ -128,6 +128,7 @@
#endif
#if BUILDFLAG(IS_LINUX)
#include "ui/base/ime/text_edit_commands.h"
#include "ui/base/ime/text_input_flags.h"
#include "ui/linux/linux_ui.h"
#endif
@ -334,8 +335,9 @@ bool BrowserCommandController::IsReservedCommandOrKey(
// it is not reserved.
auto* linux_ui = ui::LinuxUi::instance();
if (linux_ui && event.os_event &&
linux_ui->GetTextEditCommandsForEvent(
*event.os_event, ui::TEXT_INPUT_FLAG_NONE, nullptr)) {
linux_ui->GetTextEditCommandForEvent(*event.os_event,
ui::TEXT_INPUT_FLAG_NONE) !=
ui::TextEditCommand::INVALID_COMMAND) {
return false;
}
#endif

@ -3029,21 +3029,18 @@ void RenderWidgetHostViewAura::ForwardKeyboardEventWithLatencyInfo(
#if BUILDFLAG(IS_LINUX)
auto* linux_ui = ui::LinuxUi::instance();
std::vector<ui::TextEditCommandAuraLinux> commands;
if (!event.skip_if_unhandled && linux_ui && event.os_event &&
linux_ui->GetTextEditCommandsForEvent(*event.os_event,
GetTextInputFlags(), &commands)) {
// Transform from ui/ types to content/ types.
std::vector<blink::mojom::EditCommandPtr> edit_commands;
for (std::vector<ui::TextEditCommandAuraLinux>::const_iterator it =
commands.begin(); it != commands.end(); ++it) {
edit_commands.push_back(blink::mojom::EditCommand::New(
it->GetCommandString(), it->argument()));
if (!event.skip_if_unhandled && linux_ui && event.os_event) {
const auto command = linux_ui->GetTextEditCommandForEvent(
*event.os_event, GetTextInputFlags());
if (command != ui::TextEditCommand::INVALID_COMMAND) {
// Transform from ui/ types to content/ types.
std::vector<blink::mojom::EditCommandPtr> commands;
commands.push_back(blink::mojom::EditCommand::New(
ui::TextEditCommandToString(command), ""));
target_host->ForwardKeyboardEventWithCommands(
event, latency, std::move(commands), update_event);
return;
}
target_host->ForwardKeyboardEventWithCommands(
event, latency, std::move(edit_commands), update_event);
return;
}
#endif

@ -11,8 +11,8 @@ namespace ui {
// This is sent to the renderer. Keep the string representation in sync with
// third_party/WebKit/public/platform/WebEditingCommandType.h.
std::string TextEditCommandAuraLinux::GetCommandString() const {
switch (command_) {
std::string TextEditCommandToString(ui::TextEditCommand command) {
switch (command) {
case TextEditCommand::DELETE_BACKWARD:
return "DeleteBackward";
case TextEditCommand::DELETE_FORWARD:

@ -13,25 +13,8 @@ namespace ui {
enum class TextEditCommand;
// Represents a command that performs a specific operation on text.
// Copy and assignment are explicitly allowed; these objects live in vectors.
class COMPONENT_EXPORT(UI_BASE_IME_LINUX) TextEditCommandAuraLinux {
public:
TextEditCommandAuraLinux(TextEditCommand command, const std::string& argument)
: command_(command), argument_(argument) {}
TextEditCommand command() const { return command_; }
const std::string& argument() const { return argument_; }
// We communicate these commands back to blink with a string representation.
std::string GetCommandString() const;
private:
TextEditCommand command_;
// The text for TextEditCommand::INSERT_TEXT; otherwise empty and unused.
std::string argument_;
};
COMPONENT_EXPORT(UI_BASE_IME_LINUX)
std::string TextEditCommandToString(ui::TextEditCommand command);
} // namespace ui

@ -4,374 +4,130 @@
#include "ui/gtk/gtk_key_bindings_handler.h"
#include <cstddef>
#include <string>
#include <array>
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "ui/base/glib/glib_cast.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/gtk/gtk_compat.h"
#include "ui/gtk/gtk_util.h"
using ui::TextEditCommand;
namespace gtk {
GtkKeyBindingsHandler::GtkKeyBindingsHandler()
: fake_window_(gtk_offscreen_window_new()), handler_(CreateNewHandler()) {
DCHECK(!GtkCheckVersion(4));
gtk_container_add(
GlibCast<GtkContainer>(fake_window_.get(), gtk_container_get_type()),
handler_);
namespace {
using KeyWithMods = std::pair<ui::KeyboardCode, ui::EventFlags>;
constexpr char kDesktopInterface[] = "org.gnome.desktop.interface";
constexpr char kGtkKeyTheme[] = "gtk-key-theme";
constexpr char kEmacsKeyTheme[] = "Emacs";
// This should contain at least all of the bindings in gtk/gtk-keys.css.emacs.
constexpr auto kEmacsBindings =
base::MakeFixedFlatMap<KeyWithMods, ui::TextEditCommand>({
{{ui::KeyboardCode::VKEY_BACK, ui::EF_ALT_DOWN},
ui::TextEditCommand::DELETE_WORD_BACKWARD},
{{ui::KeyboardCode::VKEY_A, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::MOVE_TO_BEGINNING_OF_LINE},
{{ui::KeyboardCode::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN},
ui::TextEditCommand::MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION},
{{ui::KeyboardCode::VKEY_B, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::MOVE_BACKWARD},
{{ui::KeyboardCode::VKEY_B, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN},
ui::TextEditCommand::MOVE_BACKWARD_AND_MODIFY_SELECTION},
{{ui::KeyboardCode::VKEY_B, ui::EF_ALT_DOWN},
ui::TextEditCommand::MOVE_WORD_LEFT},
{{ui::KeyboardCode::VKEY_B, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN},
ui::TextEditCommand::MOVE_WORD_LEFT_AND_MODIFY_SELECTION},
{{ui::KeyboardCode::VKEY_D, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::DELETE_FORWARD},
{{ui::KeyboardCode::VKEY_D, ui::EF_ALT_DOWN},
ui::TextEditCommand::DELETE_WORD_FORWARD},
{{ui::KeyboardCode::VKEY_E, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::MOVE_TO_END_OF_LINE},
{{ui::KeyboardCode::VKEY_E, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN},
ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION},
{{ui::KeyboardCode::VKEY_F, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::MOVE_FORWARD},
{{ui::KeyboardCode::VKEY_F, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN},
ui::TextEditCommand::MOVE_FORWARD_AND_MODIFY_SELECTION},
{{ui::KeyboardCode::VKEY_F, ui::EF_ALT_DOWN},
ui::TextEditCommand::MOVE_WORD_RIGHT},
{{ui::KeyboardCode::VKEY_F, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN},
ui::TextEditCommand::MOVE_WORD_RIGHT_AND_MODIFY_SELECTION},
{{ui::KeyboardCode::VKEY_H, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::DELETE_BACKWARD},
{{ui::KeyboardCode::VKEY_K, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::DELETE_TO_END_OF_LINE},
{{ui::KeyboardCode::VKEY_N, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::MOVE_DOWN},
{{ui::KeyboardCode::VKEY_N, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN},
ui::TextEditCommand::MOVE_DOWN_AND_MODIFY_SELECTION},
{{ui::KeyboardCode::VKEY_P, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::MOVE_UP},
{{ui::KeyboardCode::VKEY_P, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN},
ui::TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION},
{{ui::KeyboardCode::VKEY_T, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::TRANSPOSE},
{{ui::KeyboardCode::VKEY_U, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE},
{{ui::KeyboardCode::VKEY_W, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::DELETE_WORD_BACKWARD},
{{ui::KeyboardCode::VKEY_Y, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::YANK},
{{ui::KeyboardCode::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::UNDO},
{{ui::KeyboardCode::VKEY_OEM_2, ui::EF_CONTROL_DOWN},
ui::TextEditCommand::UNDO},
});
} // namespace
GtkKeyBindingsHandler::GtkKeyBindingsHandler() {
settings_ = TakeGObject(g_settings_new(kDesktopInterface));
signal_ = ScopedGSignal(
settings_, "changed",
base::BindRepeating(&GtkKeyBindingsHandler::OnSettingsChanged,
base::Unretained(this)));
OnSettingsChanged(settings_.get(), kGtkKeyTheme);
}
GtkKeyBindingsHandler::~GtkKeyBindingsHandler() {
gtk_widget_destroy(handler_);
gtk_widget_destroy(fake_window_);
}
GtkKeyBindingsHandler::~GtkKeyBindingsHandler() = default;
bool GtkKeyBindingsHandler::MatchEvent(
const ui::Event& event,
std::vector<ui::TextEditCommandAuraLinux>* edit_commands) {
CHECK(event.IsKeyEvent());
const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event);
if (key_event.is_char())
return false;
GdkEvent* gdk_event = GdkEventFromKeyEvent(key_event);
if (!gdk_event)
return false;
edit_commands_.clear();
// If this key event matches a predefined key binding, corresponding signal
// will be emitted.
auto* key = reinterpret_cast<GdkEventKey*>(gdk_event);
DCHECK(key->type == GdkKeyPress() || key->type == GdkKeyRelease());
gtk_bindings_activate_event(G_OBJECT(handler_.get()), key);
gdk_event_free(gdk_event);
bool matched = !edit_commands_.empty();
if (edit_commands)
edit_commands->swap(edit_commands_);
return matched;
}
GtkWidget* GtkKeyBindingsHandler::CreateNewHandler() {
Handler* handler =
static_cast<Handler*>(g_object_new(HandlerGetType(), nullptr));
handler->owner = this;
// We don't need to show the |handler| object on screen, so set its size to
// zero.
gtk_widget_set_size_request(GTK_WIDGET(handler), 0, 0);
// Prevents it from handling any events by itself.
gtk_widget_set_sensitive(GTK_WIDGET(handler), FALSE);
gtk_widget_set_events(GTK_WIDGET(handler), 0);
gtk_widget_set_can_focus(GTK_WIDGET(handler), TRUE);
return GTK_WIDGET(handler);
}
void GtkKeyBindingsHandler::EditCommandMatched(TextEditCommand command,
const std::string& value) {
edit_commands_.emplace_back(command, value);
}
void GtkKeyBindingsHandler::HandlerInit(Handler* self) {
self->owner = nullptr;
}
void GtkKeyBindingsHandler::HandlerClassInit(HandlerClass* klass) {
// Overrides all virtual methods related to editor key bindings.
klass->backspace = BackSpace;
klass->copy_clipboard = CopyClipboard;
klass->cut_clipboard = CutClipboard;
klass->delete_from_cursor = DeleteFromCursor;
klass->insert_at_cursor = InsertAtCursor;
klass->move_cursor = MoveCursor;
klass->paste_clipboard = PasteClipboard;
klass->set_anchor = SetAnchor;
klass->toggle_overwrite = ToggleOverwrite;
// "move-focus", "move-viewport", "select-all" and "toggle-cursor-visible"
// have no corresponding virtual methods. Since glib 2.18 (gtk 2.14),
// g_signal_override_class_handler() is introduced to override a signal
// handler.
g_signal_override_class_handler("move-focus", G_TYPE_FROM_CLASS(klass),
G_CALLBACK(MoveFocus));
g_signal_override_class_handler("move-viewport", G_TYPE_FROM_CLASS(klass),
G_CALLBACK(MoveViewport));
g_signal_override_class_handler("select-all", G_TYPE_FROM_CLASS(klass),
G_CALLBACK(SelectAll));
g_signal_override_class_handler("toggle-cursor-visible",
G_TYPE_FROM_CLASS(klass),
G_CALLBACK(ToggleCursorVisible));
g_signal_override_class_handler("show-help", G_TYPE_FROM_CLASS(klass),
G_CALLBACK(ShowHelp));
}
GType GtkKeyBindingsHandler::HandlerGetType() {
static gsize type_id = 0;
if (g_once_init_enter(&type_id)) {
GType type = g_type_register_static_simple(
GTK_TYPE_TEXT_VIEW, g_intern_static_string("GtkKeyBindingsHandler"),
sizeof(HandlerClass),
reinterpret_cast<GClassInitFunc>(HandlerClassInit), sizeof(Handler),
reinterpret_cast<GInstanceInitFunc>(HandlerInit),
static_cast<GTypeFlags>(0));
g_once_init_leave(&type_id, type);
ui::TextEditCommand GtkKeyBindingsHandler::MatchEvent(const ui::Event& event) {
if (!emacs_theme_) {
return ui::TextEditCommand::INVALID_COMMAND;
}
return type_id;
auto* key_event = event.AsKeyEvent();
if (!key_event) {
return ui::TextEditCommand::INVALID_COMMAND;
}
if (key_event->is_char()) {
return ui::TextEditCommand::INVALID_COMMAND;
}
constexpr auto kModMask = ui::EF_ALTGR_DOWN | ui::EF_ALT_DOWN |
ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN |
ui::EF_FUNCTION_DOWN | ui::EF_MOD3_DOWN |
ui::EF_SHIFT_DOWN;
KeyWithMods key = {key_event->key_code(), key_event->flags() & kModMask};
auto it = kEmacsBindings.find(key);
return it == kEmacsBindings.end() ? ui::TextEditCommand::INVALID_COMMAND
: it->second;
}
GtkKeyBindingsHandler* GtkKeyBindingsHandler::GetHandlerOwner(
GtkTextView* text_view) {
Handler* handler =
G_TYPE_CHECK_INSTANCE_CAST(text_view, HandlerGetType(), Handler);
DCHECK(handler);
return handler->owner;
}
void GtkKeyBindingsHandler::BackSpace(GtkTextView* text_view) {
GetHandlerOwner(text_view)->EditCommandMatched(
TextEditCommand::DELETE_BACKWARD, std::string());
}
void GtkKeyBindingsHandler::CopyClipboard(GtkTextView* text_view) {
GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::COPY,
std::string());
}
void GtkKeyBindingsHandler::CutClipboard(GtkTextView* text_view) {
GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::CUT,
std::string());
}
void GtkKeyBindingsHandler::DeleteFromCursor(GtkTextView* text_view,
GtkDeleteType type,
gint count) {
if (!count)
void GtkKeyBindingsHandler::OnSettingsChanged(GSettings* settings,
const char* key) {
DCHECK(settings);
if (strcmp(key, kGtkKeyTheme) != 0) {
return;
TextEditCommand commands[2] = {
TextEditCommand::INVALID_COMMAND,
TextEditCommand::INVALID_COMMAND,
};
switch (type) {
case GTK_DELETE_CHARS:
commands[0] = (count > 0 ? TextEditCommand::DELETE_FORWARD
: TextEditCommand::DELETE_BACKWARD);
break;
case GTK_DELETE_WORD_ENDS:
commands[0] = (count > 0 ? TextEditCommand::DELETE_WORD_FORWARD
: TextEditCommand::DELETE_WORD_BACKWARD);
break;
case GTK_DELETE_WORDS:
if (count > 0) {
commands[0] = TextEditCommand::MOVE_WORD_FORWARD;
commands[1] = TextEditCommand::DELETE_WORD_BACKWARD;
} else {
commands[0] = TextEditCommand::MOVE_WORD_BACKWARD;
commands[1] = TextEditCommand::DELETE_WORD_FORWARD;
}
break;
case GTK_DELETE_DISPLAY_LINES:
commands[0] = TextEditCommand::MOVE_TO_BEGINNING_OF_LINE;
commands[1] = TextEditCommand::DELETE_TO_END_OF_LINE;
break;
case GTK_DELETE_DISPLAY_LINE_ENDS:
commands[0] = (count > 0 ? TextEditCommand::DELETE_TO_END_OF_LINE
: TextEditCommand::DELETE_TO_BEGINNING_OF_LINE);
break;
case GTK_DELETE_PARAGRAPH_ENDS:
commands[0] =
(count > 0 ? TextEditCommand::DELETE_TO_END_OF_PARAGRAPH
: TextEditCommand::DELETE_TO_BEGINNING_OF_PARAGRAPH);
break;
case GTK_DELETE_PARAGRAPHS:
commands[0] = TextEditCommand::MOVE_TO_BEGINNING_OF_PARAGRAPH;
commands[1] = TextEditCommand::DELETE_TO_END_OF_PARAGRAPH;
break;
default:
// GTK_DELETE_WHITESPACE has no corresponding editor command.
return;
}
GtkKeyBindingsHandler* owner = GetHandlerOwner(text_view);
if (count < 0)
count = -count;
for (; count > 0; --count) {
for (auto& command : commands) {
if (command != TextEditCommand::INVALID_COMMAND)
owner->EditCommandMatched(command, std::string());
}
}
}
void GtkKeyBindingsHandler::InsertAtCursor(GtkTextView* text_view,
const gchar* str) {
if (str && *str) {
GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::INSERT_TEXT,
str);
}
}
void GtkKeyBindingsHandler::MoveCursor(GtkTextView* text_view,
GtkMovementStep step,
gint count,
gboolean extend_selection) {
if (!count)
auto g_free_deleter = [](gchar* s) { g_free(s); };
std::unique_ptr<gchar, decltype(g_free_deleter)> key_theme(
g_settings_get_string(settings, kGtkKeyTheme), g_free_deleter);
if (!key_theme) {
return;
TextEditCommand command;
switch (step) {
case GTK_MOVEMENT_LOGICAL_POSITIONS:
if (extend_selection) {
command =
(count > 0 ? TextEditCommand::MOVE_FORWARD_AND_MODIFY_SELECTION
: TextEditCommand::MOVE_BACKWARD_AND_MODIFY_SELECTION);
} else {
command = (count > 0 ? TextEditCommand::MOVE_FORWARD
: TextEditCommand::MOVE_BACKWARD);
}
break;
case GTK_MOVEMENT_VISUAL_POSITIONS:
if (extend_selection) {
command = (count > 0 ? TextEditCommand::MOVE_RIGHT_AND_MODIFY_SELECTION
: TextEditCommand::MOVE_LEFT_AND_MODIFY_SELECTION);
} else {
command = (count > 0 ? TextEditCommand::MOVE_RIGHT
: TextEditCommand::MOVE_LEFT);
}
break;
case GTK_MOVEMENT_WORDS:
if (extend_selection) {
command =
(count > 0 ? TextEditCommand::MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
: TextEditCommand::MOVE_WORD_LEFT_AND_MODIFY_SELECTION);
} else {
command = (count > 0 ? TextEditCommand::MOVE_WORD_RIGHT
: TextEditCommand::MOVE_WORD_LEFT);
}
break;
case GTK_MOVEMENT_DISPLAY_LINES:
if (extend_selection) {
command = (count > 0 ? TextEditCommand::MOVE_DOWN_AND_MODIFY_SELECTION
: TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION);
} else {
command =
(count > 0 ? TextEditCommand::MOVE_DOWN : TextEditCommand::MOVE_UP);
}
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
if (extend_selection) {
command =
(count > 0
? TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
: TextEditCommand::
MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION);
} else {
command = (count > 0 ? TextEditCommand::MOVE_TO_END_OF_LINE
: TextEditCommand::MOVE_TO_BEGINNING_OF_LINE);
}
break;
case GTK_MOVEMENT_PARAGRAPH_ENDS:
if (extend_selection) {
command =
(count > 0
? TextEditCommand::
MOVE_TO_END_OF_PARAGRAPH_AND_MODIFY_SELECTION
: TextEditCommand::
MOVE_TO_BEGINNING_OF_PARAGRAPH_AND_MODIFY_SELECTION);
} else {
command = (count > 0 ? TextEditCommand::MOVE_TO_END_OF_PARAGRAPH
: TextEditCommand::MOVE_TO_BEGINNING_OF_PARAGRAPH);
}
break;
case GTK_MOVEMENT_PAGES:
if (extend_selection) {
command =
(count > 0 ? TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION
: TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION);
} else {
command = (count > 0 ? TextEditCommand::MOVE_PAGE_DOWN
: TextEditCommand::MOVE_PAGE_UP);
}
break;
case GTK_MOVEMENT_BUFFER_ENDS:
if (extend_selection) {
command =
(count > 0
? TextEditCommand::MOVE_TO_END_OF_DOCUMENT_AND_MODIFY_SELECTION
: TextEditCommand::
MOVE_TO_BEGINNING_OF_DOCUMENT_AND_MODIFY_SELECTION);
} else {
command = (count > 0 ? TextEditCommand::MOVE_TO_END_OF_DOCUMENT
: TextEditCommand::MOVE_TO_BEGINNING_OF_DOCUMENT);
}
break;
default:
// GTK_MOVEMENT_PARAGRAPHS and GTK_MOVEMENT_HORIZONTAL_PAGES have
// no corresponding editor commands.
return;
}
GtkKeyBindingsHandler* owner = GetHandlerOwner(text_view);
if (count < 0)
count = -count;
for (; count > 0; --count)
owner->EditCommandMatched(command, std::string());
}
void GtkKeyBindingsHandler::MoveViewport(GtkTextView* text_view,
GtkScrollStep step,
gint count) {
// Not supported by Blink.
}
void GtkKeyBindingsHandler::PasteClipboard(GtkTextView* text_view) {
GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::PASTE,
std::string());
}
void GtkKeyBindingsHandler::SelectAll(GtkTextView* text_view, gboolean select) {
GetHandlerOwner(text_view)->EditCommandMatched(
select ? TextEditCommand::SELECT_ALL : TextEditCommand::UNSELECT,
std::string());
}
void GtkKeyBindingsHandler::SetAnchor(GtkTextView* text_view) {
GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::SET_MARK,
std::string());
}
void GtkKeyBindingsHandler::ToggleCursorVisible(GtkTextView* text_view) {
// Not supported by Blink.
}
void GtkKeyBindingsHandler::ToggleOverwrite(GtkTextView* text_view) {
// Not supported by Blink.
}
gboolean GtkKeyBindingsHandler::ShowHelp(GtkWidget* widget,
GtkWidgetHelpType arg1) {
// Just for disabling the default handler.
return TRUE;
}
void GtkKeyBindingsHandler::MoveFocus(GtkWidget* widget,
GtkDirectionType arg1) {
// Just for disabling the default handler.
emacs_theme_ = strcmp(key_theme.get(), kEmacsKeyTheme) == 0;
}
} // namespace gtk

@ -5,178 +5,35 @@
#ifndef UI_GTK_GTK_KEY_BINDINGS_HANDLER_H_
#define UI_GTK_GTK_KEY_BINDINGS_HANDLER_H_
#include <string>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/containers/fixed_flat_map.h"
#include "ui/base/glib/scoped_gobject.h"
#include "ui/base/glib/scoped_gsignal.h"
#include "ui/base/ime/linux/text_edit_command_auralinux.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/events/event.h"
#include "ui/events/platform_event.h"
#include "ui/gtk/gtk_compat.h"
namespace ui {
class Event;
}
using GSettings = struct _GSettings;
namespace gtk {
// This class is a convenience class for handling editor key bindings defined
// in gtk keyboard theme.
// In gtk, only GtkEntry and GtkTextView support customizing editor key bindings
// through keyboard theme. And in gtk keyboard theme definition file, each key
// binding must be bound to a specific class or object. So existing keyboard
// themes only define editor key bindings exactly for GtkEntry and GtkTextView.
// Then, the only way for us to intercept editor key bindings defined in
// keyboard theme, is to create a GtkEntry or GtkTextView object and call
// gtk_bindings_activate_event() against it for the key events. If a key event
// matches a predefined key binding, corresponding signal will be emitted.
// GtkTextView is used here because it supports more key bindings than GtkEntry,
// but in order to minimize the side effect of using a GtkTextView object, a new
// class derived from GtkTextView is used, which overrides all signals related
// to key bindings, to make sure GtkTextView won't receive them.
//
// See third_party/blink/renderer/core/editing/commands/editor_command.cc for
// detailed definition of Blink edit commands.
// Translates key events into edit commands based on the key theme.
// Currently only supports the "Emacs" key theme since that's all GTK supports.
// TODO(thomasanderson): Extract this class to be used by all Linux UIs since
// the implementation is not GTK specific.
class GtkKeyBindingsHandler {
public:
GtkKeyBindingsHandler();
virtual ~GtkKeyBindingsHandler();
~GtkKeyBindingsHandler();
// Matches a key event against predefined gtk key bindings, false will be
// returned if the key event doesn't correspond to a predefined key binding.
// Edit commands matched with |event| will be stored in |edit_commands|, if
// non-nullptr.
bool MatchEvent(const ui::Event& event,
std::vector<ui::TextEditCommandAuraLinux>* commands);
ui::TextEditCommand MatchEvent(const ui::Event& event);
private:
// Object structure of Handler class, which is derived from GtkTextView.
struct Handler {
// Starting in Gtk4, GtkTextView subclasses from GtkWidget instead of
// GtkContainer. This class is only used on Gtk3, so to ensure ABI
// compatibility, we always want the Gtk3 struct layout even when building
// with Gtk4 headers. To facilitate this, we manually copy the class
// hierarchy up to GtkWidget.
GtkWidget widget;
raw_ptr<void> container_private;
raw_ptr<void> text_view_private;
raw_ptr<GtkKeyBindingsHandler> owner;
};
void OnSettingsChanged(GSettings* settings, const char* key);
// Class structure of Handler class.
struct HandlerClass {
// Class layout for types changes between GTK versions, but is stable within
// the same major version. This class is only used on Gtk3, so manually
// expand the class layout as it appears in Gtk3.
GInitiallyUnownedClass parent_class;
// GtkWidgetClass and GtkContainerClass
guint pad0;
void* pad1[95];
unsigned int pad2 : 1;
void* pad3[8];
// GtkTextViewClass
void (*populate_popup)(GtkTextView* text_view, GtkWidget* popup);
void (*move_cursor)(GtkTextView* text_view,
GtkMovementStep step,
gint count,
gboolean extend_selection);
void (*set_anchor)(GtkTextView* text_view);
void (*insert_at_cursor)(GtkTextView* text_view, const gchar* str);
void (*delete_from_cursor)(GtkTextView* text_view,
GtkDeleteType type,
gint count);
void (*backspace)(GtkTextView* text_view);
void (*cut_clipboard)(GtkTextView* text_view);
void (*copy_clipboard)(GtkTextView* text_view);
void (*paste_clipboard)(GtkTextView* text_view);
void (*toggle_overwrite)(GtkTextView* text_view);
GtkTextBuffer* (*create_buffer)(GtkTextView* text_view);
void (*draw_layer)(GtkTextView* text_view,
GtkTextViewLayer layer,
cairo_t* cr);
gboolean (*extend_selection)(GtkTextView* text_view,
GtkTextExtendSelection granularity,
const GtkTextIter* location,
GtkTextIter* start,
GtkTextIter* end);
void (*insert_emoji)(GtkTextView* text_view);
void* pad4[4];
};
// Creates a new instance of Handler class.
GtkWidget* CreateNewHandler();
// Adds an edit command to the key event.
void EditCommandMatched(ui::TextEditCommand command,
const std::string& value);
// Initializes Handler structure.
static void HandlerInit(Handler* self);
// Initializes HandlerClass structure.
static void HandlerClassInit(HandlerClass* klass);
// Registeres Handler class to GObject type system and return its type id.
static GType HandlerGetType();
// Gets the GtkKeyBindingsHandler object which owns the Handler object.
static GtkKeyBindingsHandler* GetHandlerOwner(GtkTextView* text_view);
// Handler of "backspace" signal.
static void BackSpace(GtkTextView* text_view);
// Handler of "copy-clipboard" signal.
static void CopyClipboard(GtkTextView* text_view);
// Handler of "cut-clipboard" signal.
static void CutClipboard(GtkTextView* text_view);
// Handler of "delete-from-cursor" signal.
static void DeleteFromCursor(GtkTextView* text_view,
GtkDeleteType type,
gint count);
// Handler of "insert-at-cursor" signal.
static void InsertAtCursor(GtkTextView* text_view, const gchar* str);
// Handler of "move-cursor" signal.
static void MoveCursor(GtkTextView* text_view,
GtkMovementStep step,
gint count,
gboolean extend_selection);
// Handler of "move-viewport" signal.
static void MoveViewport(GtkTextView* text_view,
GtkScrollStep step,
gint count);
// Handler of "paste-clipboard" signal.
static void PasteClipboard(GtkTextView* text_view);
// Handler of "select-all" signal.
static void SelectAll(GtkTextView* text_view, gboolean select);
// Handler of "set-anchor" signal.
static void SetAnchor(GtkTextView* text_view);
// Handler of "toggle-cursor-visible" signal.
static void ToggleCursorVisible(GtkTextView* text_view);
// Handler of "toggle-overwrite" signal.
static void ToggleOverwrite(GtkTextView* text_view);
// Handler of "show-help" signal.
static gboolean ShowHelp(GtkWidget* widget, GtkWidgetHelpType arg1);
// Handler of "move-focus" signal.
static void MoveFocus(GtkWidget* widget, GtkDirectionType arg1);
raw_ptr<GtkWidget> fake_window_;
raw_ptr<GtkWidget> handler_;
// Buffer to store the match results.
std::vector<ui::TextEditCommandAuraLinux> edit_commands_;
ScopedGObject<GSettings> settings_;
ScopedGSignal signal_;
bool emacs_theme_ = false;
};
} // namespace gtk

@ -39,6 +39,7 @@
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/linux/fake_input_method_context.h"
#include "ui/base/ime/linux/linux_input_method_context.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/base/ime/text_input_flags.h"
#include "ui/base/ozone_buildflags.h"
#include "ui/base/ui_base_switches.h"
@ -675,30 +676,15 @@ int GtkUi::GetCursorThemeSize() {
return size;
}
bool GtkUi::GetTextEditCommandsForEvent(
const ui::Event& event,
int text_flags,
std::vector<ui::TextEditCommandAuraLinux>* commands) {
// GTK4 dropped custom key bindings.
if (GtkCheckVersion(4)) {
return false;
}
// TODO(crbug.com/40627552): Use delegate's |GetGdkKeymap| here to
// determine if GtkUi's key binding handling implementation is used or not.
// Ozone/Wayland was unintentionally using GtkUi for keybinding handling, so
// early out here, for now, until a proper solution for ozone is implemented.
if (!platform_->GetGdkKeymap()) {
return false;
}
ui::TextEditCommand GtkUi::GetTextEditCommandForEvent(const ui::Event& event,
int text_flags) {
// Skip mapping arrow keys to edit commands for vertical text fields in a
// renderer. Blink handles them. See crbug.com/484651.
if (text_flags & ui::TEXT_INPUT_FLAG_VERTICAL) {
ui::KeyboardCode code = event.AsKeyEvent()->key_code();
if (code == ui::VKEY_LEFT || code == ui::VKEY_RIGHT ||
code == ui::VKEY_UP || code == ui::VKEY_DOWN) {
return false;
return ui::TextEditCommand::INVALID_COMMAND;
}
}
@ -707,7 +693,7 @@ bool GtkUi::GetTextEditCommandsForEvent(
key_bindings_handler_ = std::make_unique<GtkKeyBindingsHandler>();
}
return key_bindings_handler_->MatchEvent(event, commands);
return key_bindings_handler_->MatchEvent(event);
}
#if BUILDFLAG(ENABLE_PRINTING)

@ -83,10 +83,8 @@ class GtkUi : public ui::LinuxUiAndTheme {
int GetCursorThemeSize() override;
std::unique_ptr<ui::LinuxInputMethodContext> CreateInputMethodContext(
ui::LinuxInputMethodContextDelegate* delegate) const override;
bool GetTextEditCommandsForEvent(
const ui::Event& event,
int text_flags,
std::vector<ui::TextEditCommandAuraLinux>* commands) override;
ui::TextEditCommand GetTextEditCommandForEvent(const ui::Event& event,
int text_flags) override;
gfx::FontRenderParams GetDefaultFontRenderParams() override;
bool AnimationsEnabled() const override;
void AddWindowButtonOrderObserver(

@ -31,10 +31,6 @@ class GtkUiPlatform {
// a dummy window passed in for context.
virtual void OnInitialized(GtkWidget* widget) = 0;
// Gets the GdkKeymap instance, which is used to translate KeyEvents into
// GdkEvents before filtering them through GtkIM API.
virtual GdkKeymap* GetGdkKeymap() = 0;
// Gets the GDK key event state for a KeyEvent.
virtual GdkModifierType GetGdkKeyEventState(
const ui::KeyEvent& key_event) = 0;

@ -15,10 +15,6 @@ GtkUiPlatformStub::~GtkUiPlatformStub() = default;
void GtkUiPlatformStub::OnInitialized(GtkWidget* widget) {}
GdkKeymap* GtkUiPlatformStub::GetGdkKeymap() {
return nullptr;
}
GdkModifierType GtkUiPlatformStub::GetGdkKeyEventState(
const ui::KeyEvent& key_event) {
return static_cast<GdkModifierType>(0);

@ -18,7 +18,6 @@ class GtkUiPlatformStub : public GtkUiPlatform {
// GtkUiPlatform:
void OnInitialized(GtkWidget* widget) override;
GdkKeymap* GetGdkKeymap() override;
GdkModifierType GetGdkKeyEventState(const ui::KeyEvent& key_event) override;
int GetGdkKeyEventGroup(const ui::KeyEvent& key_event) override;
GdkWindow* GetGdkWindow(gfx::AcceleratedWidget window_id) override;

@ -704,8 +704,7 @@ GdkEvent* GdkEventFromKeyEvent(const ui::KeyEvent& key_event) {
int hw_code = GetKeyEventProperty(key_event, ui::kPropertyKeyboardHwKeyCode);
int group = GetKeyEventProperty(key_event, ui::kPropertyKeyboardGroup);
// Get GdkKeymap
GdkKeymap* keymap = GtkUi::GetPlatform()->GetGdkKeymap();
GdkKeymap* keymap = gdk_keymap_get_for_display(gdk_display_get_default());
// Get keyval and state
GdkModifierType state = GetGdkKeyEventState(key_event);

@ -37,11 +37,6 @@ void GtkUiPlatformWayland::OnInitialized(GtkWidget* widget) {
// Nothing to do upon initialization for Wayland.
}
GdkKeymap* GtkUiPlatformWayland::GetGdkKeymap() {
NOTIMPLEMENTED_LOG_ONCE();
return nullptr;
}
GdkModifierType GtkUiPlatformWayland::GetGdkKeyEventState(
const ui::KeyEvent& key_event) {
// We first reconstruct the state that was stored as a property by

@ -23,7 +23,6 @@ class GtkUiPlatformWayland : public GtkUiPlatform {
// GtkUiPlatform:
void OnInitialized(GtkWidget* widget) override;
GdkKeymap* GetGdkKeymap() override;
GdkModifierType GetGdkKeyEventState(const ui::KeyEvent& key_event) override;
int GetGdkKeyEventGroup(const ui::KeyEvent& key_event) override;
GdkWindow* GetGdkWindow(gfx::AcceleratedWidget window_id) override;

@ -44,11 +44,6 @@ void GtkUiPlatformX11::OnInitialized(GtkWidget* widget) {
x11::SetXlibErrorHandler();
}
GdkKeymap* GtkUiPlatformX11::GetGdkKeymap() {
DCHECK(!gtk::GtkCheckVersion(4));
return gdk_keymap_get_for_display(GetGdkDisplay());
}
GdkModifierType GtkUiPlatformX11::GetGdkKeyEventState(
const ui::KeyEvent& key_event) {
return gtk::GetGdkKeyEventState(key_event);

@ -26,7 +26,6 @@ class GtkUiPlatformX11 : public GtkUiPlatform {
// GtkUiPlatform:
void OnInitialized(GtkWidget* widget) override;
GdkKeymap* GetGdkKeymap() override;
GdkModifierType GetGdkKeyEventState(const ui::KeyEvent& key_event) override;
int GetGdkKeyEventGroup(const ui::KeyEvent& key_event) override;
GdkWindow* GetGdkWindow(gfx::AcceleratedWidget window_id) override;

@ -6,6 +6,7 @@
#include "base/time/time.h"
#include "ui/base/ime/linux/linux_input_method_context.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/font_render_params.h"
#include "ui/gfx/geometry/size.h"
@ -132,11 +133,10 @@ ui::NativeTheme* FakeLinuxUi::GetNativeTheme() const {
return nullptr;
}
bool FakeLinuxUi::GetTextEditCommandsForEvent(
ui::TextEditCommand FakeLinuxUi::GetTextEditCommandForEvent(
const ui::Event& event,
int text_falgs,
std::vector<ui::TextEditCommandAuraLinux>* commands) {
return false;
int text_falgs) {
return ui::TextEditCommand::INVALID_COMMAND;
}
#if BUILDFLAG(ENABLE_PRINTING)

@ -37,10 +37,8 @@ class FakeLinuxUi : public LinuxUiAndTheme {
int GetCursorThemeSize() override;
std::unique_ptr<ui::LinuxInputMethodContext> CreateInputMethodContext(
ui::LinuxInputMethodContextDelegate* delegate) const override;
bool GetTextEditCommandsForEvent(
const ui::Event& event,
int text_flags,
std::vector<ui::TextEditCommandAuraLinux>* commands) override;
ui::TextEditCommand GetTextEditCommandForEvent(const ui::Event& event,
int text_flags) override;
gfx::FontRenderParams GetDefaultFontRenderParams() override;
bool AnimationsEnabled() const override;
void AddWindowButtonOrderObserver(

@ -169,11 +169,10 @@ ui::NativeTheme* FallbackLinuxUi::GetNativeTheme() const {
return ui::NativeTheme::GetInstanceForNativeUi();
}
bool FallbackLinuxUi::GetTextEditCommandsForEvent(
ui::TextEditCommand FallbackLinuxUi::GetTextEditCommandForEvent(
const ui::Event& event,
int text_flags,
std::vector<ui::TextEditCommandAuraLinux>* commands) {
return false;
int text_flags) {
return ui::TextEditCommand::INVALID_COMMAND;
}
#if BUILDFLAG(ENABLE_PRINTING)

@ -40,10 +40,8 @@ class FallbackLinuxUi : public LinuxUiAndTheme {
int GetCursorThemeSize() override;
std::unique_ptr<ui::LinuxInputMethodContext> CreateInputMethodContext(
ui::LinuxInputMethodContextDelegate* delegate) const override;
bool GetTextEditCommandsForEvent(
const ui::Event& event,
int text_flags,
std::vector<ui::TextEditCommandAuraLinux>* commands) override;
ui::TextEditCommand GetTextEditCommandForEvent(const ui::Event& event,
int text_flags) override;
gfx::FontRenderParams GetDefaultFontRenderParams() override;
bool AnimationsEnabled() const override;
void AddWindowButtonOrderObserver(

@ -58,9 +58,9 @@ class NativeTheme;
class NavButtonProvider;
class SelectFileDialog;
class SelectFilePolicy;
class TextEditCommandAuraLinux;
class WindowButtonOrderObserver;
class WindowFrameProvider;
enum class TextEditCommand;
// Adapter class with targets to render like different toolkits. Set by any
// project that wants to do linux desktop native rendering.
@ -177,16 +177,13 @@ class COMPONENT_EXPORT(LINUX_UI) LinuxUi {
virtual std::unique_ptr<LinuxInputMethodContext> CreateInputMethodContext(
LinuxInputMethodContextDelegate* delegate) const = 0;
// Matches a key event against the users' platform specific key bindings,
// false will be returned if the key event doesn't correspond to a predefined
// key binding. Edit commands matched with |event| will be stored in
// |edit_commands|, if |edit_commands| is non-nullptr.
// Matches a key event against the users' platform specific key bindings.
// Returns ui::TextEditCommand::INVALID_COMMAND if the key event doesn't
// correspond to a predefined key binding.
//
// |text_falgs| is the current ui::TextInputFlags if available.
virtual bool GetTextEditCommandsForEvent(
const ui::Event& event,
int text_flags,
std::vector<TextEditCommandAuraLinux>* commands) = 0;
// `text_flags` is the current ui::TextInputFlags if available.
virtual TextEditCommand GetTextEditCommandForEvent(const Event& event,
int text_flags) = 0;
// Returns the default font rendering settings.
virtual gfx::FontRenderParams GetDefaultFontRenderParams() = 0;

@ -32,6 +32,7 @@
#include "chrome/browser/themes/theme_properties.h" // nogncheck
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/ime/linux/linux_input_method_context.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/color/color_mixer.h"
#include "ui/color/color_provider.h"
#include "ui/color/color_provider_manager.h"
@ -459,12 +460,10 @@ int QtUi::GetCursorThemeSize() {
return 0;
}
bool QtUi::GetTextEditCommandsForEvent(
const ui::Event& event,
int text_flags,
std::vector<ui::TextEditCommandAuraLinux>* commands) {
ui::TextEditCommand QtUi::GetTextEditCommandForEvent(const ui::Event& event,
int text_flags) {
// QT doesn't have "key themes" (eg. readline bindings) like GTK.
return false;
return ui::TextEditCommand::INVALID_COMMAND;
}
#if BUILDFLAG(ENABLE_PRINTING)

@ -55,10 +55,8 @@ class QtUi : public ui::LinuxUiAndTheme, QtInterface::Delegate {
int GetCursorThemeSize() override;
std::unique_ptr<ui::LinuxInputMethodContext> CreateInputMethodContext(
ui::LinuxInputMethodContextDelegate* delegate) const override;
bool GetTextEditCommandsForEvent(
const ui::Event& event,
int text_flags,
std::vector<ui::TextEditCommandAuraLinux>* commands) override;
ui::TextEditCommand GetTextEditCommandForEvent(const ui::Event& event,
int text_flags) override;
gfx::FontRenderParams GetDefaultFontRenderParams() override;
bool AnimationsEnabled() const override;
void AddWindowButtonOrderObserver(

@ -791,18 +791,18 @@ bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
}
#if BUILDFLAG(IS_LINUX)
auto* linux_ui = ui::LinuxUi::instance();
std::vector<ui::TextEditCommandAuraLinux> commands;
if (!handled && linux_ui &&
linux_ui->GetTextEditCommandsForEvent(event, ui::TEXT_INPUT_FLAG_NONE,
&commands)) {
for (const auto& command : commands) {
if (IsTextEditCommandEnabled(command.command())) {
ExecuteTextEditCommand(command.command());
handled = true;
if (!handled) {
if (auto* linux_ui = ui::LinuxUi::instance()) {
const auto command =
linux_ui->GetTextEditCommandForEvent(event, ui::TEXT_INPUT_FLAG_NONE);
if (command != ui::TextEditCommand::INVALID_COMMAND) {
if (IsTextEditCommandEnabled(command)) {
ExecuteTextEditCommand(command);
return true;
}
return false;
}
}
return handled;
}
#endif
@ -977,14 +977,9 @@ void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
#if BUILDFLAG(IS_LINUX)
// Skip any accelerator handling that conflicts with custom keybindings.
auto* linux_ui = ui::LinuxUi::instance();
std::vector<ui::TextEditCommandAuraLinux> commands;
if (linux_ui && linux_ui->GetTextEditCommandsForEvent(
event, ui::TEXT_INPUT_FLAG_NONE, &commands)) {
const auto is_enabled = [this](const auto& command) {
return IsTextEditCommandEnabled(command.command());
};
if (std::ranges::any_of(commands, is_enabled)) {
if (auto* linux_ui = ui::LinuxUi::instance()) {
if (IsTextEditCommandEnabled(linux_ui->GetTextEditCommandForEvent(
event, ui::TEXT_INPUT_FLAG_NONE))) {
return true;
}
}

@ -1807,11 +1807,9 @@ TEST_F(TextfieldTest, OnKeyPressBinding) {
~TestDelegate() override = default;
bool GetTextEditCommandsForEvent(
const ui::Event& event,
int text_flags,
std::vector<ui::TextEditCommandAuraLinux>* commands) override {
return false;
ui::TextEditCommand GetTextEditCommandForEvent(const ui::Event& event,
int text_flags) override {
return ui::TextEditCommand::INVALID_COMMAND;
}
};