[gtk+/wip/matthiasc/emoji-picker: 72/75] wip: emoji completions



commit c0d231594b6bc3ba9ac37615867b12be1f813cfc
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Aug 15 17:34:44 2017 -0400

    wip: emoji completions

 gtk/emoji/convert-emoji.c                          |   61 +++-
 gtk/gtkemojichooser.c                              |    8 +-
 gtk/gtkemojicompletion.c                           |  371 ++++++++++++++++++++
 gtk/gtkemojicompletion.h                           |   41 +++
 gtk/gtkemojicompletions.c                          |  192 ----------
 gtk/gtkemojicompletions.h                          |   43 ---
 gtk/gtkentry.c                                     |  124 ++-----
 gtk/gtkentryprivate.h                              |    7 +
 gtk/meson.build                                    |    1 +
 gtk/org.gtk.Settings.EmojiChooser.gschema.xml      |    4 +-
 ...tkemojicompletions.ui => gtkemojicompletion.ui} |    2 +-
 11 files changed, 514 insertions(+), 340 deletions(-)
---
diff --git a/gtk/emoji/convert-emoji.c b/gtk/emoji/convert-emoji.c
index dd406a1..202ca3f 100644
--- a/gtk/emoji/convert-emoji.c
+++ b/gtk/emoji/convert-emoji.c
@@ -23,7 +23,8 @@
 
 gboolean
 parse_code (GVariantBuilder *b,
-            const char      *code)
+            const char      *code,
+            GString         *name_key)
 {
   g_auto(GStrv) strv = NULL;
   int j;
@@ -43,7 +44,12 @@ parse_code (GVariantBuilder *b,
       if (0x1f3fb <= u && u <= 0x1f3ff)
         g_variant_builder_add (b, "u", 0);
       else
-        g_variant_builder_add (b, "u", u);
+        {
+          g_variant_builder_add (b, "u", u);
+          if (j > 0)
+            g_string_append_c (name_key, '-');
+          g_string_append_printf (name_key, "%x", u);
+        }
     }
 
   return TRUE;
@@ -84,19 +90,54 @@ main (int argc, char *argv[])
   JsonParser *parser;
   JsonNode *root;
   JsonArray *array;
+  JsonObject *ro;
+  JsonNode *node;
+  const char *name;
+  JsonObjectIter iter;
   GError *error = NULL;
   guint length, i;
   GVariantBuilder builder;
   GVariant *v;
   GString *s;
   GBytes *bytes;
+  GHashTable *names;
+  GString *name_key;
+
+  if (argc != 4)
+    {
+      g_print ("Usage: emoji-convert INPUT INPUT1 OUTPUT\n");
+      return 1;
+    }
+
+  parser = json_parser_new ();
 
-  if (argc != 3)
+  if (!json_parser_load_from_file (parser, argv[2], &error))
     {
-      g_print ("Usage: emoji-convert INPUT OUTPUT\n");
+      g_error ("%s", error->message);
       return 1;
     }
 
+  root = json_parser_get_root (parser);
+  ro = json_node_get_object (root);
+  json_object_iter_init (&iter, ro);
+
+  names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+  name_key = g_string_new ("");
+
+  while (json_object_iter_next (&iter, &name, &node))
+    {
+      JsonObject *obj = json_node_get_object (node);
+      const char *unicode;
+      const char *shortname;
+
+      unicode = json_object_get_string_member (obj, "unicode");
+      shortname = json_object_get_string_member (obj, "shortname");
+
+      g_hash_table_insert (names, g_strdup (unicode), g_strdup (shortname));
+    }
+
+  g_object_unref (parser);
+
   parser = json_parser_new ();
 
   if (!json_parser_load_from_file (parser, argv[1], &error))
@@ -109,7 +150,7 @@ main (int argc, char *argv[])
   array = json_node_get_array (root);
   length = json_array_get_length (array);
 
-  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(aus)"));
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(auss)"));
   i = 0;
   while (i < length)
     {
@@ -117,6 +158,7 @@ main (int argc, char *argv[])
       JsonObject *obj = json_node_get_object (node);
       GVariantBuilder b1;
       const char *name;
+      const char *shortname;
       char *code;
       int j;
       gboolean skip;
@@ -167,15 +209,18 @@ main (int argc, char *argv[])
           i++;
         }
 
-      if (!parse_code (&b1, code))
+      g_string_set_size (name_key, 0);
+      if (!parse_code (&b1, code, name_key))
         return 1;
 
-      g_variant_builder_add (&builder, "(aus)", &b1, name);
+      shortname = g_hash_table_lookup (names, name_key->str);
+
+      g_variant_builder_add (&builder, "(auss)", &b1, name, shortname ? shortname : "");
     }
 
   v = g_variant_builder_end (&builder);
   bytes = g_variant_get_data_as_bytes (v);
-  if (!g_file_set_contents (argv[2], g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), &error))
+  if (!g_file_set_contents (argv[3], g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), &error))
     {
       g_error ("%s", error->message);
       return 1;
diff --git a/gtk/gtkemojichooser.c b/gtk/gtkemojichooser.c
index 3d47c8e..a5cda4e 100644
--- a/gtk/gtkemojichooser.c
+++ b/gtk/gtkemojichooser.c
@@ -159,8 +159,8 @@ add_recent_item (GtkEmojiChooser *chooser,
 
   g_variant_ref (item);
 
-  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a((aus)u)"));
-  g_variant_builder_add (&builder, "(@(aus)u)", item, modifier);
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a((auss)u)"));
+  g_variant_builder_add (&builder, "(@(auss)u)", item, modifier);
 
   children = gtk_container_get_children (GTK_CONTAINER (chooser->recent.box));
   for (l = children, i = 1; l; l = l->next, i++)
@@ -180,7 +180,7 @@ add_recent_item (GtkEmojiChooser *chooser,
           continue;
         }
 
-      g_variant_builder_add (&builder, "(@(aus)u)", item2, modifier2);
+      g_variant_builder_add (&builder, "(@(auss)u)", item2, modifier2);
     }
   g_list_free (children);
 
@@ -333,7 +333,7 @@ populate_emoji_chooser (GtkEmojiChooser *chooser)
   GtkWidget *box;
 
   bytes = g_resources_lookup_data ("/org/gtk/libgtk/emoji/emoji.data", 0, NULL);
-  chooser->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(aus)"), bytes, TRUE));
+  chooser->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(auss)"), bytes, TRUE));
 
   g_variant_iter_init (&iter, chooser->data);
   box = chooser->people.box;
diff --git a/gtk/gtkemojicompletion.c b/gtk/gtkemojicompletion.c
new file mode 100644
index 0000000..e0036b2
--- /dev/null
+++ b/gtk/gtkemojicompletion.c
@@ -0,0 +1,371 @@
+/* gtkemojicompletion.c: An Emoji picker widget
+ * Copyright 2017, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkemojicompletion.h"
+
+#include "gtkentryprivate.h"
+#include "gtkbox.h"
+#include "gtkcssprovider.h"
+#include "gtklistbox.h"
+#include "gtklabel.h"
+#include "gtkpopover.h"
+#include "gtkintl.h"
+#include "gtkprivate.h"
+
+struct _GtkEmojiCompletion
+{
+  GtkPopover parent_instance;
+
+  GtkEntry *entry;
+  guint length;
+  gulong changed_id;
+
+  GtkWidget *list;
+  GtkWidget *active;
+
+  GVariant *data;
+};
+
+struct _GtkEmojiCompletionClass {
+  GtkPopoverClass parent_class;
+};
+
+static void connect_signals    (GtkEmojiCompletion *completion,
+                                GtkEntry           *entry);
+static void disconnect_signals (GtkEmojiCompletion *completion);
+static int populate_emoji_completion (GtkEmojiCompletion *completion,
+                                      const char          *text);
+
+G_DEFINE_TYPE (GtkEmojiCompletion, gtk_emoji_completion, GTK_TYPE_POPOVER)
+
+static void
+gtk_emoji_completion_finalize (GObject *object)
+{
+  GtkEmojiCompletion *completion = GTK_EMOJI_COMPLETION (object);
+
+  disconnect_signals (completion);
+
+  g_variant_unref (completion->data);
+
+  G_OBJECT_CLASS (gtk_emoji_completion_parent_class)->finalize (object);
+}
+
+static void
+update_completion (GtkEmojiCompletion *completion)
+{
+  const char *text;
+  guint length;
+  guint n_matches;
+
+  n_matches = 0;
+
+  text = gtk_entry_get_text (GTK_ENTRY (completion->entry));
+  length = strlen (text);
+
+  if (length > 0)
+    {
+      gboolean found_candidate = FALSE;
+      const char *p;
+
+      p = text + length;
+      do
+        {
+          p = g_utf8_prev_char (p);
+          if (*p == ':')
+            {
+              if (p == text || !g_unichar_isalnum (g_utf8_get_char (p - 1)))
+                found_candidate = TRUE;
+              break;
+            }
+        }
+      while (g_unichar_isalnum (g_utf8_get_char (p)) || *p == '_');
+
+      if (found_candidate)
+        n_matches = populate_emoji_completion (completion, p);
+    }
+
+  if (n_matches > 0)
+    gtk_popover_popup (GTK_POPOVER (completion));
+  else
+    gtk_popover_popdown (GTK_POPOVER (completion));
+}
+
+static void
+entry_changed (GtkEntry *entry, GtkEmojiCompletion *completion)
+{
+  update_completion (completion);
+}
+
+static void
+emoji_activated (GtkListBox    *list,
+                 GtkListBoxRow *row,
+                 gpointer       data)
+{
+  GtkEmojiCompletion *completion = data;
+  const char *emoji;
+  guint length;
+
+  gtk_popover_popdown (GTK_POPOVER (completion));
+
+  emoji = (const char *)g_object_get_data (G_OBJECT (row), "text");
+
+  g_signal_handler_block (completion->entry, completion->changed_id);
+
+  length = g_utf8_strlen (gtk_entry_get_text (completion->entry), -1);
+  gtk_entry_set_positions (completion->entry, length - completion->length, length);
+  gtk_entry_enter_text (completion->entry, emoji);
+
+  g_signal_handler_unblock (completion->entry, completion->changed_id);
+}
+
+static void
+move_active_row (GtkEmojiCompletion *completion,
+                 int                 direction)
+{
+  GtkWidget *child;
+
+  for (child = gtk_widget_get_first_child (completion->list);
+       child != NULL;
+       child = gtk_widget_get_next_sibling (child))
+    {
+      gtk_widget_unset_state_flags (child, GTK_STATE_FLAG_PRELIGHT);
+    }
+
+  if (completion->active != NULL)
+    {
+      if (direction == 1)
+        completion->active = gtk_widget_get_next_sibling (completion->active);
+      else
+        completion->active = gtk_widget_get_prev_sibling (completion->active);
+    }
+
+  if (completion->active == NULL)
+    {
+      if (direction == 1)
+        completion->active = gtk_widget_get_first_child (completion->list);
+      else
+        completion->active = gtk_widget_get_last_child (completion->list);
+    }
+
+  if (completion->active != NULL)
+    gtk_widget_set_state_flags (completion->active, GTK_STATE_FLAG_PRELIGHT, FALSE);
+}
+
+static void
+activate_active_row (GtkEmojiCompletion *completion)
+{
+  if (completion->active != NULL)
+    emoji_activated (GTK_LIST_BOX (completion->list),
+                     GTK_LIST_BOX_ROW (completion->active),
+                     completion);
+}
+
+static gboolean
+entry_key_press (GtkEntry           *entry,
+                 GdkEventKey        *event,
+                 GtkEmojiCompletion *completion)
+{
+  if (!gtk_widget_get_visible (GTK_WIDGET (completion)))
+    return FALSE;
+
+  if (event->keyval == GDK_KEY_Escape)
+    {
+      gtk_popover_popdown (GTK_POPOVER (completion));
+      return TRUE;
+    }
+
+  if (event->keyval == GDK_KEY_Up)
+    {
+      move_active_row (completion, -1);
+      return TRUE;
+    }
+
+  if (event->keyval == GDK_KEY_Down)
+    {
+      move_active_row (completion, 1);
+      return TRUE;
+    }
+
+  if (event->keyval == GDK_KEY_Return ||
+      event->keyval == GDK_KEY_KP_Enter ||
+      event->keyval == GDK_KEY_ISO_Enter)
+    {
+      activate_active_row (completion);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+entry_focus_out (GtkWidget *entry,
+                 GdkEventFocus *event,
+                 GtkEmojiCompletion *completion)
+{
+  gtk_popover_popdown (GTK_POPOVER (completion));
+  return FALSE;
+}
+
+static void
+connect_signals (GtkEmojiCompletion *completion,
+                 GtkEntry           *entry)
+{
+  completion->entry = entry;
+
+  completion->changed_id = g_signal_connect (entry, "changed", G_CALLBACK (entry_changed), completion);
+  g_signal_connect (entry, "key-press-event", G_CALLBACK (entry_key_press), completion);
+  g_signal_connect (entry, "focus-out-event", G_CALLBACK (entry_focus_out), completion);
+}
+
+static void
+disconnect_signals (GtkEmojiCompletion *completion)
+{
+  g_signal_handlers_disconnect_by_func (completion->entry, entry_changed, completion);
+  g_signal_handlers_disconnect_by_func (completion->entry, entry_key_press, completion);
+  g_signal_handlers_disconnect_by_func (completion->entry, entry_focus_out, completion);
+
+  completion->entry = NULL;
+}
+
+static void
+add_emoji (GtkWidget *list,
+           GVariant  *item)
+{
+  GtkWidget *child;
+  GtkWidget *label;
+  GtkWidget *box;
+  PangoAttrList *attrs;
+  GVariant *codes;
+  char text[64];
+  char *p = text;
+  int i;
+  const char *shortname;
+
+  codes = g_variant_get_child_value (item, 0);
+  for (i = 0; i < g_variant_n_children (codes); i++)
+    {
+      gunichar code;
+
+      g_variant_get_child (codes, i, "u", &code);
+      if (code != 0)
+        p += g_unichar_to_utf8 (code, p);
+    }
+  p[0] = 0;
+
+  label = gtk_label_new (text);
+  attrs = pango_attr_list_new ();
+  pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE));
+  gtk_label_set_attributes (GTK_LABEL (label), attrs);
+  pango_attr_list_unref (attrs);
+
+  child = gtk_list_box_row_new ();
+  gtk_widget_set_focus_on_click (child, FALSE);
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_container_add (GTK_CONTAINER (child), box);
+  gtk_box_pack_start (GTK_BOX (box), label);
+
+  g_variant_get_child (item, 2, "&s", &shortname);
+  label = gtk_label_new (shortname);
+  gtk_box_pack_start (GTK_BOX (box), label);
+
+  g_object_set_data_full (G_OBJECT (child), "text", g_strdup (text), g_free);
+  gtk_style_context_add_class (gtk_widget_get_style_context (child), "emoji-completion-row");
+
+  gtk_list_box_insert (GTK_LIST_BOX (list), child, -1);
+}
+
+#define MAX_ROWS 5
+
+static int
+populate_emoji_completion (GtkEmojiCompletion *completion,
+                            const char          *text)
+{
+  GList *children, *l;
+  gboolean n_matches;
+  GVariantIter iter;
+  GVariant *item;
+
+  completion->length = g_utf8_strlen (text, -1);
+
+  children = gtk_container_get_children (GTK_CONTAINER (completion->list));
+  for (l = children; l; l = l->next)
+    gtk_widget_destroy (GTK_WIDGET (l->data));
+  g_list_free (children);
+
+  completion->active = NULL;
+
+  n_matches = 0;
+  g_variant_iter_init (&iter, completion->data);
+  while ((item = g_variant_iter_next_value (&iter)))
+    {
+      const char *shortname;
+
+      g_variant_get_child (item, 2, "&s", &shortname);
+      if (g_str_has_prefix (shortname, text))
+        {
+          add_emoji (completion->list, item);
+          n_matches++;
+        }
+
+      if (n_matches == MAX_ROWS)
+        break;
+    }
+
+  return n_matches;
+}
+
+static void
+gtk_emoji_completion_init (GtkEmojiCompletion *completion)
+{
+  g_autoptr(GBytes) bytes = NULL;
+
+  gtk_widget_init_template (GTK_WIDGET (completion));
+
+  bytes = g_resources_lookup_data ("/org/gtk/libgtk/emoji/emoji.data", 0, NULL);
+  completion->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(auss)"), bytes, TRUE));
+}
+
+static void
+gtk_emoji_completion_class_init (GtkEmojiCompletionClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gtk_emoji_completion_finalize;
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkemojicompletion.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, GtkEmojiCompletion, list);
+
+  gtk_widget_class_bind_template_callback (widget_class, emoji_activated);
+}
+
+GtkWidget *
+gtk_emoji_completion_new (GtkEntry *entry)
+{
+  GtkEmojiCompletion *completion;
+
+  completion = GTK_EMOJI_COMPLETION (g_object_new (GTK_TYPE_EMOJI_COMPLETION,
+                                                   "relative-to", entry,
+                                                   NULL));
+
+  connect_signals (completion, entry);
+
+  return GTK_WIDGET (completion);
+}
diff --git a/gtk/gtkemojicompletion.h b/gtk/gtkemojicompletion.h
new file mode 100644
index 0000000..ff7cb1f
--- /dev/null
+++ b/gtk/gtkemojicompletion.h
@@ -0,0 +1,41 @@
+/* gtkemojicompletion.h: An Emoji picker widget
+ * Copyright 2017, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkentry.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_EMOJI_COMPLETION                 (gtk_emoji_completion_get_type ())
+#define GTK_EMOJI_COMPLETION(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GTK_TYPE_EMOJI_COMPLETION, GtkEmojiCompletion))
+#define GTK_EMOJI_COMPLETION_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), 
GTK_TYPE_EMOJI_COMPLETION, GtkEmojiCompletionClass))
+#define GTK_IS_EMOJI_COMPLETION(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GTK_TYPE_EMOJI_COMPLETION))
+#define GTK_IS_EMOJI_COMPLETION_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GTK_TYPE_EMOJI_COMPLETION))
+#define GTK_EMOJI_COMPLETION_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GTK_TYPE_EMOJI_COMPLETION, GtkEmojiCompletionClass))
+
+typedef struct _GtkEmojiCompletion      GtkEmojiCompletion;
+typedef struct _GtkEmojiCompletionClass GtkEmojiCompletionClass;
+
+GType      gtk_emoji_completion_get_type (void) G_GNUC_CONST;
+GtkWidget *gtk_emoji_completion_new      (GtkEntry *entry);
+
+G_END_DECLS
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index cd90bc5..8ac4f6e 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -69,7 +69,7 @@
 #include "gtkcssnodeprivate.h"
 #include "gtkimageprivate.h"
 #include "gtkemojichooser.h"
-#include "gtkemojicompletions.h"
+#include "gtkemojicompletion.h"
 
 #include "a11y/gtkentryaccessible.h"
 
@@ -238,6 +238,7 @@ struct _GtkEntryPrivate
 
   guint         editable                : 1;
   guint         show_emoji_icon         : 1;
+  guint         enable_emoji_completion : 1;
   guint         in_drag                 : 1;
   guint         overwrite_mode          : 1;
   guint         visible                 : 1;
@@ -358,6 +359,7 @@ enum {
   PROP_POPULATE_ALL,
   PROP_TABS,
   PROP_SHOW_EMOJI_ICON,
+  PROP_ENABLE_EMOJI_COMPLETION,
   PROP_EDITING_CANCELED,
   NUM_PROPERTIES = PROP_EDITING_CANCELED
 };
@@ -554,11 +556,6 @@ static void   gtk_entry_drag_gesture_end           (GtkGestureDrag *gesture,
 
 /* Internal routines
  */
-static void         gtk_entry_enter_text               (GtkEntry       *entry,
-                                                        const gchar    *str);
-static void         gtk_entry_set_positions            (GtkEntry       *entry,
-                                                       gint            current_pos,
-                                                       gint            selection_bound);
 static void         gtk_entry_draw_text                (GtkEntry       *entry,
                                                         cairo_t        *cr);
 static void         gtk_entry_draw_cursor              (GtkEntry       *entry,
@@ -651,7 +648,8 @@ static void         buffer_disconnect_signals          (GtkEntry       *entry);
 static GtkEntryBuffer *get_buffer                      (GtkEntry       *entry);
 static void         set_show_emoji_icon                (GtkEntry       *entry,
                                                         gboolean        value);
-static void         check_emoji_completion             (GtkEntry       *entry);
+static void         set_enable_emoji_completion        (GtkEntry       *entry,
+                                                        gboolean        value);
 
 static void     gtk_entry_measure (GtkWidget           *widget,
                                    GtkOrientation       orientation,
@@ -1397,6 +1395,13 @@ gtk_entry_class_init (GtkEntryClass *class)
                             FALSE,
                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
+  entry_props[PROP_ENABLE_EMOJI_COMPLETION] =
+      g_param_spec_boolean ("enable-emoji-completion",
+                            P_("Enable Emoji completion"),
+                            P_("Whether to suggest Emoji replacements"),
+                            FALSE,
+                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, entry_props);
 
   /**
@@ -2156,6 +2161,10 @@ gtk_entry_set_property (GObject         *object,
       set_show_emoji_icon (entry, g_value_get_boolean (value));
       break;
 
+    case PROP_ENABLE_EMOJI_COMPLETION:
+      set_enable_emoji_completion (entry, g_value_get_boolean (value));
+      break;
+
     case PROP_SCROLL_OFFSET:
     case PROP_CURSOR_POSITION:
     default:
@@ -2388,6 +2397,10 @@ gtk_entry_get_property (GObject         *object,
       g_value_set_boolean (value, priv->show_emoji_icon);
       break;
 
+    case PROP_ENABLE_EMOJI_COMPLETION:
+      g_value_set_boolean (value, priv->enable_emoji_completion);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -5052,7 +5065,6 @@ gtk_entry_backspace (GtkEntry *entry)
     }
 
   gtk_entry_pend_cursor_blink (entry);
-  check_emoji_completion (entry);
 }
 
 static void
@@ -5279,7 +5291,7 @@ gtk_entry_delete_surrounding_cb (GtkIMContext *slave,
  */
 
 /* Used for im_commit_cb and inserting Unicode chars */
-static void
+void
 gtk_entry_enter_text (GtkEntry       *entry,
                       const gchar    *str)
 {
@@ -5309,13 +5321,12 @@ gtk_entry_enter_text (GtkEntry       *entry,
   gtk_editable_set_position (editable, tmp_pos);
 
   priv->need_im_reset = old_need_im_reset;
-  check_emoji_completion (entry);
 }
 
 /* All changes to priv->current_pos and priv->selection_bound
  * should go through this function.
  */
-static void
+void
 gtk_entry_set_positions (GtkEntry *entry,
                         gint      current_pos,
                         gint      selection_bound)
@@ -9898,88 +9909,21 @@ set_show_emoji_icon (GtkEntry *entry,
 }
 
 static void
-dismiss_emoji_completions (GtkEntry *entry)
-{
-  GtkWidget *popup;
-
-g_print ("dismiss emoji completions\n");
-  popup = GTK_WIDGET (g_object_get_data (G_OBJECT (entry), "emoji-completion-popup"));
-  if (popup)
-    gtk_popover_popdown (GTK_POPOVER (popup));
-}
-
-static void
-emoji_picked (GtkEmojiCompletions *completions,
-              const char          *emoji_text,
-              gpointer             data)
-{
-  GtkEntry *entry = data;
-  const char *text;
-  const char *completion_text;
-  guint length;
-
-  text = gtk_entry_buffer_get_text (get_buffer (entry));
-  length = gtk_entry_buffer_get_length (get_buffer (entry));
-
-  completion_text = g_object_get_data (entry, "emoji-completion-text");
-  gtk_entry_set_positions (entry, length - strlen (completion_text), length);
-  gtk_entry_enter_text (entry, emoji_text);
-}
-
-static void
-update_emoji_completions (GtkEntry   *entry,
-                          const char *text)
+set_enable_emoji_completion (GtkEntry *entry,
+                             gboolean  value)
 {
-  GtkWidget *popup;
-
-g_print ("update emoji completions\n");
-
-  g_object_set_data_full (G_OBJECT (entry), "emoji-completion-text", g_strdup (text), g_free);
-
-  popup = GTK_WIDGET (g_object_get_data (G_OBJECT (entry), "emoji-completion-popup"));
-  if (popup == NULL)
-    {
-      popup = gtk_emoji_completions_new (entry);
-      g_object_set_data_full (G_OBJECT (entry), "emoji-completion-popup", popup, 
(GDestroyNotify)gtk_widget_destroy);
-      g_signal_connect (popup, "emoji-picked", G_CALLBACK (emoji_picked), entry);
-    }
-
-  gtk_emoji_completions_show (GTK_EMOJI_COMPLETIONS (popup), text);
-}
-
-static void
-check_emoji_completion (GtkEntry *entry)
-{
-  const char *text;
-  guint length;
-  const char *p;
-
-  text = gtk_entry_buffer_get_text (get_buffer (entry));
-  length = gtk_entry_buffer_get_length (get_buffer (entry));
+  GtkEntryPrivate *priv = entry->priv;
 
-  if (length > 0)
-    {
-      gboolean found_candidate = FALSE;
+  if (priv->enable_emoji_completion == value)
+    return;
 
-      p = text + length;
-      do
-        {
-          p = g_utf8_prev_char (p);
-          if (*p == ':')
-            {
-              if (p == text || !g_unichar_isalnum (g_utf8_get_char (p - 1)))
-                found_candidate = TRUE;
-              break;
-            }
-        }
-      while (g_unichar_isalnum (g_utf8_get_char (p)) || *p == '_');
+  priv->enable_emoji_completion = value;
 
-      if (found_candidate)
-        {
-          update_emoji_completions (entry, p);
-          return;
-        }
-    }
+  if (priv->enable_emoji_completion)
+    g_object_set_data_full (G_OBJECT (entry), "emoji-completion-popup",
+                            gtk_emoji_completion_new (entry), (GDestroyNotify)gtk_widget_destroy);
+  else
+    g_object_set_data (G_OBJECT (entry), "emoji-completion-popup", NULL);
 
-  dismiss_emoji_completions (entry);
+  g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_ENABLE_EMOJI_COMPLETION]);
 }
diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h
index 08fab2d..4e96084 100644
--- a/gtk/gtkentryprivate.h
+++ b/gtk/gtkentryprivate.h
@@ -88,6 +88,13 @@ GtkIMContext* _gtk_entry_get_im_context    (GtkEntry  *entry);
 void     _gtk_entry_grab_focus             (GtkEntry  *entry,
                                             gboolean   select_all);
 
+void     gtk_entry_enter_text              (GtkEntry   *entry,
+                                            const char *text);
+void     gtk_entry_set_positions           (GtkEntry   *entry,
+                                            int         current_pos,
+                                            int         selection_bound);
+
+
 G_END_DECLS
 
 #endif /* __GTK_ENTRY_PRIVATE_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index 6bc4c3e..28770ea 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -142,6 +142,7 @@ gtk_public_sources = files([
   'gtkdrawingarea.c',
   'gtkeditable.c',
   'gtkemojichooser.c',
+  'gtkemojicompletion.c',
   'gtkentry.c',
   'gtkentrybuffer.c',
   'gtkentrycompletion.c',
diff --git a/gtk/org.gtk.Settings.EmojiChooser.gschema.xml b/gtk/org.gtk.Settings.EmojiChooser.gschema.xml
index 7105cb2..20e3db4 100644
--- a/gtk/org.gtk.Settings.EmojiChooser.gschema.xml
+++ b/gtk/org.gtk.Settings.EmojiChooser.gschema.xml
@@ -2,12 +2,12 @@
 <schemalist>
 
   <schema id='org.gtk.Settings.EmojiChooser' path='/org/gtk/settings/emoji-chooser/'>
-    <key name='recent-emoji' type='a((aus)u)'>
+    <key name='recent-emoji' type='a((auss)u)'>
       <default>[]</default>
       <summary>Recently used Emoji</summary>
       <description>
         An array of Emoji definitions to show in the Emoji chooser. Each Emoji is
-        specified as an array of codepoints and a name. The extra integer after this
+        specified as an array of codepoints, name and shortname. The extra integer after this
         pair is the code of the Fitzpatrick modifier to use in place of a 0 in the
         codepoint array.
       </description>
diff --git a/gtk/ui/gtkemojicompletions.ui b/gtk/ui/gtkemojicompletion.ui
similarity index 88%
rename from gtk/ui/gtkemojicompletions.ui
rename to gtk/ui/gtkemojicompletion.ui
index 0af8c47..c6df950 100644
--- a/gtk/ui/gtkemojicompletions.ui
+++ b/gtk/ui/gtkemojicompletion.ui
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface domain="gtk40">
-  <template class="GtkEmojiCompletions" parent="GtkPopover">
+  <template class="GtkEmojiCompletion" parent="GtkPopover">
     <property name="modal">0</property>
     <style>
       <class name="emoji-completions"/>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]