[gtk/wip/fontchooser-language-filtering: 2/3] wip: fontchooser: Add filtering by language




commit 5f433ee5b4d9945933e5d190b42534929815256b
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Sep 9 06:43:26 2020 -0400

    wip: fontchooser: Add filtering by language

 gtk/gtkfontchooserwidget.c     | 251 +++++++++++++++++++++++++++++++++++++----
 gtk/ui/gtkfontchooserwidget.ui |  28 +++--
 2 files changed, 252 insertions(+), 27 deletions(-)
---
diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c
index ffe5426a00..0f89694f8a 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>
 #include <pango/pangofc-font.h>
@@ -102,7 +108,7 @@ struct _GtkFontChooserWidget
   GtkWidget    *list_stack;
   GtkSingleSelection *selection;
   GtkCustomFilter      *custom_filter;
-  GtkCustomFilter      *monospace_filter;
+  GtkCustomFilter      *user_filter;
   GtkFilterListModel   *filter_model;
 
   GtkWidget       *preview;
@@ -120,6 +126,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,17 +339,69 @@ output_cb (GtkSpinButton *spin,
 }
 
 static gboolean
-monospace_filter_cb (gpointer item,
-                     gpointer data)
+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;
+    {
+      family = item;
+      face = pango_font_family_get_face (family, NULL);
+    }
   else
-    family = pango_font_face_get_family (PANGO_FONT_FACE (item));
+    {
+      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 pango_font_family_is_monospace (family);
+      return ret;
+    }
+
+  return TRUE;
 }
 
 static void
@@ -343,17 +409,21 @@ monospace_check_changed (GtkCheckButton       *check,
                          GParamSpec           *pspec,
                          GtkFontChooserWidget *self)
 {
-  if (gtk_check_button_get_active (check))
-    {
-      gtk_custom_filter_set_filter_func (self->monospace_filter,
-                                         monospace_filter_cb,
-                                         NULL,
-                                         NULL);
-    }
-  else
-    {
-      gtk_custom_filter_set_filter_func (self->monospace_filter, NULL, NULL, NULL);
-    }
+  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
@@ -777,6 +847,7 @@ gtk_font_chooser_widget_dispose (GObject *object)
   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);
 }
@@ -829,7 +900,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, monospace_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);
@@ -841,6 +912,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);
@@ -852,6 +924,7 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
   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"));
@@ -926,6 +999,47 @@ axis_free (gpointer v)
   g_free (a);
 }
 
+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))
+    {
+      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]))
+                gtk_string_list_append (self->languages, pango_language_to_string (langs[i]));
+            }
+        }
+    }
+
+  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).
@@ -938,7 +1052,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;
@@ -947,6 +1061,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))
@@ -982,6 +1105,93 @@ update_fontlist (GtkFontChooserWidget *self)
   g_object_unref (model);
 }
 
+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
+update_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)
 {
@@ -1018,6 +1228,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);
+  update_language_list (self);
 }
 
 /**
diff --git a/gtk/ui/gtkfontchooserwidget.ui b/gtk/ui/gtkfontchooserwidget.ui
index 902555f745..46563e2c39 100644
--- a/gtk/ui/gtkfontchooserwidget.ui
+++ b/gtk/ui/gtkfontchooserwidget.ui
@@ -5,6 +5,7 @@
     <signal name="items-changed" handler="rows_changed_cb" object="GtkFontChooserWidget" swapped="1" />
     <property name="model">
       <object class="GtkFilterListModel" id="filter_model">
+        <property name="incremental">1</property>
         <property name="filter">
           <object class="GtkEveryFilter">
             <child>
@@ -18,12 +19,10 @@
               </object>
             </child>
             <child>
-              <object class="GtkCustomFilter" id="custom_filter">
-              </object>
+              <object class="GtkCustomFilter" id="custom_filter"/>
             </child>
             <child>
-              <object class="GtkCustomFilter" id="monospace_filter">
-              </object>
+              <object class="GtkCustomFilter" id="user_filter"/>
             </child>
           </object>
         </property>
@@ -73,6 +72,7 @@
                             <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>
@@ -89,11 +89,23 @@
                                   </object>
                                 </child>
                                 <child>
-                                  <object class="GtkScrolledWindow">
-                                    <property name="hscrollbar-policy">never</property>
-                                    <property name="vscrollbar-policy">automatic</property>
+                                  <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="GtkListView">
+                                      <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>


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