diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index 96df6f83d..8e5814184 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -457,11 +457,12 @@ get_background_coordinates (StThemeNode *node, static void get_background_position (StThemeNode *self, const ClutterActorBox *allocation, - ClutterActorBox *result) + ClutterActorBox *result, + ClutterActorBox *texture_coords) { gdouble painting_area_width, painting_area_height; gdouble background_image_width, background_image_height; - gdouble x, y; + gdouble x1, y1; gdouble scale_w, scale_h; /* get the background image size */ @@ -484,13 +485,31 @@ get_background_position (StThemeNode *self, get_background_coordinates (self, painting_area_width, painting_area_height, background_image_width, background_image_height, - &x, &y); + &x1, &y1); - /* place the background image */ - result->x1 = x; - result->y1 = y; - result->x2 = result->x1 + background_image_width; - result->y2 = result->y1 + background_image_height; + if (self->background_repeat) + { + gdouble width = allocation->x2 - allocation->x1 + x1; + gdouble height = allocation->y2 - allocation->y1 + y1; + + *result = *allocation; + + /* reference image is at x1, y1 */ + texture_coords->x1 = x1 / background_image_width; + texture_coords->y1 = y1 / background_image_height; + texture_coords->x2 = width / background_image_width; + texture_coords->y2 = height / background_image_height; + } + else + { + result->x1 = x1; + result->y1 = y1; + result->x2 = x1 + background_image_width; + result->y2 = y1 + background_image_height; + + texture_coords->x1 = texture_coords->y1 = 0; + texture_coords->x2 = texture_coords->y2 = 1; + } } /* Use of this function marks code which doesn't support @@ -614,15 +633,21 @@ create_cairo_pattern_of_background_image (StThemeNode *node, &x, &y); cairo_matrix_translate (&matrix, -x, -y); + if (node->background_repeat) + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + /* If it's opaque, fills up the entire allocated * area, then don't bother doing a background fill first */ - if (content != CAIRO_CONTENT_COLOR_ALPHA - && x >= 0 - && -x + background_image_width >= node->alloc_width - && y >= 0 - && -y + background_image_height >= node->alloc_height) - *needs_background_fill = FALSE; + if (content != CAIRO_CONTENT_COLOR_ALPHA) + { + if (node->background_repeat || + (x >= 0 && + y >= 0 && + background_image_width - x >= node->alloc_width && + background_image_height -y >= node->alloc_height)) + *needs_background_fill = FALSE; + } cairo_pattern_set_matrix (pattern, &matrix); @@ -1444,6 +1469,9 @@ st_theme_node_render_resources (StThemeNode *node, node->background_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, background_image); node->background_material = _st_create_texture_material (node->background_texture); + if (node->background_repeat) + cogl_material_set_layer_wrap_mode (node->background_material, 0, COGL_MATERIAL_WRAP_MODE_REPEAT); + if (background_image_shadow_spec) { node->background_shadow_material = _st_create_shadow_material (background_image_shadow_spec, @@ -1464,13 +1492,19 @@ st_theme_node_render_resources (StThemeNode *node, static void paint_material_with_opacity (CoglHandle material, ClutterActorBox *box, + ClutterActorBox *coords, guint8 paint_opacity) { cogl_material_set_color4ub (material, paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_set_source (material); - cogl_rectangle (box->x1, box->y1, box->x2, box->y2); + + if (coords) + cogl_rectangle_with_texture_coords (box->x1, box->y1, box->x2, box->y2, + coords->x1, coords->y1, coords->x2, coords->y2); + else + cogl_rectangle (box->x1, box->y1, box->x2, box->y2); } static void @@ -1947,6 +1981,7 @@ st_theme_node_paint (StThemeNode *node, paint_material_with_opacity (node->prerendered_material, &paint_box, + NULL, paint_opacity); } @@ -1963,17 +1998,18 @@ st_theme_node_paint (StThemeNode *node, if (node->background_texture != COGL_INVALID_HANDLE) { ClutterActorBox background_box; + ClutterActorBox texture_coords; gboolean has_visible_outline; - /* If the background doesn't have a border or opaque background, - * then we let its background image shadows leak out, but other - * wise we clip it. + /* If the node doesn't have an opaque or repeating background or + * a border then we let its background image shadows leak out, + * but otherwise we clip it. */ has_visible_outline = st_theme_node_has_visible_outline (node); - get_background_position (node, &allocation, &background_box); + get_background_position (node, &allocation, &background_box, &texture_coords); - if (has_visible_outline) + if (has_visible_outline || node->background_repeat) cogl_clip_push_rectangle (allocation.x1, allocation.y1, allocation.x2, allocation.y2); /* CSS based drop shadows @@ -1995,9 +2031,12 @@ st_theme_node_paint (StThemeNode *node, &background_box, paint_opacity); - paint_material_with_opacity (node->background_material, &background_box, paint_opacity); + paint_material_with_opacity (node->background_material, + &background_box, + &texture_coords, + paint_opacity); - if (has_visible_outline) + if (has_visible_outline || node->background_repeat) cogl_clip_pop (); } } diff --git a/src/st/st-theme-node-private.h b/src/st/st-theme-node-private.h index f31f1b8b5..9ceee1cc1 100644 --- a/src/st/st-theme-node-private.h +++ b/src/st/st-theme-node-private.h @@ -88,6 +88,7 @@ struct _StThemeNode { CRDeclaration *inline_properties; guint background_position_set : 1; + guint background_repeat : 1; guint properties_computed : 1; guint geometry_computed : 1; diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c index 331b5d8a2..7e966e8af 100644 --- a/src/st/st-theme-node.c +++ b/src/st/st-theme-node.c @@ -1569,6 +1569,7 @@ _st_theme_node_ensure_background (StThemeNode *node) if (node->background_computed) return; + node->background_repeat = FALSE; node->background_computed = TRUE; node->background_color = TRANSPARENT_COLOR; node->background_gradient_type = ST_GRADIENT_NONE; @@ -1658,6 +1659,14 @@ _st_theme_node_ensure_background (StThemeNode *node) else node->background_position_set = TRUE; } + else if (strcmp (property_name, "-repeat") == 0) + { + if (decl->value->type == TERM_IDENT) + { + if (strcmp (decl->value->content.str->stryng->str, "repeat") == 0) + node->background_repeat = TRUE; + } + } else if (strcmp (property_name, "-size") == 0) { if (decl->value->type == TERM_IDENT) diff --git a/tests/interactive/background-repeat.js b/tests/interactive/background-repeat.js new file mode 100644 index 000000000..3e1c869f6 --- /dev/null +++ b/tests/interactive/background-repeat.js @@ -0,0 +1,29 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +const Clutter = imports.gi.Clutter; +const St = imports.gi.St; + +const UI = imports.testcommon.ui; + +function test() { + let stage = new Clutter.Stage({ width: 640, height: 480 }); + UI.init(stage); + + let vbox = new St.BoxLayout({ width: stage.width, + height: stage.height, + style: 'background: #ffee88;' }); + stage.add_actor(vbox); + + let scroll = new St.ScrollView(); + vbox.add(scroll, { expand: true }); + + let box = new St.BoxLayout({ vertical: true }); + scroll.add_actor(box); + + let contents = new St.Widget({ width: 1000, height: 1000, + style_class: 'background-image background-repeat' }); + box.add_actor(contents); + + UI.main(stage); +} +test(); diff --git a/tests/testcommon/test.css b/tests/testcommon/test.css index eeeb8de97..6cbadfbb7 100644 --- a/tests/testcommon/test.css +++ b/tests/testcommon/test.css @@ -67,6 +67,10 @@ stage { background-color: white; } +.background-repeat { + background-repeat: repeat; +} + .push-button { background: #eeddbb; border: 1px solid black;