diff --git a/docs/reference/gtk/css-properties.xml b/docs/reference/gtk/css-properties.xml index 5d9e86be33..b6b77fe6be 100644 --- a/docs/reference/gtk/css-properties.xml +++ b/docs/reference/gtk/css-properties.xml @@ -322,6 +322,15 @@ We use for syntax productions, and each line is put in a CSS3 + + font-variation-settings + normal | [ 〈string〉〈number〉]# + normal + + + CSS4 + + diff --git a/gtk/gtkcssfontvariationsvalue.c b/gtk/gtkcssfontvariationsvalue.c new file mode 100644 index 0000000000..7cc5ccd766 --- /dev/null +++ b/gtk/gtkcssfontvariationsvalue.c @@ -0,0 +1,294 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include "gtkcssparserprivate.h" +#include "gtkcssnumbervalueprivate.h" +#include "gtkcssfontvariationsvalueprivate.h" + +struct _GtkCssValue { + GTK_CSS_VALUE_BASE + GHashTable *axes; +}; + +static GtkCssValue *default_font_variations; + +static GtkCssValue *gtk_css_font_variations_value_new_empty (void); + +static void +gtk_css_font_variations_value_add_axis (GtkCssValue *value, + const char *name, + GtkCssValue *coord) +{ + g_hash_table_insert (value->axes, g_strdup (name), coord); +} + + +static void +gtk_css_value_font_variations_free (GtkCssValue *value) +{ + g_hash_table_unref (value->axes); + + g_slice_free (GtkCssValue, value); +} + +static GtkCssValue * +gtk_css_value_font_variations_compute (GtkCssValue *specified, + guint property_id, + GtkStyleProvider *provider, + GtkCssStyle *style, + GtkCssStyle *parent_style) +{ + GHashTableIter iter; + gpointer name, coord; + GtkCssValue *computed_coord; + GtkCssValue *result; + gboolean changes = FALSE; + + result = gtk_css_font_variations_value_new_empty (); + + g_hash_table_iter_init (&iter, specified->axes); + while (g_hash_table_iter_next (&iter, &name, &coord)) + { + computed_coord = _gtk_css_value_compute (coord, property_id, provider, style, parent_style); + changes |= computed_coord != coord; + gtk_css_font_variations_value_add_axis (result, name, computed_coord); + } + + if (!changes) + { + _gtk_css_value_unref (result); + result = _gtk_css_value_ref (specified); + } + + return result; +} + +static gboolean +gtk_css_value_font_variations_equal (const GtkCssValue *value1, + const GtkCssValue *value2) +{ + gpointer name, coord1, coord2; + GHashTableIter iter; + + if (g_hash_table_size (value1->axes) != g_hash_table_size (value2->axes)) + return FALSE; + + g_hash_table_iter_init (&iter, value1->axes); + while (g_hash_table_iter_next (&iter, &name, &coord1)) + { + coord2 = g_hash_table_lookup (value2->axes, name); + if (coord2 == NULL) + return FALSE; + + if (!_gtk_css_value_equal (coord1, coord2)) + return FALSE; + } + + return TRUE; +} + +static GtkCssValue * +gtk_css_value_font_variations_transition (GtkCssValue *start, + GtkCssValue *end, + guint property_id, + double progress) +{ + const char *name; + GtkCssValue *start_coord, *end_coord; + GHashTableIter iter; + GtkCssValue *result, *transition; + + /* XXX: For value that are only in start or end but not both, + * we don't transition but just keep the value. + * That causes an abrupt transition to the new value at the end. + */ + + result = gtk_css_font_variations_value_new_empty (); + + g_hash_table_iter_init (&iter, start->axes); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&start_coord)) + { + end_coord = g_hash_table_lookup (end->axes, name); + if (end_coord == NULL) + transition = _gtk_css_value_ref (start_coord); + else + transition = _gtk_css_value_transition (start_coord, end_coord, property_id, progress); + + gtk_css_font_variations_value_add_axis (result, name, transition); + } + + g_hash_table_iter_init (&iter, end->axes); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&end_coord)) + { + start_coord = g_hash_table_lookup (start->axes, name); + if (start_coord != NULL) + continue; + + gtk_css_font_variations_value_add_axis (result, name, _gtk_css_value_ref (end_coord)); + } + + return result; +} + +static void +gtk_css_value_font_variations_print (const GtkCssValue *value, + GString *string) +{ + GHashTableIter iter; + const char *name; + GtkCssValue *coord; + gboolean first = TRUE; + + if (value == default_font_variations) + { + g_string_append (string, "normal"); + return; + } + + g_hash_table_iter_init (&iter, value->axes); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&coord)) + { + if (first) + first = FALSE; + else + g_string_append (string, ", "); + g_string_append_printf (string, "\"%s\" ", name); + _gtk_css_value_print (coord, string); + } +} + +static const GtkCssValueClass GTK_CSS_VALUE_FONT_VARIATIONS = { + gtk_css_value_font_variations_free, + gtk_css_value_font_variations_compute, + gtk_css_value_font_variations_equal, + gtk_css_value_font_variations_transition, + gtk_css_value_font_variations_print +}; + +static GtkCssValue * +gtk_css_font_variations_value_new_empty (void) +{ + GtkCssValue *result; + + result = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_FONT_VARIATIONS); + result->axes = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + (GDestroyNotify) _gtk_css_value_unref); + + return result; +} + +GtkCssValue * +gtk_css_font_variations_value_new_default (void) +{ + if (default_font_variations == NULL) + default_font_variations = gtk_css_font_variations_value_new_empty (); + + return _gtk_css_value_ref (default_font_variations); +} + +static gboolean +is_valid_opentype_tag (const char *s) +{ + if (strlen (s) != 4) + return FALSE; + + if (s[0] < 0x20 || s[0] > 0x7e || + s[1] < 0x20 || s[1] > 0x7e || + s[2] < 0x20 || s[2] > 0x7e || + s[3] < 0x20 || s[3] > 0x7e) + return FALSE; + + return TRUE; +} + +GtkCssValue * +gtk_css_font_variations_value_parse (GtkCssParser *parser) +{ + GtkCssValue *result, *coord; + char *name; + + if (_gtk_css_parser_try (parser, "normal", TRUE)) + return gtk_css_font_variations_value_new_default (); + + result = gtk_css_font_variations_value_new_empty (); + + do { + _gtk_css_parser_skip_whitespace (parser); + name = _gtk_css_parser_read_string (parser); + if (name == NULL) + { + _gtk_css_value_unref (result); + return NULL; + } + + if (!is_valid_opentype_tag (name)) + { + _gtk_css_parser_error (parser, "Not a valid OpenType tag."); + g_free (name); + _gtk_css_value_unref (result); + return NULL; + } + + coord = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER); + if (coord == NULL) + { + g_free (name); + _gtk_css_value_unref (result); + return NULL; + } + + gtk_css_font_variations_value_add_axis (result, name, coord); + g_free (name); + } while (_gtk_css_parser_try (parser, ",", TRUE)); + + return result; +} + +char * +gtk_css_font_variations_value_get_variations (GtkCssValue *value) +{ + GtkCssValue *coord; + GHashTableIter iter; + gboolean first = TRUE; + const char *name; + GString *string; + + g_return_val_if_fail (value->class == >K_CSS_VALUE_FONT_VARIATIONS, NULL); + + if (value == default_font_variations) + return NULL; + + string = g_string_new (""); + + g_hash_table_iter_init (&iter, value->axes); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&coord)) + { + if (first) + first = FALSE; + else + g_string_append (string, ","); + g_string_append_printf (string, "%s=%g", name, + _gtk_css_number_value_get (coord, 100)); + } + + return g_string_free (string, FALSE); +} diff --git a/gtk/gtkcssfontvariationsvalueprivate.h b/gtk/gtkcssfontvariationsvalueprivate.h new file mode 100644 index 0000000000..153cfeb2e1 --- /dev/null +++ b/gtk/gtkcssfontvariationsvalueprivate.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2017 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#ifndef __GTK_CSS_FONT_VARIATIONS_VALUE_PRIVATE_H__ +#define __GTK_CSS_FONT_VARIATIONS_PRIVATE_H__ + +#include "gtkcssparserprivate.h" +#include "gtkcssvalueprivate.h" + +G_BEGIN_DECLS + +GtkCssValue * gtk_css_font_variations_value_new_default (void); + +GtkCssValue * gtk_css_font_variations_value_parse (GtkCssParser *parser); + +char * gtk_css_font_variations_value_get_variations (GtkCssValue *value); + +G_END_DECLS + +#endif /* __GTK_CSS_FONT_VARIATIONS_VALUE_PRIVATE_H__ */ diff --git a/gtk/gtkcssstylepropertyimpl.c b/gtk/gtkcssstylepropertyimpl.c index fbde8b1741..08e9834536 100644 --- a/gtk/gtkcssstylepropertyimpl.c +++ b/gtk/gtkcssstylepropertyimpl.c @@ -59,6 +59,7 @@ #include "gtkcssshadowsvalueprivate.h" #include "gtkcssstringvalueprivate.h" #include "gtkcsstransformvalueprivate.h" +#include "gtkcssfontvariationsvalueprivate.h" #include "gtktypebuiltins.h" /*** REGISTRATION ***/ @@ -664,6 +665,13 @@ parse_font_feature_settings (GtkCssStyleProperty *property, return gtk_css_font_features_value_parse (parser); } +static GtkCssValue * +parse_font_variation_settings (GtkCssStyleProperty *property, + GtkCssParser *parser) +{ + return gtk_css_font_variations_value_parse (parser); +} + static GtkCssValue * box_shadow_value_parse (GtkCssStyleProperty *property, GtkCssParser *parser) @@ -1816,4 +1824,12 @@ _gtk_css_style_property_init_properties (void) parse_font_feature_settings, NULL, gtk_css_font_features_value_new_default ()); + gtk_css_style_property_register ("font-variation-settings", + GTK_CSS_PROPERTY_FONT_VARIATION_SETTINGS, + G_TYPE_NONE, + GTK_STYLE_PROPERTY_INHERIT | GTK_STYLE_PROPERTY_ANIMATED, + GTK_CSS_AFFECTS_TEXT_ATTRS | GTK_CSS_AFFECTS_TEXT_SIZE, + parse_font_variation_settings, + NULL, + gtk_css_font_variations_value_new_default ()); } diff --git a/gtk/gtkcsstypesprivate.h b/gtk/gtkcsstypesprivate.h index f7b68ee445..2dae7214f0 100644 --- a/gtk/gtkcsstypesprivate.h +++ b/gtk/gtkcsstypesprivate.h @@ -245,6 +245,7 @@ enum { /*< skip >*/ GTK_CSS_PROPERTY_CARET_COLOR, GTK_CSS_PROPERTY_SECONDARY_CARET_COLOR, GTK_CSS_PROPERTY_FONT_FEATURE_SETTINGS, + GTK_CSS_PROPERTY_FONT_VARIATION_SETTINGS, /* add more */ GTK_CSS_PROPERTY_N_PROPERTIES }; diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 5c3fda72ea..e61ef9778f 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -76,6 +76,7 @@ #include "gtkdebugupdatesprivate.h" #include "gsk/gskdebugprivate.h" #include "gtkeventcontrollerlegacyprivate.h" +#include "gtkcssfontvariationsvalueprivate.h" #include "inspector/window.h" @@ -8580,15 +8581,23 @@ update_pango_context (GtkWidget *widget, GtkStyleContext *style_context; GtkSettings *settings; cairo_font_options_t *font_options; + GtkCssValue *value; + char *variations; style_context = _gtk_widget_get_style_context (widget); gtk_style_context_get (style_context, "font", &font_desc, NULL); + value = _gtk_style_context_peek_property (_gtk_widget_get_style_context (widget), GTK_CSS_PROPERTY_FONT_VARIATION_SETTINGS); + variations = gtk_css_font_variations_value_get_variations (value); + + pango_font_description_set_variations (font_desc, variations); + pango_context_set_font_description (context, font_desc); pango_font_description_free (font_desc); + g_free (variations); pango_context_set_base_dir (context, _gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ? diff --git a/gtk/meson.build b/gtk/meson.build index f8d9a966f1..d04c35843f 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -88,6 +88,7 @@ gtk_public_sources = files([ 'gtkcssenumvalue.c', 'gtkcssfiltervalue.c', 'gtkcssfontfeaturesvalue.c', + 'gtkcssfontvariationsvalue.c', 'gtkcssiconthemevalue.c', 'gtkcssimage.c', 'gtkcssimagebuiltin.c',