Merge branch 'gtk-3-24+emoji-data-update' into 'gtk-3-24'

Backport Emoji changes to share the same (and newer) emojibase data

See merge request GNOME/gtk!3521
This commit is contained in:
Matthias Clasen 2021-05-14 00:42:04 +00:00
commit eff4d45c09
19 changed files with 378 additions and 50063 deletions

View File

@ -1355,7 +1355,10 @@ gtk.gresource.xml: Makefile.am inspector/Makefile.inc
echo " <file compressed='true' preprocess='xml-stripblanks'>inspector/$$n</file>" >> $@; \
done; \
echo " <file>inspector/logo.png</file>" >> $@; \
echo " <file>emoji/emoji.data</file>" >> $@; \
for f in $(srcdir)/emoji/*.data; do \
n=`basename $$f`; \
echo " <file>emoji/$$n</file>" >> $@; \
done; \
echo " </gresource>" >> $@; \
echo "</gresources>" >> $@;
@ -1743,8 +1746,13 @@ EXTRA_DIST += \
$(gsettings_SCHEMAS) \
compose/compose-parse.py \
emoji/convert-emoji.c \
emoji/emoji.json \
emoji/emoji1.json \
emoji/gresource.xml.in \
emoji/README.md \
emoji/de.data \
emoji/en.data \
emoji/es.data \
emoji/fr.data \
emoji/zh.data \
a11y/Makefile.inc \
deprecated/Makefile.inc \
glade/README.glade \

23
gtk/emoji/README.md Normal file
View File

@ -0,0 +1,23 @@
Emoji data
==========
We use Emoji data from Unicode and the CLDR, stored in a GVariant.
The immediate source for our data is the json files from
https://github.com/milesj/emojibase.git
To convert the data from that repository to a GVariant that GTK
can use, the convert-emoji tool can be used:
convert-emoji $emojibase/packages/data/de/data.raw.json de.data
for example (for German).
To make these usable by GTK, we wrap them in a resource bundle
that has the GVariant as
/org/gtk/libgtk/emoji/de.data
and install the resulting resource bundle at this location:
/usr/share/gtk-4.0/emoji/de.gresource

View File

@ -18,18 +18,40 @@
/* Build with gcc -o convert-emoji convert-emoji.c `pkg-config --cflags --libs json-glib-1.0`
*/
/* Reads data from the json files in emojibase, expecting
* language-specific data.raw.json as input
*/
/* The format of the generated data is: a(ausasu).
* Each member of the array has the following fields:
* au - sequence of unicode codepoints. If the
* sequence contains a 0, it marks the point
* where skin tone modifiers should be inserted
* s - name, e.g. "man worker"
* as - keywords, e.g. "man", "worker"
* u - the group that this item belongs to:
* 0: smileys-emotion
* 1: people-body
* 2: component
* 3: animals-nature
* 4: food-drink
* 5: travel-places
* 6: activities
* 7: objects
* 8: symbols
* 9: flags
*/
#include <json-glib/json-glib.h>
#include <string.h>
gboolean
parse_code (GVariantBuilder *b,
const char *code,
GString *name_key)
const char *code)
{
g_auto(GStrv) strv = NULL;
int j;
strv = g_strsplit (code, " ", -1);
strv = g_strsplit (code, "-", -1);
for (j = 0; strv[j]; j++)
{
guint32 u;
@ -44,12 +66,7 @@ parse_code (GVariantBuilder *b,
if (0x1f3fb <= u && u <= 0x1f3ff)
g_variant_builder_add (b, "u", 0);
else
{
g_variant_builder_add (b, "u", u);
if (j > 0)
g_string_append_c (name_key, '-');
g_string_append_printf (name_key, "%x", u);
}
g_variant_builder_add (b, "u", u);
}
return TRUE;
@ -60,57 +77,27 @@ main (int argc, char *argv[])
{
JsonParser *parser;
JsonNode *root;
JsonArray *array;
JsonObject *ro;
JsonArray *array;
JsonNode *node;
const char *name;
const char *unicode;
JsonObjectIter iter;
GError *error = NULL;
guint length, i;
GVariantBuilder builder;
GVariant *v;
GString *s;
GBytes *bytes;
GHashTable *names;
GString *name_key;
if (argc != 4)
if (argc != 3)
{
g_print ("Usage: emoji-convert INPUT INPUT1 OUTPUT\n");
g_print ("Usage: emoji-convert INPUT OUTPUT\n");
return 1;
}
parser = json_parser_new ();
if (!json_parser_load_from_file (parser, argv[2], &error))
{
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))
{
g_error ("%s", error->message);
@ -121,65 +108,74 @@ 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(auss)"));
i = 0;
while (i < length)
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ausasu)"));
for (i = 0; i < length; i++)
{
JsonNode *node = json_array_get_element (array, i);
JsonObject *obj = json_node_get_object (node);
JsonObject *obj = json_array_get_object_element (array, i);
GVariantBuilder b1;
GVariantBuilder b2;
guint group;
const char *name;
const char *shortname;
char *code;
int j;
gboolean skip;
gboolean has_variations;
i++;
if (!json_object_has_member (obj, "group"))
continue;
group = json_object_get_int_member (obj, "group");
name = json_object_get_string_member (obj, "annotation");
if (json_object_has_member (obj, "skins"))
{
JsonArray *a2 = json_object_get_array_member (obj, "skins");
JsonNode *n2 = json_array_get_element (a2, 0);
JsonObject *o2 = json_node_get_object (n2);
code = g_strdup (json_object_get_string_member (o2, "hexcode"));
}
else
{
code = g_strdup (json_object_get_string_member (obj, "hexcode"));
}
g_variant_builder_init (&b1, G_VARIANT_TYPE ("au"));
name = json_object_get_string_member (obj, "name");
code = g_strdup (json_object_get_string_member (obj, "code"));
has_variations = FALSE;
while (i < length)
{
JsonNode *node2 = json_array_get_element (array, i);
JsonObject *obj2 = json_node_get_object (node2);
const char *name2;
const char *code2;
name2 = json_object_get_string_member (obj2, "name");
code2 = json_object_get_string_member (obj2, "code");
if (!strstr (name2, "skin tone") || !g_str_has_prefix (name2, name))
break;
if (!has_variations)
{
has_variations = TRUE;
g_free (code);
code = g_strdup (code2);
}
i++;
}
g_string_set_size (name_key, 0);
if (!parse_code (&b1, code, name_key))
if (!parse_code (&b1, code))
return 1;
shortname = g_hash_table_lookup (names, name_key->str);
g_variant_builder_init (&b2, G_VARIANT_TYPE ("as"));
if (json_object_has_member (obj, "tags"))
{
JsonArray *tags = json_object_get_array_member (obj, "tags");
for (int j = 0; j < json_array_get_length (tags); j++)
g_variant_builder_add (&b2, "s", json_array_get_string_element (tags, j));
}
g_variant_builder_add (&builder, "(auss)", &b1, name, shortname ? shortname : "");
g_variant_builder_add (&builder, "(ausasu)", &b1, name, &b2, group);
}
v = g_variant_builder_end (&builder);
bytes = g_variant_get_data_as_bytes (v);
if (!g_file_set_contents (argv[3], g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), &error))
if (g_str_has_suffix (argv[2], ".json"))
{
g_error ("%s", error->message);
return 1;
JsonNode *node;
char *out;
node = json_gvariant_serialize (v);
out = json_to_string (node, TRUE);
if (!g_file_set_contents (argv[2], out, -1, &error))
{
g_error ("%s", error->message);
return 1;
}
}
else
{
GBytes *bytes;
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))
{
g_error ("%s", error->message);
return 1;
}
}
return 0;

BIN
gtk/emoji/de.data Normal file

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
gtk/emoji/en.data Normal file

Binary file not shown.

BIN
gtk/emoji/es.data Normal file

Binary file not shown.

BIN
gtk/emoji/fr.data Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gtk/libgtk/emoji/">
<file>@lang@.data</file>
</gresource>
</gresources>

BIN
gtk/emoji/zh.data Normal file

Binary file not shown.

View File

@ -76,7 +76,7 @@ for f in get_files('inspector', '.ui'):
xml += '''
<file>inspector/logo.png</file>
<file>emoji/emoji.data</file>
<file>emoji/en.data</file>
</gresource>
</gresources>'''

View File

@ -27,6 +27,7 @@
#include "gtkflowbox.h"
#include "gtkstack.h"
#include "gtklabel.h"
#include "gtkmain.h"
#include "gtkgesturelongpress.h"
#include "gtkgesturemultipress.h"
#include "gtkpopover.h"
@ -41,7 +42,7 @@ typedef struct {
GtkWidget *box;
GtkWidget *heading;
GtkWidget *button;
const char *first;
int group;
gunichar label;
gboolean empty;
} EmojiSection;
@ -167,14 +168,43 @@ populate_recent_section (GtkEmojiChooser *chooser)
empty = FALSE;
}
if (!empty)
{
gtk_widget_show (chooser->recent.box);
gtk_widget_set_sensitive (chooser->recent.button, TRUE);
}
gtk_widget_set_visible (chooser->recent.box, !empty);
gtk_widget_set_sensitive (chooser->recent.button, !empty);
g_variant_unref (variant);
}
static GVariant *
get_recent_emoji_data (GtkWidget *widget)
{
GVariant *emoji_data = g_object_get_data (G_OBJECT (widget), "emoji-data");
GVariantIter *codes_iter;
GVariantIter *keywords_iter;
GVariantBuilder codes_builder;
const char *name;
const char *shortname;
guint code;
guint group;
g_assert (emoji_data);
if (g_variant_is_of_type (emoji_data, G_VARIANT_TYPE ("(auss)")))
return emoji_data;
g_variant_get (emoji_data, "(au&sasu)", &codes_iter, &name, &keywords_iter, &group);
g_variant_builder_init (&codes_builder, G_VARIANT_TYPE ("au"));
while (g_variant_iter_loop (codes_iter, "u", &code))
g_variant_builder_add (&codes_builder, "u", code);
g_variant_iter_free (codes_iter);
g_variant_iter_free (keywords_iter);
shortname = "";
return g_variant_new ("(auss)", &codes_builder, name, shortname);
}
static void
add_recent_item (GtkEmojiChooser *chooser,
GVariant *item,
@ -192,7 +222,7 @@ add_recent_item (GtkEmojiChooser *chooser,
children = gtk_container_get_children (GTK_CONTAINER (chooser->recent.box));
for (l = children, i = 1; l; l = l->next, i++)
{
GVariant *item2 = g_object_get_data (G_OBJECT (l->data), "emoji-data");
GVariant *item2 = get_recent_emoji_data (GTK_WIDGET (l->data));
gunichar modifier2 = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (l->data), "modifier"));
if (modifier == modifier2 && g_variant_equal (item, item2))
@ -222,6 +252,23 @@ add_recent_item (GtkEmojiChooser *chooser,
g_variant_unref (item);
}
static gboolean
should_close (GtkEmojiChooser *chooser)
{
GdkDisplay *display;
GdkSeat *seat;
GdkDevice *device;
GdkModifierType state;
display = gtk_widget_get_display (GTK_WIDGET (chooser));
seat = gdk_display_get_default_seat (display);
device = gdk_seat_get_pointer (seat);
gdk_device_get_state (device, gtk_widget_get_window (GTK_WIDGET (chooser)),
NULL, &state);
return (state & GDK_CONTROL_MASK) == 0;
}
static void
emoji_activated (GtkFlowBox *box,
GtkFlowBoxChild *child,
@ -234,13 +281,22 @@ emoji_activated (GtkFlowBox *box,
GVariant *item;
gunichar modifier;
gtk_popover_popdown (GTK_POPOVER (chooser));
if (should_close (chooser))
gtk_popover_popdown (GTK_POPOVER (chooser));
else
{
GtkWidget *popover;
popover = gtk_widget_get_ancestor (GTK_WIDGET (box), GTK_TYPE_POPOVER);
if (popover != GTK_WIDGET (chooser))
gtk_popover_popdown (GTK_POPOVER (popover));
}
ebox = gtk_bin_get_child (GTK_BIN (child));
label = gtk_bin_get_child (GTK_BIN (ebox));
text = g_strdup (gtk_label_get_label (GTK_LABEL (label)));
item = (GVariant*) g_object_get_data (G_OBJECT (child), "emoji-data");
item = get_recent_emoji_data (GTK_WIDGET (child));
modifier = (gunichar) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (child), "modifier"));
add_recent_item (chooser, item, modifier);
@ -440,11 +496,87 @@ add_emoji (GtkWidget *box,
gtk_flow_box_insert (GTK_FLOW_BOX (box), child, prepend ? 0 : -1);
}
GBytes *
get_emoji_data (void)
{
GBytes *bytes;
const char *lang;
char q[10];
char *path;
GError *error = NULL;
lang = pango_language_to_string (gtk_get_default_language ());
if (strchr (lang, '-'))
{
int i;
for (i = 0; lang[i] != '-' && i < 9; i++)
q[i] = lang[i];
q[i] = '\0';
lang = q;
}
path = g_strconcat ("/org/gtk/libgtk/emoji/", lang, ".data", NULL);
bytes = g_resources_lookup_data (path, 0, &error);
if (bytes)
{
g_debug ("Found emoji data for %s in resource %s", lang, path);
g_free (path);
return bytes;
}
if (g_error_matches (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
{
char *filename;
char *gresource_name;
GMappedFile *file;
g_clear_error (&error);
gresource_name = g_strconcat (lang, ".gresource", NULL);
filename = g_build_filename (_gtk_get_data_prefix (), "share", "gtk-3.0",
"emoji", gresource_name, NULL);
g_clear_pointer (&gresource_name, g_free);
file = g_mapped_file_new (filename, FALSE, NULL);
if (file)
{
GBytes *data;
GResource *resource;
data = g_mapped_file_get_bytes (file);
g_mapped_file_unref (file);
resource = g_resource_new_from_data (data, NULL);
g_bytes_unref (data);
g_debug ("Registering resource for Emoji data for %s from file %s", lang, filename);
g_resources_register (resource);
g_resource_unref (resource);
bytes = g_resources_lookup_data (path, 0, NULL);
if (bytes)
{
g_debug ("Found emoji data for %s in resource %s", lang, path);
g_free (path);
g_free (filename);
return bytes;
}
}
g_free (filename);
}
g_clear_error (&error);
g_free (path);
return g_resources_lookup_data ("/org/gtk/libgtk/emoji/en.data", 0, NULL);
}
static gboolean
populate_emoji_chooser (gpointer data)
{
GtkEmojiChooser *chooser = data;
GBytes *bytes = NULL;
GVariant *item;
guint64 start, now;
@ -452,8 +584,12 @@ populate_emoji_chooser (gpointer data)
if (!chooser->data)
{
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(auss)"), bytes, TRUE));
GBytes *bytes;
bytes = get_emoji_data ();
chooser->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(ausasu)"), bytes, TRUE));
g_bytes_unref (bytes);
}
if (!chooser->iter)
@ -463,25 +599,27 @@ populate_emoji_chooser (gpointer data)
}
while ((item = g_variant_iter_next_value (chooser->iter)))
{
const char *name;
guint group;
g_variant_get_child (item, 1, "&s", &name);
g_variant_get_child (item, 3, "u", &group);
if (strcmp (name, chooser->body.first) == 0)
if (group == chooser->people.group)
chooser->box = chooser->people.box;
else if (group == chooser->body.group)
chooser->box = chooser->body.box;
else if (strcmp (name, chooser->nature.first) == 0)
else if (group == chooser->nature.group)
chooser->box = chooser->nature.box;
else if (strcmp (name, chooser->food.first) == 0)
else if (group == chooser->food.group)
chooser->box = chooser->food.box;
else if (strcmp (name, chooser->travel.first) == 0)
else if (group == chooser->travel.group)
chooser->box = chooser->travel.box;
else if (strcmp (name, chooser->activities.first) == 0)
else if (group == chooser->activities.group)
chooser->box = chooser->activities.box;
else if (strcmp (name, chooser->objects.first) == 0)
else if (group == chooser->objects.group)
chooser->box = chooser->objects.box;
else if (strcmp (name, chooser->symbols.first) == 0)
else if (group == chooser->symbols.group)
chooser->box = chooser->symbols.box;
else if (strcmp (name, chooser->flags.first) == 0)
else if (group == chooser->flags.group)
chooser->box = chooser->flags.box;
add_emoji (chooser->box, FALSE, item, 0, chooser);
@ -492,9 +630,6 @@ populate_emoji_chooser (gpointer data)
return G_SOURCE_CONTINUE;
}
/* We scroll to the top on show, so check the right button for the 1st time */
gtk_widget_set_state_flags (chooser->recent.button, GTK_STATE_FLAG_CHECKED, FALSE);
g_variant_iter_free (chooser->iter);
chooser->iter = NULL;
chooser->box = NULL;
@ -530,6 +665,9 @@ adj_value_changed (GtkAdjustment *adj,
EmojiSection const *section = sections[i];
GtkAllocation alloc;
if (!gtk_widget_get_visible (section->box))
continue;
if (section->heading)
gtk_widget_get_allocation (section->heading, &alloc);
else
@ -553,6 +691,31 @@ adj_value_changed (GtkAdjustment *adj,
}
}
static gboolean
match_tokens (const char **term_tokens,
const char **hit_tokens)
{
int i, j;
gboolean matched;
matched = TRUE;
for (i = 0; term_tokens[i]; i++)
{
for (j = 0; hit_tokens[j]; j++)
if (g_str_has_prefix (hit_tokens[j], term_tokens[i]))
goto one_matched;
matched = FALSE;
break;
one_matched:
continue;
}
return matched;
}
static gboolean
filter_func (GtkFlowBoxChild *child,
gpointer data)
@ -562,6 +725,8 @@ filter_func (GtkFlowBoxChild *child,
GVariant *emoji_data;
const char *text;
const char *name;
char **term_tokens;
char **name_tokens;
gboolean res;
res = TRUE;
@ -576,8 +741,23 @@ filter_func (GtkFlowBoxChild *child,
if (!emoji_data)
goto out;
term_tokens = g_str_tokenize_and_fold (text, "en", NULL);
g_variant_get_child (emoji_data, 1, "&s", &name);
res = g_str_match_string (text, name, TRUE);
name_tokens = g_str_tokenize_and_fold (name, "en", NULL);
res = match_tokens ((const char **)term_tokens, (const char **)name_tokens);
if (g_variant_is_of_type (emoji_data, G_VARIANT_TYPE ("(ausasu)")))
{
const char **keywords;
g_variant_get_child (emoji_data, 2, "^a&s", &keywords);
res |= match_tokens ((const char **)term_tokens, keywords);
}
g_strfreev (term_tokens);
g_strfreev (name_tokens);
out:
if (res)
@ -647,14 +827,14 @@ search_changed (GtkEntry *entry,
static void
setup_section (GtkEmojiChooser *chooser,
EmojiSection *section,
const char *first,
const char *icon)
EmojiSection *section,
int group,
const char *icon)
{
GtkAdjustment *adj;
GtkWidget *image;
section->first = first;
section->group = group;
image = gtk_bin_get_child (GTK_BIN (section->button));
gtk_image_set_from_icon_name (GTK_IMAGE (image), icon, GTK_ICON_SIZE_BUTTON);
@ -717,16 +897,16 @@ gtk_emoji_chooser_init (GtkEmojiChooser *chooser)
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
g_signal_connect (adj, "value-changed", G_CALLBACK (adj_value_changed), chooser);
setup_section (chooser, &chooser->recent, NULL, "emoji-recent-symbolic");
setup_section (chooser, &chooser->people, "grinning face", "emoji-people-symbolic");
setup_section (chooser, &chooser->body, "selfie", "emoji-body-symbolic");
setup_section (chooser, &chooser->nature, "monkey face", "emoji-nature-symbolic");
setup_section (chooser, &chooser->food, "grapes", "emoji-food-symbolic");
setup_section (chooser, &chooser->travel, "globe showing Europe-Africa", "emoji-travel-symbolic");
setup_section (chooser, &chooser->activities, "jack-o-lantern", "emoji-activities-symbolic");
setup_section (chooser, &chooser->objects, "muted speaker", "emoji-objects-symbolic");
setup_section (chooser, &chooser->symbols, "ATM sign", "emoji-symbols-symbolic");
setup_section (chooser, &chooser->flags, "chequered flag", "emoji-flags-symbolic");
setup_section (chooser, &chooser->recent, -1, "emoji-recent-symbolic");
setup_section (chooser, &chooser->people, 0, "emoji-people-symbolic");
setup_section (chooser, &chooser->body, 1, "emoji-body-symbolic");
setup_section (chooser, &chooser->nature, 3, "emoji-nature-symbolic");
setup_section (chooser, &chooser->food, 4, "emoji-food-symbolic");
setup_section (chooser, &chooser->travel, 5, "emoji-travel-symbolic");
setup_section (chooser, &chooser->activities, 6, "emoji-activities-symbolic");
setup_section (chooser, &chooser->objects, 7, "emoji-objects-symbolic");
setup_section (chooser, &chooser->symbols, 8, "emoji-symbols-symbolic");
setup_section (chooser, &chooser->flags, 9, "emoji-flags-symbolic");
populate_recent_section (chooser);
@ -744,6 +924,7 @@ gtk_emoji_chooser_show (GtkWidget *widget)
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
gtk_adjustment_set_value (adj, 0);
adj_value_changed (adj, chooser);
gtk_entry_set_text (GTK_ENTRY (chooser->search_entry), "");
}

View File

@ -116,7 +116,7 @@ next:
break;
}
}
while (g_unichar_isalnum (g_utf8_get_char (p)) || *p == '_');
while (g_unichar_isalnum (g_utf8_get_char (p)) || *p == '_' || *p == ' ');
if (found_candidate)
n_matches = populate_completion (completion, p, 0);
@ -521,7 +521,7 @@ add_emoji (GtkWidget *list,
GtkWidget *box;
PangoAttrList *attrs;
char text[64];
const char *shortname;
const char *name;
GtkWidget *stack;
gunichar modifier;
@ -551,8 +551,8 @@ add_emoji (GtkWidget *list,
gtk_box_pack_start (GTK_BOX (box), stack, FALSE, FALSE, 0);
g_object_set_data (G_OBJECT (child), "stack", stack);
g_variant_get_child (emoji_data, 2, "&s", &shortname);
label = gtk_label_new (shortname);
g_variant_get_child (emoji_data, 1, "&s", &name);
label = gtk_label_new (name);
gtk_widget_show (label);
gtk_label_set_xalign (GTK_LABEL (label), 0);
@ -610,10 +610,11 @@ populate_completion (GtkEmojiCompletion *completion,
g_variant_iter_init (&iter, completion->data);
while ((item = g_variant_iter_next_value (&iter)))
{
const char *shortname;
const char *name;
g_variant_get_child (item, 2, "&s", &shortname);
if (g_str_has_prefix (shortname, text))
g_variant_get_child (item, 1, "&s", &name);
if (g_str_has_prefix (name, text + 1))
{
n_matches++;
@ -663,8 +664,9 @@ gtk_emoji_completion_init (GtkEmojiCompletion *completion)
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));
bytes = get_emoji_data ();
completion->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(ausasu)"), bytes, TRUE));
g_bytes_unref (bytes);
completion->long_press = gtk_gesture_long_press_new (completion->list);

View File

@ -111,6 +111,8 @@ gboolean gtk_simulate_touchscreen (void);
guint gtk_get_display_debug_flags (GdkDisplay *display);
GBytes *get_emoji_data (void);
#ifdef G_ENABLE_DEBUG
#define GTK_DISPLAY_DEBUG_CHECK(display,type) G_UNLIKELY (gtk_get_display_debug_flags (display) & GTK_DEBUG_##type)

View File

@ -1027,6 +1027,24 @@ libgtk_dep = declare_dependency(sources: gtk_dep_sources,
link_with: libgtk,
link_args: common_ldflags)
foreach lang : [ 'de', 'fr', 'es', 'zh' ]
conf = configuration_data()
conf.set('lang', lang)
resxml = configure_file(input: 'emoji/gresource.xml.in',
output: lang + '.gresource.xml',
configuration: conf
)
gnome.compile_resources(lang,
resxml,
source_dir: 'emoji',
gresource_bundle: true,
install: true,
install_dir: join_paths(gtk_datadir, 'gtk-3.0', 'emoji'),
)
endforeach
if quartz_enabled
install_data(['gtk-keys.css.mac'],
install_dir: join_paths(get_option('datadir'), 'themes/Mac/gtk-3.0'),

View File

@ -71,7 +71,7 @@ NULL=
!if [for %s in (scalable) do @(for %c in (status) do @(for %f in (..\gtk\icons\%s\%c\*.svg) do @call create-lists.bat file resources_sources.mak %f))]
!endif
!if [for %f in (..\gtk\inspector\*.ui ..\gtk\inspector\logo.png ..\gtk\emoji\emoji.data) do @call create-lists.bat file resources_sources.mak %f]
!if [for %f in (..\gtk\inspector\*.ui ..\gtk\inspector\logo.png ..\gtk\emoji\*.data) do @call create-lists.bat file resources_sources.mak %f]
!endif
!if [call create-lists.bat footer resources_sources.mak]

View File

@ -156,7 +156,7 @@ all: \
@for %%s in (scalable) do @(for %%c in (status) do @(for %%f in (..\gtk\icons\%%s\%%c\*.svg) do @echo ^<file^>icons/%%s/%%c/%%~nxf^</file^>>> $@))
@for %%f in (..\gtk\inspector\*.ui) do @echo ^<file compressed='true' preprocess='xml-stripblanks'^>inspector/%%~nxf^</file^>>> $@
@echo ^<file^>inspector/logo.png^</file^>>> $@
@echo ^<file^>emoji/emoji.data^</file^>>> $@
@for %%f in (..\gtk\emoji\*.data) do @echo ^<file^>emoji/%%~nxf^</file^>>> $@
@echo ^</gresource^>>> $@
@echo ^</gresources^>>> $@