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:

committed by
Chromium LUCI CQ

parent
d80e3fbdbc
commit
9d6ab0ac1e
chrome/browser
content/browser/renderer_host
ui
base
gtk
gtk_key_bindings_handler.ccgtk_key_bindings_handler.hgtk_ui.ccgtk_ui.hgtk_ui_platform.hgtk_ui_platform_stub.ccgtk_ui_platform_stub.hgtk_util.cc
wayland
x
linux
qt
views
controls
textfield
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user