Commit 45b2e162 authored by Matthias Clasen's avatar Matthias Clasen

Add an Emoji chooser widget

Add a popover that shows color Emoji, with a search entry.
The recently-used Emoji are stored in a GSetting.
parent a584bcb8
......@@ -467,6 +467,7 @@ gtk_private_h_sources = \
gtkcustompaperunixdialog.h \
gtkdialogprivate.h \
gtkdndprivate.h \
gtkemojichooser.h \
gtkentryprivate.h \
gtkeventcontrollerprivate.h \
gtkfilechooserembed.h \
......@@ -743,6 +744,7 @@ gtk_base_c_sources = \
gtkdragsource.c \
gtkdrawingarea.c \
gtkeditable.c \
gtkemojichooser.c \
gtkentry.c \
gtkentrybuffer.c \
gtkentrycompletion.c \
......@@ -1187,6 +1189,7 @@ templates = \
ui/gtkcombobox.ui \
ui/gtkdialog.ui \
ui/gtkfilechooserbutton.ui \
ui/gtkemojichooser.ui \
ui/gtkfilechooserwidget.ui \
ui/gtkfilechooserdialog.ui \
ui/gtkfontbutton.ui \
......@@ -1706,6 +1709,7 @@ files:
gsettings_SCHEMAS = \
org.gtk.Settings.FileChooser.gschema.xml \
org.gtk.Settings.ColorChooser.gschema.xml \
org.gtk.Settings.EmojiChooser.gschema.xml \
org.gtk.Settings.Debug.gschema.xml
@GSETTINGS_RULES@
......
This diff is collapsed.
/* gtkemojichooser.h: An Emoji chooser 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/gtkwidget.h>
G_BEGIN_DECLS
#define GTK_TYPE_EMOJI_CHOOSER (gtk_emoji_chooser_get_type ())
#define GTK_EMOJI_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_EMOJI_CHOOSER, GtkEmojiChooser))
#define GTK_EMOJI_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_EMOJI_CHOOSER, GtkEmojiChooserClass))
#define GTK_IS_EMOJI_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_EMOJI_CHOOSER))
#define GTK_IS_EMOJI_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_EMOJI_CHOOSER))
#define GTK_EMOJI_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_EMOJI_CHOOSER, GtkEmojiChooserClass))
typedef struct _GtkEmojiChooser GtkEmojiChooser;
typedef struct _GtkEmojiChooserClass GtkEmojiChooserClass;
GType gtk_emoji_chooser_get_type (void) G_GNUC_CONST;
GtkWidget *gtk_emoji_chooser_new (void);
G_END_DECLS
<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
<schema id='org.gtk.Settings.EmojiChooser' path='/org/gtk/settings/emoji-chooser/'>
<key name='recent-emoji' type='a(ausaau)'>
<default>[]</default>
<summary>Recently used Emoji</summary>
<description>
An array of Emoji definitions to show in the Emoji chooser. Each Emoji is
specified as an array of codepoints, a name, and an optional array of
nested Emoji.
</description>
</key>
</schema>
</schemalist>
......@@ -4413,3 +4413,52 @@ stackswitcher button.text-button.circular { // FIXME aggregate with buttons
min-height: 32px;
padding: 0;
}
popover.emoji-picker { padding-left: 0; padding-right: 0; }
button.emoji-section {
border-color: transparent;
border-width: 3px;
border-style: none none solid;
border-radius: 0;
margin: 2px 4px 2px 4px;
padding: 3px 0 0;
min-width: 32px;
min-height: 28px;
/* reset props inherited from the button style */
background: none;
box-shadow: none;
text-shadow: none;
outline-offset: -5px;
}
button.emoji-section label { padding: 0; }
button.emoji-section:hover { border-color: $borders_color; }
button.emoji-section:checked { border-color: $selected_bg_color; }
button.emoji-section:backdrop:not(:checked) { border-color: transparent; }
button.emoji-section { color: $borders_color; }
button.emoji-section:hover { color: mix($fg_color, $borders_color, 0.5); }
button.emoji-section:checked { color: $fg_color; }
button.emoji-section:backdrop { color: $backdrop_borders_color; }
button.emoji-section:backdrop:checked { color: $backdrop_fg_color; }
.emoji {
font-size: x-large;
padding: 6px;
border-radius: 6px;
}
.emoji :hover {
background: $selected_bg_color;
}
......@@ -1852,9 +1852,9 @@ headerbar.selection-mode button.titlebutton, .titlebar.selection-mode button.tit
headerbar.selection-mode button.titlebutton:backdrop, .titlebar.selection-mode button.titlebutton:backdrop { -gtk-icon-shadow: none; }
.view:selected:focus, iconview:selected:focus, .view:selected, iconview:selected, .view text:selected:focus, iconview text:selected:focus, textview text:selected:focus, .view text:selected, iconview text:selected, textview text:selected, .view text selection:focus, iconview text selection:focus, .view text selection, iconview text selection, textview text selection:focus, textview text selection, flowbox flowboxchild:selected, spinbutton:not(.vertical) selection, entry selection, modelbutton.flat:selected, .menuitem.button.flat:selected, treeview.view:selected:focus, treeview.view:selected, row:selected, calendar:selected { background-color: #215d9c; }
.view:selected:focus, .view:selected, iconview:selected, .view text:selected, iconview text:selected, textview text:selected, .view text selection:focus, iconview text selection:focus, .view text selection, iconview text selection, textview text selection:focus, textview text selection, flowbox flowboxchild:selected, spinbutton:not(.vertical) selection, entry selection, modelbutton.flat:selected, .menuitem.button.flat:selected, treeview.view:selected:focus, treeview.view:selected, row:selected, calendar:selected { background-color: #215d9c; }
row:selected label, label:selected, .selection-mode button.titlebutton, .view:selected:focus, iconview:selected:focus, .view:selected, iconview:selected, .view text:selected:focus, iconview text:selected:focus, textview text:selected:focus, .view text:selected, iconview text:selected, textview text:selected, .view text selection:focus, iconview text selection:focus, .view text selection, iconview text selection, textview text selection:focus, textview text selection, flowbox flowboxchild:selected, spinbutton:not(.vertical) selection, entry selection, modelbutton.flat:selected, .menuitem.button.flat:selected, treeview.view:selected:focus, treeview.view:selected, row:selected, calendar:selected { color: #ffffff; }
row:selected label, label:selected, .selection-mode button.titlebutton, .view:selected:focus, .view:selected, iconview:selected, .view text:selected, iconview text:selected, textview text:selected, .view text selection:focus, iconview text selection:focus, .view text selection, iconview text selection, textview text selection:focus, textview text selection, flowbox flowboxchild:selected, spinbutton:not(.vertical) selection, entry selection, modelbutton.flat:selected, .menuitem.button.flat:selected, treeview.view:selected:focus, treeview.view:selected, row:selected, calendar:selected { color: #ffffff; }
row:selected label:disabled, label:disabled:selected, .selection-mode button.titlebutton:disabled, iconview:disabled:selected:focus, .view:disabled:selected, iconview:disabled:selected, iconview text:disabled:selected:focus, textview text:disabled:selected:focus, .view text:disabled:selected, iconview text:disabled:selected, textview text:disabled:selected, iconview text selection:disabled:focus, .view text selection:disabled, iconview text selection:disabled, textview text selection:disabled, flowbox flowboxchild:disabled:selected, label:disabled selection, spinbutton:not(.vertical) selection:disabled, entry selection:disabled, modelbutton.flat:disabled:selected, .menuitem.button.flat:disabled:selected, row:disabled:selected, calendar:disabled:selected { color: #90aece; }
......@@ -1897,6 +1897,32 @@ stackswitcher button.text-button { min-width: 100px; }
stackswitcher button.circular, stackswitcher button.text-button.circular { min-width: 32px; min-height: 32px; padding: 0; }
popover.emoji-picker { padding-left: 0; padding-right: 0; }
button.emoji-section { border-color: transparent; border-width: 3px; border-style: none none solid; border-radius: 0; margin: 2px 4px 2px 4px; padding: 3px 0 0; min-width: 32px; min-height: 28px; /* reset props inherited from the button style */ background: none; box-shadow: none; text-shadow: none; outline-offset: -5px; }
button.emoji-section label { padding: 0; }
button.emoji-section:hover { border-color: #1b1f20; }
button.emoji-section:checked { border-color: #215d9c; }
button.emoji-section:backdrop:not(:checked) { border-color: transparent; }
button.emoji-section { color: #1b1f20; }
button.emoji-section:hover { color: #1c2021; }
button.emoji-section:checked { color: #eeeeec; }
button.emoji-section:backdrop { color: #202425; }
button.emoji-section:backdrop:checked { color: #919494; }
.emoji { font-size: x-large; padding: 6px; border-radius: 6px; }
.emoji :hover { background: #215d9c; }
/* GTK NAMED COLORS ---------------- use responsibly! */
/*
widget text/foreground color */
......
......@@ -1872,9 +1872,9 @@ headerbar.selection-mode button.titlebutton, .titlebar.selection-mode button.tit
headerbar.selection-mode button.titlebutton:backdrop, .titlebar.selection-mode button.titlebutton:backdrop { -gtk-icon-shadow: none; }
.view:selected:focus, iconview:selected:focus, .view:selected, iconview:selected, .view text:selected:focus, iconview text:selected:focus, textview text:selected:focus, .view text:selected, iconview text:selected, textview text:selected, .view text selection:focus, iconview text selection:focus, .view text selection, iconview text selection, textview text selection:focus, textview text selection, flowbox flowboxchild:selected, spinbutton:not(.vertical) selection, entry selection, modelbutton.flat:selected, .menuitem.button.flat:selected, treeview.view:selected:focus, treeview.view:selected, row:selected, calendar:selected { background-color: #4a90d9; }
.view:selected:focus, .view:selected, iconview:selected, .view text:selected, iconview text:selected, textview text:selected, .view text selection:focus, iconview text selection:focus, .view text selection, iconview text selection, textview text selection:focus, textview text selection, flowbox flowboxchild:selected, spinbutton:not(.vertical) selection, entry selection, modelbutton.flat:selected, .menuitem.button.flat:selected, treeview.view:selected:focus, treeview.view:selected, row:selected, calendar:selected { background-color: #4a90d9; }
row:selected label, label:selected, .selection-mode button.titlebutton, .view:selected:focus, iconview:selected:focus, .view:selected, iconview:selected, .view text:selected:focus, iconview text:selected:focus, textview text:selected:focus, .view text:selected, iconview text:selected, textview text:selected, .view text selection:focus, iconview text selection:focus, .view text selection, iconview text selection, textview text selection:focus, textview text selection, flowbox flowboxchild:selected, spinbutton:not(.vertical) selection, entry selection, modelbutton.flat:selected, .menuitem.button.flat:selected, treeview.view:selected:focus, treeview.view:selected, row:selected, calendar:selected { color: #ffffff; }
row:selected label, label:selected, .selection-mode button.titlebutton, .view:selected:focus, .view:selected, iconview:selected, .view text:selected, iconview text:selected, textview text:selected, .view text selection:focus, iconview text selection:focus, .view text selection, iconview text selection, textview text selection:focus, textview text selection, flowbox flowboxchild:selected, spinbutton:not(.vertical) selection, entry selection, modelbutton.flat:selected, .menuitem.button.flat:selected, treeview.view:selected:focus, treeview.view:selected, row:selected, calendar:selected { color: #ffffff; }
row:selected label:disabled, label:disabled:selected, .selection-mode button.titlebutton:disabled, iconview:disabled:selected:focus, .view:disabled:selected, iconview:disabled:selected, iconview text:disabled:selected:focus, textview text:disabled:selected:focus, .view text:disabled:selected, iconview text:disabled:selected, textview text:disabled:selected, iconview text selection:disabled:focus, .view text selection:disabled, iconview text selection:disabled, textview text selection:disabled, flowbox flowboxchild:disabled:selected, label:disabled selection, spinbutton:not(.vertical) selection:disabled, entry selection:disabled, modelbutton.flat:disabled:selected, .menuitem.button.flat:disabled:selected, row:disabled:selected, calendar:disabled:selected { color: #a5c8ec; }
......@@ -1917,6 +1917,32 @@ stackswitcher button.text-button { min-width: 100px; }
stackswitcher button.circular, stackswitcher button.text-button.circular { min-width: 32px; min-height: 32px; padding: 0; }
popover.emoji-picker { padding-left: 0; padding-right: 0; }
button.emoji-section { border-color: transparent; border-width: 3px; border-style: none none solid; border-radius: 0; margin: 2px 4px 2px 4px; padding: 3px 0 0; min-width: 32px; min-height: 28px; /* reset props inherited from the button style */ background: none; box-shadow: none; text-shadow: none; outline-offset: -5px; }
button.emoji-section label { padding: 0; }
button.emoji-section:hover { border-color: #b6b6b3; }
button.emoji-section:checked { border-color: #4a90d9; }
button.emoji-section:backdrop:not(:checked) { border-color: transparent; }
button.emoji-section { color: #b6b6b3; }
button.emoji-section:hover { color: #b5b5b2; }
button.emoji-section:checked { color: #2e3436; }
button.emoji-section:backdrop { color: #c0c0bd; }
button.emoji-section:backdrop:checked { color: #8b8e8f; }
.emoji { font-size: x-large; padding: 6px; border-radius: 6px; }
.emoji :hover { background: #4a90d9; }
/* GTK NAMED COLORS ---------------- use responsibly! */
/*
widget text/foreground color */
......
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="gtk40">
<template class="GtkEmojiChooser" parent="GtkPopover">
<property name="modal">1</property>
<style>
<class name="emoji-picker"/>
</style>
<child>
<object class="GtkBox" id="box">
<property name="orientation">vertical</property>
<property name="visible">1</property>
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="visible">1</property>
<signal name="search-changed" handler="search_changed"/>
</object>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="visible">1</property>
<child>
<object class="GtkBox">
<property name="visible">1</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow" id="scrolled_window">
<property name="visible">1</property>
<property name="vexpand">1</property>
<property name="hscrollbar-policy">never</property>
<property name="min-content-height">250</property>
<style>
<class name="view"/>
</style>
<child>
<object class="GtkBox" id="emoji_box">
<property name="visible">1</property>
<property name="orientation">vertical</property>
<property name="margin">6</property>
<property name="spacing">6</property>
<child>
<object class="GtkFlowBox" id="recent.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="people.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Smileys &amp; People</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="people.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="body.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Body &amp; Clothing</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="body.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="nature.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Animals &amp; Nature</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="nature.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="food.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Food &amp; Drink</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="food.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="travel.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Travel &amp; Places</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="travel.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="activities.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Activities</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="activities.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="objects.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Objects</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="objects.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="symbols.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Symbols</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="symbols.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="flags.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Flags</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="flags.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">1</property>
<child>
<object class="GtkButton" id="recent.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="people.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="body.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="nature.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="food.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="travel.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="activities.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="objects.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="symbols.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="flags.button">
<property name="visible">1</property>
<property name="relief">none</property>
<style>
<class name="emoji-section"/>
</style>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="name">list</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">1</property>
<property name="row-spacing">12</property>
<property name="halign">center</property>
<property name="valign">center</property>
<style>
<class name="dim-label"/>
</style>
<child>
<object class="GtkImage">
<property name="visible">1</property>
<property name="icon-name">edit-find-symbolic</property>
<property name="pixel-size">72</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">1</property>
<property name="label" translatable="yes">No Results Found</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.44"/>
</attributes>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">1</property>
<property name="label" translatable="yes">Try a different search</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
</object>
<packing>
<property name="name">empty</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>
......@@ -135,6 +135,7 @@ gtk/gtkdragdest.c
gtk/gtkdragsource.c
gtk/gtkdrawingarea.c
gtk/gtkeditable.c
gtk/gtkemojichooser.c
gtk/gtkentrybuffer.c
gtk/gtkentry.c
gtk/gtkentrycompletion.c
......@@ -353,6 +354,7 @@ gtk/ui/gtkcolorchooserdialog.ui
gtk/ui/gtkcoloreditor.ui
gtk/ui/gtkdialog.ui
gtk/ui/gtkfilechooserbutton.ui
gtk/ui/gtkemojichooser.ui
gtk/ui/gtkfilechooserdialog.ui
gtk/ui/gtkfilechooserwidget.ui
gtk/ui/gtkfontbutton.ui
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment