[gtk+/wip/matthiasc/font-variations] font chooser: Add a script filter



commit ccbda7eede2597afa9097f003a7c3842057be077
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Dec 21 13:40:41 2017 -0500

    font chooser: Add a script filter
    
    Allow filtering the list by coverage of scripts.
    Currently, this uses fontconfig directly, and is
    very slow. Pango needs to gain api that lets us
    access this information without recreating it.
    
    Still to do: Figure out a good way to obtain a
    representative list of scripts, without including
    all the obscure and historic scripts that are
    defined in Unicode.

 gtk/gtkfontchooserwidget.c     |  239 +++++++++++++++++++++++++++++++++++++++-
 gtk/ui/gtkfontchooserwidget.ui |   36 ++++---
 2 files changed, 258 insertions(+), 17 deletions(-)
---
diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c
index 9ecdea0..b736b38 100644
--- a/gtk/gtkfontchooserwidget.c
+++ b/gtk/gtkfontchooserwidget.c
@@ -50,6 +50,8 @@
 #include "gtkwidget.h"
 #include "gtksettings.h"
 #include "gtkdialog.h"
+#include "gtkcombobox.h"
+#include "gtkcelllayout.h"
 
 #if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
 #include <pango/pangofc-font.h>
@@ -88,6 +90,7 @@ struct _GtkFontChooserWidgetPrivate
 {
   GtkWidget    *grid;
   GtkWidget    *search_entry;
+  GtkWidget    *script_combo;
   GtkWidget    *family_face_list;
   GtkTreeViewColumn *family_face_column;
   GtkCellRenderer *family_face_cell;
@@ -115,6 +118,8 @@ struct _GtkFontChooserWidgetPrivate
   gpointer          filter_data;
   GDestroyNotify    filter_data_destroy;
 
+  PangoScript script;
+
   guint last_fontconfig_timestamp;
 
   GHashTable *axes;
@@ -172,6 +177,7 @@ static void     gtk_font_chooser_widget_set_show_preview_entry (GtkFontChooserWi
 static void     gtk_font_chooser_widget_set_cell_size          (GtkFontChooserWidget *fontchooser);
 static void     gtk_font_chooser_widget_load_fonts             (GtkFontChooserWidget *fontchooser,
                                                                 gboolean              force);
+static void     gtk_font_chooser_widget_populate_scripts       (GtkFontChooserWidget *fontchooser);
 static gboolean visible_func                                   (GtkTreeModel *model,
                                                                GtkTreeIter  *iter,
                                                                gpointer      user_data);
@@ -196,6 +202,8 @@ typedef struct _GtkDelayedFontDescription GtkDelayedFontDescription;
 struct _GtkDelayedFontDescription {
   PangoFontFace        *face;
   PangoFontDescription *desc;
+  PangoScript          *scripts;
+  int                   n_scripts;
   guint                 ref_count;
 };
 
@@ -208,6 +216,8 @@ gtk_delayed_font_description_new (PangoFontFace *face)
 
   result->face = g_object_ref (face);
   result->desc = NULL;
+  result->scripts = NULL;
+  result->n_scripts = -1;
   result->ref_count = 1;
 
   return result;
@@ -233,6 +243,8 @@ gtk_delayed_font_description_unref (GtkDelayedFontDescription *desc)
   if (desc->desc)
     pango_font_description_free (desc->desc);
 
+  g_free (desc->scripts);
+
   g_slice_free (GtkDelayedFontDescription, desc);
 }
 
@@ -245,6 +257,76 @@ gtk_delayed_font_description_get (GtkDelayedFontDescription *desc)
   return desc->desc;
 }
 
+PangoScript *
+gtk_delayed_font_description_get_scripts (GtkDelayedFontDescription *desc,
+                                          GtkWidget *fontchooser,
+                                          int *n_scripts)
+{
+  if (desc->n_scripts < 0)
+    {
+#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+      PangoFontDescription *font_desc;
+      PangoFont *font;
+      FcPattern *pattern;
+      FcResult res;
+      FcLangSet *ls;
+      FcStrSet *ss;
+      FcStrList *sl;
+      char *s;
+      GHashTable *all_scripts;
+      GHashTableIter iter;
+      int n, i;
+      PangoScript one_script;
+
+      font_desc = pango_font_description_copy_static (gtk_delayed_font_description_get (desc));
+      pango_font_description_set_size (font_desc, 11 * PANGO_SCALE);
+ 
+      font = pango_context_load_font (gtk_widget_get_pango_context (fontchooser),
+                                      font_desc);
+      g_object_get (font, "pattern", &pattern, NULL);
+      res = FcPatternGetLangSet (pattern, FC_LANG, 0, (FcLangSet **)&ls);
+      g_assert (res == FcResultMatch);
+      ss = FcLangSetGetLangs (ls);
+      sl = FcStrListCreate (ss);
+      FcStrListFirst (sl);
+
+      all_scripts = g_hash_table_new (NULL, NULL);
+      while ((s = FcStrListNext (sl)))
+        {
+          int n;
+          PangoScript *scripts;
+
+          scripts = pango_language_get_scripts (pango_language_from_string (s), &n);
+
+          for (i = 0; i < n; i++)
+            g_hash_table_add (all_scripts, GINT_TO_POINTER (scripts[i]));
+        }
+
+      desc->n_scripts = g_hash_table_size (all_scripts);
+      desc->scripts = g_new (PangoScript, desc->n_scripts);
+
+      i = 0;
+      g_hash_table_iter_init (&iter, all_scripts);
+      while (g_hash_table_iter_next (&iter, (gpointer *)&one_script, NULL))
+        desc->scripts[i++] = GPOINTER_TO_INT(one_script);
+
+      g_hash_table_unref (all_scripts);
+
+      FcStrListDone (sl);
+      FcStrSetDestroy (ss);
+
+      g_object_unref (font);
+      pango_font_description_free (font_desc);
+#else
+      desc->n_scripts = 0;
+#endif
+    }
+
+  *n_scripts = desc->n_scripts;
+
+  return desc->scripts;
+}
+
 #define GTK_TYPE_DELAYED_FONT_DESCRIPTION (gtk_delayed_font_description_get_type ())
 GType gtk_delayed_font_description_get_type (void);
 
@@ -664,6 +746,7 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
                                               "/org/gtk/libgtk/ui/gtkfontchooserwidget.ui");
 
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, search_entry);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, script_combo);
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_list);
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_column);
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_cell);
@@ -774,7 +857,7 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
 
   /* Setup treeview/model auxilary functions */
   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
-                                          visible_func, (gpointer)priv, NULL);
+                                          visible_func, fontchooser, NULL);
 
   gtk_tree_view_column_set_cell_data_func (priv->family_face_column,
                                            priv->family_face_cell,
@@ -784,6 +867,7 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
 
   /* Load data and set initial style-dependent parameters */
   gtk_font_chooser_widget_load_fonts (fontchooser, TRUE);
+  gtk_font_chooser_widget_populate_scripts (fontchooser);
   gtk_font_chooser_widget_set_cell_size (fontchooser);
   gtk_font_chooser_widget_take_font_desc (fontchooser, NULL);
 }
@@ -911,18 +995,169 @@ gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
   gtk_font_chooser_widget_ensure_selection (fontchooser);
 }
 
+static gint
+compare_scripts (GtkTreeModel *model,
+                 GtkTreeIter  *a,
+                 GtkTreeIter  *b,
+                 gpointer      data)
+{
+  PangoScript script_a, script_b;
+  char *name_a, *name_b;
+  int res;
+
+  gtk_tree_model_get (model, a,
+                      0, &name_a,
+                      1, &script_a,
+                      -1);
+  gtk_tree_model_get (model,
+                      b,
+                      0, &name_b,
+                      1, &script_b,
+                      -1);
+
+  if (script_a == 0)
+    res = -1;
+  else if (script_b == 0)
+    res = 1;
+  else
+    res = strcmp (name_a, name_b);
+
+  g_free (name_a);
+  g_free (name_b);
+
+  return res;
+}
+
+static void
+script_changed (GtkComboBox *combo,
+                GtkFontChooserWidget *fontchooser)
+{
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  PangoScript script;
+
+  gtk_combo_box_get_active_iter (combo, &iter);
+  model = gtk_combo_box_get_model (combo);
+  gtk_tree_model_get (model, &iter, 1, &script, -1);
+
+  priv->script = script;
+  gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
+}
+
+static void
+gtk_font_chooser_widget_populate_scripts (GtkFontChooserWidget *fontchooser)
+{
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+  GtkListStore *store;
+  GtkCellRenderer *renderer;
+
+  store = gtk_list_store_new (2, G_TYPE_STRING, PANGO_TYPE_SCRIPT);
+
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, _("All Scripts"),
+                                     1, 0,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Arabic"),
+                                     1, PANGO_SCRIPT_ARABIC,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Bengali"),
+                                     1, PANGO_SCRIPT_BENGALI,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Latin"),
+                                     1, PANGO_SCRIPT_LATIN,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Cyrillic"),
+                                     1, PANGO_SCRIPT_CYRILLIC,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Devanagari"),
+                                     1, PANGO_SCRIPT_DEVANAGARI,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Han"),
+                                     1, PANGO_SCRIPT_HAN,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Hangul"),
+                                     1, PANGO_SCRIPT_HANGUL,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Hebrew"),
+                                     1, PANGO_SCRIPT_HEBREW,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Gujarati"),
+                                     1, PANGO_SCRIPT_GUJARATI,
+                                     -1);
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     0, C_("Script", "Gurmukhi"),
+                                     1, PANGO_SCRIPT_GURMUKHI,
+                                     -1);
+
+  gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store),
+                                           compare_scripts,
+                                           NULL, NULL);
+
+  gtk_combo_box_set_model (GTK_COMBO_BOX (priv->script_combo), GTK_TREE_MODEL (store));
+  gtk_combo_box_set_active (GTK_COMBO_BOX (priv->script_combo), 0);
+
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->script_combo),
+                              renderer, FALSE);
+  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->script_combo),
+                                 renderer, "text", 0);
+
+  g_signal_connect (priv->script_combo, "changed",
+                    G_CALLBACK (script_changed), fontchooser);
+
+  g_object_unref (store);
+#else
+  gtk_widget_hide (priv->script_combo);
+#endif
+}
+
 static gboolean
 visible_func (GtkTreeModel *model,
               GtkTreeIter  *iter,
               gpointer      user_data)
 {
-  GtkFontChooserWidgetPrivate *priv = user_data;
+  GtkFontChooserWidget *fontchooser = user_data;
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
   gboolean result = TRUE;
   const gchar *search_text;
   gchar **split_terms;
   gchar *font_name, *font_name_casefold;
   guint i;
 
+  if (priv->script != 0)
+    {
+      GtkDelayedFontDescription *desc;
+      PangoScript *scripts;
+      int n, i;
+
+      gtk_tree_model_get (priv->model, iter,
+                          FONT_DESC_COLUMN, &desc,
+                          -1);
+
+      scripts = gtk_delayed_font_description_get_scripts (desc, GTK_WIDGET (fontchooser), &n);
+      gtk_delayed_font_description_unref (desc);
+
+      for (i = 0; i < n; i++)
+        {
+          if (scripts[i] == priv->script)
+            break;
+        }
+
+      if (i == n)
+        return FALSE;
+    }
+
   if (priv->filter_func != NULL)
     {
       PangoFontFamily *family;
diff --git a/gtk/ui/gtkfontchooserwidget.ui b/gtk/ui/gtkfontchooserwidget.ui
index c5993ba..b5df5b1 100644
--- a/gtk/ui/gtkfontchooserwidget.ui
+++ b/gtk/ui/gtkfontchooserwidget.ui
@@ -37,24 +37,31 @@
         <property name="row-spacing">6</property>
         <property name="column-spacing">6</property>
         <child>
-          <object class="GtkSearchEntry" id="search_entry">
-            <property name="visible">1</property>
-            <property name="can-focus">1</property>
-            <property name="hexpand">1</property>
-            <property name="activates-default">1</property>
-            <property name="primary-icon-name">edit-find-symbolic</property>
-            <property name="primary-icon-activatable">0</property>
-            <property name="secondary-icon-activatable">0</property>
-            <property name="primary-icon-sensitive">0</property>
-            <property name="secondary-icon-sensitive">0</property>
-            <property name="placeholder-text" translatable="yes">Search font name</property>
-            <signal name="search-changed" handler="text_changed_cb" swapped="no"/>
-            <signal name="stop-search" handler="stop_search_cb" swapped="no"/>
+          <object class="GtkBox">
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkSearchEntry" id="search_entry">
+                <property name="visible">1</property>
+                <property name="can-focus">1</property>
+                <property name="hexpand">1</property>
+                <property name="activates-default">1</property>
+                <property name="primary-icon-name">edit-find-symbolic</property>
+                <property name="primary-icon-activatable">0</property>
+                <property name="secondary-icon-activatable">0</property>
+                <property name="primary-icon-sensitive">0</property>
+                <property name="secondary-icon-sensitive">0</property>
+                <property name="placeholder-text" translatable="yes">Search font name</property>
+                <signal name="search-changed" handler="text_changed_cb" swapped="no"/>
+                <signal name="stop-search" handler="stop_search_cb" swapped="no"/>
+              </object>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="script_combo"/>
+            </child>
           </object>
           <packing>
             <property name="left-attach">0</property>
             <property name="top-attach">0</property>
-            <property name="width">2</property>
           </packing>
         </child>
         <child>
@@ -213,7 +220,6 @@
           <packing>
             <property name="left-attach">0</property>
             <property name="top-attach">1</property>
-            <property name="width">2</property>
           </packing>
         </child>
       </object>


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