From 2d84a1c63ef7d8d24b1476b4b90afa3acb1171d7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 20 Aug 2021 19:01:01 -0400 Subject: [PATCH 1/3] css: Implement text-transform Implement the text-transform property from https://www.w3.org/TR/css-text-3/#text-transform-property using a new Pango attribute. --- docs/reference/gtk/css-properties.md | 1 + gtk/gtkcssanimatedstyle.c | 4 +++ gtk/gtkcssenumvalue.c | 52 ++++++++++++++++++++++++++++ gtk/gtkcssenumvalueprivate.h | 3 ++ gtk/gtkcssstaticstyle.c | 5 +++ gtk/gtkcssstyle.c | 26 ++++++++++++++ gtk/gtkcssstyleprivate.h | 1 + gtk/gtkcssstylepropertyimpl.c | 18 ++++++++++ gtk/gtkcsstypesprivate.h | 8 +++++ 9 files changed, 118 insertions(+) diff --git a/docs/reference/gtk/css-properties.md b/docs/reference/gtk/css-properties.md index c6818184a4..5b3587e75a 100644 --- a/docs/reference/gtk/css-properties.md +++ b/docs/reference/gtk/css-properties.md @@ -162,6 +162,7 @@ done with |caret-color|[CSS Basic User Interface Level 3](https://www.w3.org/TR/css3-ui/#caret-color) | CSS allows an auto value | |-gtk-secondary-caret-color|[Color](https://www.w3.org/TR/css-color-3/#valuea-def-color) | used for the secondary caret in bidirectional text | |letter-spacing| [CSS Text Level 3](https://www.w3.org/TR/css3-text/#letter-spacing) | | +|text-transform| [CSS Text Level 3](https://www.w3.org/TR/css-text-3/#text-transform-property) | CSS allows full-width and full-size-kana. Since 4.6 | |line-height| [CSS Inline Layout Level 3](https://www.w3.org/TR/css-inline-3/#line-height-property) | Since 4.6 | |text-decoration-line| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-line-property) | | |text-decoration-color| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-color-property) | | diff --git a/gtk/gtkcssanimatedstyle.c b/gtk/gtkcssanimatedstyle.c index eb62780360..fdfcf17f1f 100644 --- a/gtk/gtkcssanimatedstyle.c +++ b/gtk/gtkcssanimatedstyle.c @@ -220,6 +220,10 @@ gtk_css_animated_style_set_animated_value (GtkCssAnimatedStyle *animated, unshare_font_variant (animated); gtk_css_take_value (&style->font_variant->text_decoration_style, value); break; + case GTK_CSS_PROPERTY_TEXT_TRANSFORM: + unshare_font_variant (animated); + gtk_css_take_value (&style->font_variant->text_transform, value); + break; case GTK_CSS_PROPERTY_FONT_KERNING: unshare_font_variant (animated); gtk_css_take_value (&style->font_variant->font_kerning, value); diff --git a/gtk/gtkcssenumvalue.c b/gtk/gtkcssenumvalue.c index ce96a23e0c..dfb7a0d394 100644 --- a/gtk/gtkcssenumvalue.c +++ b/gtk/gtkcssenumvalue.c @@ -1568,3 +1568,55 @@ _gtk_css_font_variant_east_asian_value_get (const GtkCssValue *value) return value->value; } + +/* GtkTextTransform */ + +static const GtkCssValueClass GTK_CSS_VALUE_TEXT_TRANSFORM = { + "GtkCssTextTransformValue", + gtk_css_value_enum_free, + gtk_css_value_enum_compute, + gtk_css_value_enum_equal, + gtk_css_value_enum_transition, + NULL, + NULL, + gtk_css_value_enum_print +}; + +static GtkCssValue text_transform_values[] = { + { >K_CSS_VALUE_TEXT_TRANSFORM, 1, TRUE, GTK_CSS_TEXT_TRANSFORM_NONE, "none" }, + { >K_CSS_VALUE_TEXT_TRANSFORM, 1, TRUE, GTK_CSS_TEXT_TRANSFORM_LOWERCASE, "lowercase" }, + { >K_CSS_VALUE_TEXT_TRANSFORM, 1, TRUE, GTK_CSS_TEXT_TRANSFORM_UPPERCASE, "uppercase" }, + { >K_CSS_VALUE_TEXT_TRANSFORM, 1, TRUE, GTK_CSS_TEXT_TRANSFORM_CAPITALIZE, "capitalize" }, +}; + +GtkCssValue * +_gtk_css_text_transform_value_new (GtkTextTransform transform) +{ + g_return_val_if_fail (transform < G_N_ELEMENTS (text_transform_values), NULL); + + return _gtk_css_value_ref (&text_transform_values[transform]); +} + +GtkCssValue * +_gtk_css_text_transform_value_try_parse (GtkCssParser *parser) +{ + guint i; + + g_return_val_if_fail (parser != NULL, NULL); + + for (i = 0; i < G_N_ELEMENTS (text_transform_values); i++) + { + if (gtk_css_parser_try_ident (parser, text_transform_values[i].name)) + return _gtk_css_value_ref (&text_transform_values[i]); + } + + return NULL; +} + +GtkTextTransform +_gtk_css_text_transform_value_get (const GtkCssValue *value) +{ + g_return_val_if_fail (value->class == >K_CSS_VALUE_TEXT_TRANSFORM, GTK_CSS_TEXT_TRANSFORM_NONE); + + return value->value; +} diff --git a/gtk/gtkcssenumvalueprivate.h b/gtk/gtkcssenumvalueprivate.h index a102a8650b..b6d3b0f757 100644 --- a/gtk/gtkcssenumvalueprivate.h +++ b/gtk/gtkcssenumvalueprivate.h @@ -115,6 +115,9 @@ GtkCssFontVariantEastAsian _gtk_css_font_variant_east_asian_try_parse_one (GtkCs GtkCssFontVariantEastAsian base); GtkCssFontVariantEastAsian _gtk_css_font_variant_east_asian_value_get (const GtkCssValue *value); +GtkCssValue * _gtk_css_text_transform_value_new (GtkTextTransform transform); +GtkCssValue * _gtk_css_text_transform_value_try_parse (GtkCssParser *parser); +GtkTextTransform _gtk_css_text_transform_value_get (const GtkCssValue *value); G_END_DECLS #endif /* __GTK_CSS_ENUM_VALUE_PRIVATE_H__ */ diff --git a/gtk/gtkcssstaticstyle.c b/gtk/gtkcssstaticstyle.c index ad906431d8..ba7d2ff958 100644 --- a/gtk/gtkcssstaticstyle.c +++ b/gtk/gtkcssstaticstyle.c @@ -118,6 +118,7 @@ static const int font_variant_props[] = { GTK_CSS_PROPERTY_TEXT_DECORATION_LINE, GTK_CSS_PROPERTY_TEXT_DECORATION_COLOR, GTK_CSS_PROPERTY_TEXT_DECORATION_STYLE, + GTK_CSS_PROPERTY_TEXT_TRANSFORM, GTK_CSS_PROPERTY_FONT_KERNING, GTK_CSS_PROPERTY_FONT_VARIANT_LIGATURES, GTK_CSS_PROPERTY_FONT_VARIANT_POSITION, @@ -430,6 +431,9 @@ gtk_css_static_style_set_value (GtkCssStaticStyle *sstyle, case GTK_CSS_PROPERTY_TEXT_DECORATION_STYLE: gtk_css_take_value (&style->font_variant->text_decoration_style, value); break; + case GTK_CSS_PROPERTY_TEXT_TRANSFORM: + gtk_css_take_value (&style->font_variant->text_transform, value); + break; case GTK_CSS_PROPERTY_FONT_KERNING: gtk_css_take_value (&style->font_variant->font_kerning, value); break; @@ -810,6 +814,7 @@ gtk_css_font_variant_create_initial_values (void) values->text_decoration_line = _gtk_css_initial_value_new_compute (GTK_CSS_PROPERTY_TEXT_DECORATION_LINE, NULL, NULL, NULL); values->text_decoration_color = NULL; values->text_decoration_style = _gtk_css_initial_value_new_compute (GTK_CSS_PROPERTY_TEXT_DECORATION_STYLE, NULL, NULL, NULL); + values->text_transform = _gtk_css_initial_value_new_compute (GTK_CSS_PROPERTY_TEXT_TRANSFORM, NULL, NULL, NULL); values->font_kerning = _gtk_css_initial_value_new_compute (GTK_CSS_PROPERTY_FONT_KERNING, NULL, NULL, NULL); values->font_variant_ligatures = _gtk_css_initial_value_new_compute (GTK_CSS_PROPERTY_FONT_VARIANT_LIGATURES, NULL, NULL, NULL); values->font_variant_position = _gtk_css_initial_value_new_compute (GTK_CSS_PROPERTY_FONT_VARIANT_POSITION, NULL, NULL, NULL); diff --git a/gtk/gtkcssstyle.c b/gtk/gtkcssstyle.c index 92a7398988..652f9a7a78 100644 --- a/gtk/gtkcssstyle.c +++ b/gtk/gtkcssstyle.c @@ -126,6 +126,8 @@ gtk_css_style_get_value (GtkCssStyle *style, return style->font_variant->text_decoration_color ? style->font_variant->text_decoration_color : style->core->color; case GTK_CSS_PROPERTY_TEXT_DECORATION_STYLE: return style->font_variant->text_decoration_style; + case GTK_CSS_PROPERTY_TEXT_TRANSFORM: + return style->font_variant->text_transform; case GTK_CSS_PROPERTY_FONT_KERNING: return style->font_variant->font_kerning; case GTK_CSS_PROPERTY_FONT_VARIANT_LIGATURES: @@ -400,6 +402,24 @@ get_pango_underline_from_style (GtkTextDecorationStyle style) g_return_val_if_reached (PANGO_UNDERLINE_SINGLE); } +static PangoTextTransform +get_pango_text_transform_from_style (GtkTextTransform transform) +{ + switch (transform) + { + case GTK_CSS_TEXT_TRANSFORM_NONE: + return PANGO_TEXT_TRANSFORM_NONE; + case GTK_CSS_TEXT_TRANSFORM_LOWERCASE: + return PANGO_TEXT_TRANSFORM_LOWERCASE; + case GTK_CSS_TEXT_TRANSFORM_UPPERCASE: + return PANGO_TEXT_TRANSFORM_UPPERCASE; + case GTK_CSS_TEXT_TRANSFORM_CAPITALIZE: + return PANGO_TEXT_TRANSFORM_CAPITALIZE; + default: + return PANGO_TEXT_TRANSFORM_NONE; + } +} + static PangoOverline get_pango_overline_from_style (GtkTextDecorationStyle style) { @@ -445,6 +465,7 @@ gtk_css_style_get_pango_attributes (GtkCssStyle *style) GtkCssFontVariantEastAsian east_asian; GString *s; char *settings; + GtkTextTransform transform; /* text-decoration */ decoration_line = _gtk_css_text_decoration_line_value_get (style->font_variant->text_decoration_line); @@ -655,6 +676,11 @@ gtk_css_style_get_pango_attributes (GtkCssStyle *style) g_string_free (s, TRUE); } + transform = _gtk_css_text_transform_value_get (style->font_variant->text_transform); + + if (transform != GTK_CSS_TEXT_TRANSFORM_NONE) + attrs = add_pango_attr (attrs, pango_attr_text_transform_new (get_pango_text_transform_from_style (transform))); + return attrs; } diff --git a/gtk/gtkcssstyleprivate.h b/gtk/gtkcssstyleprivate.h index 5a78d3d962..da878606f0 100644 --- a/gtk/gtkcssstyleprivate.h +++ b/gtk/gtkcssstyleprivate.h @@ -159,6 +159,7 @@ struct _GtkCssFontVariantValues { GtkCssValue *text_decoration_line; GtkCssValue *text_decoration_color; // NULL if currentColor GtkCssValue *text_decoration_style; + GtkCssValue *text_transform; GtkCssValue *font_kerning; GtkCssValue *font_variant_ligatures; GtkCssValue *font_variant_position; diff --git a/gtk/gtkcssstylepropertyimpl.c b/gtk/gtkcssstylepropertyimpl.c index a98573c69f..4ee8a200ef 100644 --- a/gtk/gtkcssstylepropertyimpl.c +++ b/gtk/gtkcssstylepropertyimpl.c @@ -366,6 +366,18 @@ parse_text_decoration_style (GtkCssStyleProperty *property, return value; } +static GtkCssValue * +parse_text_transform (GtkCssStyleProperty *property, + GtkCssParser *parser) +{ + GtkCssValue *value = _gtk_css_text_transform_value_try_parse (parser); + + if (value == NULL) + gtk_css_parser_error_syntax (parser, "unknown text transform value"); + + return value; +} + static GtkCssValue * parse_font_kerning (GtkCssStyleProperty *property, GtkCssParser *parser) @@ -917,6 +929,12 @@ _gtk_css_style_property_init_properties (void) GTK_CSS_AFFECTS_TEXT_ATTRS, parse_text_decoration_style, _gtk_css_text_decoration_style_value_new (GTK_CSS_TEXT_DECORATION_STYLE_SOLID)); + gtk_css_style_property_register ("text-transform", + GTK_CSS_PROPERTY_TEXT_TRANSFORM, + 0, + GTK_CSS_AFFECTS_TEXT_ATTRS | GTK_CSS_AFFECTS_TEXT_SIZE, + parse_text_transform, + _gtk_css_text_transform_value_new (GTK_CSS_TEXT_TRANSFORM_NONE)); gtk_css_style_property_register ("font-kerning", GTK_CSS_PROPERTY_FONT_KERNING, 0, diff --git a/gtk/gtkcsstypesprivate.h b/gtk/gtkcsstypesprivate.h index 1a2320a6bb..8fc0c39c33 100644 --- a/gtk/gtkcsstypesprivate.h +++ b/gtk/gtkcsstypesprivate.h @@ -198,6 +198,7 @@ enum { /*< skip >*/ GTK_CSS_PROPERTY_TEXT_DECORATION_LINE, GTK_CSS_PROPERTY_TEXT_DECORATION_COLOR, GTK_CSS_PROPERTY_TEXT_DECORATION_STYLE, + GTK_CSS_PROPERTY_TEXT_TRANSFORM, GTK_CSS_PROPERTY_FONT_KERNING, GTK_CSS_PROPERTY_FONT_VARIANT_LIGATURES, GTK_CSS_PROPERTY_FONT_VARIANT_POSITION, @@ -338,6 +339,13 @@ typedef enum /*< skip >*/ { GTK_CSS_TEXT_DECORATION_STYLE_WAVY } GtkTextDecorationStyle; +typedef enum /*< skip >*/ { + GTK_CSS_TEXT_TRANSFORM_NONE, + GTK_CSS_TEXT_TRANSFORM_LOWERCASE, + GTK_CSS_TEXT_TRANSFORM_UPPERCASE, + GTK_CSS_TEXT_TRANSFORM_CAPITALIZE, +} GtkTextTransform; + /* for the order in arrays */ typedef enum /*< skip >*/ { GTK_CSS_TOP, From 417b3f9c6b849a833c2ff71890ba15b25b5b07b7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 20 Aug 2021 20:06:38 -0400 Subject: [PATCH 2/3] Implement text transforms for GtkTextView Add a property to GtkTextTag and do all the legwork to translate it to the Pango attribute. --- demos/gtk-demo/fontify.c | 1 + gtk/gtktextattributes.c | 6 +++++- gtk/gtktextattributes.h | 1 + gtk/gtktextbuffer.c | 1 + gtk/gtktextlayout.c | 9 +++++++++ gtk/gtktexttag.c | 40 ++++++++++++++++++++++++++++++++++++++++ gtk/gtktexttagprivate.h | 1 + 7 files changed, 58 insertions(+), 1 deletion(-) diff --git a/demos/gtk-demo/fontify.c b/demos/gtk-demo/fontify.c index de6a9ca699..b39c3ffee6 100644 --- a/demos/gtk-demo/fontify.c +++ b/demos/gtk-demo/fontify.c @@ -271,6 +271,7 @@ insert_tags_for_attributes (GtkTextBuffer *buffer, break; case PANGO_ATTR_TEXT_TRANSFORM: + INT_ATTR (text_transform); break; case PANGO_ATTR_INVALID: diff --git a/gtk/gtktextattributes.c b/gtk/gtktextattributes.c index fbc63a7d63..87208a8100 100644 --- a/gtk/gtktextattributes.c +++ b/gtk/gtktextattributes.c @@ -438,6 +438,9 @@ _gtk_text_attributes_fill_from_tags (GtkTextAttributes *dest, if (tag->priv->insert_hyphens_set) dest->no_hyphens = vals->no_hyphens; + + if (tag->priv->text_transform_set) + dest->text_transform = vals->text_transform; } dest->left_margin += left_margin_accumulative; @@ -467,7 +470,8 @@ _gtk_text_tag_affects_size (GtkTextTag *tag) priv->wrap_mode_set || priv->invisible_set || priv->font_features_set || - priv->letter_spacing_set; + priv->letter_spacing_set || + priv->text_transform_set; } gboolean diff --git a/gtk/gtktextattributes.h b/gtk/gtktextattributes.h index 3648dabf7c..f1ebc857db 100644 --- a/gtk/gtktextattributes.h +++ b/gtk/gtktextattributes.h @@ -159,6 +159,7 @@ struct _GtkTextAttributes guint show_spaces : 3; /* PangoShowFlags */ guint no_hyphens : 1; guint line_height_is_absolute : 1; + guint text_transform : 3; /* PangoTextTransform */ }; GtkTextAttributes* gtk_text_attributes_new (void); diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c index 38315286b4..fd457df699 100644 --- a/gtk/gtktextbuffer.c +++ b/gtk/gtktextbuffer.c @@ -4713,6 +4713,7 @@ insert_tags_for_attributes (GtkTextBuffer *buffer, break; case PANGO_ATTR_TEXT_TRANSFORM: + INT_ATTR (text_transform); break; case PANGO_ATTR_SHAPE: diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c index d6ad07ceaf..88d4c409b4 100644 --- a/gtk/gtktextlayout.c +++ b/gtk/gtktextlayout.c @@ -1683,6 +1683,15 @@ add_text_attrs (GtkTextLayout *layout, attr->start_index = start; attr->end_index = start + byte_count; + pango_attr_list_insert (attrs, attr); + } + + if (style->text_transform != PANGO_TEXT_TRANSFORM_NONE) + { + attr = pango_attr_text_transform_new (style->text_transform); + attr->start_index = start; + attr->end_index = start + byte_count; + pango_attr_list_insert (attrs, attr); } } diff --git a/gtk/gtktexttag.c b/gtk/gtktexttag.c index cebc1ddbe4..f998ad9a40 100644 --- a/gtk/gtktexttag.c +++ b/gtk/gtktexttag.c @@ -134,6 +134,7 @@ enum { PROP_ALLOW_BREAKS, PROP_SHOW_SPACES, PROP_INSERT_HYPHENS, + PROP_TEXT_TRANSFORM, /* Behavior args */ PROP_ACCUMULATIVE_MARGIN, @@ -176,6 +177,7 @@ enum { PROP_ALLOW_BREAKS_SET, PROP_SHOW_SPACES_SET, PROP_INSERT_HYPHENS_SET, + PROP_TEXT_TRANSFORM_SET, LAST_ARG }; @@ -856,6 +858,22 @@ gtk_text_tag_class_init (GtkTextTagClass *klass) TRUE, GTK_PARAM_READWRITE)); + /** + * GtkTextTag:text-transform: + * + * How to transform the text for display. + * + * Since: 4.6 + */ + g_object_class_install_property (object_class, + PROP_TEXT_TRANSFORM, + g_param_spec_enum ("text-transform", + P_("Text Transform"), + P_("Whether to transform text for display."), + PANGO_TYPE_TEXT_TRANSFORM, + PANGO_TEXT_TRANSFORM_NONE, + GTK_PARAM_READWRITE)); + /** * GtkTextTag:accumulative-margin: * @@ -1034,6 +1052,10 @@ gtk_text_tag_class_init (GtkTextTagClass *klass) ADD_SET_PROP ("insert-hyphens-set", PROP_INSERT_HYPHENS_SET, P_("Insert hyphens set"), P_("Whether this tag affects insertion of hyphens")); + + ADD_SET_PROP ("text-transform-set", PROP_TEXT_TRANSFORM_SET, + P_("Text transform set"), + P_("Whether this tag affects text transformation")); } static void @@ -1783,6 +1805,12 @@ gtk_text_tag_set_property (GObject *object, g_object_notify (object, "insert-hyphens-set"); break; + case PROP_TEXT_TRANSFORM: + priv->text_transform_set = TRUE; + priv->values->text_transform = g_value_get_enum (value); + g_object_notify (object, "text-transform-set"); + break; + case PROP_ACCUMULATIVE_MARGIN: priv->accumulative_margin = g_value_get_boolean (value); g_object_notify (object, "accumulative-margin"); @@ -1947,6 +1975,10 @@ gtk_text_tag_set_property (GObject *object, priv->insert_hyphens_set = g_value_get_boolean (value); break; + case PROP_TEXT_TRANSFORM_SET: + priv->text_transform_set = g_value_get_boolean (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2159,6 +2191,10 @@ gtk_text_tag_get_property (GObject *object, g_value_set_boolean (value, !priv->values->no_hyphens); break; + case PROP_TEXT_TRANSFORM: + g_value_set_enum (value, priv->values->text_transform); + break; + case PROP_ACCUMULATIVE_MARGIN: g_value_set_boolean (value, priv->accumulative_margin); break; @@ -2301,6 +2337,10 @@ gtk_text_tag_get_property (GObject *object, g_value_set_boolean (value, priv->insert_hyphens_set); break; + case PROP_TEXT_TRANSFORM_SET: + g_value_set_boolean (value, priv->text_transform_set); + break; + case PROP_BACKGROUND: case PROP_FOREGROUND: case PROP_PARAGRAPH_BACKGROUND: diff --git a/gtk/gtktexttagprivate.h b/gtk/gtktexttagprivate.h index 2b7b078bda..ee6a5fa591 100644 --- a/gtk/gtktexttagprivate.h +++ b/gtk/gtktexttagprivate.h @@ -87,6 +87,7 @@ struct _GtkTextTagPrivate guint allow_breaks_set : 1; guint show_spaces_set : 1; guint insert_hyphens_set : 1; + guint text_transform_set : 1; /* Whether these margins accumulate or override */ guint accumulative_margin : 1; From bf21df41955c64d2b8671a58c8e5a74aaf7b51c3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 22 Aug 2021 12:17:32 -0400 Subject: [PATCH 3/3] Add an example for text transforms Show some transformed text in the markup demo. --- demos/gtk-demo/markup.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/demos/gtk-demo/markup.txt b/demos/gtk-demo/markup.txt index 9e42a399a6..1b39ebab43 100644 --- a/demos/gtk-demo/markup.txt +++ b/demos/gtk-demo/markup.txt @@ -21,3 +21,5 @@ Shortcuts: MonospaceBoldItalicBigLine height: This is an example of widely spaced text. It was achieved by setting the line-height factor to 1.33. You can set the line-height factor to any value between 0 and 10. Note that the line height affects the spacing between paragraphs as well as between the wrapped lines inside a paragraph. + +Transforms: straße up, up and away