Merge branch 'font-chooser-feature-work' into 'main'

fontchooser: Handle font features better

See merge request GNOME/gtk!4984
This commit is contained in:
Matthias Clasen 2022-08-26 22:10:52 +00:00
commit 137af44f81
6 changed files with 382 additions and 104 deletions

View File

@ -59,4 +59,8 @@ void gdk_source_set_static_name_by_id (guint tag,
#define g_source_set_static_name(source, name) g_source_set_name ((source), (name)) #define g_source_set_static_name(source, name) g_source_set_name ((source), (name))
#endif #endif
#ifndef I_
#define I_(string) g_intern_static_string (string)
#endif
#endif /* __GDK__PRIVATE_H__ */ #endif /* __GDK__PRIVATE_H__ */

View File

@ -29,6 +29,7 @@
#include "gdkintl.h" #include "gdkintl.h"
#include "gdkpipeiostreamprivate.h" #include "gdkpipeiostreamprivate.h"
#include "gdktexture.h" #include "gdktexture.h"
#include "gdk-private.h"
#include <gobject/gvaluecollector.h> #include <gobject/gvaluecollector.h>
@ -408,7 +409,7 @@ gdk_clipboard_class_init (GdkClipboardClass *class)
* Emitted when the clipboard changes ownership. * Emitted when the clipboard changes ownership.
*/ */
signals[CHANGED] = signals[CHANGED] =
g_signal_new ("changed", g_signal_new (I_("changed"),
G_TYPE_FROM_CLASS (class), G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdkClipboardClass, changed), G_STRUCT_OFFSET (GdkClipboardClass, changed),

View File

@ -23,6 +23,7 @@
#include "gdkclipboard.h" #include "gdkclipboard.h"
#include "gdkcontentformats.h" #include "gdkcontentformats.h"
#include "gdkintl.h" #include "gdkintl.h"
#include "gdk-private.h"
/** /**
* GdkContentProvider: * GdkContentProvider:
@ -197,7 +198,7 @@ gdk_content_provider_class_init (GdkContentProviderClass *class)
* Emitted whenever the content provided by this provider has changed. * Emitted whenever the content provided by this provider has changed.
*/ */
signals[CONTENT_CHANGED] = signals[CONTENT_CHANGED] =
g_signal_new ("content-changed", g_signal_new (I_("content-changed"),
G_TYPE_FROM_CLASS (class), G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdkContentProviderClass, content_changed), G_STRUCT_OFFSET (GdkContentProviderClass, content_changed),

View File

@ -22,6 +22,7 @@
#include "gdkpaintable.h" #include "gdkpaintable.h"
#include "gdksnapshotprivate.h" #include "gdksnapshotprivate.h"
#include "gdk-private.h"
/* HACK: So we don't need to include any (not-yet-created) GSK or GTK headers */ /* HACK: So we don't need to include any (not-yet-created) GSK or GTK headers */
void gtk_snapshot_push_debug (GdkSnapshot *snapshot, void gtk_snapshot_push_debug (GdkSnapshot *snapshot,
@ -170,7 +171,7 @@ gdk_paintable_default_init (GdkPaintableInterface *iface)
* the icon theme for an icon changing. * the icon theme for an icon changing.
*/ */
signals[INVALIDATE_CONTENTS] = signals[INVALIDATE_CONTENTS] =
g_signal_new ("invalidate-contents", g_signal_new (I_("invalidate-contents"),
GDK_TYPE_PAINTABLE, GDK_TYPE_PAINTABLE,
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
0, 0,
@ -194,7 +195,7 @@ gdk_paintable_default_init (GdkPaintableInterface *iface)
* the contents of a toplevel surface being resized. * the contents of a toplevel surface being resized.
*/ */
signals[INVALIDATE_SIZE] = signals[INVALIDATE_SIZE] =
g_signal_new ("invalidate-size", g_signal_new (I_("invalidate-size"),
GDK_TYPE_PAINTABLE, GDK_TYPE_PAINTABLE,
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
0, 0,

View File

@ -24,6 +24,7 @@
#include "gdkdisplay.h" #include "gdkdisplay.h"
#include "gdkenumtypes.h" #include "gdkenumtypes.h"
#include "gdkintl.h" #include "gdkintl.h"
#include "gdk-private.h"
#include <graphene-gobject.h> #include <graphene-gobject.h>
#include <math.h> #include <math.h>
@ -247,7 +248,7 @@ gdk_toplevel_default_init (GdkToplevelInterface *iface)
* will result in an arbitrary size being used as a result. * will result in an arbitrary size being used as a result.
*/ */
signals[COMPUTE_SIZE] = signals[COMPUTE_SIZE] =
g_signal_new ("compute-size", g_signal_new (I_("compute-size"),
GDK_TYPE_TOPLEVEL, GDK_TYPE_TOPLEVEL,
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
0, 0,

View File

@ -62,6 +62,7 @@
#include "gtklistview.h" #include "gtklistview.h"
#include "gtksortlistmodel.h" #include "gtksortlistmodel.h"
#include "gtkstringsorter.h" #include "gtkstringsorter.h"
#include "gtkdropdown.h"
#include <hb-ot.h> #include <hb-ot.h>
@ -1882,76 +1883,114 @@ find_affected_text (GtkFontChooserWidget *fontchooser,
{ {
unsigned int lookup_indexes[32]; unsigned int lookup_indexes[32];
unsigned int lookup_count = 32; unsigned int lookup_count = 32;
int count; unsigned int count;
int n_chars = 0; int n_chars = 0;
count = hb_ot_layout_feature_get_lookups (hb_face, count = hb_ot_layout_feature_get_characters (hb_face,
HB_OT_TAG_GSUB, HB_OT_TAG_GSUB,
feature_index, feature_index,
0, 0,
&lookup_count, NULL, NULL);
lookup_indexes);
if (count > 0) if (count > 0)
{ {
hb_set_t *glyphs_before = NULL; hb_codepoint_t *ch;
hb_set_t *glyphs_after = NULL;
hb_set_t *glyphs_output = NULL;
hb_set_t *glyphs_input;
hb_codepoint_t gid;
char buf[5] = { 0, };
hb_tag_to_string (feature_tag, buf); ch = g_alloca (sizeof (hb_codepoint_t) * count);
hb_ot_layout_feature_get_characters (hb_face,
HB_OT_TAG_GSUB,
feature_index,
0,
&count,
ch);
glyphs_input = hb_set_create (); for (unsigned int i = 0; i < MIN (count, max_chars); i++)
g_string_append_unichar (chars, ch[i]);
for (int i = 0; i < count; i++) }
else
{
count = hb_ot_layout_feature_get_lookups (hb_face,
HB_OT_TAG_GSUB,
feature_index,
0,
&lookup_count,
lookup_indexes);
if (count > 0)
{ {
hb_ot_layout_lookup_collect_glyphs (hb_face, hb_set_t *glyphs_before = NULL;
HB_OT_TAG_GSUB, hb_set_t *glyphs_after = NULL;
lookup_indexes[i], hb_set_t *glyphs_output = NULL;
glyphs_before, hb_set_t *glyphs_input;
glyphs_input, hb_codepoint_t gid;
glyphs_after, char buf[5] = { 0, };
glyphs_output);
}
if (!fontchooser->glyphmap) hb_tag_to_string (feature_tag, buf);
{
fontchooser->glyphmap = hb_map_create (); glyphs_input = hb_set_create ();
for (hb_codepoint_t ch = 0; ch < 0xffff; ch++)
for (int i = 0; i < count; i++)
{ {
hb_codepoint_t glyph = 0; hb_ot_layout_lookup_collect_glyphs (hb_face,
if (hb_font_get_nominal_glyph (hb_font, ch, &glyph) && HB_OT_TAG_GSUB,
!hb_map_has (fontchooser->glyphmap, glyph)) lookup_indexes[i],
hb_map_set (fontchooser->glyphmap, glyph, ch); glyphs_before,
glyphs_input,
glyphs_after,
glyphs_output);
} }
}
gid = HB_SET_VALUE_INVALID; if (!fontchooser->glyphmap)
while (hb_set_next (glyphs_input, &gid))
{
hb_codepoint_t ch;
if (n_chars == max_chars)
{ {
g_string_append (chars, ""); fontchooser->glyphmap = hb_map_create ();
break; for (hb_codepoint_t ch = 0; ch < 0xffff; ch++)
{
hb_codepoint_t glyph = 0;
if (hb_font_get_nominal_glyph (hb_font, ch, &glyph) &&
!hb_map_has (fontchooser->glyphmap, glyph))
hb_map_set (fontchooser->glyphmap, glyph, ch);
}
} }
ch = hb_map_get (fontchooser->glyphmap, gid);
if (ch != HB_MAP_VALUE_INVALID)
{
g_string_append_unichar (chars, (gunichar)ch);
n_chars++;
}
}
hb_set_destroy (glyphs_input); gid = HB_SET_VALUE_INVALID;
while (hb_set_next (glyphs_input, &gid))
{
hb_codepoint_t ch;
if (n_chars == max_chars)
{
g_string_append (chars, "");
break;
}
ch = hb_map_get (fontchooser->glyphmap, gid);
if (ch != HB_MAP_VALUE_INVALID)
{
g_string_append_unichar (chars, (gunichar)ch);
n_chars++;
}
}
hb_set_destroy (glyphs_input);
}
} }
} }
return g_string_free (chars, FALSE); return g_string_free (chars, FALSE);
} }
static char *
get_name (hb_face_t *hb_face,
hb_ot_name_id_t id)
{
unsigned int len;
char *name;
len = hb_ot_name_get_utf8 (hb_face, id, HB_LANGUAGE_INVALID, NULL, NULL);
len++;
name = g_new (char, len);
hb_ot_name_get_utf8 (hb_face, id, HB_LANGUAGE_INVALID, &len, name);
return name;
}
static void static void
update_feature_label (GtkFontChooserWidget *fontchooser, update_feature_label (GtkFontChooserWidget *fontchooser,
FeatureItem *item, FeatureItem *item,
@ -1961,9 +2000,8 @@ update_feature_label (GtkFontChooserWidget *fontchooser,
{ {
hb_face_t *hb_face; hb_face_t *hb_face;
unsigned int script_index, lang_index, feature_index; unsigned int script_index, lang_index, feature_index;
hb_ot_name_id_t id; hb_ot_name_id_t label_id, first_param_id;
unsigned int len; unsigned int num_params;
char *label;
hb_face = hb_font_get_face (hb_font); hb_face = hb_font_get_face (hb_font);
@ -1978,22 +2016,106 @@ update_feature_label (GtkFontChooserWidget *fontchooser,
G_GNUC_END_IGNORE_DEPRECATIONS G_GNUC_END_IGNORE_DEPRECATIONS
if (hb_ot_layout_language_find_feature (hb_face, HB_OT_TAG_GSUB, script_index, lang_index, item->tag, &feature_index) && if (hb_ot_layout_language_find_feature (hb_face, HB_OT_TAG_GSUB, script_index, lang_index, item->tag, &feature_index) &&
hb_ot_layout_feature_get_name_ids (hb_face, HB_OT_TAG_GSUB, feature_index, &id, NULL, NULL, NULL, NULL)) hb_ot_layout_feature_get_name_ids (hb_face, HB_OT_TAG_GSUB, feature_index, &label_id, NULL, NULL, &num_params, &first_param_id))
{ {
len = hb_ot_name_get_utf8 (hb_face, id, HB_LANGUAGE_INVALID, NULL, NULL); if (label_id != HB_OT_NAME_ID_INVALID)
len++; {
label = g_new (char, len); char *label = get_name (hb_face, label_id);
hb_ot_name_get_utf8 (hb_face, id, HB_LANGUAGE_INVALID, &len, label); if (GTK_IS_CHECK_BUTTON (item->feat))
gtk_check_button_set_label (GTK_CHECK_BUTTON (item->feat), label);
else
{
GtkWidget *l = gtk_widget_get_prev_sibling (item->feat);
gtk_label_set_label (GTK_LABEL (l), label);
}
g_free (label);
}
else
{
if (GTK_IS_CHECK_BUTTON (item->feat))
gtk_check_button_set_label (GTK_CHECK_BUTTON (item->feat), item->name);
else
{
GtkWidget *l = gtk_widget_get_prev_sibling (item->feat);
gtk_label_set_label (GTK_LABEL (l), item->name);
}
}
char *s = g_strdup_printf ("%s (%s)", label, item->name); if (GTK_IS_DROP_DOWN (item->feat))
gtk_check_button_set_label (GTK_CHECK_BUTTON (item->feat), s); {
g_free (s); unsigned int n_lookups;
GtkStringList *strings;
unsigned int *lookups;
unsigned int n_alternates;
g_free (label); n_lookups = hb_ot_layout_feature_get_lookups (hb_face,
HB_OT_TAG_GSUB,
feature_index,
0,
NULL, NULL);
lookups = g_alloca (sizeof (unsigned int) * n_lookups);
hb_ot_layout_feature_get_lookups (hb_face,
HB_OT_TAG_GSUB,
feature_index,
0,
&n_lookups,
lookups);
n_alternates = 0;
for (unsigned int l = 0; l < n_lookups; l++)
{
hb_set_t *glyphs;
hb_codepoint_t glyph_index;
unsigned int lookup = lookups[l];
glyphs = hb_set_create ();
hb_ot_layout_lookup_collect_glyphs (hb_face,
HB_OT_TAG_GSUB,
lookup,
NULL,
glyphs,
NULL, NULL);
glyph_index = HB_SET_VALUE_INVALID;
while (hb_set_next (glyphs, &glyph_index))
n_alternates = MAX (n_alternates,
hb_ot_layout_lookup_get_glyph_alternates (hb_face,
lookup,
glyph_index,
0, NULL, NULL));
hb_set_destroy (glyphs);
}
strings = gtk_string_list_new (NULL);
gtk_string_list_append (strings, C_("Font feature value", "None"));
for (unsigned int i = 0; i < num_params; i++)
{
char *name;
name = get_name (hb_face, first_param_id + i);
gtk_string_list_append (strings, name);
g_free (name);
}
for (int i = num_params; i < n_alternates; i++)
{
char buf[64];
g_snprintf (buf, sizeof (buf), "%d", i + 1);
gtk_string_list_append (strings, buf);
}
if (g_list_model_get_n_items (G_LIST_MODEL (strings)) == 1)
gtk_string_list_append (strings, C_("Font feature value", "Enable"));
gtk_drop_down_set_model (GTK_DROP_DOWN (item->feat), G_LIST_MODEL (strings));
g_object_unref (strings);
}
} }
else else
{ {
label = get_feature_display_name (item->tag); char *label = get_feature_display_name (item->tag);
gtk_check_button_set_label (GTK_CHECK_BUTTON (item->feat), label); gtk_check_button_set_label (GTK_CHECK_BUTTON (item->feat), label);
g_free (label); g_free (label);
} }
@ -2010,15 +2132,27 @@ update_feature_example (GtkFontChooserWidget *fontchooser,
const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case", NULL }; const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case", NULL };
const char *number_case[] = { "xxxx", "lnum", "onum", NULL }; const char *number_case[] = { "xxxx", "lnum", "onum", NULL };
const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL }; const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL };
const char *number_formatting[] = { "zero", "nalt", "frac", NULL }; const char *fraction[] = { "xxxx", "frac", "afrc", NULL };
const char *char_variants[] = { const char *char_variants[] = {
"zero", "nalt",
"swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand", "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand",
"ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10", "ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10",
"ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20", "ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20",
"cv01", "cv02", "cv03", "cv04", "cv05", "cv06", "cv07", "cv08", "cv09", "cv10",
"cv11", "cv12", "cv13", "cv14", "cv15", "cv16", "cv17", "cv18", "cv19", "cv20",
"cv21", "cv22", "cv23", "cv24", "cv25", "cv26", "cv27", "cv28", "cv29", "cv30",
"cv31", "cv32", "cv33", "cv34", "cv35", "cv36", "cv37", "cv38", "cv39", "cv40",
"cv41", "cv42", "cv43", "cv44", "cv45", "cv46", "cv47", "cv48", "cv49", "cv50",
"cv51", "cv52", "cv53", "cv54", "cv55", "cv56", "cv57", "cv58", "cv59", "cv60",
"cv61", "cv62", "cv63", "cv64", "cv65", "cv66", "cv67", "cv68", "cv69", "cv70",
"cv71", "cv72", "cv73", "cv74", "cv75", "cv76", "cv77", "cv78", "cv79", "cv80",
"cv81", "cv82", "cv83", "cv84", "cv85", "cv86", "cv87", "cv88", "cv89", "cv90",
"cv91", "cv92", "cv93", "cv94", "cv95", "cv96", "cv97", "cv98", "cv99",
NULL }; NULL };
if (g_strv_contains (number_case, item->name) || if (g_strv_contains (number_case, item->name) ||
g_strv_contains (number_spacing, item->name)) g_strv_contains (number_spacing, item->name) ||
g_strv_contains (fraction, item->name))
{ {
PangoAttrList *attrs; PangoAttrList *attrs;
PangoAttribute *attr; PangoAttribute *attr;
@ -2035,13 +2169,15 @@ update_feature_example (GtkFontChooserWidget *fontchooser,
attr = pango_attr_font_features_new (str); attr = pango_attr_font_features_new (str);
pango_attr_list_insert (attrs, attr); pango_attr_list_insert (attrs, attr);
gtk_label_set_text (GTK_LABEL (item->example), "0123456789"); if (g_strv_contains (fraction, item->name))
gtk_label_set_text (GTK_LABEL (item->example), "1/2 2/3 7/8");
else
gtk_label_set_text (GTK_LABEL (item->example), "0123456789");
gtk_label_set_attributes (GTK_LABEL (item->example), attrs); gtk_label_set_attributes (GTK_LABEL (item->example), attrs);
pango_attr_list_unref (attrs); pango_attr_list_unref (attrs);
} }
else if (g_strv_contains (letter_case, item->name) || else if (g_strv_contains (letter_case, item->name) ||
g_strv_contains (number_formatting, item->name) ||
g_strv_contains (char_variants, item->name)) g_strv_contains (char_variants, item->name))
{ {
char *input = NULL; char *input = NULL;
@ -2053,8 +2189,6 @@ update_feature_example (GtkFontChooserWidget *fontchooser,
input = g_strdup ("AaBbCc…"); input = g_strdup ("AaBbCc…");
else if (strcmp (item->name, "zero") == 0) else if (strcmp (item->name, "zero") == 0)
input = g_strdup ("0"); input = g_strdup ("0");
else if (strcmp (item->name, "frac") == 0)
input = g_strdup ("1/2 2/3 7/8");
else if (strcmp (item->name, "nalt") == 0) else if (strcmp (item->name, "nalt") == 0)
input = find_affected_text (fontchooser, item->tag, hb_font, script_tag, lang_tag, 3); input = find_affected_text (fontchooser, item->tag, hb_font, script_tag, lang_tag, 3);
else else
@ -2067,7 +2201,7 @@ update_feature_example (GtkFontChooserWidget *fontchooser,
PangoFontDescription *desc; PangoFontDescription *desc;
char *str; char *str;
text = g_strconcat (input, " ", input, NULL); text = g_strconcat (input, " ", input, NULL);
attrs = pango_attr_list_new (); attrs = pango_attr_list_new ();
@ -2080,9 +2214,17 @@ update_feature_example (GtkFontChooserWidget *fontchooser,
attr->start_index = 0; attr->start_index = 0;
attr->end_index = strlen (input); attr->end_index = strlen (input);
pango_attr_list_insert (attrs, attr); pango_attr_list_insert (attrs, attr);
attr = pango_attr_fallback_new (FALSE);
attr->start_index = 0;
attr->end_index = strlen (input);
pango_attr_list_insert (attrs, attr);
str = g_strconcat (item->name, " 1", NULL); str = g_strconcat (item->name, " 1", NULL);
attr = pango_attr_font_features_new (str); attr = pango_attr_font_features_new (str);
attr->start_index = strlen (input) + strlen (""); attr->start_index = strlen (input) + strlen ("");
attr->end_index = attr->start_index + strlen (input);
pango_attr_list_insert (attrs, attr);
attr = pango_attr_fallback_new (FALSE);
attr->start_index = strlen (input) + strlen ("");
attr->end_index = attr->start_index + strlen (input); attr->end_index = attr->start_index + strlen (input);
pango_attr_list_insert (attrs, attr); pango_attr_list_insert (attrs, attr);
@ -2109,9 +2251,10 @@ font_feature_toggled_cb (GtkCheckButton *check_button,
} }
static void static void
add_check_group (GtkFontChooserWidget *fontchooser, add_check_group (GtkFontChooserWidget *fontchooser,
const char *title, const char *title,
const char **tags) const char **tags,
unsigned int n_tags)
{ {
GtkWidget *label; GtkWidget *label;
GtkWidget *group; GtkWidget *group;
@ -2131,7 +2274,7 @@ add_check_group (GtkFontChooserWidget *fontchooser,
pango_attr_list_unref (attrs); pango_attr_list_unref (attrs);
gtk_box_append (GTK_BOX (group), label); gtk_box_append (GTK_BOX (group), label);
for (i = 0; tags[i]; i++) for (i = 0; i < n_tags; i++)
{ {
hb_tag_t tag; hb_tag_t tag;
GtkWidget *feat; GtkWidget *feat;
@ -2179,9 +2322,87 @@ add_check_group (GtkFontChooserWidget *fontchooser,
} }
static void static void
add_radio_group (GtkFontChooserWidget *fontchooser, font_enum_feature_changed_cb (GObject *object,
const char *title, GParamSpec *pspec,
const char **tags) gpointer data)
{
GtkFontChooserWidget *fontchooser = data;
update_font_features (fontchooser);
}
static void
add_enum_group (GtkFontChooserWidget *fontchooser,
const char *title,
const char **tags,
unsigned int n_tags)
{
GtkWidget *label;
GtkWidget *group;
PangoAttrList *attrs;
int i;
group = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (group), 6);
gtk_grid_set_column_spacing (GTK_GRID (group), 12);
label = gtk_label_new (title);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_widget_set_halign (label, GTK_ALIGN_START);
g_object_set (label, "margin-top", 10, "margin-bottom", 10, NULL);
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
gtk_label_set_attributes (GTK_LABEL (label), attrs);
pango_attr_list_unref (attrs);
gtk_grid_attach (GTK_GRID (group), label, 0, -1, 3, 1);
for (i = 0; i < n_tags; i++)
{
hb_tag_t tag;
GtkWidget *feat;
FeatureItem *item;
GtkWidget *example;
char *name;
tag = hb_tag_from_string (tags[i], -1);
name = get_feature_display_name (tag);
label = gtk_label_new (name);
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_grid_attach (GTK_GRID (group), label, 0, i, 1, 1);
g_free (name);
feat = gtk_drop_down_new (NULL, NULL);
gtk_grid_attach (GTK_GRID (group), feat, 1, i, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), feat);
g_signal_connect (feat, "notify::selected", G_CALLBACK (font_enum_feature_changed_cb), fontchooser);
example = gtk_label_new ("");
gtk_label_set_selectable (GTK_LABEL (example), TRUE);
gtk_widget_set_halign (example, GTK_ALIGN_START);
gtk_grid_attach (GTK_GRID (group), example, 2, i, 1, 1);
item = g_new (FeatureItem, 1);
item->name = tags[i];
item->tag = tag;
item->top = NULL;
item->feat = feat;
item->example = example;
fontchooser->feature_items = g_list_prepend (fontchooser->feature_items, item);
}
gtk_box_append (GTK_BOX (fontchooser->feature_box), group);
}
static void
add_radio_group (GtkFontChooserWidget *fontchooser,
const char *title,
const char **tags,
unsigned int n_tags)
{ {
GtkWidget *label; GtkWidget *label;
GtkWidget *group; GtkWidget *group;
@ -2202,7 +2423,7 @@ add_radio_group (GtkFontChooserWidget *fontchooser,
pango_attr_list_unref (attrs); pango_attr_list_unref (attrs);
gtk_box_append (GTK_BOX (group), label); gtk_box_append (GTK_BOX (group), label);
for (i = 0; tags[i]; i++) for (i = 0; i < n_tags; i++)
{ {
hb_tag_t tag; hb_tag_t tag;
GtkWidget *feat; GtkWidget *feat;
@ -2249,23 +2470,37 @@ add_radio_group (GtkFontChooserWidget *fontchooser,
static void static void
gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser) gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser)
{ {
const char *ligatures[] = { "liga", "dlig", "hlig", "clig", NULL }; const char *ligatures[] = { "liga", "dlig", "hlig", "clig" };
const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case", NULL }; const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case" };
const char *number_case[] = { "xxxx", "lnum", "onum", NULL }; const char *number_case[] = { "xxxx", "lnum", "onum" };
const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL }; const char *number_spacing[] = { "xxxx", "pnum", "tnum" };
const char *number_formatting[] = { "zero", "nalt", "frac", NULL }; const char *fractions[] = { "xxxx", "frac", "afrc" };
const char *char_variants[] = { const char *style_variants[] = {
"zero", "nalt",
"swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand", "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand",
"ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10", "ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10",
"ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20", "ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20",
NULL }; };
const char *char_variants[] = {
"cv01", "cv02", "cv03", "cv04", "cv05", "cv06", "cv07", "cv08", "cv09", "cv10",
"cv11", "cv12", "cv13", "cv14", "cv15", "cv16", "cv17", "cv18", "cv19", "cv20",
"cv21", "cv22", "cv23", "cv24", "cv25", "cv26", "cv27", "cv28", "cv29", "cv30",
"cv31", "cv32", "cv33", "cv34", "cv35", "cv36", "cv37", "cv38", "cv39", "cv40",
"cv41", "cv42", "cv43", "cv44", "cv45", "cv46", "cv47", "cv48", "cv49", "cv50",
"cv51", "cv52", "cv53", "cv54", "cv55", "cv56", "cv57", "cv58", "cv59", "cv60",
"cv61", "cv62", "cv63", "cv64", "cv65", "cv66", "cv67", "cv68", "cv69", "cv70",
"cv71", "cv72", "cv73", "cv74", "cv75", "cv76", "cv77", "cv78", "cv79", "cv80",
"cv81", "cv82", "cv83", "cv84", "cv85", "cv86", "cv87", "cv88", "cv89", "cv90",
"cv91", "cv92", "cv93", "cv94", "cv95", "cv96", "cv97", "cv98", "cv99",
};
add_check_group (fontchooser, _("Ligatures"), ligatures); add_check_group (fontchooser, _("Ligatures"), ligatures, G_N_ELEMENTS (ligatures));
add_check_group (fontchooser, _("Letter Case"), letter_case); add_check_group (fontchooser, _("Letter Case"), letter_case, G_N_ELEMENTS (letter_case));
add_radio_group (fontchooser, _("Number Case"), number_case); add_radio_group (fontchooser, _("Number Case"), number_case, G_N_ELEMENTS (number_case));
add_radio_group (fontchooser, _("Number Spacing"), number_spacing); add_radio_group (fontchooser, _("Number Spacing"), number_spacing, G_N_ELEMENTS (number_spacing));
add_check_group (fontchooser, _("Number Formatting"), number_formatting); add_radio_group (fontchooser, _("Fractions"), fractions, G_N_ELEMENTS (fractions));
add_check_group (fontchooser, _("Character Variants"), char_variants); add_check_group (fontchooser, _("Style Variations"), style_variants, G_N_ELEMENTS (style_variants));
add_enum_group (fontchooser, _("Character Variations"), char_variants, G_N_ELEMENTS (char_variants));
update_font_features (fontchooser); update_font_features (fontchooser);
} }
@ -2286,8 +2521,18 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
for (l = fontchooser->feature_items; l; l = l->next) for (l = fontchooser->feature_items; l; l = l->next)
{ {
FeatureItem *item = l->data; FeatureItem *item = l->data;
gtk_widget_hide (item->top); if (item->top)
gtk_widget_hide (gtk_widget_get_parent (item->top)); {
gtk_widget_hide (item->top);
gtk_widget_hide (gtk_widget_get_parent (item->top));
}
else
{
gtk_widget_hide (gtk_widget_get_parent (item->feat));
gtk_widget_hide (item->feat);
gtk_widget_hide (gtk_widget_get_prev_sibling (item->feat));
gtk_widget_hide (item->example);
}
} }
if ((fontchooser->level & GTK_FONT_CHOOSER_LEVEL_FEATURES) == 0) if ((fontchooser->level & GTK_FONT_CHOOSER_LEVEL_FEATURES) == 0)
@ -2304,13 +2549,14 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
hb_tag_t features[80]; hb_tag_t features[80];
unsigned int count; unsigned int count;
unsigned int n_features; unsigned int n_features;
hb_tag_t *feat;
hb_face = hb_font_get_face (hb_font); hb_face = hb_font_get_face (hb_font);
find_language_and_script (fontchooser, hb_face, &lang_tag, &script_tag); find_language_and_script (fontchooser, hb_face, &lang_tag, &script_tag);
n_features = 0; n_features = 0;
for (i = 0; i < 2; i++) for (i = 0; i < G_N_ELEMENTS (table); i++)
{ {
hb_ot_layout_table_find_script (hb_face, table[i], script_tag, &script_index); hb_ot_layout_table_find_script (hb_face, table[i], script_tag, &script_index);
@ -2318,14 +2564,15 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
hb_ot_layout_script_find_language (hb_face, table[i], script_index, lang_tag, &lang_index); hb_ot_layout_script_find_language (hb_face, table[i], script_index, lang_tag, &lang_index);
G_GNUC_END_IGNORE_DEPRECATIONS G_GNUC_END_IGNORE_DEPRECATIONS
count = G_N_ELEMENTS (features); feat = features + n_features;
count = G_N_ELEMENTS (features) - n_features;
hb_ot_layout_language_get_feature_tags (hb_face, hb_ot_layout_language_get_feature_tags (hb_face,
table[i], table[i],
script_index, script_index,
lang_index, lang_index,
n_features, n_features,
&count, &count,
features); feat);
n_features += count; n_features += count;
} }
@ -2338,8 +2585,18 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
continue; continue;
has_feature = TRUE; has_feature = TRUE;
gtk_widget_show (item->top); if (item->top)
gtk_widget_show (gtk_widget_get_parent (item->top)); {
gtk_widget_show (item->top);
gtk_widget_show (gtk_widget_get_parent (item->top));
}
else
{
gtk_widget_show (gtk_widget_get_parent (item->feat));
gtk_widget_show (item->feat);
gtk_widget_show (gtk_widget_get_prev_sibling (item->feat));
gtk_widget_show (item->example);
}
update_feature_label (fontchooser, item, hb_font, script_tag, lang_tag); update_feature_label (fontchooser, item, hb_font, script_tag, lang_tag);
update_feature_example (fontchooser, item, hb_font, script_tag, lang_tag, fontchooser->font_desc); update_feature_example (fontchooser, item, hb_font, script_tag, lang_tag, fontchooser->font_desc);
@ -2411,6 +2668,19 @@ update_font_features (GtkFontChooserWidget *fontchooser)
g_string_append_c (s, ','); g_string_append_c (s, ',');
g_string_append (s, buf); g_string_append (s, buf);
} }
else if (GTK_IS_DROP_DOWN (item->feat))
{
guint value;
value = gtk_drop_down_get_selected (GTK_DROP_DOWN (item->feat));
if (value == 0 || value == GTK_INVALID_LIST_POSITION)
continue;
hb_feature_to_string (&(hb_feature_t) { item->tag, value, 0, -1 }, buf, sizeof (buf));
if (s->len > 0)
g_string_append_c (s, ',');
g_string_append (s, buf);
}
} }
if (g_strcmp0 (fontchooser->font_features, s->str) != 0) if (g_strcmp0 (fontchooser->font_features, s->str) != 0)