summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2018-04-01 19:35:55 -0400
committerMatthias Clasen <mclasen@redhat.com>2018-06-04 18:58:08 -0400
commit71acf2369727356d90a99eecccad6c675ea801f7 (patch)
tree21982f95ec9fb6e73f0434bfb8b49b5ab1dda5f7
parent19b9659cce4c767988bda52bcbf8fec4b072cc6b (diff)
downloadgtk+-71acf2369727356d90a99eecccad6c675ea801f7.tar.gz
font chooser: Add examples for font features
For some font features, we can figure out affected glyphs, and show before/after. For some others, we hardcode typical sequences. Still to do: figure out how to find ligatures and show them.
-rw-r--r--gtk/gtkfontchooserwidget.c227
1 files changed, 216 insertions, 11 deletions
diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c
index 3749e8968c..28d6b97b30 100644
--- a/gtk/gtkfontchooserwidget.c
+++ b/gtk/gtkfontchooserwidget.c
@@ -1793,7 +1793,9 @@ update_language_combo (GtkFontChooserWidget *fontchooser,
typedef struct {
hb_tag_t tag;
const char *name;
+ GtkWidget *top;
GtkWidget *feat;
+ GtkWidget *example;
} FeatureItem;
static const char *
@@ -1849,6 +1851,180 @@ feat_pressed (GtkGesture *gesture,
set_inconsistent (GTK_CHECK_BUTTON (feat), !inconsistent);
}
+static char *
+find_affected_text (hb_tag_t feature_tag,
+ hb_face_t *hb_face,
+ hb_tag_t script_tag,
+ hb_tag_t lang_tag,
+ int max_chars)
+{
+ unsigned int script_index = 0;
+ unsigned int lang_index = 0;
+ unsigned int feature_index = 0;
+ GString *chars;
+
+ chars = g_string_new ("");
+
+ hb_ot_layout_table_find_script (hb_face, HB_OT_TAG_GSUB, script_tag, &script_index);
+ hb_ot_layout_script_find_language (hb_face, HB_OT_TAG_GSUB, script_index, lang_tag, &lang_index);
+ if (hb_ot_layout_language_find_feature (hb_face, HB_OT_TAG_GSUB, script_index, lang_index, feature_tag, &feature_index))
+ {
+ unsigned int lookup_indexes[32];
+ unsigned int lookup_count = 32;
+ int count;
+ int n_chars = 0;
+
+ count = hb_ot_layout_feature_get_lookups (hb_face,
+ HB_OT_TAG_GSUB,
+ feature_index,
+ 0,
+ &lookup_count,
+ lookup_indexes);
+ if (count > 0)
+ {
+ hb_set_t* glyphs_before = NULL;
+ hb_set_t* glyphs_input = NULL;
+ hb_set_t* glyphs_after = NULL;
+ hb_set_t* glyphs_output = NULL;
+ hb_font_t *hb_font = NULL;
+ hb_codepoint_t gid;
+
+ glyphs_input = hb_set_create ();
+
+ // XXX For now, just look at first index
+ hb_ot_layout_lookup_collect_glyphs (hb_face,
+ HB_OT_TAG_GSUB,
+ lookup_indexes[0],
+ glyphs_before,
+ glyphs_input,
+ glyphs_after,
+ glyphs_output);
+
+ hb_font = hb_font_create (hb_face);
+ hb_ft_font_set_funcs (hb_font);
+
+ gid = -1;
+ while (hb_set_next (glyphs_input, &gid)) {
+ hb_codepoint_t ch;
+ if (n_chars == max_chars)
+ {
+ g_string_append (chars, "…");
+ break;
+ }
+ for (ch = 0; ch < 0xffff; ch++) {
+ hb_codepoint_t glyph = 0;
+ hb_font_get_nominal_glyph (hb_font, ch, &glyph);
+ if (glyph == gid) {
+ g_string_append_unichar (chars, (gunichar)ch);
+ n_chars++;
+ break;
+ }
+ }
+ }
+ hb_set_destroy (glyphs_input);
+ hb_font_destroy (hb_font);
+ }
+ }
+
+ return g_string_free (chars, FALSE);
+}
+
+static void
+update_feature_example (FeatureItem *item,
+ hb_face_t *hb_face,
+ hb_tag_t script_tag,
+ hb_tag_t lang_tag,
+ PangoFontDescription *font_desc)
+{
+ const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case", NULL };
+ const char *number_case[] = { "xxxx", "lnum", "onum", NULL };
+ const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL };
+ const char *number_formatting[] = { "zero", "nalt", NULL };
+ const char *char_variants[] = {
+ "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand",
+ "ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10",
+ "ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20",
+ NULL };
+
+ if (g_strv_contains (number_case, item->name) ||
+ g_strv_contains (number_spacing, item->name))
+ {
+ PangoAttrList *attrs;
+ PangoAttribute *attr;
+ PangoFontDescription *desc;
+ char *str;
+
+ attrs = pango_attr_list_new ();
+
+ desc = pango_font_description_copy (font_desc);
+ pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
+ pango_attr_list_insert (attrs, pango_attr_font_desc_new (desc));
+ pango_font_description_free (desc);
+ str = g_strconcat (item->name, " 1", NULL);
+ attr = pango_attr_font_features_new (str);
+ pango_attr_list_insert (attrs, attr);
+
+ gtk_label_set_text (GTK_LABEL (item->example), "0123456789");
+ gtk_label_set_attributes (GTK_LABEL (item->example), attrs);
+
+ pango_attr_list_unref (attrs);
+ }
+ else if (g_strv_contains (letter_case, item->name) ||
+ g_strv_contains (number_formatting, item->name) ||
+ g_strv_contains (char_variants, item->name))
+ {
+ char *input = NULL;
+ char *text;
+
+ if (strcmp (item->name, "case") == 0)
+ input = g_strdup ("A-B[Cq]");
+ else if (g_strv_contains (letter_case, item->name))
+ input = g_strdup ("AaBbCc…");
+ else if (strcmp (item->name, "zero") == 0)
+ input = g_strdup ("0");
+ else if (strcmp (item->name, "nalt") == 0)
+ input = find_affected_text (item->tag, hb_face, script_tag, lang_tag, 3);
+ else
+ input = find_affected_text (item->tag, hb_face, script_tag, lang_tag, 10);
+
+ if (input[0] != '\0')
+ {
+ PangoAttrList *attrs;
+ PangoAttribute *attr;
+ PangoFontDescription *desc;
+ char *str;
+
+ text = g_strconcat (input, " ⟶ ", input, NULL);
+
+ attrs = pango_attr_list_new ();
+
+ desc = pango_font_description_copy (font_desc);
+ pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
+ pango_attr_list_insert (attrs, pango_attr_font_desc_new (desc));
+ pango_font_description_free (desc);
+ str = g_strconcat (item->name, " 0", NULL);
+ attr = pango_attr_font_features_new (str);
+ attr->start_index = 0;
+ attr->end_index = strlen (input);
+ pango_attr_list_insert (attrs, attr);
+ str = g_strconcat (item->name, " 1", NULL);
+ attr = pango_attr_font_features_new (str);
+ attr->start_index = strlen (input) + strlen (" ⟶ ");
+ attr->end_index = attr->start_index + strlen (input);
+ pango_attr_list_insert (attrs, attr);
+
+ gtk_label_set_text (GTK_LABEL (item->example), text);
+ gtk_label_set_attributes (GTK_LABEL (item->example), attrs);
+
+ g_free (text);
+ pango_attr_list_unref (attrs);
+ }
+ else
+ gtk_label_set_markup (GTK_LABEL (item->example), "");
+ g_free (input);
+ }
+}
+
static void
add_check_group (GtkFontChooserWidget *fontchooser,
const char *title,
@@ -1861,7 +2037,7 @@ add_check_group (GtkFontChooserWidget *fontchooser,
int i;
group = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_widget_set_halign (group, GTK_ALIGN_START);
+ gtk_widget_set_halign (group, GTK_ALIGN_FILL);
label = gtk_label_new (title);
gtk_widget_show (label);
@@ -1880,12 +2056,13 @@ add_check_group (GtkFontChooserWidget *fontchooser,
GtkWidget *feat;
FeatureItem *item;
GtkGesture *gesture;
+ GtkWidget *box;
+ GtkWidget *example;
tag = hb_tag_from_string (tags[i], -1);
feat = gtk_check_button_new_with_label (get_feature_display_name (tag));
set_inconsistent (GTK_CHECK_BUTTON (feat), TRUE);
-
g_signal_connect_swapped (feat, "notify::active", G_CALLBACK (update_font_features), fontchooser);
g_signal_connect_swapped (feat, "notify::inconsistent", G_CALLBACK (update_font_features), fontchooser);
g_signal_connect (feat, "clicked", G_CALLBACK (feat_clicked), NULL);
@@ -1896,12 +2073,22 @@ add_check_group (GtkFontChooserWidget *fontchooser,
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
g_signal_connect (gesture, "pressed", G_CALLBACK (feat_pressed), feat);
- gtk_container_add (GTK_CONTAINER (group), feat);
+ example = gtk_label_new ("");
+ gtk_label_set_selectable (GTK_LABEL (example), TRUE);
+ gtk_widget_set_halign (example, GTK_ALIGN_START);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+ gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
+ gtk_container_add (GTK_CONTAINER (box), feat);
+ gtk_container_add (GTK_CONTAINER (box), example);
+ gtk_container_add (GTK_CONTAINER (group), box);
item = g_new (FeatureItem, 1);
item->name = tags[i];
item->tag = tag;
+ item->top = box;
item->feat = feat;
+ item->example = example;
priv->feature_items = g_list_prepend (priv->feature_items, item);
}
@@ -1922,7 +2109,7 @@ add_radio_group (GtkFontChooserWidget *fontchooser,
PangoAttrList *attrs;
group = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_widget_set_halign (group, GTK_ALIGN_START);
+ gtk_widget_set_halign (group, GTK_ALIGN_FILL);
label = gtk_label_new (title);
gtk_widget_show (label);
@@ -1941,6 +2128,8 @@ add_radio_group (GtkFontChooserWidget *fontchooser,
GtkWidget *feat;
FeatureItem *item;
const char *name;
+ GtkWidget *box;
+ GtkWidget *example;
tag = hb_tag_from_string (tags[i], -1);
name = get_feature_display_name (tag);
@@ -1953,12 +2142,22 @@ add_radio_group (GtkFontChooserWidget *fontchooser,
g_signal_connect_swapped (feat, "notify::active", G_CALLBACK (update_font_features), fontchooser);
g_object_set_data (G_OBJECT (feat), "default", group_button);
- gtk_container_add (GTK_CONTAINER (group), feat);
+ example = gtk_label_new ("");
+ gtk_label_set_selectable (GTK_LABEL (example), TRUE);
+ gtk_widget_set_halign (example, GTK_ALIGN_START);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+ gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
+ gtk_container_add (GTK_CONTAINER (box), feat);
+ gtk_container_add (GTK_CONTAINER (box), example);
+ gtk_container_add (GTK_CONTAINER (group), box);
item = g_new (FeatureItem, 1);
item->name = tags[i];
item->tag = tag;
+ item->top = box;
item->feat = feat;
+ item->example = example;
priv->feature_items = g_list_prepend (priv->feature_items, item);
}
@@ -1974,7 +2173,11 @@ gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser)
const char *number_case[] = { "xxxx", "lnum", "onum", NULL };
const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL };
const char *number_formatting[] = { "zero", "nalt", NULL };
- const char *char_variants[] = { "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand", NULL };
+ const char *char_variants[] = {
+ "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand",
+ "ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10",
+ "ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20",
+ NULL };
add_check_group (fontchooser, _("Ligatures"), ligatures);
add_check_group (fontchooser, _("Letter Case"), letter_case);
@@ -2006,8 +2209,8 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
for (l = priv->feature_items; l; l = l->next)
{
FeatureItem *item = l->data;
- gtk_widget_hide (item->feat);
- gtk_widget_hide (gtk_widget_get_parent (item->feat));
+ gtk_widget_hide (item->top);
+ gtk_widget_hide (gtk_widget_get_parent (item->top));
}
if ((priv->level & GTK_FONT_CHOOSER_LEVEL_FEATURES) == 0)
@@ -2055,14 +2258,16 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
continue;
has_feature = TRUE;
- gtk_widget_show (item->feat);
- gtk_widget_show (gtk_widget_get_parent (item->feat));
+ gtk_widget_show (item->top);
+ gtk_widget_show (gtk_widget_get_parent (item->top));
gtk_widget_show (priv->feature_language_combo);
+ update_feature_example (item, hb_face, script_tag, lang_tag, priv->font_desc);
+
if (GTK_IS_RADIO_BUTTON (item->feat))
{
GtkWidget *def = GTK_WIDGET (g_object_get_data (G_OBJECT (item->feat), "default"));
- gtk_widget_show (def);
+ gtk_widget_show (gtk_widget_get_parent (def));
}
else if (GTK_IS_CHECK_BUTTON (item->feat))
{