diff options
author | Matthias Clasen <mclasen@redhat.com> | 2020-09-19 15:06:56 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2020-09-19 15:06:56 +0000 |
commit | 84d82dcda9d1db2c6f738706202230312a4191c7 (patch) | |
tree | dcca4f195e5edec30993ed6d81ab10dcf4e23c15 | |
parent | 169c208b7a732423f4661650413385af4050c68d (diff) | |
parent | 4e35d562630802075607705225069557883cbf43 (diff) | |
download | gtk+-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.c | 292 | ||||
-rw-r--r-- | gtk/language-names.c | 17 | ||||
-rw-r--r-- | gtk/ui/gtkfontchooserwidget.ui | 78 |
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> |