forked from AuroraMiddleware/gtk
Merge branch 'wip/matthiasc/emoji-picker' into 'master'
Wip/matthiasc/emoji picker See merge request GNOME/gtk!115
This commit is contained in:
commit
29d77be03f
@ -468,7 +468,8 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkEntry" id="entry1">
|
<object class="GtkEntry" id="entry1">
|
||||||
<property name="can-focus">1</property>
|
<property name="can-focus">1</property>
|
||||||
<property name="invisible-char">•</property>
|
<property name="enable-emoji-completion">1</property>
|
||||||
|
<property name="invisible_char">•</property>
|
||||||
<property name="placeholder-text" translatable="yes">Click icon to change mode</property>
|
<property name="placeholder-text" translatable="yes">Click icon to change mode</property>
|
||||||
<property name="secondary-icon-name">view-refresh-symbolic</property>
|
<property name="secondary-icon-name">view-refresh-symbolic</property>
|
||||||
<property name="secondary-icon-tooltip-text">Change mode</property>
|
<property name="secondary-icon-tooltip-text">Change mode</property>
|
||||||
|
665
gtk/gtkemojicompletion.c
Normal file
665
gtk/gtkemojicompletion.c
Normal file
@ -0,0 +1,665 @@
|
|||||||
|
/* 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"
|
||||||
|
#include "gtkgesturelongpress.h"
|
||||||
|
#include "gtkflowbox.h"
|
||||||
|
#include "gtkstack.h"
|
||||||
|
|
||||||
|
struct _GtkEmojiCompletion
|
||||||
|
{
|
||||||
|
GtkPopover parent_instance;
|
||||||
|
|
||||||
|
GtkEntry *entry;
|
||||||
|
char *text;
|
||||||
|
guint length;
|
||||||
|
guint offset;
|
||||||
|
gulong changed_id;
|
||||||
|
guint n_matches;
|
||||||
|
|
||||||
|
GtkWidget *list;
|
||||||
|
GtkWidget *active;
|
||||||
|
GtkWidget *active_variation;
|
||||||
|
|
||||||
|
GVariant *data;
|
||||||
|
|
||||||
|
GtkGesture *long_press;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GtkEmojiCompletionClass {
|
||||||
|
GtkPopoverClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void connect_signals (GtkEmojiCompletion *completion,
|
||||||
|
GtkEntry *entry);
|
||||||
|
static void disconnect_signals (GtkEmojiCompletion *completion);
|
||||||
|
static int populate_completion (GtkEmojiCompletion *completion,
|
||||||
|
const char *text,
|
||||||
|
guint offset);
|
||||||
|
|
||||||
|
#define MAX_ROWS 5
|
||||||
|
|
||||||
|
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_free (completion->text);
|
||||||
|
g_variant_unref (completion->data);
|
||||||
|
|
||||||
|
g_clear_object (&completion->long_press);
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
next:
|
||||||
|
p = g_utf8_prev_char (p);
|
||||||
|
if (*p == ':')
|
||||||
|
{
|
||||||
|
if (p + 1 == text + length)
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
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_completion (completion, p, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (GtkWidget *row,
|
||||||
|
GtkEmojiCompletion *completion)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
row_activated (GtkListBox *list,
|
||||||
|
GtkListBoxRow *row,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GtkEmojiCompletion *completion = data;
|
||||||
|
|
||||||
|
emoji_activated (GTK_WIDGET (row), completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
child_activated (GtkFlowBox *box,
|
||||||
|
GtkFlowBoxChild *child,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GtkEmojiCompletion *completion = data;
|
||||||
|
|
||||||
|
g_print ("child activated\n");
|
||||||
|
emoji_activated (GTK_WIDGET (child), completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
move_active_row (GtkEmojiCompletion *completion,
|
||||||
|
int direction)
|
||||||
|
{
|
||||||
|
GtkWidget *child;
|
||||||
|
GtkWidget *base;
|
||||||
|
|
||||||
|
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);
|
||||||
|
base = GTK_WIDGET (g_object_get_data (G_OBJECT (child), "base"));
|
||||||
|
gtk_widget_unset_state_flags (base, 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);
|
||||||
|
|
||||||
|
if (completion->active_variation)
|
||||||
|
{
|
||||||
|
gtk_widget_unset_state_flags (completion->active_variation, GTK_STATE_FLAG_PRELIGHT);
|
||||||
|
completion->active_variation = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
activate_active_row (GtkEmojiCompletion *completion)
|
||||||
|
{
|
||||||
|
if (GTK_IS_FLOW_BOX_CHILD (completion->active_variation))
|
||||||
|
emoji_activated (completion->active_variation, completion);
|
||||||
|
else if (completion->active != NULL)
|
||||||
|
emoji_activated (completion->active, completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
show_variations (GtkEmojiCompletion *completion,
|
||||||
|
GtkWidget *row,
|
||||||
|
gboolean visible)
|
||||||
|
{
|
||||||
|
GtkWidget *stack;
|
||||||
|
GtkWidget *box;
|
||||||
|
gboolean is_visible;
|
||||||
|
|
||||||
|
if (!row)
|
||||||
|
return;
|
||||||
|
|
||||||
|
stack = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "stack"));
|
||||||
|
box = gtk_stack_get_child_by_name (GTK_STACK (stack), "variations");
|
||||||
|
if (!box)
|
||||||
|
return;
|
||||||
|
|
||||||
|
is_visible = gtk_stack_get_visible_child (GTK_STACK (stack)) == box;
|
||||||
|
if (is_visible == visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (visible)
|
||||||
|
gtk_widget_unset_state_flags (row, GTK_STATE_FLAG_PRELIGHT);
|
||||||
|
else
|
||||||
|
gtk_widget_set_state_flags (row, GTK_STATE_FLAG_PRELIGHT, FALSE);
|
||||||
|
|
||||||
|
gtk_stack_set_visible_child_name (GTK_STACK (stack), visible ? "variations" : "text");
|
||||||
|
if (completion->active_variation)
|
||||||
|
{
|
||||||
|
gtk_widget_unset_state_flags (completion->active_variation, GTK_STATE_FLAG_PRELIGHT);
|
||||||
|
completion->active_variation = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
move_active_variation (GtkEmojiCompletion *completion,
|
||||||
|
int direction)
|
||||||
|
{
|
||||||
|
GtkWidget *base;
|
||||||
|
GtkWidget *stack;
|
||||||
|
GtkWidget *box;
|
||||||
|
GtkWidget *next;
|
||||||
|
|
||||||
|
if (!completion->active)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
base = GTK_WIDGET (g_object_get_data (G_OBJECT (completion->active), "base"));
|
||||||
|
stack = GTK_WIDGET (g_object_get_data (G_OBJECT (completion->active), "stack"));
|
||||||
|
box = gtk_stack_get_child_by_name (GTK_STACK (stack), "variations");
|
||||||
|
|
||||||
|
if (gtk_stack_get_visible_child (GTK_STACK (stack)) != box)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
next = NULL;
|
||||||
|
|
||||||
|
if (!completion->active_variation)
|
||||||
|
next = base;
|
||||||
|
else if (completion->active_variation == base && direction == 1)
|
||||||
|
next = gtk_widget_get_first_child (box);
|
||||||
|
else if (completion->active_variation == gtk_widget_get_first_child (box) && direction == -1)
|
||||||
|
next = base;
|
||||||
|
else if (direction == 1)
|
||||||
|
next = gtk_widget_get_next_sibling (completion->active_variation);
|
||||||
|
else if (direction == -1)
|
||||||
|
next = gtk_widget_get_prev_sibling (completion->active_variation);
|
||||||
|
|
||||||
|
if (next)
|
||||||
|
{
|
||||||
|
if (completion->active_variation)
|
||||||
|
gtk_widget_unset_state_flags (completion->active_variation, GTK_STATE_FLAG_PRELIGHT);
|
||||||
|
completion->active_variation = next;
|
||||||
|
gtk_widget_set_state_flags (completion->active_variation, GTK_STATE_FLAG_PRELIGHT, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
entry_key_press (GtkEntry *entry,
|
||||||
|
GdkEventKey *event,
|
||||||
|
GtkEmojiCompletion *completion)
|
||||||
|
{
|
||||||
|
guint keyval;
|
||||||
|
|
||||||
|
if (!gtk_widget_get_visible (GTK_WIDGET (completion)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
gdk_event_get_keyval ((GdkEvent*)event, &keyval);
|
||||||
|
|
||||||
|
if (keyval == GDK_KEY_Escape)
|
||||||
|
{
|
||||||
|
gtk_popover_popdown (GTK_POPOVER (completion));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyval == GDK_KEY_Tab)
|
||||||
|
{
|
||||||
|
show_variations (completion, completion->active, FALSE);
|
||||||
|
|
||||||
|
guint offset = completion->offset + MAX_ROWS;
|
||||||
|
if (offset >= completion->n_matches)
|
||||||
|
offset = 0;
|
||||||
|
populate_completion (completion, completion->text, offset);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyval == GDK_KEY_Up)
|
||||||
|
{
|
||||||
|
show_variations (completion, completion->active, FALSE);
|
||||||
|
|
||||||
|
move_active_row (completion, -1);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyval == GDK_KEY_Down)
|
||||||
|
{
|
||||||
|
show_variations (completion, completion->active, FALSE);
|
||||||
|
|
||||||
|
move_active_row (completion, 1);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyval == GDK_KEY_Return ||
|
||||||
|
keyval == GDK_KEY_KP_Enter ||
|
||||||
|
keyval == GDK_KEY_ISO_Enter)
|
||||||
|
{
|
||||||
|
activate_active_row (completion);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyval == GDK_KEY_Right)
|
||||||
|
{
|
||||||
|
show_variations (completion, completion->active, TRUE);
|
||||||
|
move_active_variation (completion, 1);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyval == GDK_KEY_Left)
|
||||||
|
{
|
||||||
|
if (!move_active_variation (completion, -1))
|
||||||
|
show_variations (completion, completion->active, FALSE);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
entry_focus_out (GtkWidget *entry,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
GtkEmojiCompletion *completion)
|
||||||
|
{
|
||||||
|
if (!gtk_widget_has_focus (entry))
|
||||||
|
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, "notify::has-focus", 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 gboolean
|
||||||
|
has_variations (GVariant *emoji_data)
|
||||||
|
{
|
||||||
|
GVariant *codes;
|
||||||
|
int i;
|
||||||
|
gboolean has_variations;
|
||||||
|
|
||||||
|
has_variations = FALSE;
|
||||||
|
codes = g_variant_get_child_value (emoji_data, 0);
|
||||||
|
for (i = 0; i < g_variant_n_children (codes); i++)
|
||||||
|
{
|
||||||
|
gunichar code;
|
||||||
|
g_variant_get_child (codes, i, "u", &code);
|
||||||
|
if (code == 0)
|
||||||
|
{
|
||||||
|
has_variations = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_variant_unref (codes);
|
||||||
|
|
||||||
|
return has_variations;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_text (GVariant *emoji_data,
|
||||||
|
gunichar modifier,
|
||||||
|
char *text,
|
||||||
|
gsize length)
|
||||||
|
{
|
||||||
|
GVariant *codes;
|
||||||
|
int i;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
p = text;
|
||||||
|
codes = g_variant_get_child_value (emoji_data, 0);
|
||||||
|
for (i = 0; i < g_variant_n_children (codes); i++)
|
||||||
|
{
|
||||||
|
gunichar code;
|
||||||
|
|
||||||
|
g_variant_get_child (codes, i, "u", &code);
|
||||||
|
if (code == 0)
|
||||||
|
code = modifier;
|
||||||
|
if (code != 0)
|
||||||
|
p += g_unichar_to_utf8 (code, p);
|
||||||
|
}
|
||||||
|
g_variant_unref (codes);
|
||||||
|
p[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_emoji_variation (GtkWidget *box,
|
||||||
|
GVariant *emoji_data,
|
||||||
|
gunichar modifier)
|
||||||
|
{
|
||||||
|
GtkWidget *child;
|
||||||
|
GtkWidget *label;
|
||||||
|
PangoAttrList *attrs;
|
||||||
|
char text[64];
|
||||||
|
|
||||||
|
get_text (emoji_data, modifier, text, 64);
|
||||||
|
|
||||||
|
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_flow_box_child_new ();
|
||||||
|
gtk_style_context_add_class (gtk_widget_get_style_context (child), "emoji");
|
||||||
|
g_object_set_data_full (G_OBJECT (child), "text", g_strdup (text), g_free);
|
||||||
|
g_object_set_data_full (G_OBJECT (child), "emoji-data",
|
||||||
|
g_variant_ref (emoji_data),
|
||||||
|
(GDestroyNotify)g_variant_unref);
|
||||||
|
if (modifier != 0)
|
||||||
|
g_object_set_data (G_OBJECT (child), "modifier", GUINT_TO_POINTER (modifier));
|
||||||
|
|
||||||
|
gtk_container_add (GTK_CONTAINER (child), label);
|
||||||
|
gtk_flow_box_insert (GTK_FLOW_BOX (box), child, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_emoji (GtkWidget *list,
|
||||||
|
GVariant *emoji_data,
|
||||||
|
GtkEmojiCompletion *completion)
|
||||||
|
{
|
||||||
|
GtkWidget *child;
|
||||||
|
GtkWidget *label;
|
||||||
|
GtkWidget *box;
|
||||||
|
PangoAttrList *attrs;
|
||||||
|
char text[64];
|
||||||
|
const char *shortname;
|
||||||
|
GtkWidget *stack;
|
||||||
|
gunichar modifier;
|
||||||
|
|
||||||
|
get_text (emoji_data, 0, text, 64);
|
||||||
|
|
||||||
|
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);
|
||||||
|
gtk_style_context_add_class (gtk_widget_get_style_context (label), "emoji");
|
||||||
|
|
||||||
|
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_object_set_data (G_OBJECT (child), "base", label);
|
||||||
|
|
||||||
|
stack = gtk_stack_new ();
|
||||||
|
gtk_stack_set_homogeneous (GTK_STACK (stack), TRUE);
|
||||||
|
gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT);
|
||||||
|
gtk_box_pack_start (GTK_BOX (box), stack);
|
||||||
|
g_object_set_data (G_OBJECT (child), "stack", stack);
|
||||||
|
|
||||||
|
g_variant_get_child (emoji_data, 2, "&s", &shortname);
|
||||||
|
label = gtk_label_new (shortname);
|
||||||
|
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
||||||
|
|
||||||
|
gtk_stack_add_named (GTK_STACK (stack), label, "text");
|
||||||
|
|
||||||
|
if (has_variations (emoji_data))
|
||||||
|
{
|
||||||
|
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), 5);
|
||||||
|
gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX (box), 5);
|
||||||
|
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);
|
||||||
|
g_signal_connect (box, "child-activated", G_CALLBACK (child_activated), completion);
|
||||||
|
for (modifier = 0x1f3fb; modifier <= 0x1f3ff; modifier++)
|
||||||
|
add_emoji_variation (box, emoji_data, modifier);
|
||||||
|
|
||||||
|
gtk_stack_add_named (GTK_STACK (stack), box, "variations");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set_data_full (G_OBJECT (child), "text", g_strdup (text), g_free);
|
||||||
|
g_object_set_data_full (G_OBJECT (child), "emoji-data",
|
||||||
|
g_variant_ref (emoji_data), (GDestroyNotify)g_variant_unref);
|
||||||
|
gtk_style_context_add_class (gtk_widget_get_style_context (child), "emoji-completion-row");
|
||||||
|
|
||||||
|
gtk_list_box_insert (GTK_LIST_BOX (list), child, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
populate_completion (GtkEmojiCompletion *completion,
|
||||||
|
const char *text,
|
||||||
|
guint offset)
|
||||||
|
{
|
||||||
|
GList *children, *l;
|
||||||
|
int n_matches;
|
||||||
|
int n_added;
|
||||||
|
GVariantIter iter;
|
||||||
|
GVariant *item;
|
||||||
|
|
||||||
|
text = g_strdup (text);
|
||||||
|
g_free (completion->text);
|
||||||
|
completion->text = g_strdup (text);
|
||||||
|
completion->length = g_utf8_strlen (text, -1);
|
||||||
|
completion->offset = offset;
|
||||||
|
|
||||||
|
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;
|
||||||
|
n_added = 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))
|
||||||
|
{
|
||||||
|
n_matches++;
|
||||||
|
|
||||||
|
if (n_matches > offset && n_added < MAX_ROWS)
|
||||||
|
{
|
||||||
|
add_emoji (completion->list, item, completion);
|
||||||
|
n_added++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completion->n_matches = n_matches;
|
||||||
|
|
||||||
|
if (n_added > 0)
|
||||||
|
{
|
||||||
|
completion->active = gtk_widget_get_first_child (completion->list);
|
||||||
|
gtk_widget_set_state_flags (completion->active, GTK_STATE_FLAG_PRELIGHT, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n_added;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
long_pressed_cb (GtkGesture *gesture,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GtkEmojiCompletion *completion = data;
|
||||||
|
GtkWidget *row;
|
||||||
|
|
||||||
|
row = GTK_WIDGET (gtk_list_box_get_row_at_y (GTK_LIST_BOX (completion->list), y));
|
||||||
|
if (!row)
|
||||||
|
return;
|
||||||
|
|
||||||
|
show_variations (completion, row, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
completion->long_press = gtk_gesture_long_press_new (completion->list);
|
||||||
|
g_signal_connect (completion->long_press, "pressed", G_CALLBACK (long_pressed_cb), completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, row_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);
|
||||||
|
}
|
41
gtk/gtkemojicompletion.h
Normal file
41
gtk/gtkemojicompletion.h
Normal file
@ -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
|
@ -41,7 +41,9 @@
|
|||||||
#include "gtkdnd.h"
|
#include "gtkdnd.h"
|
||||||
#include "gtkdndprivate.h"
|
#include "gtkdndprivate.h"
|
||||||
#include "gtkemojichooser.h"
|
#include "gtkemojichooser.h"
|
||||||
|
#include "gtkemojicompletion.h"
|
||||||
#include "gtkentrybuffer.h"
|
#include "gtkentrybuffer.h"
|
||||||
|
#include "gtkeventcontrollerkey.h"
|
||||||
#include "gtkgesturedrag.h"
|
#include "gtkgesturedrag.h"
|
||||||
#include "gtkgesturemultipress.h"
|
#include "gtkgesturemultipress.h"
|
||||||
#include "gtkgesturesingle.h"
|
#include "gtkgesturesingle.h"
|
||||||
@ -72,7 +74,6 @@
|
|||||||
#include "gtktypebuiltins.h"
|
#include "gtktypebuiltins.h"
|
||||||
#include "gtkwidgetprivate.h"
|
#include "gtkwidgetprivate.h"
|
||||||
#include "gtkwindow.h"
|
#include "gtkwindow.h"
|
||||||
#include "gtkeventcontrollerkey.h"
|
|
||||||
|
|
||||||
#include "a11y/gtkentryaccessible.h"
|
#include "a11y/gtkentryaccessible.h"
|
||||||
|
|
||||||
@ -253,6 +254,7 @@ struct _GtkEntryPrivate
|
|||||||
|
|
||||||
guint editable : 1;
|
guint editable : 1;
|
||||||
guint show_emoji_icon : 1;
|
guint show_emoji_icon : 1;
|
||||||
|
guint enable_emoji_completion : 1;
|
||||||
guint in_drag : 1;
|
guint in_drag : 1;
|
||||||
guint overwrite_mode : 1;
|
guint overwrite_mode : 1;
|
||||||
guint visible : 1;
|
guint visible : 1;
|
||||||
@ -371,6 +373,7 @@ enum {
|
|||||||
PROP_POPULATE_ALL,
|
PROP_POPULATE_ALL,
|
||||||
PROP_TABS,
|
PROP_TABS,
|
||||||
PROP_SHOW_EMOJI_ICON,
|
PROP_SHOW_EMOJI_ICON,
|
||||||
|
PROP_ENABLE_EMOJI_COMPLETION,
|
||||||
PROP_EDITING_CANCELED,
|
PROP_EDITING_CANCELED,
|
||||||
NUM_PROPERTIES = PROP_EDITING_CANCELED
|
NUM_PROPERTIES = PROP_EDITING_CANCELED
|
||||||
};
|
};
|
||||||
@ -561,11 +564,6 @@ static gboolean gtk_entry_key_controller_key_pressed (GtkEventControllerKey *co
|
|||||||
|
|
||||||
/* Internal routines
|
/* 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,
|
static void gtk_entry_draw_text (GtkEntry *entry,
|
||||||
GtkSnapshot *snapshot);
|
GtkSnapshot *snapshot);
|
||||||
static void gtk_entry_draw_cursor (GtkEntry *entry,
|
static void gtk_entry_draw_cursor (GtkEntry *entry,
|
||||||
@ -657,6 +655,8 @@ static void buffer_disconnect_signals (GtkEntry *entry);
|
|||||||
static GtkEntryBuffer *get_buffer (GtkEntry *entry);
|
static GtkEntryBuffer *get_buffer (GtkEntry *entry);
|
||||||
static void set_show_emoji_icon (GtkEntry *entry,
|
static void set_show_emoji_icon (GtkEntry *entry,
|
||||||
gboolean value);
|
gboolean value);
|
||||||
|
static void set_enable_emoji_completion (GtkEntry *entry,
|
||||||
|
gboolean value);
|
||||||
|
|
||||||
static void gtk_entry_measure (GtkWidget *widget,
|
static void gtk_entry_measure (GtkWidget *widget,
|
||||||
GtkOrientation orientation,
|
GtkOrientation orientation,
|
||||||
@ -1409,6 +1409,13 @@ gtk_entry_class_init (GtkEntryClass *class)
|
|||||||
FALSE,
|
FALSE,
|
||||||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
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);
|
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, entry_props);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2184,6 +2191,10 @@ gtk_entry_set_property (GObject *object,
|
|||||||
set_show_emoji_icon (entry, g_value_get_boolean (value));
|
set_show_emoji_icon (entry, g_value_get_boolean (value));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PROP_ENABLE_EMOJI_COMPLETION:
|
||||||
|
set_enable_emoji_completion (entry, g_value_get_boolean (value));
|
||||||
|
break;
|
||||||
|
|
||||||
case PROP_SCROLL_OFFSET:
|
case PROP_SCROLL_OFFSET:
|
||||||
case PROP_CURSOR_POSITION:
|
case PROP_CURSOR_POSITION:
|
||||||
default:
|
default:
|
||||||
@ -2416,6 +2427,10 @@ gtk_entry_get_property (GObject *object,
|
|||||||
g_value_set_boolean (value, priv->show_emoji_icon);
|
g_value_set_boolean (value, priv->show_emoji_icon);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PROP_ENABLE_EMOJI_COMPLETION:
|
||||||
|
g_value_set_boolean (value, priv->enable_emoji_completion);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@ -5269,7 +5284,7 @@ gtk_entry_delete_surrounding_cb (GtkIMContext *slave,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* Used for im_commit_cb and inserting Unicode chars */
|
/* Used for im_commit_cb and inserting Unicode chars */
|
||||||
static void
|
void
|
||||||
gtk_entry_enter_text (GtkEntry *entry,
|
gtk_entry_enter_text (GtkEntry *entry,
|
||||||
const gchar *str)
|
const gchar *str)
|
||||||
{
|
{
|
||||||
@ -5304,7 +5319,7 @@ gtk_entry_enter_text (GtkEntry *entry,
|
|||||||
/* All changes to priv->current_pos and priv->selection_bound
|
/* All changes to priv->current_pos and priv->selection_bound
|
||||||
* should go through this function.
|
* should go through this function.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
gtk_entry_set_positions (GtkEntry *entry,
|
gtk_entry_set_positions (GtkEntry *entry,
|
||||||
gint current_pos,
|
gint current_pos,
|
||||||
gint selection_bound)
|
gint selection_bound)
|
||||||
@ -9585,3 +9600,23 @@ set_show_emoji_icon (GtkEntry *entry,
|
|||||||
g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_SHOW_EMOJI_ICON]);
|
g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_SHOW_EMOJI_ICON]);
|
||||||
gtk_widget_queue_resize (GTK_WIDGET (entry));
|
gtk_widget_queue_resize (GTK_WIDGET (entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_enable_emoji_completion (GtkEntry *entry,
|
||||||
|
gboolean value)
|
||||||
|
{
|
||||||
|
GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
|
||||||
|
|
||||||
|
if (priv->enable_emoji_completion == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv->enable_emoji_completion = value;
|
||||||
|
|
||||||
|
if (priv->enable_emoji_completion)
|
||||||
|
g_object_set_data (G_OBJECT (entry), "emoji-completion-popup",
|
||||||
|
gtk_emoji_completion_new (entry));
|
||||||
|
else
|
||||||
|
g_object_set_data (G_OBJECT (entry), "emoji-completion-popup", NULL);
|
||||||
|
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_ENABLE_EMOJI_COMPLETION]);
|
||||||
|
}
|
||||||
|
@ -91,6 +91,13 @@ GtkIMContext* _gtk_entry_get_im_context (GtkEntry *entry);
|
|||||||
void _gtk_entry_grab_focus (GtkEntry *entry,
|
void _gtk_entry_grab_focus (GtkEntry *entry,
|
||||||
gboolean select_all);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __GTK_ENTRY_PRIVATE_H__ */
|
#endif /* __GTK_ENTRY_PRIVATE_H__ */
|
||||||
|
@ -66,57 +66,15 @@
|
|||||||
* by typing Ctrl-Shift-u, followed by a hexadecimal Unicode codepoint.
|
* by typing Ctrl-Shift-u, followed by a hexadecimal Unicode codepoint.
|
||||||
* For example, Ctrl-Shift-u 1 2 3 Enter yields U+0123 LATIN SMALL LETTER
|
* For example, Ctrl-Shift-u 1 2 3 Enter yields U+0123 LATIN SMALL LETTER
|
||||||
* G WITH CEDILLA, i.e. ģ.
|
* G WITH CEDILLA, i.e. ģ.
|
||||||
*
|
|
||||||
* ## Emoji
|
|
||||||
*
|
|
||||||
* GtkIMContextSimple also supports entry of Emoji by their name.
|
|
||||||
* This works by first typing Ctrl-Shift-e, followed by an Emoji name.
|
|
||||||
*
|
|
||||||
* The following names are supported:
|
|
||||||
* - :-) 🙂
|
|
||||||
* - 8-) 😍
|
|
||||||
* - <3 ❤
|
|
||||||
* - kiss 💋
|
|
||||||
* - grin 😁
|
|
||||||
* - joy 😂
|
|
||||||
* - :-* 😚
|
|
||||||
* - xD 😆
|
|
||||||
* - like 👍
|
|
||||||
* - dislike 👎
|
|
||||||
* - up 👆
|
|
||||||
* - v ✌
|
|
||||||
* - ok 👌
|
|
||||||
* - B-) 😎
|
|
||||||
* - ;-) 😉
|
|
||||||
* - ;-P 😜
|
|
||||||
* - :-p 😋
|
|
||||||
* - 3( 😔
|
|
||||||
* - :-( 😞
|
|
||||||
* - :] 😏
|
|
||||||
* - :'( 😢
|
|
||||||
* - :_( 😭
|
|
||||||
* - :(( 😩
|
|
||||||
* - :o 😨
|
|
||||||
* - :| 😐
|
|
||||||
* - 3-) 😌
|
|
||||||
* - >( 😠
|
|
||||||
* - >(( 😡
|
|
||||||
* - O:) 😇
|
|
||||||
* - ;o 😰
|
|
||||||
* - 8| 😳
|
|
||||||
* - 8o 😲
|
|
||||||
* - :X 😷
|
|
||||||
* - }:) 😈
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct _GtkIMContextSimplePrivate
|
struct _GtkIMContextSimplePrivate
|
||||||
{
|
{
|
||||||
guint16 compose_buffer[MAX(GTK_MAX_COMPOSE_LEN + 1, 9)];
|
guint16 compose_buffer[GTK_MAX_COMPOSE_LEN + 1];
|
||||||
gunichar tentative_match;
|
gunichar tentative_match;
|
||||||
gint tentative_match_len;
|
gint tentative_match_len;
|
||||||
|
|
||||||
guint in_hex_sequence : 1;
|
guint in_hex_sequence : 1;
|
||||||
guint in_emoji_sequence : 1;
|
|
||||||
guint modifiers_dropped : 1;
|
guint modifiers_dropped : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -346,10 +304,9 @@ gtk_im_context_simple_commit_char (GtkIMContext *context,
|
|||||||
len = g_unichar_to_utf8 (ch, buf);
|
len = g_unichar_to_utf8 (ch, buf);
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
|
|
||||||
if (priv->tentative_match || priv->in_hex_sequence || priv->in_emoji_sequence)
|
if (priv->tentative_match || priv->in_hex_sequence)
|
||||||
{
|
{
|
||||||
priv->in_hex_sequence = FALSE;
|
priv->in_hex_sequence = FALSE;
|
||||||
priv->in_emoji_sequence = FALSE;
|
|
||||||
priv->tentative_match = 0;
|
priv->tentative_match = 0;
|
||||||
priv->tentative_match_len = 0;
|
priv->tentative_match_len = 0;
|
||||||
g_signal_emit_by_name (context_simple, "preedit-changed");
|
g_signal_emit_by_name (context_simple, "preedit-changed");
|
||||||
@ -917,110 +874,6 @@ check_hex (GtkIMContextSimple *context_simple,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char *name;
|
|
||||||
gunichar ch;
|
|
||||||
} EmojiItem;
|
|
||||||
|
|
||||||
static EmojiItem emoji[] = {
|
|
||||||
{ ":-)", 0x1f642 },
|
|
||||||
{ "8-)", 0x1f60d },
|
|
||||||
{ "<3", 0x02764 },
|
|
||||||
{ "kiss", 0x1f48b },
|
|
||||||
{ "grin", 0x1f601 },
|
|
||||||
{ "joy", 0x1f602 },
|
|
||||||
{ ":-*", 0x1f61a },
|
|
||||||
{ "xD", 0x1f606 },
|
|
||||||
{ "like", 0x1f44d },
|
|
||||||
{ "dislike", 0x1f44e },
|
|
||||||
{ "up", 0x1f446 },
|
|
||||||
{ "v", 0x0270c },
|
|
||||||
{ "ok", 0x1f44c },
|
|
||||||
{ "B-)", 0x1f60e },
|
|
||||||
{ ":-D", 0x1f603 },
|
|
||||||
{ ";-)", 0x1f609 },
|
|
||||||
{ ";-P", 0x1f61c },
|
|
||||||
{ ":-p", 0x1f60b },
|
|
||||||
{ "3(", 0x1f614 },
|
|
||||||
{ ":-(", 0x1f61e },
|
|
||||||
{ ":]", 0x1f60f },
|
|
||||||
{ ":'(", 0x1f622 },
|
|
||||||
{ ":_(", 0x1f62d },
|
|
||||||
{ ":((", 0x1f629 },
|
|
||||||
{ ":o", 0x1f628 },
|
|
||||||
{ ":|", 0x1f610 },
|
|
||||||
{ "3-)", 0x1f60c },
|
|
||||||
{ ">(", 0x1f620 },
|
|
||||||
{ ">((", 0x1f621 },
|
|
||||||
{ "O:)", 0x1f607 },
|
|
||||||
{ ";o", 0x1f630 },
|
|
||||||
{ "8|", 0x1f633 },
|
|
||||||
{ "8o", 0x1f632 },
|
|
||||||
{ ":X", 0x1f637 },
|
|
||||||
{ "}:)", 0x1f608 },
|
|
||||||
{ NULL, 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
check_emoji (GtkIMContextSimple *context_simple,
|
|
||||||
gint n_compose)
|
|
||||||
{
|
|
||||||
GtkIMContextSimplePrivate *priv = context_simple->priv;
|
|
||||||
GString *str;
|
|
||||||
gint i;
|
|
||||||
gchar buf[7];
|
|
||||||
char *lower;
|
|
||||||
gboolean has_completion;
|
|
||||||
|
|
||||||
priv->tentative_match = 0;
|
|
||||||
priv->tentative_match_len = 0;
|
|
||||||
|
|
||||||
str = g_string_new (NULL);
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < n_compose)
|
|
||||||
{
|
|
||||||
gunichar ch;
|
|
||||||
|
|
||||||
ch = gdk_keyval_to_unicode (priv->compose_buffer[i]);
|
|
||||||
|
|
||||||
if (ch == 0)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
buf[g_unichar_to_utf8 (ch, buf)] = '\0';
|
|
||||||
|
|
||||||
g_string_append (str, buf);
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
lower = g_utf8_strdown (str->str, str->len);
|
|
||||||
|
|
||||||
has_completion = FALSE;
|
|
||||||
for (i = 0; emoji[i].name; i++)
|
|
||||||
{
|
|
||||||
if (strcmp (str->str, emoji[i].name) == 0 ||
|
|
||||||
strcmp (lower, emoji[i].name) == 0)
|
|
||||||
{
|
|
||||||
priv->tentative_match = emoji[i].ch;
|
|
||||||
priv->tentative_match_len = n_compose;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_completion &&
|
|
||||||
(g_str_has_prefix (emoji[i].name, str->str) ||
|
|
||||||
g_str_has_prefix (emoji[i].name, lower)))
|
|
||||||
{
|
|
||||||
has_completion = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_string_free (str, TRUE);
|
|
||||||
g_free (lower);
|
|
||||||
|
|
||||||
return priv->tentative_match != 0 || has_completion;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
beep_surface (GdkSurface *surface)
|
beep_surface (GdkSurface *surface)
|
||||||
{
|
{
|
||||||
@ -1147,15 +1000,6 @@ canonical_hex_keyval (GdkEventKey *event)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint
|
|
||||||
canonical_emoji_keyval (GdkEventKey *event)
|
|
||||||
{
|
|
||||||
guint keyval;
|
|
||||||
gdk_event_get_keyval ((GdkEvent *) event, &keyval);
|
|
||||||
|
|
||||||
return keyval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
||||||
GdkEventKey *event)
|
GdkEventKey *event)
|
||||||
@ -1170,12 +1014,10 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
GdkModifierType hex_mod_mask;
|
GdkModifierType hex_mod_mask;
|
||||||
gboolean have_hex_mods;
|
gboolean have_hex_mods;
|
||||||
gboolean is_hex_start;
|
gboolean is_hex_start;
|
||||||
gboolean is_end;
|
gboolean is_hex_end;
|
||||||
gboolean is_emoji_start;
|
|
||||||
gboolean is_backspace;
|
gboolean is_backspace;
|
||||||
gboolean is_escape;
|
gboolean is_escape;
|
||||||
guint hex_keyval;
|
guint hex_keyval;
|
||||||
guint emoji_keyval;
|
|
||||||
int i;
|
int i;
|
||||||
gboolean compose_finish;
|
gboolean compose_finish;
|
||||||
gboolean compose_match;
|
gboolean compose_match;
|
||||||
@ -1191,11 +1033,11 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
|
|
||||||
if (gdk_event_get_event_type ((GdkEvent *) event) == GDK_KEY_RELEASE)
|
if (gdk_event_get_event_type ((GdkEvent *) event) == GDK_KEY_RELEASE)
|
||||||
{
|
{
|
||||||
if ((keyval == GDK_KEY_Control_L || keyval == GDK_KEY_Control_R ||
|
if (priv->in_hex_sequence &&
|
||||||
|
(keyval == GDK_KEY_Control_L || keyval == GDK_KEY_Control_R ||
|
||||||
keyval == GDK_KEY_Shift_L || keyval == GDK_KEY_Shift_R))
|
keyval == GDK_KEY_Shift_L || keyval == GDK_KEY_Shift_R))
|
||||||
{
|
{
|
||||||
if ((priv->in_hex_sequence || priv->in_emoji_sequence) &&
|
if (priv->tentative_match &&
|
||||||
priv->tentative_match &&
|
|
||||||
g_unichar_validate (priv->tentative_match))
|
g_unichar_validate (priv->tentative_match))
|
||||||
{
|
{
|
||||||
gtk_im_context_simple_commit_char (context, priv->tentative_match);
|
gtk_im_context_simple_commit_char (context, priv->tentative_match);
|
||||||
@ -1203,8 +1045,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
else if (priv->in_emoji_sequence ||
|
else if (n_compose == 0)
|
||||||
(priv->in_hex_sequence && n_compose == 0))
|
|
||||||
{
|
{
|
||||||
priv->modifiers_dropped = TRUE;
|
priv->modifiers_dropped = TRUE;
|
||||||
|
|
||||||
@ -1217,7 +1058,6 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
|
|
||||||
priv->tentative_match = 0;
|
priv->tentative_match = 0;
|
||||||
priv->in_hex_sequence = FALSE;
|
priv->in_hex_sequence = FALSE;
|
||||||
priv->in_emoji_sequence = FALSE;
|
|
||||||
priv->compose_buffer[0] = 0;
|
priv->compose_buffer[0] = 0;
|
||||||
|
|
||||||
g_signal_emit_by_name (context_simple, "preedit-changed");
|
g_signal_emit_by_name (context_simple, "preedit-changed");
|
||||||
@ -1238,21 +1078,19 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
hex_mod_mask = gdk_keymap_get_modifier_mask (keymap, GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
|
hex_mod_mask = gdk_keymap_get_modifier_mask (keymap, GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
|
||||||
hex_mod_mask |= GDK_SHIFT_MASK;
|
hex_mod_mask |= GDK_SHIFT_MASK;
|
||||||
|
|
||||||
if ((priv->in_hex_sequence || priv->in_emoji_sequence) && priv->modifiers_dropped)
|
if (priv->in_hex_sequence && priv->modifiers_dropped)
|
||||||
have_hex_mods = TRUE;
|
have_hex_mods = TRUE;
|
||||||
else
|
else
|
||||||
have_hex_mods = (state & (hex_mod_mask)) == hex_mod_mask;
|
have_hex_mods = (state & (hex_mod_mask)) == hex_mod_mask;
|
||||||
is_hex_start = keyval == GDK_KEY_U;
|
is_hex_start = keyval == GDK_KEY_U;
|
||||||
is_emoji_start = (keyval == GDK_KEY_E) && !priv->in_hex_sequence;
|
is_hex_end = (keyval == GDK_KEY_space ||
|
||||||
is_end = (keyval == GDK_KEY_space ||
|
keyval == GDK_KEY_KP_Space ||
|
||||||
keyval == GDK_KEY_KP_Space ||
|
keyval == GDK_KEY_Return ||
|
||||||
keyval == GDK_KEY_Return ||
|
keyval == GDK_KEY_ISO_Enter ||
|
||||||
keyval == GDK_KEY_ISO_Enter ||
|
keyval == GDK_KEY_KP_Enter);
|
||||||
keyval == GDK_KEY_KP_Enter);
|
|
||||||
is_backspace = keyval == GDK_KEY_BackSpace;
|
is_backspace = keyval == GDK_KEY_BackSpace;
|
||||||
is_escape = keyval == GDK_KEY_Escape;
|
is_escape = keyval == GDK_KEY_Escape;
|
||||||
hex_keyval = canonical_hex_keyval (event);
|
hex_keyval = canonical_hex_keyval (event);
|
||||||
emoji_keyval = canonical_emoji_keyval (event);
|
|
||||||
|
|
||||||
/* If we are already in a non-hex sequence, or
|
/* If we are already in a non-hex sequence, or
|
||||||
* this keystroke is not hex modifiers + hex digit, don't filter
|
* this keystroke is not hex modifiers + hex digit, don't filter
|
||||||
@ -1262,18 +1100,17 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
* ISO_Level3_Switch.
|
* ISO_Level3_Switch.
|
||||||
*/
|
*/
|
||||||
if (!have_hex_mods ||
|
if (!have_hex_mods ||
|
||||||
(n_compose > 0 && !priv->in_hex_sequence && !priv->in_emoji_sequence) ||
|
(n_compose > 0 && !priv->in_hex_sequence) ||
|
||||||
(n_compose == 0 && !priv->in_hex_sequence && !priv->in_emoji_sequence &&
|
(n_compose == 0 && !priv->in_hex_sequence && !is_hex_start) ||
|
||||||
!is_hex_start && !is_emoji_start) ||
|
|
||||||
(priv->in_hex_sequence && !hex_keyval &&
|
(priv->in_hex_sequence && !hex_keyval &&
|
||||||
!is_hex_start && !is_end && !is_escape && !is_backspace))
|
!is_hex_start && !is_hex_end && !is_escape && !is_backspace))
|
||||||
{
|
{
|
||||||
GdkModifierType no_text_input_mask;
|
GdkModifierType no_text_input_mask;
|
||||||
|
|
||||||
no_text_input_mask = gdk_keymap_get_modifier_mask (keymap, GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
|
no_text_input_mask = gdk_keymap_get_modifier_mask (keymap, GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
|
||||||
|
|
||||||
if (state & no_text_input_mask ||
|
if (state & no_text_input_mask ||
|
||||||
((priv->in_hex_sequence || priv->in_emoji_sequence) && priv->modifiers_dropped &&
|
(priv->in_hex_sequence && priv->modifiers_dropped &&
|
||||||
(keyval == GDK_KEY_Return ||
|
(keyval == GDK_KEY_Return ||
|
||||||
keyval == GDK_KEY_ISO_Enter ||
|
keyval == GDK_KEY_ISO_Enter ||
|
||||||
keyval == GDK_KEY_KP_Enter)))
|
keyval == GDK_KEY_KP_Enter)))
|
||||||
@ -1283,21 +1120,17 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Handle backspace */
|
/* Handle backspace */
|
||||||
if ((priv->in_hex_sequence || priv->in_emoji_sequence) && have_hex_mods && is_backspace)
|
if (priv->in_hex_sequence && have_hex_mods && is_backspace)
|
||||||
{
|
{
|
||||||
if (n_compose > 0)
|
if (n_compose > 0)
|
||||||
{
|
{
|
||||||
n_compose--;
|
n_compose--;
|
||||||
priv->compose_buffer[n_compose] = 0;
|
priv->compose_buffer[n_compose] = 0;
|
||||||
if (priv->in_hex_sequence)
|
check_hex (context_simple, n_compose);
|
||||||
check_hex (context_simple, n_compose);
|
|
||||||
else if (priv->in_emoji_sequence)
|
|
||||||
check_emoji (context_simple, n_compose);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
priv->in_hex_sequence = FALSE;
|
priv->in_hex_sequence = FALSE;
|
||||||
priv->in_emoji_sequence = FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_signal_emit_by_name (context_simple, "preedit-changed");
|
g_signal_emit_by_name (context_simple, "preedit-changed");
|
||||||
@ -1343,20 +1176,6 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for emoji sequence start */
|
|
||||||
if (!priv->in_emoji_sequence && have_hex_mods && is_emoji_start)
|
|
||||||
{
|
|
||||||
priv->compose_buffer[0] = 0;
|
|
||||||
priv->in_emoji_sequence = TRUE;
|
|
||||||
priv->modifiers_dropped = FALSE;
|
|
||||||
priv->tentative_match = 0;
|
|
||||||
|
|
||||||
g_signal_emit_by_name (context_simple, "preedit-start");
|
|
||||||
g_signal_emit_by_name (context_simple, "preedit-changed");
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Then, check for compose sequences */
|
/* Then, check for compose sequences */
|
||||||
if (priv->in_hex_sequence)
|
if (priv->in_hex_sequence)
|
||||||
{
|
{
|
||||||
@ -1367,52 +1186,25 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
gtk_im_context_simple_reset (context);
|
gtk_im_context_simple_reset (context);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
else if (!is_end)
|
else if (!is_hex_end)
|
||||||
{
|
{
|
||||||
/* non-hex character in hex sequence */
|
/* non-hex character in hex sequence */
|
||||||
beep_surface (surface);
|
beep_surface (surface);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (priv->in_emoji_sequence)
|
|
||||||
{
|
|
||||||
if (emoji_keyval)
|
|
||||||
priv->compose_buffer[n_compose++] = emoji_keyval;
|
|
||||||
else if (is_escape)
|
|
||||||
{
|
|
||||||
gtk_im_context_simple_reset (context);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
beep_surface (surface);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
priv->compose_buffer[n_compose++] = keyval;
|
priv->compose_buffer[n_compose++] = keyval;
|
||||||
|
|
||||||
if (n_compose == MAX(GTK_MAX_COMPOSE_LEN + 1, 9))
|
|
||||||
{
|
|
||||||
beep_surface (surface);
|
|
||||||
priv->tentative_match = 0;
|
|
||||||
priv->in_hex_sequence = FALSE;
|
|
||||||
priv->in_emoji_sequence = FALSE;
|
|
||||||
priv->compose_buffer[0] = 0;
|
|
||||||
g_signal_emit_by_name (context_simple, "preedit-changed");
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->compose_buffer[n_compose] = 0;
|
priv->compose_buffer[n_compose] = 0;
|
||||||
|
|
||||||
if (priv->in_hex_sequence || priv->in_emoji_sequence)
|
if (priv->in_hex_sequence)
|
||||||
{
|
{
|
||||||
/* If the modifiers are still held down, consider the sequence again */
|
/* If the modifiers are still held down, consider the sequence again */
|
||||||
if (have_hex_mods)
|
if (have_hex_mods)
|
||||||
{
|
{
|
||||||
/* space or return ends the sequence, and we eat the key */
|
/* space or return ends the sequence, and we eat the key */
|
||||||
if (n_compose > 0 && is_end)
|
if (n_compose > 0 && is_hex_end)
|
||||||
{
|
{
|
||||||
if (priv->tentative_match &&
|
if (priv->tentative_match &&
|
||||||
g_unichar_validate (priv->tentative_match))
|
g_unichar_validate (priv->tentative_match))
|
||||||
@ -1427,17 +1219,15 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
|
|||||||
|
|
||||||
priv->tentative_match = 0;
|
priv->tentative_match = 0;
|
||||||
priv->in_hex_sequence = FALSE;
|
priv->in_hex_sequence = FALSE;
|
||||||
priv->in_emoji_sequence = FALSE;
|
|
||||||
priv->compose_buffer[0] = 0;
|
priv->compose_buffer[0] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((priv->in_hex_sequence && !check_hex (context_simple, n_compose)) ||
|
else if (!check_hex (context_simple, n_compose))
|
||||||
(priv->in_emoji_sequence && !check_emoji (context_simple, n_compose)))
|
|
||||||
beep_surface (surface);
|
beep_surface (surface);
|
||||||
|
|
||||||
g_signal_emit_by_name (context_simple, "preedit-changed");
|
g_signal_emit_by_name (context_simple, "preedit-changed");
|
||||||
|
|
||||||
if (!priv->in_hex_sequence && !priv->in_emoji_sequence)
|
if (!priv->in_hex_sequence)
|
||||||
g_signal_emit_by_name (context_simple, "preedit-end");
|
g_signal_emit_by_name (context_simple, "preedit-end");
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -1558,10 +1348,9 @@ gtk_im_context_simple_reset (GtkIMContext *context)
|
|||||||
|
|
||||||
priv->compose_buffer[0] = 0;
|
priv->compose_buffer[0] = 0;
|
||||||
|
|
||||||
if (priv->tentative_match || priv->in_hex_sequence || priv->in_emoji_sequence)
|
if (priv->tentative_match || priv->in_hex_sequence)
|
||||||
{
|
{
|
||||||
priv->in_hex_sequence = FALSE;
|
priv->in_hex_sequence = FALSE;
|
||||||
priv->in_emoji_sequence = FALSE;
|
|
||||||
priv->tentative_match = 0;
|
priv->tentative_match = 0;
|
||||||
priv->tentative_match_len = 0;
|
priv->tentative_match_len = 0;
|
||||||
g_signal_emit_by_name (context_simple, "preedit-changed");
|
g_signal_emit_by_name (context_simple, "preedit-changed");
|
||||||
@ -1580,11 +1369,11 @@ gtk_im_context_simple_get_preedit_string (GtkIMContext *context,
|
|||||||
char outbuf[37]; /* up to 6 hex digits */
|
char outbuf[37]; /* up to 6 hex digits */
|
||||||
int len = 0;
|
int len = 0;
|
||||||
|
|
||||||
if (priv->in_hex_sequence || priv->in_emoji_sequence)
|
if (priv->in_hex_sequence)
|
||||||
{
|
{
|
||||||
int hexchars = 0;
|
int hexchars = 0;
|
||||||
|
|
||||||
outbuf[0] = priv->in_hex_sequence ? 'u' : 'e';
|
outbuf[0] = 'u';
|
||||||
len = 1;
|
len = 1;
|
||||||
|
|
||||||
while (priv->compose_buffer[hexchars] != 0)
|
while (priv->compose_buffer[hexchars] != 0)
|
||||||
|
@ -197,6 +197,7 @@ gtk_public_sources = files([
|
|||||||
'gtkdrawingarea.c',
|
'gtkdrawingarea.c',
|
||||||
'gtkeditable.c',
|
'gtkeditable.c',
|
||||||
'gtkemojichooser.c',
|
'gtkemojichooser.c',
|
||||||
|
'gtkemojicompletion.c',
|
||||||
'gtkentry.c',
|
'gtkentry.c',
|
||||||
'gtkentrybuffer.c',
|
'gtkentrybuffer.c',
|
||||||
'gtkentrycompletion.c',
|
'gtkentrycompletion.c',
|
||||||
|
@ -4556,7 +4556,7 @@ button.emoji-section {
|
|||||||
&:checked label { opacity: 1; }
|
&:checked label { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji {
|
popover.emoji-picker .emoji {
|
||||||
font-size: x-large;
|
font-size: x-large;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@ -4565,3 +4565,17 @@ button.emoji-section {
|
|||||||
background: $selected_bg_color;
|
background: $selected_bg_color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
popover.emoji-completion arrow {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
popover.emoji-completion contents row box {
|
||||||
|
border-spacing: 10px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
popover.emoji-completion .emoji:hover {
|
||||||
|
background-color: $popover_hover_color;
|
||||||
|
}
|
||||||
|
16
gtk/ui/gtkemojicompletion.ui
Normal file
16
gtk/ui/gtkemojicompletion.ui
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface domain="gtk40">
|
||||||
|
<template class="GtkEmojiCompletion" parent="GtkPopover">
|
||||||
|
<property name="modal">0</property>
|
||||||
|
<style>
|
||||||
|
<class name="emoji-completion"/>
|
||||||
|
</style>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox" id="list">
|
||||||
|
<property name="selection-mode">none</property>
|
||||||
|
<property name="activate-on-single-click">1</property>
|
||||||
|
<signal name="row-activated" handler="row_activated"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</template>
|
||||||
|
</interface>
|
@ -126,6 +126,7 @@ gtk/gtkdragsource.c
|
|||||||
gtk/gtkdrawingarea.c
|
gtk/gtkdrawingarea.c
|
||||||
gtk/gtkeditable.c
|
gtk/gtkeditable.c
|
||||||
gtk/gtkemojichooser.c
|
gtk/gtkemojichooser.c
|
||||||
|
gtk/gtkemojicompletion.c
|
||||||
gtk/gtkentrybuffer.c
|
gtk/gtkentrybuffer.c
|
||||||
gtk/gtkentry.c
|
gtk/gtkentry.c
|
||||||
gtk/gtkentrycompletion.c
|
gtk/gtkentrycompletion.c
|
||||||
@ -350,6 +351,7 @@ gtk/ui/gtkcolorchooserdialog.ui
|
|||||||
gtk/ui/gtkcoloreditor.ui
|
gtk/ui/gtkcoloreditor.ui
|
||||||
gtk/ui/gtkdialog.ui
|
gtk/ui/gtkdialog.ui
|
||||||
gtk/ui/gtkemojichooser.ui
|
gtk/ui/gtkemojichooser.ui
|
||||||
|
gtk/ui/gtkemojicompletion.ui
|
||||||
gtk/ui/gtkfilechooserdialog.ui
|
gtk/ui/gtkfilechooserdialog.ui
|
||||||
gtk/ui/gtkfilechooserwidget.ui
|
gtk/ui/gtkfilechooserwidget.ui
|
||||||
gtk/ui/gtkfontchooserdialog.ui
|
gtk/ui/gtkfontchooserdialog.ui
|
||||||
|
Loading…
Reference in New Issue
Block a user