WebKit Bugzilla
Attachment 370039 Details for
Bug 176760
: [GTK] Need WebKitContextMenuItemType to open emoji picker
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
wkgtk-emoji-chooser.diff (text/plain), 60.74 KB, created by
Carlos Garcia Campos
on 2019-05-16 06:14:37 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Carlos Garcia Campos
Created:
2019-05-16 06:14:37 PDT
Size:
60.74 KB
patch
obsolete
>diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 1757f4fe17b..d8af6bae99e 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,23 @@ >+2019-05-16 Carlos Garcia Campos <cgarcia@igalia.com> >+ >+ [GTK] Need WebKitContextMenuItemType to open emoji picker >+ https://bugs.webkit.org/show_bug.cgi?id=176760 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add a new context menu item to insert an emoji. >+ >+ * loader/EmptyClients.cpp: Empty implementation of ContextMenuClient::insertEmoji(). >+ * page/ContextMenuClient.h: Add insertEmoji for GTK port. >+ * page/ContextMenuController.cpp: >+ (WebCore::ContextMenuController::contextMenuItemSelected): Handle insert emoji action. >+ (WebCore::ContextMenuController::populate): Add insert emoji item after select all. >+ (WebCore::ContextMenuController::checkOrEnableIfNeeded const): Handle insert emoji action. >+ * platform/ContextMenuItem.h: Add insert emoji action. >+ * platform/LocalizedStrings.h: >+ * platform/gtk/LocalizedStringsGtk.cpp: >+ (WebCore::contextMenuItemTagInsertEmoji): >+ > 2019-05-16 Carlos Garcia Campos <cgarcia@igalia.com> > > [FreeType] Some character sequences with a variation selector are not rendered >diff --git a/Source/WebCore/loader/EmptyClients.cpp b/Source/WebCore/loader/EmptyClients.cpp >index 55d083e9412..a754aad22e0 100644 >--- a/Source/WebCore/loader/EmptyClients.cpp >+++ b/Source/WebCore/loader/EmptyClients.cpp >@@ -109,6 +109,10 @@ class EmptyContextMenuClient final : public ContextMenuClient { > void searchWithSpotlight() final { } > #endif > >+#if PLATFORM(GTK) >+ void insertEmoji(Frame&) final { } >+#endif >+ > #if USE(ACCESSIBILITY_CONTEXT_MENUS) > void showContextMenu() final { } > #endif >diff --git a/Source/WebCore/page/ContextMenuClient.h b/Source/WebCore/page/ContextMenuClient.h >index 889ee997612..5c5d50a6263 100644 >--- a/Source/WebCore/page/ContextMenuClient.h >+++ b/Source/WebCore/page/ContextMenuClient.h >@@ -49,6 +49,10 @@ public: > virtual void searchWithSpotlight() = 0; > #endif > >+#if PLATFORM(GTK) >+ virtual void insertEmoji(Frame&) = 0; >+#endif >+ > #if USE(ACCESSIBILITY_CONTEXT_MENUS) > virtual void showContextMenu() = 0; > #endif >diff --git a/Source/WebCore/page/ContextMenuController.cpp b/Source/WebCore/page/ContextMenuController.cpp >index bd7a3db199b..15825756122 100644 >--- a/Source/WebCore/page/ContextMenuController.cpp >+++ b/Source/WebCore/page/ContextMenuController.cpp >@@ -357,11 +357,12 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuAction action, co > case ContextMenuItemTagUnicodeInsertZWNJMark: > insertUnicodeCharacter(zeroWidthNonJoiner, *frame); > break; >-#endif >-#if PLATFORM(GTK) > case ContextMenuItemTagSelectAll: > frame->editor().command("SelectAll").execute(); > break; >+ case ContextMenuItemTagInsertEmoji: >+ m_client.insertEmoji(*frame); >+ break; > #endif > case ContextMenuItemTagSpellingGuess: { > VisibleSelection selection = frame->selection().selection(); >@@ -811,9 +812,8 @@ void ContextMenuController::populate() > ContextMenuItem PasteItem(ActionType, ContextMenuItemTagPaste, contextMenuItemTagPaste()); > #if PLATFORM(GTK) > ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete()); >-#endif >-#if PLATFORM(GTK) > ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll()); >+ ContextMenuItem InsertEmojiItem(ActionType, ContextMenuItemTagInsertEmoji, contextMenuItemTagInsertEmoji()); > #endif > > #if PLATFORM(GTK) || PLATFORM(WIN) >@@ -1049,9 +1049,8 @@ void ContextMenuController::populate() > #if PLATFORM(GTK) > appendItem(DeleteItem, m_contextMenu.get()); > appendItem(*separatorItem(), m_contextMenu.get()); >-#endif >-#if PLATFORM(GTK) > appendItem(SelectAllItem, m_contextMenu.get()); >+ appendItem(InsertEmojiItem, m_contextMenu.get()); > #endif > > if (!inPasswordField) { >@@ -1206,6 +1205,10 @@ void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const > case ContextMenuItemTagDelete: > shouldEnable = frame->editor().canDelete(); > break; >+ case ContextMenuItemTagInsertEmoji: >+ shouldEnable = frame->editor().canEdit(); >+ break; >+ case ContextMenuItemTagSelectAll: > case ContextMenuItemTagInputMethods: > case ContextMenuItemTagUnicode: > case ContextMenuItemTagUnicodeInsertLRMMark: >@@ -1220,11 +1223,6 @@ void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const > case ContextMenuItemTagUnicodeInsertZWNJMark: > shouldEnable = true; > break; >-#endif >-#if PLATFORM(GTK) >- case ContextMenuItemTagSelectAll: >- shouldEnable = true; >- break; > #endif > case ContextMenuItemTagUnderline: { > shouldCheck = frame->editor().selectionHasStyle(CSSPropertyWebkitTextDecorationsInEffect, "underline") != FalseTriState; >diff --git a/Source/WebCore/platform/ContextMenuItem.h b/Source/WebCore/platform/ContextMenuItem.h >index 1b0f7450b45..d633d23766a 100644 >--- a/Source/WebCore/platform/ContextMenuItem.h >+++ b/Source/WebCore/platform/ContextMenuItem.h >@@ -68,6 +68,7 @@ enum ContextMenuAction { > ContextMenuItemTagUnicodeInsertZWSMark, > ContextMenuItemTagUnicodeInsertZWJMark, > ContextMenuItemTagUnicodeInsertZWNJMark, >+ ContextMenuItemTagInsertEmoji, > #endif > ContextMenuItemTagSpellingGuess, > ContextMenuItemTagNoGuessesFound, >diff --git a/Source/WebCore/platform/LocalizedStrings.h b/Source/WebCore/platform/LocalizedStrings.h >index e8029ff9c51..8a7ac88c42d 100644 >--- a/Source/WebCore/platform/LocalizedStrings.h >+++ b/Source/WebCore/platform/LocalizedStrings.h >@@ -83,9 +83,8 @@ namespace WebCore { > String contextMenuItemTagUnicodeInsertZWSMark(); > String contextMenuItemTagUnicodeInsertZWJMark(); > String contextMenuItemTagUnicodeInsertZWNJMark(); >-#endif >-#if PLATFORM(GTK) > String contextMenuItemTagSelectAll(); >+ String contextMenuItemTagInsertEmoji(); > #endif > String contextMenuItemTagNoGuessesFound(); > String contextMenuItemTagIgnoreSpelling(); >diff --git a/Source/WebCore/platform/gtk/LocalizedStringsGtk.cpp b/Source/WebCore/platform/gtk/LocalizedStringsGtk.cpp >index ee74b5ab38a..07b7f47419f 100644 >--- a/Source/WebCore/platform/gtk/LocalizedStringsGtk.cpp >+++ b/Source/WebCore/platform/gtk/LocalizedStringsGtk.cpp >@@ -98,6 +98,11 @@ String contextMenuItemTagSelectAll() > return String::fromUTF8(_("Select _All")); > } > >+String contextMenuItemTagInsertEmoji() >+{ >+ return String::fromUTF8(_("Insert _Emoji")); >+} >+ > String contextMenuItemTagUnicode() > { > return String::fromUTF8(_("_Insert Unicode Control Character")); >diff --git a/Source/WebCore/platform/gtk/po/ChangeLog b/Source/WebCore/platform/gtk/po/ChangeLog >index dd104a8df73..16a84152174 100644 >--- a/Source/WebCore/platform/gtk/po/ChangeLog >+++ b/Source/WebCore/platform/gtk/po/ChangeLog >@@ -1,3 +1,12 @@ >+2019-05-16 Carlos Garcia Campos <cgarcia@igalia.com> >+ >+ [GTK] Need WebKitContextMenuItemType to open emoji picker >+ https://bugs.webkit.org/show_bug.cgi?id=176760 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * POTFILES.in: Add WebKitEmojiChooser.cpp. >+ > 2019-04-30 Ãlvaro Torralba <donfrutosgomez@gmail.com> > > Update Spanish Translation >diff --git a/Source/WebCore/platform/gtk/po/POTFILES.in b/Source/WebCore/platform/gtk/po/POTFILES.in >index a329a5bb06f..b54d21d3672 100644 >--- a/Source/WebCore/platform/gtk/po/POTFILES.in >+++ b/Source/WebCore/platform/gtk/po/POTFILES.in >@@ -31,6 +31,7 @@ LocalizedStringsGtk.cpp > ../../../WebKit/UIProcess/API/glib/WebKitWindowProperties.cpp > ../../../WebKit/UIProcess/API/gtk/WebKitAuthenticationDialog.cpp > ../../../WebKit/UIProcess/API/gtk/WebKitColorChooserRequest.cpp >+../../../WebKit/UIProcess/API/gtk/WebKitEmojiChooser.cpp > ../../../WebKit/UIProcess/API/gtk/WebKitPrintCustomWidget.cpp > ../../../WebKit/UIProcess/API/gtk/WebKitPrintOperation.cpp > ../../../WebKit/UIProcess/API/gtk/WebKitScriptDialogImpl.cpp >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index f9ab477b64c..b30a2c7adb7 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,73 @@ >+2019-05-16 Carlos Garcia Campos <cgarcia@igalia.com> >+ >+ [GTK] Need WebKitContextMenuItemType to open emoji picker >+ https://bugs.webkit.org/show_bug.cgi?id=176760 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add a default implementation to show the emoji chooser when requested by the application, either using the >+ context menu or keyboard shortcuts. GtkEmojiChooser is proivate in GTK, so we include our own copy, adapted to >+ the WebKit coding style. The emoji chooser is always shown by default when using GTK >= 3.24 for any editable >+ content. I'm going to add public API in a follow up patch to be able to use your own chooser, or event prevent >+ the default chooser from being shown, similar to what we do for other UI elements like file chooser, color >+ chooser, print dialog, etc. >+ >+ * Shared/API/glib/WebKitContextMenuActions.cpp: >+ (webkitContextMenuActionGetActionTag): Handle insert emoji action. >+ (webkitContextMenuActionGetForContextMenuItem): Ditto. >+ (webkitContextMenuActionGetLabel): Ditto. >+ * SourcesGTK.txt: >+ * UIProcess/API/gtk/WebKitContextMenuActions.h: >+ * UIProcess/API/gtk/WebKitEmojiChooser.cpp: Added. >+ (webkitEmojiChooserAddEmoji): >+ (webkitEmojiChooserAddRecentItem): >+ (emojiActivated): >+ (emojiDataHasVariations): >+ (webkitEmojiChooserShowVariations): >+ (emojiLongPressed): >+ (emojiPressed): >+ (emojiPopupMenu): >+ (verticalAdjustmentChanged): >+ (webkitEmojiChooserSetupSectionBox): >+ (scrollToSection): >+ (webkitEmojiChooserSetupSectionButton): >+ (webkitEmojiChooserSetupRecent): >+ (webkitEmojiChooserEnsureEmptyResult): >+ (webkitEmojiChooserSearchChanged): >+ (webkitEmojiChooserSetupFilters): >+ (webkitEmojiChooserInitializeEmojiMaxWidth): >+ (webkitEmojiChooserConstructed): >+ (webkitEmojiChooserShow): >+ (webkit_emoji_chooser_class_init): >+ (webkitEmojiChooserNew): >+ * UIProcess/API/gtk/WebKitEmojiChooser.h: Added. >+ * UIProcess/API/gtk/WebKitWebViewBase.cpp: >+ (_WebKitWebViewBasePrivate::_WebKitWebViewBasePrivate): Add a timer to release the emoji chooser if not used >+ after 2 minutes. >+ (_WebKitWebViewBasePrivate::releaseEmojiChooserTimerFired): Destroy the emoji chooser. >+ (emojiChooserEmojiPicked): Complete the operation using the given emoji text. >+ (emojiChooserClosed): Complete the operation if needed using an empty string. >+ (webkitWebViewBaseShowEmojiChooser): Create the emoji chooser if needed and show it. >+ * UIProcess/API/gtk/WebKitWebViewBasePrivate.h: >+ * UIProcess/WebPageProxy.h: Add showEmojiPicker(). >+ * UIProcess/WebPageProxy.messages.in: Add ShowEmojiPicker message. >+ * UIProcess/gtk/KeyBindingTranslator.cpp: >+ (WebKit::insertEmojiCallback): Add GtkInsertEmoji command. >+ (WebKit::KeyBindingTranslator::KeyBindingTranslator): Connect to insert-emoji signal. >+ * UIProcess/gtk/WebPageProxyGtk.cpp: >+ (WebKit::WebPageProxy::showEmojiPicker): Call webkitWebViewBaseShowEmojiChooser(). >+ * WebProcess/WebCoreSupport/WebContextMenuClient.h: Override insertEmoji() for GTK port. >+ * WebProcess/WebCoreSupport/WebEditorClient.h: Add insertEmoji() for GTK port. >+ * WebProcess/WebCoreSupport/gtk/WebContextMenuClientGtk.cpp: >+ (WebKit::WebContextMenuClient::insertEmoji): Call WebPage::showEmojiPicker(). >+ * WebProcess/WebCoreSupport/gtk/WebEditorClientGtk.cpp: >+ (WebKit::WebEditorClient::handleGtkEditorCommand): Call WebPage::showEmojiPicker() if command is GtkInsertEmoji. >+ (WebKit::WebEditorClient::executePendingEditorCommands): Handle Gtk specific commands. >+ (WebKit::WebEditorClient::handleKeyboardEvent): Use a reference instead of a pointer for Frame. >+ * WebProcess/WebPage/WebPage.h: >+ * WebProcess/WebPage/gtk/WebPageGtk.cpp: >+ (WebKit::WebPage::showEmojiPicker): Send ShowEmojiPicker message to the UI process. >+ > 2019-05-15 Devin Rousso <drousso@apple.com> > > Web Inspector: user gesture toggle should also force user interaction flag >diff --git a/Source/WebKit/Shared/API/glib/WebKitContextMenuActions.cpp b/Source/WebKit/Shared/API/glib/WebKitContextMenuActions.cpp >index dc57d5318fc..2150e89258c 100644 >--- a/Source/WebKit/Shared/API/glib/WebKitContextMenuActions.cpp >+++ b/Source/WebKit/Shared/API/glib/WebKitContextMenuActions.cpp >@@ -83,6 +83,8 @@ ContextMenuAction webkitContextMenuActionGetActionTag(WebKitContextMenuAction ac > return ContextMenuItemTagDelete; > case WEBKIT_CONTEXT_MENU_ACTION_SELECT_ALL: > return ContextMenuItemTagSelectAll; >+ case WEBKIT_CONTEXT_MENU_ACTION_INSERT_EMOJI: >+ return ContextMenuItemTagInsertEmoji; > case WEBKIT_CONTEXT_MENU_ACTION_INPUT_METHODS: > return ContextMenuItemTagInputMethods; > case WEBKIT_CONTEXT_MENU_ACTION_UNICODE: >@@ -183,6 +185,8 @@ WebKitContextMenuAction webkitContextMenuActionGetForContextMenuItem(const WebKi > return WEBKIT_CONTEXT_MENU_ACTION_DELETE; > case ContextMenuItemTagSelectAll: > return WEBKIT_CONTEXT_MENU_ACTION_SELECT_ALL; >+ case ContextMenuItemTagInsertEmoji: >+ return WEBKIT_CONTEXT_MENU_ACTION_INSERT_EMOJI; > case ContextMenuItemTagInputMethods: > return WEBKIT_CONTEXT_MENU_ACTION_INPUT_METHODS; > case ContextMenuItemTagUnicode: >@@ -281,6 +285,8 @@ String webkitContextMenuActionGetLabel(WebKitContextMenuAction action) > return contextMenuItemTagDelete(); > case WEBKIT_CONTEXT_MENU_ACTION_SELECT_ALL: > return contextMenuItemTagSelectAll(); >+ case WEBKIT_CONTEXT_MENU_ACTION_INSERT_EMOJI: >+ return contextMenuItemTagInsertEmoji(); > case WEBKIT_CONTEXT_MENU_ACTION_INPUT_METHODS: > return contextMenuItemTagInputMethods(); > case WEBKIT_CONTEXT_MENU_ACTION_UNICODE: >diff --git a/Source/WebKit/SourcesGTK.txt b/Source/WebKit/SourcesGTK.txt >index 7c3d86ffb5d..8b5367f307d 100644 >--- a/Source/WebKit/SourcesGTK.txt >+++ b/Source/WebKit/SourcesGTK.txt >@@ -189,6 +189,7 @@ UIProcess/API/gtk/PageClientImpl.cpp @no-unify > UIProcess/API/gtk/WebKitAuthenticationDialog.cpp @no-unify > UIProcess/API/gtk/WebKitColorChooser.cpp @no-unify > UIProcess/API/gtk/WebKitColorChooserRequest.cpp @no-unify >+UIProcess/API/gtk/WebKitEmojiChooser.cpp @no-unify > UIProcess/API/gtk/WebKitOptionMenu.cpp @no-unify > UIProcess/API/gtk/WebKitOptionMenuItem.cpp @no-unify > UIProcess/API/gtk/WebKitPopupMenu.cpp @no-unify >diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitContextMenuActions.h b/Source/WebKit/UIProcess/API/gtk/WebKitContextMenuActions.h >index 61dc24399a2..48c8fdde98e 100644 >--- a/Source/WebKit/UIProcess/API/gtk/WebKitContextMenuActions.h >+++ b/Source/WebKit/UIProcess/API/gtk/WebKitContextMenuActions.h >@@ -74,6 +74,7 @@ G_BEGIN_DECLS > * @WEBKIT_CONTEXT_MENU_ACTION_MEDIA_MUTE: Mute current media element. > * @WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_VIDEO_TO_DISK: Download video to disk. Since 2.2 > * @WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_AUDIO_TO_DISK: Download audio to disk. Since 2.2 >+ * @WEBKIT_CONTEXT_MENU_ACTION_INSERT_EMOJI: Insert an emoji. Since 2.26 > * @WEBKIT_CONTEXT_MENU_ACTION_CUSTOM: Custom action defined by applications. > * > * Enum values used to denote the stock actions for >@@ -125,6 +126,7 @@ typedef enum { > WEBKIT_CONTEXT_MENU_ACTION_MEDIA_MUTE, > WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_VIDEO_TO_DISK, > WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_AUDIO_TO_DISK, >+ WEBKIT_CONTEXT_MENU_ACTION_INSERT_EMOJI, > > WEBKIT_CONTEXT_MENU_ACTION_CUSTOM = 10000 > } WebKitContextMenuAction; >diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitEmojiChooser.cpp b/Source/WebKit/UIProcess/API/gtk/WebKitEmojiChooser.cpp >new file mode 100644 >index 00000000000..4e741d6ba95 >--- /dev/null >+++ b/Source/WebKit/UIProcess/API/gtk/WebKitEmojiChooser.cpp >@@ -0,0 +1,607 @@ >+/* >+ * Copyright (C) 2019 Igalia S.L. >+ * Copyright (C) 2017 Red Hat, Inc. >+ * >+ * This library is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU Library General Public >+ * License as published by the Free Software Foundation; either >+ * version 2 of the License, or (at your option) any later version. >+ * >+ * This library is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * Library General Public License for more details. >+ * >+ * You should have received a copy of the GNU Library General Public License >+ * along with this library; see the file COPYING.LIB. If not, write to >+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, >+ * Boston, MA 02110-1301, USA. >+ */ >+ >+// GtkEmojiChooser is private in GTK 3, so this is based in the GTK code, just adapted to >+// WebKit coding style, using some internal types from WTF to simplify the implementation >+// and not using GtkBuilder for the UI. >+ >+#include "config.h" >+#include "WebKitEmojiChooser.h" >+ >+#if GTK_CHECK_VERSION(3, 24, 0) >+ >+#include <glib/gi18n-lib.h> >+#include <wtf/HashSet.h> >+#include <wtf/Vector.h> >+#include <wtf/glib/GRefPtr.h> >+#include <wtf/glib/GUniquePtr.h> >+#include <wtf/glib/WTFGType.h> >+#include <wtf/text/CString.h> >+ >+enum { >+ EMOJI_PICKED, >+ >+ LAST_SIGNAL >+}; >+ >+struct EmojiSection { >+ GtkWidget* heading { nullptr }; >+ GtkWidget* box { nullptr }; >+ GtkWidget* button { nullptr }; >+ bool isEmpty { false }; >+}; >+ >+using SectionList = Vector<EmojiSection, 9>; >+ >+struct _WebKitEmojiChooserPrivate { >+ GtkWidget* stack; >+ GtkWidget* swindow; >+ GtkWidget* searchEntry; >+ SectionList sections; >+ GRefPtr<GSettings> settings; >+ HashSet<GRefPtr<GtkGesture>> gestures; >+ int emojiMaxWidth; >+}; >+ >+static guint signals[LAST_SIGNAL] = { 0, }; >+ >+WEBKIT_DEFINE_TYPE(WebKitEmojiChooser, webkit_emoji_chooser, GTK_TYPE_POPOVER) >+ >+static void emojiPopupMenu(GtkWidget*, WebKitEmojiChooser*); >+ >+static const unsigned boxSpace = 6; >+ >+static void emojiHovered(GtkWidget* widget, GdkEvent* event) >+{ >+ if (event->type == GDK_ENTER_NOTIFY) >+ gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_PRELIGHT, FALSE); >+ else >+ gtk_widget_unset_state_flags(widget, GTK_STATE_FLAG_PRELIGHT); >+} >+ >+static GtkWidget* webkitEmojiChooserAddEmoji(WebKitEmojiChooser* chooser, GtkFlowBox* parent, GVariant* item, bool prepend = false, gunichar modifier = 0) >+{ >+ char text[64]; >+ char* textPtr = text; >+ GRefPtr<GVariant> codes = adoptGRef(g_variant_get_child_value(item, 0)); >+ for (unsigned i = 0; i < g_variant_n_children(codes.get()); ++i) { >+ gunichar code; >+ g_variant_get_child(codes.get(), i, "u", &code); >+ if (!code) >+ code = modifier; >+ if (code) >+ textPtr += g_unichar_to_utf8(code, textPtr); >+ } >+ // U+FE0F is the Emoji variation selector >+ textPtr += g_unichar_to_utf8(0xFE0F, textPtr); >+ textPtr[0] = '\0'; >+ >+ GtkWidget* label = gtk_label_new(text); >+ PangoAttrList* attributes = pango_attr_list_new(); >+ pango_attr_list_insert(attributes, pango_attr_scale_new(PANGO_SCALE_X_LARGE)); >+ gtk_label_set_attributes(GTK_LABEL(label), attributes); >+ pango_attr_list_unref(attributes); >+ >+ PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(label)); >+ PangoRectangle rect; >+ pango_layout_get_extents(layout, &rect, nullptr); >+ // Check for fallback rendering that generates too wide items. >+ if (pango_layout_get_unknown_glyphs_count(layout) || rect.width >= 1.5 * chooser->priv->emojiMaxWidth) { >+ gtk_widget_destroy(label); >+ return nullptr; >+ } >+ >+ GtkWidget* child = gtk_flow_box_child_new(); >+ gtk_style_context_add_class(gtk_widget_get_style_context(child), "emoji"); >+ g_object_set_data_full(G_OBJECT(child), "emoji-data", g_variant_ref(item), reinterpret_cast<GDestroyNotify>(g_variant_unref)); >+ if (modifier) >+ g_object_set_data(G_OBJECT(child), "modifier", GUINT_TO_POINTER(modifier)); >+ >+ GtkWidget* eventBox = gtk_event_box_new(); >+ gtk_widget_add_events(eventBox, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); >+ g_signal_connect(eventBox, "enter-notify-event", G_CALLBACK(emojiHovered), nullptr); >+ g_signal_connect(eventBox, "leave-notify-event", G_CALLBACK(emojiHovered), nullptr); >+ gtk_container_add(GTK_CONTAINER(eventBox), label); >+ gtk_widget_show(label); >+ >+ gtk_container_add(GTK_CONTAINER(child), eventBox); >+ gtk_widget_show(eventBox); >+ >+ gtk_flow_box_insert(parent, child, prepend ? 0 : -1); >+ gtk_widget_show(child); >+ >+ return child; >+} >+ >+static void webkitEmojiChooserAddRecentItem(WebKitEmojiChooser* chooser, GVariant* item, gunichar modifier) >+{ >+ GRefPtr<GVariant> protectItem(item); >+ GVariantBuilder builder; >+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a((auss)u)")); >+ g_variant_builder_add(&builder, "(@(auss)u)", item, modifier); >+ >+ auto& section = chooser->priv->sections.first(); >+ >+ static const unsigned maxRecentItems = 7 * 3; >+ >+ GUniquePtr<GList> children(gtk_container_get_children(GTK_CONTAINER(section.box))); >+ unsigned i = 1; >+ for (auto* l = children.get(); l; l = g_list_next(l), ++i) { >+ auto* item2 = static_cast<GVariant*>(g_object_get_data(G_OBJECT(l->data), "emoji-data")); >+ auto modifier2 = static_cast<gunichar>(GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(l->data), "modifier"))); >+ if (modifier == modifier2 && g_variant_equal(item, item2)) { >+ gtk_widget_destroy(GTK_WIDGET(l->data)); >+ --i; >+ continue; >+ } >+ >+ if (i >= maxRecentItems) { >+ gtk_widget_destroy(GTK_WIDGET(l->data)); >+ continue; >+ } >+ >+ g_variant_builder_add(&builder, "(@(auss)u)", item2, modifier2); >+ } >+ >+ auto* child = webkitEmojiChooserAddEmoji(chooser, GTK_FLOW_BOX(section.box), item, true, modifier); >+ if (child) >+ g_signal_connect(child, "popup-menu", G_CALLBACK(emojiPopupMenu), chooser); >+ >+ gtk_widget_show(section.box); >+ gtk_widget_set_sensitive(section.button, TRUE); >+ >+ g_settings_set_value(chooser->priv->settings.get(), "recent-emoji", g_variant_builder_end(&builder)); >+} >+ >+static void emojiActivated(GtkFlowBox* box, GtkFlowBoxChild* child, WebKitEmojiChooser* chooser) >+{ >+ GtkWidget* label = gtk_bin_get_child(GTK_BIN(gtk_bin_get_child(GTK_BIN(child)))); >+ GUniquePtr<char> text(g_strdup(gtk_label_get_label(GTK_LABEL(label)))); >+ >+ auto* item = static_cast<GVariant*>(g_object_get_data(G_OBJECT(child), "emoji-data")); >+ auto modifier = static_cast<gunichar>(GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(child), "modifier"))); >+ webkitEmojiChooserAddRecentItem(chooser, item, modifier); >+ g_signal_emit(chooser, signals[EMOJI_PICKED], 0, text.get()); >+ >+ gtk_popover_popdown(GTK_POPOVER(chooser)); >+} >+ >+static bool emojiDataHasVariations(GVariant* emojiData) >+{ >+ GRefPtr<GVariant> codes = adoptGRef(g_variant_get_child_value(emojiData, 0)); >+ for (size_t i = 0; i < g_variant_n_children(codes.get()); ++i) { >+ gunichar code; >+ g_variant_get_child(codes.get(), i, "u", &code); >+ if (!code) >+ return true; >+ } >+ return false; >+} >+ >+static void webkitEmojiChooserShowVariations(WebKitEmojiChooser* chooser, GtkWidget* child) >+{ >+ if (!child) >+ return; >+ >+ auto* emojiData = static_cast<GVariant*>(g_object_get_data(G_OBJECT(child), "emoji-data")); >+ if (!emojiData) >+ return; >+ >+ if (!emojiDataHasVariations(emojiData)) >+ return; >+ >+ GtkWidget* popover = gtk_popover_new(child); >+ GtkWidget* view = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); >+ gtk_style_context_add_class(gtk_widget_get_style_context(view), "view"); >+ GtkWidget* box = gtk_flow_box_new(); >+ g_signal_connect(box, "child-activated", G_CALLBACK(emojiActivated), chooser); >+ gtk_flow_box_set_homogeneous(GTK_FLOW_BOX(box), TRUE); >+ gtk_flow_box_set_min_children_per_line(GTK_FLOW_BOX(box), 6); >+ gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(box), 6); >+ gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(box), TRUE); >+ gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(box), GTK_SELECTION_NONE); >+ gtk_container_add(GTK_CONTAINER(view), box); >+ gtk_widget_show(box); >+ gtk_container_add(GTK_CONTAINER(popover), view); >+ gtk_widget_show(view); >+ >+ webkitEmojiChooserAddEmoji(chooser, GTK_FLOW_BOX(box), emojiData); >+ for (gunichar modifier = 0x1F3FB; modifier <= 0x1F3FF; ++modifier) >+ webkitEmojiChooserAddEmoji(chooser, GTK_FLOW_BOX(box), emojiData, modifier); >+ >+ gtk_popover_popup(GTK_POPOVER(popover)); >+} >+ >+static void emojiLongPressed(GtkGesture* gesture, double x, double y, WebKitEmojiChooser* chooser) >+{ >+ auto* box = GTK_FLOW_BOX(gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture))); >+ webkitEmojiChooserShowVariations(chooser, GTK_WIDGET(gtk_flow_box_get_child_at_pos(box, x, y))); >+} >+ >+static void emojiPressed(GtkGesture* gesture, int, double x, double y, WebKitEmojiChooser* chooser) >+{ >+ emojiLongPressed(gesture, x, y, chooser); >+} >+ >+static void emojiPopupMenu(GtkWidget* child, WebKitEmojiChooser* chooser) >+{ >+ webkitEmojiChooserShowVariations(chooser, child); >+} >+ >+static void verticalAdjustmentChanged(GtkAdjustment* adjustment, WebKitEmojiChooser* chooser) >+{ >+ double value = gtk_adjustment_get_value(adjustment); >+ EmojiSection* sectionToSelect = nullptr; >+ for (auto& section : chooser->priv->sections) { >+ GtkAllocation allocation; >+ if (section.heading) >+ gtk_widget_get_allocation(section.heading, &allocation); >+ else >+ gtk_widget_get_allocation(section.box, &allocation); >+ >+ if (value < allocation.y - boxSpace) >+ break; >+ >+ sectionToSelect = §ion; >+ } >+ >+ if (!sectionToSelect) >+ sectionToSelect = &chooser->priv->sections[0]; >+ >+ for (auto& section : chooser->priv->sections) { >+ if (§ion == sectionToSelect) >+ gtk_widget_set_state_flags(section.button, GTK_STATE_FLAG_CHECKED, FALSE); >+ else >+ gtk_widget_unset_state_flags(section.button, GTK_STATE_FLAG_CHECKED); >+ } >+} >+ >+static GtkWidget* webkitEmojiChooserSetupSectionBox(WebKitEmojiChooser* chooser, GtkBox* parent, const char* title, GtkAdjustment* adjustment, gboolean canHaveVariations = FALSE) >+{ >+ EmojiSection section; >+ if (title) { >+ GtkWidget* label = gtk_label_new(title); >+ section.heading = label; >+ gtk_label_set_xalign(GTK_LABEL(label), 0); >+ gtk_box_pack_start(parent, label, FALSE, FALSE, 0); >+ gtk_widget_show(label); >+ } >+ >+ GtkWidget* box = gtk_flow_box_new(); >+ section.box = box; >+ g_signal_connect(box, "child-activated", G_CALLBACK(emojiActivated), chooser); >+ gtk_flow_box_set_homogeneous(GTK_FLOW_BOX(box), TRUE); >+ gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(box), GTK_SELECTION_NONE); >+ gtk_container_set_focus_vadjustment(GTK_CONTAINER(box), adjustment); >+ gtk_box_pack_start(parent, box, FALSE, FALSE, 0); >+ gtk_widget_show(box); >+ >+ if (canHaveVariations) { >+ GRefPtr<GtkGesture> gesture = adoptGRef(gtk_gesture_long_press_new(box)); >+ g_signal_connect(gesture.get(), "pressed", G_CALLBACK(emojiLongPressed), chooser); >+ chooser->priv->gestures.add(WTFMove(gesture)); >+ GRefPtr<GtkGesture> multiGesture = adoptGRef(gtk_gesture_multi_press_new(box)); >+ gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(multiGesture.get()), GDK_BUTTON_SECONDARY); >+ g_signal_connect(multiGesture.get(), "pressed", G_CALLBACK(emojiPressed), chooser); >+ chooser->priv->gestures.add(WTFMove(multiGesture)); >+ } >+ >+ chooser->priv->sections.append(WTFMove(section)); >+ return box; >+} >+ >+static void scrollToSection(GtkButton* button, gpointer data) >+{ >+ auto* chooser = WEBKIT_EMOJI_CHOOSER(gtk_widget_get_ancestor(GTK_WIDGET(button), WEBKIT_TYPE_EMOJI_CHOOSER)); >+ auto& section = chooser->priv->sections[GPOINTER_TO_UINT(data)]; >+ GtkAdjustment* adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(chooser->priv->swindow)); >+ if (section.heading) { >+ GtkAllocation allocation = { 0, 0, 0, 0 }; >+ gtk_widget_get_allocation(section.heading, &allocation); >+ gtk_adjustment_set_value(adjustment, allocation.y - boxSpace); >+ } else >+ gtk_adjustment_set_value(adjustment, 0); >+} >+ >+static void webkitEmojiChooserSetupSectionButton(WebKitEmojiChooser* chooser, GtkBox* parent, const char* iconName, const char* tooltip) >+{ >+ GtkWidget* button = gtk_button_new_from_icon_name(iconName, GTK_ICON_SIZE_BUTTON); >+ chooser->priv->sections.last().button = button; >+ gtk_style_context_add_class(gtk_widget_get_style_context(button), "emoji-section"); >+ gtk_widget_set_tooltip_text(button, tooltip); >+ g_signal_connect(button, "clicked", G_CALLBACK(scrollToSection), GUINT_TO_POINTER(chooser->priv->sections.size() - 1)); >+ gtk_box_pack_start(parent, button, FALSE, FALSE, 0); >+ gtk_widget_show(button); >+} >+ >+static void webkitEmojiChooserSetupRecent(WebKitEmojiChooser* chooser, GtkBox* emojiBox, GtkBox* buttonBox, GtkAdjustment* adjustment) >+{ >+ GtkWidget* flowBox = webkitEmojiChooserSetupSectionBox(chooser, emojiBox, nullptr, adjustment, true); >+ webkitEmojiChooserSetupSectionButton(chooser, buttonBox, "emoji-recent-symbolic", _("Recent")); >+ >+ bool isEmpty = true; >+ GRefPtr<GVariant> variant = adoptGRef(g_settings_get_value(chooser->priv->settings.get(), "recent-emoji")); >+ GVariantIter iter; >+ g_variant_iter_init(&iter, variant.get()); >+ while (GRefPtr<GVariant> item = adoptGRef(g_variant_iter_next_value(&iter))) { >+ GRefPtr<GVariant> emojiData = adoptGRef(g_variant_get_child_value(item.get(), 0)); >+ gunichar modifier; >+ g_variant_get_child(item.get(), 1, "u", &modifier); >+ if (auto* child = webkitEmojiChooserAddEmoji(chooser, GTK_FLOW_BOX(flowBox), emojiData.get(), true, modifier)) >+ g_signal_connect(child, "popup-menu", G_CALLBACK(emojiPopupMenu), chooser); >+ isEmpty = false; >+ } >+ >+ if (isEmpty) { >+ gtk_widget_hide(flowBox); >+ gtk_widget_set_sensitive(chooser->priv->sections.first().button, FALSE); >+ } >+} >+ >+static void webkitEmojiChooserEnsureEmptyResult(WebKitEmojiChooser* chooser) >+{ >+ if (gtk_stack_get_child_by_name(GTK_STACK(chooser->priv->stack), "empty")) >+ return; >+ >+ GtkWidget* grid = gtk_grid_new(); >+ gtk_grid_set_row_spacing(GTK_GRID(grid), 12); >+ gtk_widget_set_halign(grid, GTK_ALIGN_CENTER); >+ gtk_widget_set_valign(grid, GTK_ALIGN_CENTER); >+ gtk_style_context_add_class(gtk_widget_get_style_context(grid), "dim-label"); >+ >+ GtkWidget* image = gtk_image_new_from_icon_name("edit-find-symbolic", GTK_ICON_SIZE_DIALOG); >+ gtk_image_set_pixel_size(GTK_IMAGE(image), 72); >+ gtk_style_context_add_class(gtk_widget_get_style_context(image), "dim-label"); >+ gtk_grid_attach(GTK_GRID(grid), image, 0, 0, 1, 1); >+ gtk_widget_show(image); >+ >+ GtkWidget* label = gtk_label_new(_("No Results Found")); >+ PangoAttrList* attributes = pango_attr_list_new(); >+ pango_attr_list_insert(attributes, pango_attr_scale_new(1.44)); >+ pango_attr_list_insert(attributes, pango_attr_weight_new(PANGO_WEIGHT_BOLD)); >+ gtk_label_set_attributes(GTK_LABEL(label), attributes); >+ pango_attr_list_unref(attributes); >+ gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1); >+ gtk_widget_show(label); >+ >+ label = gtk_label_new(_("Try a different search")); >+ gtk_style_context_add_class(gtk_widget_get_style_context(label), "dim-label"); >+ gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1); >+ gtk_widget_show(label); >+ >+ gtk_stack_add_named(GTK_STACK(chooser->priv->stack), grid, "empty"); >+ gtk_widget_show(grid); >+} >+ >+static void webkitEmojiChooserSearchChanged(WebKitEmojiChooser* chooser) >+{ >+ for (auto& section : chooser->priv->sections) { >+ section.isEmpty = true; >+ gtk_flow_box_invalidate_filter(GTK_FLOW_BOX(section.box)); >+ } >+ >+ bool resultsFound = false; >+ for (auto& section : chooser->priv->sections) { >+ if (section.heading) { >+ gtk_widget_set_visible(section.heading, !section.isEmpty); >+ gtk_widget_set_visible(section.box, !section.isEmpty); >+ } >+ resultsFound = resultsFound || !section.isEmpty; >+ } >+ >+ if (!resultsFound) { >+ webkitEmojiChooserEnsureEmptyResult(chooser); >+ gtk_stack_set_visible_child_name(GTK_STACK(chooser->priv->stack), "empty"); >+ } else >+ gtk_stack_set_visible_child_name(GTK_STACK(chooser->priv->stack), "list"); >+} >+ >+static void webkitEmojiChooserSetupFilters(WebKitEmojiChooser* chooser) >+{ >+ for (size_t i = 0; i < chooser->priv->sections.size(); ++i) { >+ gtk_flow_box_set_filter_func(GTK_FLOW_BOX(chooser->priv->sections[i].box), [](GtkFlowBoxChild* child, gpointer userData) -> gboolean { >+ auto* chooser = WEBKIT_EMOJI_CHOOSER(gtk_widget_get_ancestor(GTK_WIDGET(child), WEBKIT_TYPE_EMOJI_CHOOSER)); >+ auto& section = chooser->priv->sections[GPOINTER_TO_UINT(userData)]; >+ const char* text = gtk_entry_get_text(GTK_ENTRY(chooser->priv->searchEntry)); >+ if (!text || !*text) { >+ section.isEmpty = false; >+ return TRUE; >+ } >+ >+ auto* emojiData = static_cast<GVariant*>(g_object_get_data(G_OBJECT(child), "emoji-data")); >+ if (!emojiData) { >+ section.isEmpty = false; >+ return TRUE; >+ } >+ >+ const char* name; >+ g_variant_get_child(emojiData, 1, "&s", &name); >+ if (g_str_match_string(text, name, TRUE)) { >+ section.isEmpty = false; >+ return TRUE; >+ } >+ >+ return FALSE; >+ }, GUINT_TO_POINTER(i), nullptr); >+ } >+} >+ >+static void webkitEmojiChooserInitializeEmojiMaxWidth(WebKitEmojiChooser* chooser) >+{ >+ // Get a reasonable maximum width for an emoji. We do this to skip overly wide fallback >+ // rendering for certain emojis the font does not contain and therefore end up being >+ // rendered as multiple glyphs. >+ GRefPtr<PangoLayout> layout = adoptGRef(gtk_widget_create_pango_layout(GTK_WIDGET(chooser), "ð")); >+ auto* attributes = pango_attr_list_new(); >+ pango_attr_list_insert(attributes, pango_attr_scale_new(PANGO_SCALE_X_LARGE)); >+ pango_layout_set_attributes(layout.get(), attributes); >+ pango_attr_list_unref(attributes); >+ >+ PangoRectangle rect; >+ pango_layout_get_extents(layout.get(), &rect, nullptr); >+ chooser->priv->emojiMaxWidth = rect.width; >+} >+ >+static void webkitEmojiChooserConstructed(GObject* object) >+{ >+ WebKitEmojiChooser* chooser = WEBKIT_EMOJI_CHOOSER(object); >+ chooser->priv->settings = adoptGRef(g_settings_new("org.gtk.Settings.EmojiChooser")); >+ >+ G_OBJECT_CLASS(webkit_emoji_chooser_parent_class)->constructed(object); >+ >+ webkitEmojiChooserInitializeEmojiMaxWidth(chooser); >+ >+ gtk_style_context_add_class(gtk_widget_get_style_context(GTK_WIDGET(object)), "emoji-picker"); >+ >+ GtkWidget* mainBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); >+ GtkWidget* searchEntry = gtk_search_entry_new(); >+ chooser->priv->searchEntry = searchEntry; >+ g_signal_connect_swapped(searchEntry, "search-changed", G_CALLBACK(webkitEmojiChooserSearchChanged), chooser); >+ gtk_entry_set_input_hints(GTK_ENTRY(searchEntry), GTK_INPUT_HINT_NO_EMOJI); >+ gtk_box_pack_start(GTK_BOX(mainBox), searchEntry, TRUE, FALSE, 0); >+ gtk_widget_show(searchEntry); >+ >+ GtkWidget* stack = gtk_stack_new(); >+ chooser->priv->stack = stack; >+ GtkWidget* box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); >+ GtkWidget* swindow = gtk_scrolled_window_new(nullptr, nullptr); >+ chooser->priv->swindow = swindow; >+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); >+ gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(swindow), 250); >+ gtk_style_context_add_class(gtk_widget_get_style_context(swindow), "view"); >+ gtk_box_pack_start(GTK_BOX(box), swindow, TRUE, TRUE, 0); >+ gtk_widget_show(swindow); >+ >+ GtkWidget* emojiBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); >+ g_object_set(emojiBox, "margin", 6, nullptr); >+ gtk_container_add(GTK_CONTAINER(swindow), emojiBox); >+ gtk_widget_show(emojiBox); >+ >+ GtkWidget* buttonBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); >+ gtk_box_pack_start(GTK_BOX(box), buttonBox, TRUE, FALSE, 0); >+ gtk_widget_show(buttonBox); >+ >+ GtkAdjustment* vAdjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(swindow)); >+ g_signal_connect(vAdjustment, "value-changed", G_CALLBACK(verticalAdjustmentChanged), chooser); >+ >+ webkitEmojiChooserSetupRecent(chooser, GTK_BOX(emojiBox), GTK_BOX(buttonBox), vAdjustment); >+ >+ GRefPtr<GBytes> bytes = adoptGRef(g_resources_lookup_data("/org/gtk/libgtk/emoji/emoji.data", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr)); >+ GRefPtr<GVariant> data = g_variant_new_from_bytes(G_VARIANT_TYPE("a(auss)"), bytes.get(), TRUE); >+ GVariantIter iter; >+ g_variant_iter_init(&iter, data.get()); >+ GtkWidget* flowBox = nullptr; >+ while (GRefPtr<GVariant> item = adoptGRef(g_variant_iter_next_value(&iter))) { >+ const char* name; >+ g_variant_get_child(item.get(), 1, "&s", &name); >+ >+ if (!g_strcmp0(name, "grinning face")) { >+ const char* title = _("Smileys & People"); >+ flowBox = webkitEmojiChooserSetupSectionBox(chooser, GTK_BOX(emojiBox), title, vAdjustment, true); >+ webkitEmojiChooserSetupSectionButton(chooser, GTK_BOX(buttonBox), "emoji-people-symbolic", title); >+ } else if (!g_strcmp0(name, "selfie")) { >+ const char* title = _("Body & Clothing"); >+ flowBox = webkitEmojiChooserSetupSectionBox(chooser, GTK_BOX(emojiBox), title, vAdjustment, true); >+ webkitEmojiChooserSetupSectionButton(chooser, GTK_BOX(buttonBox), "emoji-body-symbolic", title); >+ } else if (!g_strcmp0(name, "monkey")) { >+ const char* title = _("Animals & Nature"); >+ flowBox = webkitEmojiChooserSetupSectionBox(chooser, GTK_BOX(emojiBox), title, vAdjustment); >+ webkitEmojiChooserSetupSectionButton(chooser, GTK_BOX(buttonBox), "emoji-nature-symbolic", title); >+ } else if (!g_strcmp0(name, "grapes")) { >+ const char* title = _("Food & Drink"); >+ flowBox = webkitEmojiChooserSetupSectionBox(chooser, GTK_BOX(emojiBox), title, vAdjustment); >+ webkitEmojiChooserSetupSectionButton(chooser, GTK_BOX(buttonBox), "emoji-food-symbolic", title); >+ } else if (!g_strcmp0(name, "globe showing Europe-Africa")) { >+ const char* title = _("Travel & Places"); >+ flowBox = webkitEmojiChooserSetupSectionBox(chooser, GTK_BOX(emojiBox), title, vAdjustment); >+ webkitEmojiChooserSetupSectionButton(chooser, GTK_BOX(buttonBox), "emoji-travel-symbolic", title); >+ } else if (!g_strcmp0(name, "jack-o-lantern")) { >+ const char* title = _("Activities"); >+ flowBox = webkitEmojiChooserSetupSectionBox(chooser, GTK_BOX(emojiBox), title, vAdjustment); >+ webkitEmojiChooserSetupSectionButton(chooser, GTK_BOX(buttonBox), "emoji-activities-symbolic", title); >+ } else if (!g_strcmp0(name, "muted speaker")) { >+ const char* title = _("Objects"); >+ flowBox = webkitEmojiChooserSetupSectionBox(chooser, GTK_BOX(emojiBox), title, vAdjustment); >+ webkitEmojiChooserSetupSectionButton(chooser, GTK_BOX(buttonBox), "emoji-objects-symbolic", title); >+ } else if (!g_strcmp0(name, "ATM sign")) { >+ const char* title = _("Symbols"); >+ flowBox = webkitEmojiChooserSetupSectionBox(chooser, GTK_BOX(emojiBox), title, vAdjustment); >+ webkitEmojiChooserSetupSectionButton(chooser, GTK_BOX(buttonBox), "emoji-symbols-symbolic", title); >+ } else if (!g_strcmp0(name, "chequered flag")) { >+ const char* title = _("Flags"); >+ flowBox = webkitEmojiChooserSetupSectionBox(chooser, GTK_BOX(emojiBox), title, vAdjustment); >+ webkitEmojiChooserSetupSectionButton(chooser, GTK_BOX(buttonBox), "emoji-flags-symbolic", title); >+ } >+ auto* child = webkitEmojiChooserAddEmoji(chooser, GTK_FLOW_BOX(flowBox), item.get()); >+ if (child) >+ g_signal_connect(child, "popup-menu", G_CALLBACK(emojiPopupMenu), chooser); >+ } >+ >+ gtk_widget_set_state_flags(chooser->priv->sections.first().button, GTK_STATE_FLAG_CHECKED, FALSE); >+ >+ gtk_stack_add_named(GTK_STACK(stack), box, "list"); >+ gtk_widget_show(box); >+ >+ gtk_box_pack_start(GTK_BOX(mainBox), stack, TRUE, TRUE, 0); >+ gtk_widget_show(stack); >+ >+ gtk_container_add(GTK_CONTAINER(object), mainBox); >+ gtk_widget_show(mainBox); >+ >+ webkitEmojiChooserSetupFilters(chooser); >+} >+ >+static void webkitEmojiChooserShow(GtkWidget* widget) >+{ >+ GTK_WIDGET_CLASS(webkit_emoji_chooser_parent_class)->show(widget); >+ >+ WebKitEmojiChooser* chooser = WEBKIT_EMOJI_CHOOSER(widget); >+ auto* adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(chooser->priv->swindow)); >+ gtk_adjustment_set_value(adjustment, 0); >+ >+ gtk_entry_set_text(GTK_ENTRY(chooser->priv->searchEntry), ""); >+} >+ >+static void webkit_emoji_chooser_class_init(WebKitEmojiChooserClass* klass) >+{ >+ GObjectClass* objectClass = G_OBJECT_CLASS(klass); >+ objectClass->constructed = webkitEmojiChooserConstructed; >+ >+ GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(klass); >+ widgetClass->show = webkitEmojiChooserShow; >+ >+ signals[EMOJI_PICKED] = g_signal_new( >+ "emoji-picked", >+ G_OBJECT_CLASS_TYPE(objectClass), >+ G_SIGNAL_RUN_LAST, >+ 0, >+ nullptr, nullptr, >+ nullptr, >+ G_TYPE_NONE, 1, >+ G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE); >+} >+ >+GtkWidget* webkitEmojiChooserNew() >+{ >+ WebKitEmojiChooser* authDialog = WEBKIT_EMOJI_CHOOSER(g_object_new(WEBKIT_TYPE_EMOJI_CHOOSER, nullptr)); >+ return GTK_WIDGET(authDialog); >+} >+ >+#endif // GTK_CHECK_VERSION(3, 24, 0) >diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitEmojiChooser.h b/Source/WebKit/UIProcess/API/gtk/WebKitEmojiChooser.h >new file mode 100644 >index 00000000000..2b3d0e50fd1 >--- /dev/null >+++ b/Source/WebKit/UIProcess/API/gtk/WebKitEmojiChooser.h >@@ -0,0 +1,54 @@ >+/* >+ * Copyright (C) 2019 Igalia S.L. >+ * >+ * This library is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU Library General Public >+ * License as published by the Free Software Foundation; either >+ * version 2 of the License, or (at your option) any later version. >+ * >+ * This library is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * Library General Public License for more details. >+ * >+ * You should have received a copy of the GNU Library General Public License >+ * along with this library; see the file COPYING.LIB. If not, write to >+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, >+ * Boston, MA 02110-1301, USA. >+ */ >+ >+#pragma once >+ >+#include <gtk/gtk.h> >+ >+#if GTK_CHECK_VERSION(3, 24, 0) >+ >+G_BEGIN_DECLS >+ >+#define WEBKIT_TYPE_EMOJI_CHOOSER (webkit_emoji_chooser_get_type()) >+#define WEBKIT_EMOJI_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_EMOJI_CHOOSER, WebKitEmojiChooser)) >+#define WEBKIT_IS_EMOJI_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_EMOJI_CHOOSER)) >+#define WEBKIT_EMOJI_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_EMOJI_CHOOSER, WebKitEmojiChooserClass)) >+#define WEBKIT_IS_EMOJI_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_EMOJI_CHOOSER)) >+#define WEBKIT_EMOJI_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_EMOJI_CHOOSER, WebKitEmojiChooserClass)) >+ >+typedef struct _WebKitEmojiChooser WebKitEmojiChooser; >+typedef struct _WebKitEmojiChooserClass WebKitEmojiChooserClass; >+typedef struct _WebKitEmojiChooserPrivate WebKitEmojiChooserPrivate; >+ >+struct _WebKitEmojiChooser { >+ GtkPopover parent; >+ >+ WebKitEmojiChooserPrivate* priv; >+}; >+ >+struct _WebKitEmojiChooserClass { >+ GtkPopoverClass parentClass; >+}; >+ >+GType webkit_emoji_chooser_get_type(); >+GtkWidget* webkitEmojiChooserNew(); >+ >+G_END_DECLS >+ >+#endif // GTK_CHECK_VERSION(3, 24, 0) >diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp >index 18b60979661..186fd3b8d20 100644 >--- a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp >+++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp >@@ -42,6 +42,7 @@ > #include "WebEventFactory.h" > #include "WebInspectorProxy.h" > #include "WebKit2Initialize.h" >+#include "WebKitEmojiChooser.h" > #include "WebKitWebViewBaseAccessible.h" > #include "WebKitWebViewBasePrivate.h" > #include "WebPageGroup.h" >@@ -67,6 +68,7 @@ > #include <wtf/Compiler.h> > #include <wtf/HashMap.h> > #include <wtf/glib/GRefPtr.h> >+#include <wtf/glib/RunLoopSourcePriority.h> > #include <wtf/glib/WTFGType.h> > #include <wtf/text/CString.h> > >@@ -147,7 +149,13 @@ typedef HashMap<uint32_t, GUniquePtr<GdkEvent>> TouchEventsMap; > struct _WebKitWebViewBasePrivate { > _WebKitWebViewBasePrivate() > : updateActivityStateTimer(RunLoop::main(), this, &_WebKitWebViewBasePrivate::updateActivityStateTimerFired) >+#if GTK_CHECK_VERSION(3, 24, 0) >+ , releaseEmojiChooserTimer(RunLoop::main(), this, &_WebKitWebViewBasePrivate::releaseEmojiChooserTimerFired) >+#endif > { >+#if GTK_CHECK_VERSION(3, 24, 0) >+ releaseEmojiChooserTimer.setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer); >+#endif > } > > void updateActivityStateTimerFired() >@@ -158,6 +166,16 @@ struct _WebKitWebViewBasePrivate { > activityStateFlagsToUpdate = { }; > } > >+#if GTK_CHECK_VERSION(3, 24, 0) >+ void releaseEmojiChooserTimerFired() >+ { >+ if (emojiChooser) { >+ gtk_widget_destroy(emojiChooser); >+ emojiChooser = nullptr; >+ } >+ } >+#endif >+ > WebKitWebViewChildrenMap children; > std::unique_ptr<PageClientImpl> pageClient; > RefPtr<WebPageProxy> pageProxy; >@@ -207,6 +225,12 @@ struct _WebKitWebViewBasePrivate { > #endif > std::unique_ptr<ViewGestureController> viewGestureController; > bool isBackForwardNavigationGestureEnabled { false }; >+ >+#if GTK_CHECK_VERSION(3, 24, 0) >+ GtkWidget* emojiChooser; >+ CompletionHandler<void(String)> emojiChooserCompletionHandler; >+ RunLoop::Timer<WebKitWebViewBasePrivate> releaseEmojiChooserTimer; >+#endif > }; > > WEBKIT_DEFINE_TYPE(WebKitWebViewBase, webkit_web_view_base, GTK_TYPE_CONTAINER) >@@ -1754,3 +1778,47 @@ void webkitWebViewBaseDidRestoreScrollPosition(WebKitWebViewBase* webkitWebViewB > if (controller && controller->isSwipeGestureEnabled()) > webkitWebViewBase->priv->viewGestureController->didRestoreScrollPosition(); > } >+ >+#if GTK_CHECK_VERSION(3, 24, 0) >+static void emojiChooserEmojiPicked(WebKitWebViewBase* webkitWebViewBase, const char* text) >+{ >+ WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv; >+ auto completionHandler = std::exchange(priv->emojiChooserCompletionHandler, nullptr); >+ if (completionHandler) >+ completionHandler(String::fromUTF8(text)); >+} >+ >+static void emojiChooserClosed(WebKitWebViewBase* webkitWebViewBase) >+{ >+ WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv; >+ auto completionHandler = std::exchange(priv->emojiChooserCompletionHandler, nullptr); >+ if (completionHandler) >+ completionHandler(emptyString()); >+ priv->releaseEmojiChooserTimer.startOneShot(2_min); >+} >+#endif >+ >+void webkitWebViewBaseShowEmojiChooser(WebKitWebViewBase* webkitWebViewBase, const IntRect& caretRect, CompletionHandler<void(String)>&& completionHandler) >+{ >+#if GTK_CHECK_VERSION(3, 24, 0) >+ WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv; >+ priv->releaseEmojiChooserTimer.stop(); >+ >+ if (!priv->emojiChooser) { >+ priv->emojiChooser = webkitEmojiChooserNew(); >+ g_signal_connect_swapped(priv->emojiChooser, "emoji-picked", G_CALLBACK(emojiChooserEmojiPicked), webkitWebViewBase); >+ g_signal_connect_swapped(priv->emojiChooser, "closed", G_CALLBACK(emojiChooserClosed), webkitWebViewBase); >+ gtk_popover_set_relative_to(GTK_POPOVER(priv->emojiChooser), GTK_WIDGET(webkitWebViewBase)); >+ } >+ >+ priv->emojiChooserCompletionHandler = WTFMove(completionHandler); >+ >+ GdkRectangle gdkCaretRect = caretRect; >+ gtk_popover_set_pointing_to(GTK_POPOVER(priv->emojiChooser), &gdkCaretRect); >+ gtk_popover_popup(GTK_POPOVER(priv->emojiChooser)); >+#else >+ UNUSED_PARAM(webkitWebViewBase); >+ UNUSED_PARAM(caretRect); >+ completionHandler(emptyString()); >+#endif >+} >diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h >index 54fe01e5c30..66393f7577f 100644 >--- a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h >+++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h >@@ -93,3 +93,5 @@ void webkitWebViewBaseDidFinishLoadForMainFrame(WebKitWebViewBase*); > void webkitWebViewBaseDidFailLoadForMainFrame(WebKitWebViewBase*); > void webkitWebViewBaseDidSameDocumentNavigationForMainFrame(WebKitWebViewBase*, WebKit::SameDocumentNavigationType); > void webkitWebViewBaseDidRestoreScrollPosition(WebKitWebViewBase*); >+ >+void webkitWebViewBaseShowEmojiChooser(WebKitWebViewBase*, const WebCore::IntRect&, CompletionHandler<void(String)>&&); >diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h >index df8a4f5d37c..062a4c89905 100644 >--- a/Source/WebKit/UIProcess/WebPageProxy.h >+++ b/Source/WebKit/UIProcess/WebPageProxy.h >@@ -1774,6 +1774,7 @@ private: > #if PLATFORM(GTK) > void getEditorCommandsForKeyEvent(const AtomicString&, Vector<String>&); > void bindAccessibilityTree(const String&); >+ void showEmojiPicker(const WebCore::IntRect&, CompletionHandler<void(String)>&&); > #endif > > // Popup Menu. >diff --git a/Source/WebKit/UIProcess/WebPageProxy.messages.in b/Source/WebKit/UIProcess/WebPageProxy.messages.in >index 691b417e917..8a7f8c4fe91 100644 >--- a/Source/WebKit/UIProcess/WebPageProxy.messages.in >+++ b/Source/WebKit/UIProcess/WebPageProxy.messages.in >@@ -563,4 +563,8 @@ messages -> WebPageProxy { > #endif > > ConfigureLoggingChannel(String channelName, enum:uint8_t WTFLogChannelState state, enum:uint8_t WTFLogLevel level) >+ >+#if PLATFORM(GTK) >+ ShowEmojiPicker(WebCore::IntRect caretRect) -> (String result) Async >+#endif > } >diff --git a/Source/WebKit/UIProcess/gtk/KeyBindingTranslator.cpp b/Source/WebKit/UIProcess/gtk/KeyBindingTranslator.cpp >index f179f16f058..4490ce60131 100644 >--- a/Source/WebKit/UIProcess/gtk/KeyBindingTranslator.cpp >+++ b/Source/WebKit/UIProcess/gtk/KeyBindingTranslator.cpp >@@ -60,6 +60,14 @@ static void toggleOverwriteCallback(GtkWidget* widget, KeyBindingTranslator* tra > translator->addPendingEditorCommand("OverWrite"); > } > >+#if GTK_CHECK_VERSION(3, 24, 0) >+static void insertEmojiCallback(GtkWidget* widget, KeyBindingTranslator* translator) >+{ >+ g_signal_stop_emission_by_name(widget, "insert-emoji"); >+ translator->addPendingEditorCommand("GtkInsertEmoji"); >+} >+#endif >+ > // GTK+ will still send these signals to the web view. So we can safely stop signal > // emission without breaking accessibility. > static void popupMenuCallback(GtkWidget* widget, KeyBindingTranslator*) >@@ -173,6 +181,9 @@ KeyBindingTranslator::KeyBindingTranslator() > g_signal_connect(m_nativeWidget.get(), "toggle-overwrite", G_CALLBACK(toggleOverwriteCallback), this); > g_signal_connect(m_nativeWidget.get(), "popup-menu", G_CALLBACK(popupMenuCallback), this); > g_signal_connect(m_nativeWidget.get(), "show-help", G_CALLBACK(showHelpCallback), this); >+#if GTK_CHECK_VERSION(3, 24, 0) >+ g_signal_connect(m_nativeWidget.get(), "insert-emoji", G_CALLBACK(insertEmojiCallback), this); >+#endif > } > > struct KeyCombinationEntry { >diff --git a/Source/WebKit/UIProcess/gtk/WebPageProxyGtk.cpp b/Source/WebKit/UIProcess/gtk/WebPageProxyGtk.cpp >index f6186a5d26c..520aac92c01 100644 >--- a/Source/WebKit/UIProcess/gtk/WebPageProxyGtk.cpp >+++ b/Source/WebKit/UIProcess/gtk/WebPageProxyGtk.cpp >@@ -163,4 +163,9 @@ bool WebPageProxy::makeGLContextCurrent() > return webkitWebViewBaseMakeGLContextCurrent(WEBKIT_WEB_VIEW_BASE(viewWidget())); > } > >+void WebPageProxy::showEmojiPicker(const WebCore::IntRect& caretRect, CompletionHandler<void(String)>&& completionHandler) >+{ >+ webkitWebViewBaseShowEmojiChooser(WEBKIT_WEB_VIEW_BASE(viewWidget()), caretRect, WTFMove(completionHandler)); >+} >+ > } // namespace WebKit >diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebContextMenuClient.h b/Source/WebKit/WebProcess/WebCoreSupport/WebContextMenuClient.h >index 5d8e7d6d0fa..6c29b4e0812 100644 >--- a/Source/WebKit/WebProcess/WebCoreSupport/WebContextMenuClient.h >+++ b/Source/WebKit/WebProcess/WebCoreSupport/WebContextMenuClient.h >@@ -55,6 +55,10 @@ private: > void searchWithSpotlight() override; > #endif > >+#if PLATFORM(GTK) >+ void insertEmoji(WebCore::Frame&) override; >+#endif >+ > #if USE(ACCESSIBILITY_CONTEXT_MENUS) > void showContextMenu() override; > #endif >diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h b/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h >index 1964860a43e..63673c6ca7c 100644 >--- a/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h >+++ b/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h >@@ -140,7 +140,8 @@ private: > #endif > > #if PLATFORM(GTK) >- bool executePendingEditorCommands(WebCore::Frame*, const Vector<WTF::String>&, bool); >+ bool executePendingEditorCommands(WebCore::Frame&, const Vector<WTF::String>&, bool); >+ bool handleGtkEditorCommand(WebCore::Frame&, const String& command, bool); > void getEditorCommandsForKeyEvent(const WebCore::KeyboardEvent*, Vector<WTF::String>&); > void updateGlobalSelection(WebCore::Frame*); > #endif >diff --git a/Source/WebKit/WebProcess/WebCoreSupport/gtk/WebContextMenuClientGtk.cpp b/Source/WebKit/WebProcess/WebCoreSupport/gtk/WebContextMenuClientGtk.cpp >index 9cd11d4c51d..2fe27cf8e3d 100644 >--- a/Source/WebKit/WebProcess/WebCoreSupport/gtk/WebContextMenuClientGtk.cpp >+++ b/Source/WebKit/WebProcess/WebCoreSupport/gtk/WebContextMenuClientGtk.cpp >@@ -29,6 +29,7 @@ > > #if ENABLE(CONTEXT_MENUS) > >+#include "WebPage.h" > #include <WebCore/NotImplemented.h> > > namespace WebKit { >@@ -55,5 +56,10 @@ void WebContextMenuClient::stopSpeaking() > notImplemented(); > } > >+void WebContextMenuClient::insertEmoji(Frame& frame) >+{ >+ m_page->showEmojiPicker(frame); >+} >+ > } // namespace WebKit > #endif // ENABLE(CONTEXT_MENUS) >diff --git a/Source/WebKit/WebProcess/WebCoreSupport/gtk/WebEditorClientGtk.cpp b/Source/WebKit/WebProcess/WebCoreSupport/gtk/WebEditorClientGtk.cpp >index db99772a187..b275dea6728 100644 >--- a/Source/WebKit/WebProcess/WebCoreSupport/gtk/WebEditorClientGtk.cpp >+++ b/Source/WebKit/WebProcess/WebCoreSupport/gtk/WebEditorClientGtk.cpp >@@ -28,25 +28,48 @@ > #include <WebCore/Pasteboard.h> > #include <WebCore/PlatformKeyboardEvent.h> > #include <WebCore/markup.h> >+#include <wtf/Variant.h> > #include <wtf/glib/GRefPtr.h> > > namespace WebKit { > using namespace WebCore; > >-bool WebEditorClient::executePendingEditorCommands(Frame* frame, const Vector<WTF::String>& pendingEditorCommands, bool allowTextInsertion) >+bool WebEditorClient::handleGtkEditorCommand(Frame& frame, const String& command, bool allowTextInsertion) > { >- Vector<Editor::Command> commands; >- for (auto& commandString : pendingEditorCommands) { >- Editor::Command command = frame->editor().command(commandString); >- if (command.isTextInsertion() && !allowTextInsertion) >+ if (command == "GtkInsertEmoji"_s) { >+ if (!allowTextInsertion) > return false; >+ m_page->showEmojiPicker(frame); >+ return true; >+ } > >- commands.append(WTFMove(command)); >+ return false; >+} >+ >+bool WebEditorClient::executePendingEditorCommands(Frame& frame, const Vector<WTF::String>& pendingEditorCommands, bool allowTextInsertion) >+{ >+ Vector<Variant<Editor::Command, String>> commands; >+ for (auto& commandString : pendingEditorCommands) { >+ if (commandString.startsWith("Gtk")) >+ commands.append(commandString); >+ else { >+ Editor::Command command = frame.editor().command(commandString); >+ if (command.isTextInsertion() && !allowTextInsertion) >+ return false; >+ >+ commands.append(WTFMove(command)); >+ } > } > >- for (auto& command : commands) { >- if (!command.execute()) >- return false; >+ for (auto& commandVariant : commands) { >+ if (WTF::holds_alternative<String>(commandVariant)) { >+ if (!handleGtkEditorCommand(frame, WTF::get<String>(commandVariant), allowTextInsertion)) >+ return false; >+ } else { >+ auto& command = WTF::get<Editor::Command>(commandVariant); >+ if (!command.execute()) >+ return false; >+ } > } > > return true; >@@ -73,14 +96,14 @@ void WebEditorClient::handleKeyboardEvent(KeyboardEvent& event) > // the insertion until the keypress event. We want keydown to bubble up > // through the DOM first. > if (platformEvent->type() == PlatformEvent::RawKeyDown) { >- if (executePendingEditorCommands(frame, pendingEditorCommands, false)) >+ if (executePendingEditorCommands(*frame, pendingEditorCommands, false)) > event.setDefaultHandled(); > > return; > } > > // Only allow text insertion commands if the current node is editable. >- if (executePendingEditorCommands(frame, pendingEditorCommands, frame->editor().canEdit())) { >+ if (executePendingEditorCommands(*frame, pendingEditorCommands, frame->editor().canEdit())) { > event.setDefaultHandled(); > return; > } >diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.h b/Source/WebKit/WebProcess/WebPage/WebPage.h >index 732890c7c57..c486fc46714 100644 >--- a/Source/WebKit/WebProcess/WebPage/WebPage.h >+++ b/Source/WebKit/WebProcess/WebPage/WebPage.h >@@ -763,6 +763,7 @@ public: > void cancelComposition(); > > void collapseSelectionInFrame(uint64_t frameID); >+ void showEmojiPicker(WebCore::Frame&); > #endif > > #if PLATFORM (GTK) && HAVE(GTK_GESTURES) >diff --git a/Source/WebKit/WebProcess/WebPage/gtk/WebPageGtk.cpp b/Source/WebKit/WebProcess/WebPage/gtk/WebPageGtk.cpp >index 47e25325d72..8c3a3d17107 100644 >--- a/Source/WebKit/WebProcess/WebPage/gtk/WebPageGtk.cpp >+++ b/Source/WebKit/WebProcess/WebPage/gtk/WebPageGtk.cpp >@@ -195,6 +195,15 @@ void WebPage::collapseSelectionInFrame(uint64_t frameID) > frame->coreFrame()->selection().setBase(selection.extent(), selection.affinity()); > } > >+void WebPage::showEmojiPicker(Frame& frame) >+{ >+ CompletionHandler<void(String)> completionHandler = [frame = makeRef(frame)](String result) { >+ if (!result.isEmpty()) >+ frame->editor().insertText(result, nullptr); >+ }; >+ sendWithAsyncReply(Messages::WebPageProxy::ShowEmojiPicker(frame.selection().absoluteCaretBounds()), WTFMove(completionHandler)); >+} >+ > void WebPage::effectiveAppearanceDidChange(bool useDarkAppearance, bool useInactiveAppearance) > { > if (auto* settings = gtk_settings_get_default()) >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 912093f0ffe..fab8216efbc 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,14 @@ >+2019-05-16 Carlos Garcia Campos <cgarcia@igalia.com> >+ >+ [GTK] Need WebKitContextMenuItemType to open emoji picker >+ https://bugs.webkit.org/show_bug.cgi?id=176760 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Update context menu test to check insert emoji action is included in default context menu for editable content. >+ >+ * TestWebKitAPI/Tests/WebKitGtk/TestContextMenu.cpp: >+ > 2019-05-15 Aakash Jain <aakash_jain@apple.com> > > Replace double-quotes with single quotes in steps.py >diff --git a/Tools/TestWebKitAPI/Tests/WebKitGtk/TestContextMenu.cpp b/Tools/TestWebKitAPI/Tests/WebKitGtk/TestContextMenu.cpp >index d7fc734848e..502b5e6379d 100644 >--- a/Tools/TestWebKitAPI/Tests/WebKitGtk/TestContextMenu.cpp >+++ b/Tools/TestWebKitAPI/Tests/WebKitGtk/TestContextMenu.cpp >@@ -402,6 +402,7 @@ public: > iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DELETE, Visible); > iter = checkCurrentItemIsSeparatorAndGetNext(iter); > iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_SELECT_ALL, Visible | Enabled); >+ iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_INSERT_EMOJI, Visible | Enabled); > iter = checkCurrentItemIsSeparatorAndGetNext(iter); > iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_UNICODE, Visible | Enabled); > break;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
mcatanzaro
:
review+
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 176760
: 370039