[gtk+/wip/matthiasc/emoji-picker] wip: emoji picker



commit fe5e665e5a7a101abcc22adf6006ec6c31b6b560
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Aug 6 07:39:30 2017 -0400

    wip: emoji picker

 tests/Makefile.am       |    1 +
 tests/gtkemojipicker.c  |  456 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/gtkemojipicker.h  |   13 ++
 tests/gtkemojipicker.ui |  158 ++++++++++++++++
 tests/testentryicons.c  |   45 +++++
 5 files changed, 673 insertions(+), 0 deletions(-)
---
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 40fafa9..fa667ba 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -310,6 +310,7 @@ testentrycompletion_SOURCES =       \
        testentrycompletion.c
 
 testentryicons_SOURCES =       \
+       gtkemojipicker.c        \
        testentryicons.c
 
 testfilechooser_SOURCES =      \
diff --git a/tests/gtkemojipicker.c b/tests/gtkemojipicker.c
new file mode 100644
index 0000000..b798e3c
--- /dev/null
+++ b/tests/gtkemojipicker.c
@@ -0,0 +1,456 @@
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gtkemojipicker.h"
+
+typedef struct {
+  gunichar code;
+  gboolean is_modifier_base;
+  gunichar code2;
+} EmojiData;
+
+static EmojiData people[] = {
+  // face-positive
+  { 0x1f600, 0, 0 },
+  { 0x1f601, 0, 0 },
+  { 0x1f602, 0, 0 },
+  { 0x1f923, 0, 0 },
+  { 0x1f603, 0, 0 },
+  { 0x1f604, 0, 0 },
+  { 0x1f605, 0, 0 },
+  { 0x1f606, 0, 0 },
+  { 0x1f609, 0, 0 },
+  { 0x1f60a, 0, 0 },
+  { 0x1f60b, 0, 0 },
+  { 0x1f60e, 0, 0 },
+  { 0x1f60d, 0, 0 },
+  { 0x1f618, 0, 0 },
+  { 0x1f617, 0, 0 },
+  { 0x1f619, 0, 0 },
+  { 0x1f61a, 0, 0 },
+  { 0x1f642, 0, 0 },
+  { 0x1f917, 0, 0 },
+  //{ 0x1f929, 0, 0 },
+  // face-neutral
+  { 0x1f914, 0, 0 },
+  //{ 0x1f928, 0 },
+  { 0x1f610, 0, 0 },
+  { 0x1f611, 0, 0 },
+  { 0x1f636, 0, 0 },
+  { 0x1f644, 0, 0 },
+  { 0x1f60f, 0, 0 },
+  { 0x1f623, 0, 0 },
+  { 0x1f625, 0, 0 },
+  { 0x1f62e, 0, 0 },
+  { 0x1f910, 0, 0 },
+  { 0x1f62f, 0, 0 },
+  { 0x1f62a, 0, 0 },
+  { 0x1f62b, 0, 0 },
+  { 0x1f634, 0, 0 },
+  { 0x1f60c, 0, 0 },
+  { 0x1f61b, 0, 0 },
+  { 0x1f61c, 0, 0 },
+  { 0x1f61d, 0, 0 },
+  { 0x1f924, 0, 0 },
+  { 0x1f612, 0, 0 },
+  { 0x1f613, 0, 0 },
+  { 0x1f614, 0, 0 },
+  { 0x1f615, 0, 0 },
+  { 0x1f643, 0, 0 },
+  { 0x1f911, 0, 0 },
+  { 0x1f632, 0, 0 },
+  //face-negative
+  { 0x02639, 0, 0 },
+  { 0x1f641, 0, 0 },
+  { 0x1f616, 0, 0 },
+  { 0x1f61e, 0, 0 },
+  { 0x1f61f, 0, 0 },
+  { 0x1f624, 0, 0 },
+  { 0x1f622, 0, 0 },
+  { 0x1f62d, 0, 0 },
+  { 0x1f626, 0, 0 },
+  { 0x1f627, 0, 0 },
+  { 0x1f628, 0, 0 },
+  { 0x1f629, 0, 0 },
+  //{ 0x1f92f, 0, 0 },
+  { 0x1f62c, 0, 0 },
+  { 0x1f630, 0, 0 },
+  { 0x1f631, 0, 0 },
+  { 0x1f633, 0, 0 },
+  //{ 0x1f92a, 0, 0 },
+  { 0x1f635, 0, 0 },
+  { 0x1f621, 0, 0 },
+  { 0x1f620, 0, 0 },
+  //{ 0x1f92c, 0, 0 },
+  // face-sick
+  { 0x1f637, 0, 0 },
+  { 0x1f912, 0, 0 },
+  { 0x1f915, 0, 0 },
+  { 0x1f922, 0, 0 },
+  //{ 0x1f92e, 0, 0 },
+  { 0x1f927, 0, 0 },
+  // face-role
+  { 0x1f607, 0, 0 },
+  { 0x1f920, 0, 0 },
+  { 0x1f921, 0, 0 },
+  { 0x1f925, 0, 0 },
+  //{ 0x1f92b, 0, 0 },
+  //{ 0x1f92d, 0, 0 },
+  //{ 0x1f9d0, 0, 0 },
+  { 0x1f913, 0, 0 },
+  // face-fantasy
+  { 0x1f608, 0, 0 },
+  { 0x1f47f, 0, 0 },
+  { 0x1f479, 0, 0 },
+  { 0x1f47a, 0, 0 },
+  { 0x1f480, 0, 0 },
+  { 0x02620, 0, 0 },
+  { 0x1f47b, 0, 0 },
+  { 0x1f47d, 0, 0 },
+  { 0x1f47e, 0, 0 },
+  { 0x1f916, 0, 0 },
+  { 0x1f4a9, 0, 0 },
+  // cat-face
+  { 0x1f63a, 0, 0 },
+  { 0x1f638, 0, 0 },
+  { 0x1f63b, 0, 0 },
+  { 0x1f63c, 0, 0 },
+  { 0x1f63d, 0, 0 },
+  { 0x1f640, 0, 0 },
+  { 0x1f63f, 0, 0 },
+  { 0x1f63e, 0, 0 },
+  // monkey-face
+  { 0x1f648, 0, 0 },
+  { 0x1f649, 0, 0 },
+  { 0x1f64a, 0, 0 },
+  // person
+  { 0x1f476, 1, 0 },
+  //{ 0x1f9d2, 1, 0 },
+  { 0x1f466, 1, 0 },
+  { 0x1f467, 1, 0 },
+  //{ 0x1f9d1, 1, 0 },
+  { 0x1f468, 1, 0 },
+  { 0x1f469, 1, 0 },
+  //{ 0x1f9d3, 1, 0 },
+  { 0x1f474, 1, 0 },
+  { 0x1f475, 1, 0 },
+  // person-role
+  //{ 0x1f468, 1, 0x02695 },
+  //{ 0x1f469, 1, 0x02695 },
+  //{ 0x1f468, 1, 0x1f393 },
+  //{ 0x1f469, 1, 0x1f393 },
+  //{ 0x1f468, 1, 0x1f3eb },
+  //{ 0x1f469, 1, 0x1f3eb },
+  //{ 0x1f468, 1, 0x02696 },
+  //{ 0x1f469, 1, 0x02696 },
+  //{ 0x1f468, 1, 0x1f33e },
+  //{ 0x1f469, 1, 0x1f33e },
+  //{ 0x1f468, 1, 0x1f373 },
+  //{ 0x1f469, 1, 0x1f373 },
+  //{ 0x1f468, 1, 0x1f527 },
+  //{ 0x1f469, 1, 0x1f527 },
+  //{ 0x1f468, 1, 0x1f3ed },
+  //{ 0x1f469, 1, 0x1f3ed },
+  //{ 0x1f468, 1, 0x1f4bc },
+  //{ 0x1f469, 1, 0x1f4bc },
+  //{ 0x1f468, 1, 0x1f52c },
+  //{ 0x1f469, 1, 0x1f52c },
+  //{ 0x1f468, 1, 0x1f4bb },
+  //{ 0x1f469, 1, 0x1f4bb },
+  //{ 0x1f468, 1, 0x1f3a4 },
+  //{ 0x1f469, 1, 0x1f3a4 },
+  //{ 0x1f468, 1, 0x1f3a8 },
+  //{ 0x1f469, 1, 0x1f3a8 },
+  //{ 0x1f468, 1, 0x02708 },
+  //{ 0x1f469, 1, 0x02708 },
+  //{ 0x1f468, 1, 0x1f680 },
+  //{ 0x1f469, 1, 0x1f680 },
+  //{ 0x1f468, 1, 0x1f692 },
+  //{ 0x1f469, 1, 0x1f692 },
+  { 0x1f46e, 1, 0 },
+  //{ 0x1f46e, 1, 0x2642 },
+  //{ 0x1f46e, 1, 0x2640 },
+  { 0x1f575, 1, 0 },
+  //{ 0x1f575, 1, 0x2642 },
+  //{ 0x1f575, 1, 0x2640 },
+  { 0x1f482, 1, 0 },
+  //{ 0x1f482, 1, 0x2642 },
+  //{ 0x1f482, 1, 0x2640 },
+  { 0x1f477, 1, 0 },
+  //{ 0x1f477, 1, 0x2642 },
+  //{ 0x1f477, 1, 0x2640 },
+  { 0x1f934, 1, 0 },
+  //{ 0x1f934, 1, 0x2642 },
+  //{ 0x1f934, 1, 0x2640 },
+  { 0x1f478, 1, 0 },
+  //{ 0x1f478, 1, 0x2642 },
+  //{ 0x1f478, 1, 0x2640 },
+  { 0x1f473, 1, 0 },
+  //{ 0x1f473, 1, 0x2642 },
+  //{ 0x1f473, 1, 0x2640 },
+  { 0x1f472, 1, 0 },
+  //{ 0x1f9d5, 1, 0 },
+  //{ 0x1f9d4, 1, 0 },
+  { 0x1f471, 1, 0 },
+  //{ 0x1f471, 1, 0x2642 },
+  //{ 0x1f471, 1, 0x2640 },
+  { 0x1f935, 1, 0 },
+  { 0x1f470, 1, 0 },
+  { 0x1f930, 1, 0 },
+  //{ 0x1f931, 1, 0 },
+  // person-fantasy
+  { 0x1f47c, 1, 0 },
+  { 0x1f385, 1, 0 },
+  { 0x1f936, 1, 0 },
+  //{ 0x1f9d9, 1, 0 },
+  //{ 0x1f9d9, 1, 0x2642 },
+  //{ 0x1f9d9, 1, 0x2640 },
+  //...
+  // person-gesture
+  { 0x1f64d, 1, 0 },
+  //{ 0x1f64d, 1, 0x2642 },
+  //{ 0x1f64d, 1, 0x2640 },
+  { 0x1f64e, 1, 0 },
+  //{ 0x1f64e, 1, 0x2642 },
+  //{ 0x1f64e, 1, 0x2640 },
+  { 0x1f645, 1, 0 },
+  //{ 0x1f645, 1, 0x2642 },
+  //{ 0x1f645, 1, 0x2640 },
+  { 0x1f646, 1, 0 },
+  //{ 0x1f646, 1, 0x2642 },
+  //{ 0x1f646, 1, 0x2640 },
+  { 0x1f481, 1, 0 },
+  //{ 0x1f481, 1, 0x2642 },
+  //{ 0x1f481, 1, 0x2640 },
+  { 0x1f64b, 1, 0 },
+  //{ 0x1f64b, 1, 0x2642 },
+  //{ 0x1f64b, 1, 0x2640 },
+  { 0x1f647, 1, 0 },
+  //{ 0x1f647, 1, 0x2642 },
+  //{ 0x1f647, 1, 0x2640 },
+  { 0x1f926, 1, 0 },
+  //{ 0x1f926, 1, 0x2642 },
+  //{ 0x1f926, 1, 0x2640 },
+  { 0x1f937, 1, 0 },
+  //{ 0x1f937, 1, 0x2642 },
+  //{ 0x1f937, 1, 0x2640 },
+  // person-activity
+  { 0x1f486, 1, 0 },
+  //{ 0x1f486, 1, 0x2642 },
+  //{ 0x1f486, 1, 0x2640 },
+  { 0x1f487, 1, 0 },
+  //{ 0x1f487, 1, 0x2642 },
+  //{ 0x1f487, 1, 0x2640 },
+  { 0x1f6b6, 1, 0 },
+  //{ 0x1f6b6, 1, 0x2642 },
+  //{ 0x1f6b6, 1, 0x2640 },
+  { 0x1f3c3, 1, 0 },
+  //{ 0x1f3c3, 1, 0x2642 },
+  //{ 0x1f3c3, 1, 0x2640 },
+  { 0x1f483, 1, 0 },
+  { 0x1f57a, 1, 0 },
+  { 0x1f46f, 1, 0 },
+  //{ 0x1f46f, 1, 0x2642 },
+  //{ 0x1f46f, 1, 0x2640 },
+  //...
+  // person-sport
+  // family
+  // body
+  // emotion
+  // clothing
+};
+
+
+struct _GtkEmojiPicker
+{
+        GtkPopover parent_instance;
+
+        GtkWidget *people_box;
+        GtkWidget *nature_box;
+
+        GtkGesture *people_press;
+};
+
+enum {
+  ACTIVATED,
+  LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (GtkEmojiPicker, gtk_emoji_picker, GTK_TYPE_POPOVER)
+
+static void
+gtk_emoji_picker_finalize (GObject *object)
+{
+        G_OBJECT_CLASS (gtk_emoji_picker_parent_class)->finalize (object);
+}
+
+static void
+emoji_activated (GtkFlowBox      *box,
+                 GtkFlowBoxChild *child,
+                 gpointer         data)
+{
+        const char *text;
+        GtkWidget *label;
+
+        gtk_popover_popdown (GTK_POPOVER (data));
+
+        label = gtk_bin_get_child (GTK_BIN (child));
+        text = gtk_label_get_label (GTK_LABEL (label));
+        g_signal_emit (data, signals[ACTIVATED], 0, text);
+}
+
+static void
+add_emoji (GtkWidget  *box,
+           gunichar    code,
+           gunichar    modifier,
+           gboolean    is_modifier_base,
+           gunichar    code2);
+
+static void
+people_pressed_cb (GtkGesture *gesture,
+                   double x,
+                   double y,
+                   gpointer data)
+{
+        GtkEmojiPicker *picker = data;
+        GtkWidget *child;
+        GtkWidget *popover;
+        GtkWidget *view;
+        GtkWidget *box;
+        gunichar code;
+        gunichar modifier;
+        gunichar code2;
+        gboolean is_modifier_base;
+        GtkWidget *parent_popover;
+
+        child = GTK_WIDGET (gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (picker->people_box), x, y));
+        if (!child)
+                return;
+
+        code = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (child), "code"));
+        code2 = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (child), "code2"));
+        is_modifier_base = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child), "is-modifier-base"));
+        if (!is_modifier_base)
+                return;
+
+        parent_popover = gtk_widget_get_ancestor (child, GTK_TYPE_POPOVER);
+        popover = gtk_popover_new (child);
+        view = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+        gtk_style_context_add_class (gtk_widget_get_style_context (view), "view");
+        box = gtk_flow_box_new ();
+        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 (popover), view);
+        gtk_container_add (GTK_CONTAINER (view), box);
+
+        g_signal_connect (box, "child-activated", G_CALLBACK (emoji_activated), parent_popover);
+
+        add_emoji (box, code, 0, FALSE, code2);
+        for (modifier = 0x1f3fb; modifier <= 0x1f3ff; modifier++) {
+                add_emoji (box, code, modifier, FALSE, code2);
+        }
+
+        gtk_popover_popup (GTK_POPOVER (popover));
+}
+
+static void
+add_emoji (GtkWidget  *box,
+           gunichar    code,
+           gunichar    modifier,
+           gboolean    is_modifier_base,
+           gunichar    code2)
+{
+        GtkWidget *child;
+        GtkWidget *label;
+        PangoAttrList *attrs;
+        char text[32];
+        char *p = text;
+
+        p += g_unichar_to_utf8 (code, p);
+        if (modifier != 0)
+                p += g_unichar_to_utf8 (modifier, p);
+        if (code2 != 0) {
+                p += g_unichar_to_utf8 (0x200d, p);
+                p += g_unichar_to_utf8 (code2, p);
+                p += g_unichar_to_utf8 (0xfe0f, p);
+        }
+        p[0] = 0;
+
+        label = gtk_label_new (text);
+        g_object_set(label, "margin", 6, NULL);
+        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_flow_box_child_new ();
+        gtk_style_context_add_class (gtk_widget_get_style_context (child), "emojicell");
+        g_object_set_data (G_OBJECT (child), "code", GUINT_TO_POINTER (code));
+        g_object_set_data (G_OBJECT (child), "code2", GUINT_TO_POINTER (code2));
+        g_object_set_data (G_OBJECT (child), "is-modifier-base", GINT_TO_POINTER (is_modifier_base));
+
+        gtk_container_add (GTK_CONTAINER (child), label);
+        gtk_container_add (GTK_CONTAINER (box), child);
+}
+
+static void
+populate_emoji_picker (GtkEmojiPicker *picker)
+{
+        int i;
+
+        for (i = 0; i < G_N_ELEMENTS (people); i++) {
+                add_emoji (picker->people_box, people[i].code, 0, people[i].is_modifier_base, 
people[i].code2);
+        }
+}
+
+static void
+gtk_emoji_picker_init (GtkEmojiPicker *picker)
+{
+        gtk_widget_init_template (GTK_WIDGET (picker));
+
+        picker->people_press = gtk_gesture_long_press_new (picker->people_box);
+        g_signal_connect (picker->people_press, "pressed", G_CALLBACK (people_pressed_cb), picker);
+
+        populate_emoji_picker (picker);
+}
+
+static void
+gtk_emoji_picker_class_init (GtkEmojiPickerClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+        GMappedFile *mf;
+        GBytes *bytes;
+
+        object_class->finalize = gtk_emoji_picker_finalize;
+
+        signals[ACTIVATED] = g_signal_new ("activated",
+                                           G_OBJECT_CLASS_TYPE (object_class),
+                                           G_SIGNAL_RUN_LAST,
+                                           0,
+                                           NULL, NULL,
+                                           NULL,
+                                           G_TYPE_NONE, 1, G_TYPE_STRING|G_SIGNAL_TYPE_STATIC_SCOPE);
+
+        mf = g_mapped_file_new ("gtkemojipicker.ui", FALSE, NULL);
+        bytes = g_mapped_file_get_bytes (mf);
+        gtk_widget_class_set_template (widget_class, bytes);
+        g_bytes_unref (bytes);
+        g_mapped_file_unref (mf);
+
+        gtk_widget_class_bind_template_child (widget_class, GtkEmojiPicker, people_box);
+        gtk_widget_class_bind_template_child (widget_class, GtkEmojiPicker, nature_box);
+
+        gtk_widget_class_bind_template_callback (widget_class, emoji_activated);
+}
+
+GtkWidget *
+gtk_emoji_picker_new (void)
+{
+        return GTK_WIDGET (g_object_new (GTK_EMOJI_PICKER_TYPE, NULL));
+}
diff --git a/tests/gtkemojipicker.h b/tests/gtkemojipicker.h
new file mode 100644
index 0000000..8ef6e4a
--- /dev/null
+++ b/tests/gtkemojipicker.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_EMOJI_PICKER_TYPE (gtk_emoji_picker_get_type ())
+
+G_DECLARE_FINAL_TYPE (GtkEmojiPicker, gtk_emoji_picker, GTK, EMOJI_PICKER, GtkPopover)
+
+GtkWidget       *gtk_emoji_picker_new (void);
+
+G_END_DECLS
diff --git a/tests/gtkemojipicker.ui b/tests/gtkemojipicker.ui
new file mode 100644
index 0000000..6a35ed8
--- /dev/null
+++ b/tests/gtkemojipicker.ui
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gtk-3">
+  <template class="GtkEmojiPicker" parent="GtkPopover">
+    <property name="vexpand">1</property>
+    <property name="modal">1</property>
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkSearchEntry" id="search_entry">
+            <property name="visible">0</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="swin">
+            <property name="vexpand">1</property>
+            <property name="hscrollbar-policy">never</property>
+            <property name="vscrollbar-policy">automatic</property>
+            <style>
+              <class name="view"/>
+            </style>
+            <child>
+              <object class="GtkBox" id="emoji_box">
+                <property name="orientation">vertical</property>
+                <property name="margin">6</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">People</property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkFlowBox" id="people_box">
+                    <property name="homogeneous">1</property>
+                    <property name="selection-mode">none</property>
+                    <property name="min-children-per-line">6</property>
+                    <property name="max-children-per-line">6</property>
+                    <property name="activate-on-single-click">1</property>
+                    <signal name="child-activated" handler="emoji_activated"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">Nature</property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkFlowBox" id="nature_box">
+                    <property name="homogeneous">1</property>
+                    <property name="selection-mode">none</property>
+                    <property name="row-spacing">6</property>
+                    <property name="column-spacing">6</property>
+                    <property name="min-children-per-line">6</property>
+                    <property name="max-children-per-line">6</property>
+                    <property name="activate-on-single-click">1</property>
+                    <signal name="child-activated" handler="emoji_activated"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">Food</property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkFlowBox" id="food_box">
+                    <property name="homogeneous">1</property>
+                    <property name="selection-mode">none</property>
+                    <property name="row-spacing">6</property>
+                    <property name="column-spacing">6</property>
+                    <property name="min-children-per-line">6</property>
+                    <property name="max-children-per-line">6</property>
+                    <property name="activate-on-single-click">1</property>
+                    <signal name="child-activated" handler="emoji_activated"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">Travel</property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkFlowBox" id="travel_box">
+                    <property name="homogeneous">1</property>
+                    <property name="selection-mode">none</property>
+                    <property name="row-spacing">6</property>
+                    <property name="column-spacing">6</property>
+                    <property name="min-children-per-line">6</property>
+                    <property name="max-children-per-line">6</property>
+                    <property name="activate-on-single-click">1</property>
+                    <signal name="child-activated" handler="emoji_activated"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">Activities</property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkFlowBox" id="activities_box">
+                    <property name="homogeneous">1</property>
+                    <property name="selection-mode">none</property>
+                    <property name="row-spacing">6</property>
+                    <property name="column-spacing">6</property>
+                    <property name="min-children-per-line">6</property>
+                    <property name="max-children-per-line">6</property>
+                    <property name="activate-on-single-click">1</property>
+                    <signal name="child-activated" handler="emoji_activated"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">Objects</property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkFlowBox" id="objects_box">
+                    <property name="homogeneous">1</property>
+                    <property name="selection-mode">none</property>
+                    <property name="row-spacing">6</property>
+                    <property name="column-spacing">6</property>
+                    <property name="min-children-per-line">6</property>
+                    <property name="max-children-per-line">6</property>
+                    <property name="activate-on-single-click">1</property>
+                    <signal name="child-activated" handler="emoji_activated"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">Symbols</property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkFlowBox" id="symbols_box">
+                    <property name="homogeneous">1</property>
+                    <property name="selection-mode">none</property>
+                    <property name="row-spacing">6</property>
+                    <property name="column-spacing">6</property>
+                    <property name="min-children-per-line">6</property>
+                    <property name="max-children-per-line">6</property>
+                    <property name="activate-on-single-click">1</property>
+                    <signal name="child-activated" handler="emoji_activated"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/tests/testentryicons.c b/tests/testentryicons.c
index 41196a2..40eb0fc 100644
--- a/tests/testentryicons.c
+++ b/tests/testentryicons.c
@@ -1,5 +1,28 @@
 #include <gtk/gtk.h>
 #include <stdio.h>
+#include "gtkemojipicker.h"
+
+static void
+emoji_activated (GtkEmojiPicker *picker, const char *text, GtkEntry *entry)
+{
+        GtkEntryBuffer *buffer;
+        buffer = gtk_entry_get_buffer (entry);
+        gtk_entry_buffer_insert_text (buffer, -1, text, -1);
+}
+
+static void
+pick_emoji (GtkEntry *entry, gint icon, GdkEvent *event, gpointer data)
+{
+  GtkWidget *picker = gtk_emoji_picker_new ();
+  GdkRectangle rect;
+
+  gtk_popover_set_relative_to (GTK_POPOVER (picker), GTK_WIDGET (entry));
+  gtk_entry_get_icon_area (entry, GTK_ENTRY_ICON_SECONDARY, &rect);
+  gtk_popover_set_pointing_to (GTK_POPOVER (picker), &rect);
+  g_signal_connect (picker, "activated", G_CALLBACK (emoji_activated), entry);
+
+  gtk_popover_popup (GTK_POPOVER (picker));
+}
 
 static void
 clear_pressed (GtkEntry *entry, gint icon, GdkEvent *event, gpointer data)
@@ -270,8 +293,30 @@ main (int argc, char **argv)
   g_signal_connect (button4, "toggled", G_CALLBACK (set_pixbuf), entry);
   gtk_container_add (GTK_CONTAINER (box), button4);
 
+  label = gtk_label_new ("Emoji:");
+  gtk_grid_attach (GTK_GRID (grid), label, 0, 6, 1, 1);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+  gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
+
+  entry = gtk_entry_new ();
+  gtk_widget_set_hexpand (entry, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), entry, 1, 6, 1, 1);
   gtk_widget_show (window);
 
+  gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
+                                     GTK_ENTRY_ICON_SECONDARY,
+                                     "face-smile-symbolic");
+
+  gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
+                               GTK_ENTRY_ICON_SECONDARY,
+                               TRUE);
+
+  gtk_entry_set_icon_activatable (GTK_ENTRY (entry),
+                                 GTK_ENTRY_ICON_SECONDARY,
+                                 TRUE);
+
+  g_signal_connect (entry, "icon-press", G_CALLBACK (pick_emoji), NULL);
+
   gtk_main();
 
   return 0;


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