summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2020-09-19 15:06:56 +0000
committerMatthias Clasen <mclasen@redhat.com>2020-09-19 15:06:56 +0000
commit84d82dcda9d1db2c6f738706202230312a4191c7 (patch)
treedcca4f195e5edec30993ed6d81ab10dcf4e23c15
parent169c208b7a732423f4661650413385af4050c68d (diff)
parent4e35d562630802075607705225069557883cbf43 (diff)
downloadgtk+-84d82dcda9d1db2c6f738706202230312a4191c7.tar.gz
Merge branch 'wip/fontchooser-language-filtering' into 'master'
fontchooser: add language filtering See merge request GNOME/gtk!2551
-rw-r--r--gtk/gtkfontchooserwidget.c292
-rw-r--r--gtk/language-names.c17
-rw-r--r--gtk/ui/gtkfontchooserwidget.ui78
3 files changed, 378 insertions, 9 deletions
diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c
index 9be3d33cd2..0f380f34b9 100644
--- a/gtk/gtkfontchooserwidget.c
+++ b/gtk/gtkfontchooserwidget.c
@@ -56,6 +56,12 @@
#include "gtkflattenlistmodel.h"
#include "gtkslicelistmodel.h"
#include "gtkmaplistmodel.h"
+#include "gtklistitem.h"
+#include "gtksignallistitemfactory.h"
+#include "gtkstringlist.h"
+#include "gtklistview.h"
+#include "gtksortlistmodel.h"
+#include "gtkstringsorter.h"
#include <hb-ot.h>
#if defined(HAVE_PANGOFT) && defined(HAVE_HARFBUZZ)
@@ -104,6 +110,7 @@ struct _GtkFontChooserWidget
GtkWidget *list_stack;
GtkSingleSelection *selection;
GtkCustomFilter *custom_filter;
+ GtkCustomFilter *user_filter;
GtkFilterListModel *filter_model;
GtkWidget *preview;
@@ -121,6 +128,14 @@ struct _GtkFontChooserWidget
GtkWidget *axis_grid;
GtkWidget *feature_box;
+ GtkWidget *language_list;
+ GtkStringList *languages;
+ GHashTable *language_table;
+
+ PangoLanguage *filter_language;
+ gboolean filter_by_language;
+ gboolean filter_by_monospace;
+
PangoFontMap *font_map;
PangoFontDescription *font_desc;
@@ -325,6 +340,94 @@ output_cb (GtkSpinButton *spin,
return TRUE;
}
+static gboolean
+user_filter_cb (gpointer item,
+ gpointer data)
+{
+ GtkFontChooserWidget *self = GTK_FONT_CHOOSER_WIDGET (data);
+ PangoFontFamily *family;
+ PangoFontFace *face;
+
+ if (PANGO_IS_FONT_FAMILY (item))
+ {
+ family = item;
+ face = pango_font_family_get_face (family, NULL);
+ }
+ else
+ {
+ face = PANGO_FONT_FACE (item);
+ family = pango_font_face_get_family (face);
+ }
+
+ if (self->filter_by_monospace &&
+ !pango_font_family_is_monospace (family))
+ return FALSE;
+
+ if (self->filter_by_language &&
+ self->filter_language)
+ {
+ PangoFontDescription *desc;
+ PangoContext *context;
+ PangoFont *font;
+ gboolean ret;
+
+ desc = pango_font_face_describe (face);
+ pango_font_description_set_size (desc, 20);
+
+ context = gtk_widget_get_pango_context (GTK_WIDGET (self));
+ font = pango_context_load_font (context, desc);
+
+ ret = TRUE;
+
+ if (PANGO_IS_FC_FONT (font))
+ {
+ PangoLanguage **langs;
+ int i;
+
+ ret = FALSE;
+
+ langs = pango_fc_font_get_languages (PANGO_FC_FONT (font));
+ for (i = 0; langs[i]; i++)
+ {
+ if (langs[i] == self->filter_language)
+ {
+ ret = TRUE;
+ break;
+ }
+ }
+ }
+
+ g_object_unref (font);
+ pango_font_description_free (desc);
+
+ return ret;
+ }
+
+ return TRUE;
+}
+
+static void
+monospace_check_changed (GtkCheckButton *check,
+ GParamSpec *pspec,
+ GtkFontChooserWidget *self)
+{
+ self->filter_by_monospace = gtk_check_button_get_active (check);
+ gtk_filter_changed (GTK_FILTER (self->user_filter),
+ self->filter_by_monospace ? GTK_FILTER_CHANGE_MORE_STRICT
+ : GTK_FILTER_CHANGE_LESS_STRICT);
+}
+
+static void
+language_check_changed (GtkCheckButton *check,
+ GParamSpec *pspec,
+ GtkFontChooserWidget *self)
+{
+ self->filter_by_language = gtk_check_button_get_active (check);
+ gtk_filter_changed (GTK_FILTER (self->user_filter),
+ self->filter_by_language ? GTK_FILTER_CHANGE_MORE_STRICT
+ : GTK_FILTER_CHANGE_LESS_STRICT);
+}
+
static void
gtk_font_chooser_widget_update_marks (GtkFontChooserWidget *self)
{
@@ -451,6 +554,13 @@ maybe_update_preview_text (GtkFontChooserWidget *self,
if (self->preview_text_set)
return;
+ if (self->filter_by_language && self->filter_language)
+ {
+ sample = pango_language_get_sample_string (self->filter_language);
+ gtk_font_chooser_widget_set_preview_text (self, sample);
+ return;
+ }
+
/* We do the work only once, and cache the result on the PangoFontFace */
sample = (const char *)g_object_get_data (G_OBJECT (face), "gtk-sample-text");
if (sample)
@@ -664,7 +774,8 @@ rows_changed_cb (GtkFontChooserWidget *self)
{
const char *page;
- if (g_list_model_get_n_items (G_LIST_MODEL (self->selection)) == 0)
+ if (g_list_model_get_n_items (G_LIST_MODEL (self->selection)) == 0 &&
+ gtk_filter_list_model_get_pending (GTK_FILTER_LIST_MODEL (self->filter_model)) == 0)
page = "empty";
else
page = "list";
@@ -745,10 +856,14 @@ gtk_font_chooser_widget_dispose (GObject *object)
{
GtkFontChooserWidget *self = GTK_FONT_CHOOSER_WIDGET (object);
+ g_signal_handlers_disconnect_by_func (self->selection, rows_changed_cb, self);
+ g_signal_handlers_disconnect_by_func (self->filter_model, rows_changed_cb, self);
+
self->filter_func = NULL;
g_clear_pointer (&self->filter_data, self->filter_data_destroy);
g_clear_pointer (&self->stack, gtk_widget_unparent);
+ g_clear_pointer (&self->language_table, g_hash_table_unref);
G_OBJECT_CLASS (gtk_font_chooser_widget_parent_class)->dispose (object);
}
@@ -801,6 +916,7 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, filter_model);
gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, selection);
gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, custom_filter);
+ gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, user_filter);
gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, preview);
gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, preview2);
gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, size_label);
@@ -812,6 +928,7 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, font_name_label);
gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, feature_box);
gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, axis_grid);
+ gtk_widget_class_bind_template_child (widget_class, GtkFontChooserWidget, language_list);
gtk_widget_class_bind_template_callback (widget_class, get_font_name);
gtk_widget_class_bind_template_callback (widget_class, get_font_attributes);
@@ -822,6 +939,8 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
gtk_widget_class_bind_template_callback (widget_class, output_cb);
gtk_widget_class_bind_template_callback (widget_class, selection_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, resize_by_scroll_cb);
+ gtk_widget_class_bind_template_callback (widget_class, monospace_check_changed);
+ gtk_widget_class_bind_template_callback (widget_class, language_check_changed);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (widget_class, I_("fontchooser"));
@@ -896,6 +1015,76 @@ axis_free (gpointer v)
g_free (a);
}
+static void
+select_added (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ gpointer data)
+{
+ GtkSingleSelection *selection = GTK_SINGLE_SELECTION (model);
+
+ g_assert (removed == 0);
+ g_assert (added == 1);
+
+ gtk_single_selection_set_selected (selection, position);
+}
+
+static void
+add_languages_from_font (GtkFontChooserWidget *self,
+ gpointer item)
+{
+ PangoFontFace *face;
+ PangoFontDescription *desc;
+ PangoFont *font;
+ PangoContext *context;
+
+ if (PANGO_IS_FONT_FAMILY (item))
+ face = pango_font_family_get_face (PANGO_FONT_FAMILY (item), NULL);
+ else
+ face = PANGO_FONT_FACE (item);
+
+ desc = pango_font_face_describe (face);
+ pango_font_description_set_size (desc, 20);
+
+ context = gtk_widget_get_pango_context (GTK_WIDGET (self));
+ font = pango_context_load_font (context, desc);
+
+ if (PANGO_IS_FC_FONT (font))
+ {
+ GtkSelectionModel *model = gtk_list_view_get_model (GTK_LIST_VIEW (self->language_list));
+ PangoLanguage *default_lang = pango_language_get_default ();
+ PangoLanguage **langs;
+ int i;
+
+ langs = pango_fc_font_get_languages (PANGO_FC_FONT (font));
+ for (i = 0; langs[i]; i++)
+ {
+ if (!g_hash_table_contains (self->language_table, langs[i]))
+ {
+ g_hash_table_add (self->language_table, langs[i]);
+ if (get_language_name (langs[i]))
+ {
+ const char *l = pango_language_to_string (langs[i]);
+ gulong id = 0;
+
+ /* Pre-select the default language */
+ if (pango_language_matches (default_lang, l))
+ id = g_signal_connect (model, "items-changed", G_CALLBACK (select_added), NULL);
+
+ gtk_string_list_append (self->languages, l);
+
+ if (id)
+ g_signal_handler_disconnect (model, id);
+ }
+ }
+ }
+ }
+
+ g_object_unref (font);
+ pango_font_description_free (desc);
+}
+
/* We incrementally populate our fontlist to prevent blocking
* the font chooser for a long time with expensive FcFontSort
* calls in pango for every row in the list).
@@ -908,7 +1097,7 @@ add_to_fontlist (GtkWidget *widget,
GtkFontChooserWidget *self = GTK_FONT_CHOOSER_WIDGET (widget);
GtkSliceListModel *model = user_data;
GListModel *child_model;
- guint n;
+ guint i, n;
if (gtk_filter_list_model_get_model (self->filter_model) != G_LIST_MODEL (model))
return G_SOURCE_REMOVE;
@@ -917,6 +1106,15 @@ add_to_fontlist (GtkWidget *widget,
n = gtk_slice_list_model_get_size (model);
+ for (i = n; i < n + 10; i++)
+ {
+ gpointer item = g_list_model_get_item (child_model, i);
+ if (!item)
+ break;
+ add_languages_from_font (self, item);
+ g_object_unref (item);
+ }
+
n += 10;
if (n >= g_list_model_get_n_items (child_model))
@@ -953,6 +1151,93 @@ update_fontlist (GtkFontChooserWidget *self)
}
static void
+setup_lang_item (GtkSignalListItemFactory *factory,
+ gpointer item,
+ gpointer data)
+{
+ GtkWidget *label;
+
+ label = gtk_label_new (NULL);
+ gtk_label_set_xalign (GTK_LABEL (label), 0);
+ gtk_list_item_set_child (GTK_LIST_ITEM (item), label);
+}
+
+static void
+bind_lang_item (GtkSignalListItemFactory *factory,
+ gpointer item,
+ gpointer data)
+{
+ GtkWidget *label;
+ gpointer obj;
+ const char *str;
+ PangoLanguage *language;
+ const char *name;
+
+ obj = gtk_list_item_get_item (GTK_LIST_ITEM (item));
+ str = gtk_string_object_get_string (GTK_STRING_OBJECT (obj));
+ language = pango_language_from_string (str);
+ name = get_language_name (language);
+
+ label = gtk_list_item_get_child (GTK_LIST_ITEM (item));
+ gtk_label_set_label (GTK_LABEL (label), name);
+}
+
+static char *
+get_lang_name (gpointer this,
+ const char *lang)
+{
+ return g_strdup (get_language_name (pango_language_from_string (lang)));
+}
+
+static void
+language_selection_changed (GtkSelectionModel *model,
+ guint position,
+ guint n_items,
+ GtkFontChooserWidget *self)
+{
+ gpointer obj;
+
+ obj = gtk_single_selection_get_selected_item (GTK_SINGLE_SELECTION (model));
+
+ if (obj)
+ self->filter_language = pango_language_from_string (gtk_string_object_get_string (obj));
+ else
+ self->filter_language = NULL;
+
+ if (self->filter_by_language)
+ gtk_filter_changed (GTK_FILTER (self->user_filter), GTK_FILTER_CHANGE_DIFFERENT);
+}
+
+static void
+setup_language_list (GtkFontChooserWidget *self)
+{
+ GtkListItemFactory *factory;
+ GtkExpression *expression;
+ GListModel *model;
+ GtkSelectionModel *selection;
+
+ self->languages = gtk_string_list_new (NULL);
+ self->language_table = g_hash_table_new (NULL, NULL);
+
+ expression = gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string");
+ expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL, 1, &expression, (GCallback)get_lang_name, NULL, NULL);
+
+ model = G_LIST_MODEL (gtk_sort_list_model_new (G_LIST_MODEL (self->languages),
+ GTK_SORTER (gtk_string_sorter_new (expression))));
+
+ selection = GTK_SELECTION_MODEL (gtk_single_selection_new (model));
+ g_signal_connect (selection, "selection-changed", G_CALLBACK (language_selection_changed), self);
+ gtk_list_view_set_model (GTK_LIST_VIEW (self->language_list), selection);
+ g_object_unref (selection);
+
+ factory = gtk_signal_list_item_factory_new ();
+ g_signal_connect (factory, "setup", G_CALLBACK (setup_lang_item), self);
+ g_signal_connect (factory, "bind", G_CALLBACK (bind_lang_item), self);
+ gtk_list_view_set_factory (GTK_LIST_VIEW (self->language_list), factory);
+ g_object_unref (factory);
+}
+
+static void
gtk_font_chooser_widget_init (GtkFontChooserWidget *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
@@ -988,6 +1273,9 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *self)
gtk_font_chooser_widget_populate_features (self);
gtk_font_chooser_widget_take_font_desc (self, NULL);
+
+ gtk_custom_filter_set_filter_func (self->user_filter, user_filter_cb, self, NULL);
+ setup_language_list (self);
}
/**
diff --git a/gtk/language-names.c b/gtk/language-names.c
index 8928366ab7..087f86b922 100644
--- a/gtk/language-names.c
+++ b/gtk/language-names.c
@@ -85,6 +85,15 @@ languages_parse_start_tag (GMarkupParseContext *ctx,
const char *ccode_id;
const char *lang_name;
char *display_name;
+ const char *long_names[] = {
+ "Dogri",
+ "Greek, Modern",
+ "Interlingua",
+ "Konkani",
+ "Tonga",
+ "Turkish, Ottoman",
+ };
+ int i;
if (!(g_str_equal (element_name, "iso_639_entry") ||
g_str_equal (element_name, "iso_639_3_entry")) ||
@@ -151,6 +160,14 @@ languages_parse_start_tag (GMarkupParseContext *ctx,
display_name = get_display_name (lang_name);
+ /* Fix up some egregious names */
+ for (i = 0; i < G_N_ELEMENTS (long_names); i++)
+ {
+ if (g_str_has_prefix (display_name, long_names[i]))
+ display_name[strlen (long_names[i]) + 1] = '\0';
+ }
+
+
if (ccode != NULL)
g_hash_table_insert (language_map,
pango_language_from_string (ccode),
diff --git a/gtk/ui/gtkfontchooserwidget.ui b/gtk/ui/gtkfontchooserwidget.ui
index acfbf57570..96c259687e 100644
--- a/gtk/ui/gtkfontchooserwidget.ui
+++ b/gtk/ui/gtkfontchooserwidget.ui
@@ -5,6 +5,8 @@
<signal name="items-changed" handler="rows_changed_cb" object="GtkFontChooserWidget" swapped="1" />
<property name="model">
<object class="GtkFilterListModel" id="filter_model">
+ <signal name="notify::pending" handler="rows_changed_cb" object="GtkFontChooserWidget" swapped="1" />
+ <property name="incremental">1</property>
<property name="filter">
<object class="GtkEveryFilter">
<child>
@@ -18,8 +20,10 @@
</object>
</child>
<child>
- <object class="GtkCustomFilter" id="custom_filter">
- </object>
+ <object class="GtkCustomFilter" id="custom_filter"/>
+ </child>
+ <child>
+ <object class="GtkCustomFilter" id="user_filter"/>
</child>
</object>
</property>
@@ -49,11 +53,71 @@
<property name="row-spacing">6</property>
<property name="column-spacing">6</property>
<child>
- <object class="GtkSearchEntry" id="search_entry">
- <property name="hexpand">1</property>
- <property name="activates-default">1</property>
- <property name="placeholder-text" translatable="yes">Search font name</property>
- <signal name="stop-search" handler="stop_search_cb" swapped="no"/>
+ <object class="GtkBox">
+ <style>
+ <class name="linked"/>
+ </style>
+ <child>
+ <object class="GtkSearchEntry" id="search_entry">
+ <property name="hexpand">1</property>
+ <property name="activates-default">1</property>
+ <property name="placeholder-text" translatable="yes">Search font name</property>
+ <signal name="stop-search" handler="stop_search_cb" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuButton">
+ <property name="icon-name">pan-down-symbolic</property>
+ <property name="popover">
+ <object class="GtkPopover">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Filter by</property>
+ <property name="width-chars">20</property>
+ <property name="margin-bottom">10</property>
+ <style>
+ <class name="title-4"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="monospace_button">
+ <property name="label" translatable="yes">Monospace</property>
+ <signal name="notify::active" handler="monospace_check_changed"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="language_button">
+ <property name="label" translatable="yes">Language</property>
+ <signal name="notify::active" handler="language_check_changed"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="margin-start">12</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="min-content-height">200</property>
+ <property name="hscrollbar-policy">never</property>
+ <property name="vscrollbar-policy">automatic</property>
+ <child>
+ <object class="GtkListView" id="language_list">
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
<layout>
<property name="column">0</property>
<property name="row">0</property>