diff --git a/gtk/gtkcssstylepropertyimpl.c b/gtk/gtkcssstylepropertyimpl.c index f6d4b491f3..7dd1cf40b8 100644 --- a/gtk/gtkcssstylepropertyimpl.c +++ b/gtk/gtkcssstylepropertyimpl.c @@ -682,6 +682,181 @@ background_size_compute (GtkCssStyleProperty *property, return _gtk_css_value_ref (specified); } +static gboolean +background_position_parse (GtkCssStyleProperty *property, + GValue *value, + GtkCssParser *parser, + GFile *base) +{ + static const struct { + const char *name; + guint percentage; + gboolean horizontal; + gboolean vertical; + } names[] = { + { "left", 0, TRUE, FALSE }, + { "right", 100, TRUE, FALSE }, + { "center", 50, TRUE, TRUE }, + { "top", 0, FALSE, TRUE }, + { "bottom", 100, FALSE, TRUE }, + { NULL , 0, TRUE, FALSE }, /* used for numbers */ + { NULL , 50, TRUE, TRUE } /* used for no value */ + }; + GtkCssBackgroundPosition pos; + GtkCssNumber *missing; + guint first, second; + + for (first = 0; names[first].name != NULL; first++) + { + if (_gtk_css_parser_try (parser, names[first].name, TRUE)) + { + if (names[first].horizontal) + { + _gtk_css_number_init (&pos.x, names[first].percentage, GTK_CSS_PERCENT); + missing = &pos.y; + } + else + { + _gtk_css_number_init (&pos.y, names[first].percentage, GTK_CSS_PERCENT); + missing = &pos.x; + } + break; + } + } + if (names[first].name == NULL) + { + missing = &pos.y; + if (!_gtk_css_parser_read_number (parser, + &pos.x, + GTK_CSS_PARSE_PERCENT + | GTK_CSS_PARSE_LENGTH)) + return FALSE; + } + + for (second = 0; names[second].name != NULL; second++) + { + if (_gtk_css_parser_try (parser, names[second].name, TRUE)) + { + _gtk_css_number_init (missing, names[second].percentage, GTK_CSS_PERCENT); + break; + } + } + + if (names[second].name == NULL) + { + if (_gtk_css_parser_has_number (parser)) + { + if (missing != &pos.y) + { + _gtk_css_parser_error (parser, "Invalid combination of values"); + return FALSE; + } + if (!_gtk_css_parser_read_number (parser, + missing, + GTK_CSS_PARSE_PERCENT + | GTK_CSS_PARSE_LENGTH)) + return FALSE; + } + else + { + second++; + _gtk_css_number_init (missing, 50, GTK_CSS_PERCENT); + } + } + else + { + if ((names[first].horizontal && !names[second].vertical) || + (!names[first].horizontal && !names[second].horizontal)) + { + _gtk_css_parser_error (parser, "Invalid combination of values"); + return FALSE; + } + } + + g_value_set_boxed (value, &pos); + return TRUE; +} + +static void +background_position_print (GtkCssStyleProperty *property, + const GValue *value, + GString *string) +{ + GtkCssBackgroundPosition *pos = g_value_get_boxed (value); + static const GtkCssNumber center = GTK_CSS_NUMBER_INIT (50, GTK_CSS_PERCENT); + static const struct { + const char *x_name; + const char *y_name; + GtkCssNumber number; + } values[] = { + { "left", "top", GTK_CSS_NUMBER_INIT (0, GTK_CSS_PERCENT) }, + { "right", "bottom", GTK_CSS_NUMBER_INIT (100, GTK_CSS_PERCENT) } + }; + guint i; + + if (_gtk_css_number_equal (&pos->x, ¢er)) + { + if (_gtk_css_number_equal (&pos->y, ¢er)) + { + g_string_append (string, "center"); + return; + } + } + else + { + for (i = 0; i < G_N_ELEMENTS (values); i++) + { + if (_gtk_css_number_equal (&pos->x, &values[i].number)) + { + g_string_append (string, values[i].x_name); + break; + } + } + if (i == G_N_ELEMENTS (values)) + _gtk_css_number_print (&pos->x, string); + + if (_gtk_css_number_equal (&pos->y, ¢er)) + return; + + g_string_append_c (string, ' '); + } + + for (i = 0; i < G_N_ELEMENTS (values); i++) + { + if (_gtk_css_number_equal (&pos->y, &values[i].number)) + { + g_string_append (string, values[i].y_name); + break; + } + } + if (i == G_N_ELEMENTS (values)) + { + if (_gtk_css_number_equal (&pos->x, ¢er)) + g_string_append (string, "center "); + _gtk_css_number_print (&pos->y, string); + } +} + +static GtkCssValue * +background_position_compute (GtkCssStyleProperty *property, + GtkStyleContext *context, + GtkCssValue *specified) +{ + GtkCssBackgroundPosition *spos = _gtk_css_value_get_background_position (specified); + GtkCssBackgroundPosition cpos; + gboolean changed; + + changed = _gtk_css_number_compute (&cpos.x, + &spos->x, + context); + changed |= _gtk_css_number_compute (&cpos.y, + &spos->y, + context); + if (changed) + return _gtk_css_value_new_from_background_position (&cpos); + return _gtk_css_value_ref (specified); +} + /*** REGISTRATION ***/ static GtkSymbolicColor * @@ -702,6 +877,7 @@ _gtk_css_style_property_init_properties (void) GtkCssNumber number; GtkSymbolicColor *symbolic; GtkCssBackgroundSize default_background_size = { GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX), GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX), FALSE, FALSE }; + GtkCssBackgroundPosition default_background_position = { GTK_CSS_NUMBER_INIT (0, GTK_CSS_PERCENT), GTK_CSS_NUMBER_INIT (0, GTK_CSS_PERCENT)}; GtkCssBorderCornerRadius no_corner_radius = { GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX), GTK_CSS_NUMBER_INIT (0, GTK_CSS_PX) }; GtkBorder border_of_ones = { 1, 1, 1, 1 }; GtkCssBorderImageRepeat border_image_repeat = { GTK_CSS_REPEAT_STYLE_STRETCH, GTK_CSS_REPEAT_STYLE_STRETCH }; @@ -1054,6 +1230,15 @@ _gtk_css_style_property_init_properties (void) background_size_print, background_size_compute, &default_background_size); + gtk_css_style_property_register ("background-position", + GTK_TYPE_CSS_BACKGROUND_POSITION, + GTK_TYPE_CSS_BACKGROUND_POSITION, + G_TYPE_NONE, + 0, + background_position_parse, + background_position_print, + background_position_compute, + &default_background_position); gtk_css_style_property_register ("border-top-color", GTK_TYPE_SYMBOLIC_COLOR, diff --git a/gtk/gtkthemingbackground.c b/gtk/gtkthemingbackground.c index 161cc5a8ee..89d0acc652 100644 --- a/gtk/gtkthemingbackground.c +++ b/gtk/gtkthemingbackground.c @@ -162,10 +162,12 @@ _gtk_theming_background_paint (GtkThemingBackground *bg, { GtkCssBackgroundRepeat hrepeat, vrepeat; GtkCssBackgroundSize *size; + GtkCssBackgroundPosition *pos; double image_width, image_height; double width, height; size = _gtk_css_value_get_background_size (_gtk_style_context_peek_property (bg->context, "background-size")); + pos = _gtk_css_value_get_background_position (_gtk_style_context_peek_property (bg->context, "background-position")); gtk_style_context_get (bg->context, bg->flags, "background-repeat", &hrepeat, NULL); @@ -199,6 +201,9 @@ _gtk_theming_background_paint (GtkThemingBackground *bg, if (hrepeat == GTK_CSS_BACKGROUND_NO_REPEAT && vrepeat == GTK_CSS_BACKGROUND_NO_REPEAT) { + cairo_translate (cr, + _gtk_css_number_get (&pos->x, bg->image_rect.width - image_width), + _gtk_css_number_get (&pos->y, bg->image_rect.height - image_height)); /* shortcut for normal case */ _gtk_css_image_draw (bg->image, cr, image_width, image_height); } @@ -276,8 +281,8 @@ _gtk_theming_background_paint (GtkThemingBackground *bg, cairo_destroy (cr2); cairo_set_source_surface (cr, surface, - /* background-position goes here */ - 0, 0); + _gtk_css_number_get (&pos->x, bg->image_rect.width - image_width), + _gtk_css_number_get (&pos->y, bg->image_rect.height - image_height)); cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT); cairo_surface_destroy (surface);