St: Implement background-size CSS property

Implement the background-size CSS property, specified by the CSS
Backgrounds and Borders Module Level 3, including the keywords
"contain", "cover", and fixed-size backgrounds.

https://bugzilla.gnome.org/show_bug.cgi?id=633462
This commit is contained in:
Quentin Glidic 2011-12-23 18:59:20 +01:00 committed by Jasper St. Pierre
parent bea4faacd4
commit 25948f214e
10 changed files with 360 additions and 133 deletions

View File

@ -60,6 +60,7 @@ StScrollBar StBin#trough {
StScrollBar StButton#vhandle
{
background-image: url("scroll-vhandle.svg");
background-size: contain;
background-color: #252525;
border: 1px solid #080808;
border-radius: 8px;
@ -68,6 +69,7 @@ StScrollBar StButton#vhandle
StScrollBar StButton#hhandle
{
background-image: url("scroll-hhandle.svg");
background-size: contain;
background-color: #252525;
border: 1px solid #080808;
border-radius: 8px;
@ -224,16 +226,20 @@ StTooltip StLabel {
.toggle-switch-us {
background-image: url("toggle-off-us.svg");
background-size: contain;
}
.toggle-switch-us:checked {
background-image: url("toggle-on-us.svg");
background-size: contain;
}
.toggle-switch-intl {
background-image: url("toggle-off-intl.svg");
background-size: contain;
}
.toggle-switch-intl:checked {
background-image: url("toggle-on-intl.svg");
background-size: contain;
}
.nm-menu-item-icons {
@ -356,6 +362,7 @@ StTooltip StLabel {
.panel-button:focus {
border-image: url("panel-button-border.svg") 10 10 0 2;
background-image: url("panel-button-highlight-wide.svg");
background-size: contain;
color: white;
text-shadow: black 0px 2px 2px;
}
@ -364,6 +371,7 @@ StTooltip StLabel {
.panel-status-button:checked,
.panel-status-button:focus {
background-image: url("panel-button-highlight-narrow.svg");
background-size: contain;
}
.panel-button:active > .system-status-icon,
@ -477,6 +485,7 @@ StTooltip StLabel {
.window-close {
background-image: url("close-window.svg");
background-size: 34px;
height: 34px;
width: 34px;
-shell-close-overlap: 20px;
@ -511,6 +520,7 @@ StTooltip StLabel {
.placeholder {
background-image: url("dash-placeholder.svg");
background-size: contain;
height: 24px;
}
@ -697,11 +707,13 @@ StTooltip StLabel {
.app-filter:selected {
color: #ffffff;
background-image: url("filter-selected-ltr.svg");
background-size: contain;
background-position: 190px 10px;
}
.app-filter:selected:rtl {
background-image: url("filter-selected-rtl.svg");
background-size: contain;
background-position: 10px 10px;
}
@ -782,6 +794,7 @@ StTooltip StLabel {
.app-well-app.running > .overview-icon {
text-shadow: black 0px 2px 2px;
background-image: url("running-indicator.svg");
background-size: contain;
}
.contact:selected,
@ -1443,6 +1456,7 @@ StTooltip StLabel {
.summary-source-button:selected .summary-source {
background-image: url("panel-button-highlight-narrow.svg");
background-size: contain;
border-image: url("source-button-border.svg") 10 10 0 1;
}
@ -1453,6 +1467,7 @@ StTooltip StLabel {
.summary-source-button:expanded:selected {
background-image: url("panel-button-highlight-wide.svg");
background-size: contain;
border-image: url("source-button-border.svg") 10 10 0 1;
}
@ -1566,6 +1581,7 @@ StTooltip StLabel {
width: 52px;
height: 52px;
background-image: url("corner-ripple-ltr.png");
background-size: contain;
}
.ripple-box:rtl {
@ -1607,6 +1623,7 @@ StTooltip StLabel {
border: 0px;
background: rgba(255,255,255,0.5);
background-image: url("ws-switch-arrow-up.svg");
background-size: contain;
border-radius: 8px;
}
@ -1615,6 +1632,7 @@ StTooltip StLabel {
border: 0px;
background: rgba(255,255,255,0.5);
background-image: url("ws-switch-arrow-down.svg");
background-size: contain;
border-radius: 8px;
}

View File

@ -5,6 +5,7 @@
* Copyright 2009, 2010 Red Hat, Inc.
* Copyright 2009, 2010 Florian Müllner
* Copyright 2010 Intel Corporation.
* Copyright 2011 Quentin "Sardem FF7" Glidic
*
* Contains code derived from:
* rectangle.c: Rounded rectangle.
@ -390,69 +391,110 @@ st_theme_node_lookup_corner (StThemeNode *node,
return material;
}
static void
get_background_scale (StThemeNode *node,
gdouble painting_area_width,
gdouble painting_area_height,
gdouble background_image_width,
gdouble background_image_height,
gdouble *scale_w,
gdouble *scale_h)
{
*scale_w = -1.0;
*scale_h = -1.0;
switch (node->background_size)
{
case ST_BACKGROUND_SIZE_AUTO:
*scale_w = 1.0;
break;
case ST_BACKGROUND_SIZE_CONTAIN:
if (background_image_width > background_image_height)
*scale_w = painting_area_width / background_image_width;
else
*scale_w = painting_area_height / background_image_height;
break;
case ST_BACKGROUND_SIZE_COVER:
if (background_image_width < background_image_height)
*scale_w = painting_area_width / background_image_width;
else
*scale_w = painting_area_height / background_image_height;
break;
case ST_BACKGROUND_SIZE_FIXED:
if (node->background_size_w > -1)
{
*scale_w = node->background_size_w / background_image_width;
if (node->background_size_h > -1)
*scale_h = node->background_size_h / background_image_height;
}
else if (node->background_size_h > -1)
*scale_w = node->background_size_h / background_image_height;
break;
}
if (*scale_h < 0.0)
*scale_h = *scale_w;
}
static void
get_background_coordinates (StThemeNode *node,
gdouble painting_area_width,
gdouble painting_area_height,
gdouble background_image_width,
gdouble background_image_height,
gdouble *x,
gdouble *y)
{
/* honor the specified position if any */
if (node->background_position_set)
{
*x = node->background_position_x;
*y = node->background_position_y;
}
else
{
/* center the background on the widget */
*x = (painting_area_width / 2.0) - (background_image_width / 2.0);
*y = (painting_area_height / 2.0) - (background_image_height / 2.0);
}
}
static void
get_background_position (StThemeNode *self,
const ClutterActorBox *allocation,
ClutterActorBox *result)
{
gfloat w, h;
gdouble painting_area_width, painting_area_height;
gdouble background_image_width, background_image_height;
gdouble x, y;
gdouble scale_w, scale_h;
result->x1 = result->y1 = 0;
result->x2 = allocation->x2 - allocation->x1;
result->y2 = allocation->y2 - allocation->y1;
/* get the background image size */
background_image_width = allocation->x2 - allocation->x1;
background_image_height = allocation->y2 - allocation->y1;
w = cogl_texture_get_width (self->background_texture);
h = cogl_texture_get_height (self->background_texture);
/* get the painting area size */
painting_area_width = cogl_texture_get_width (self->background_texture);
painting_area_height = cogl_texture_get_height (self->background_texture);
/* scale the background into the allocated bounds, when not being absolutely positioned */
if ((w > result->x2 || h > result->y2) && !self->background_position_set)
{
gint new_h, new_w, offset;
gint box_w, box_h;
/* scale if requested */
get_background_scale (self,
painting_area_width, painting_area_height,
background_image_width, background_image_height,
&scale_w, &scale_h);
background_image_width *= scale_w;
background_image_height *= scale_h;
box_w = (int) result->x2;
box_h = (int) result->y2;
/* get coordinates */
get_background_coordinates (self,
painting_area_width, painting_area_height,
background_image_width, background_image_height,
&x, &y);
/* scale to fit */
new_h = (int)((h / w) * ((gfloat) box_w));
new_w = (int)((w / h) * ((gfloat) box_h));
if (new_h > box_h)
{
/* center for new width */
offset = ((box_w) - new_w) * 0.5;
result->x1 = offset;
result->x2 = offset + new_w;
result->y2 = box_h;
}
else
{
/* center for new height */
offset = ((box_h) - new_h) * 0.5;
result->y1 = offset;
result->y2 = offset + new_h;
result->x2 = box_w;
}
}
else
{
/* honor the specified position if any */
if (self->background_position_set)
{
result->x1 = self->background_position_x;
result->y1 = self->background_position_y;
}
else
{
/* center the background on the widget */
result->x1 = (int)(((allocation->x2 - allocation->x1) / 2) - (w / 2));
result->y1 = (int)(((allocation->y2 - allocation->y1) / 2) - (h / 2));
}
result->x2 = result->x1 + w;
result->y2 = result->y1 + h;
}
/* place the background image */
result->x1 = x;
result->y1 = y;
result->x2 = result->x1 + background_image_width;
result->y2 = result->y1 + background_image_height;
}
/* Use of this function marks code which doesn't support
@ -533,12 +575,13 @@ create_cairo_pattern_of_background_image (StThemeNode *node,
cairo_content_t content;
cairo_matrix_t matrix;
const char *file;
double height_ratio, width_ratio;
int file_width;
int file_height;
StTextureCache *texture_cache;
gdouble background_image_width, background_image_height;
gdouble x, y;
gdouble scale_w, scale_h;
file = st_theme_node_get_background_image (node);
texture_cache = st_texture_cache_get_default ();
@ -553,90 +596,39 @@ create_cairo_pattern_of_background_image (StThemeNode *node,
content = cairo_surface_get_content (surface);
pattern = cairo_pattern_create_for_surface (surface);
file_width = cairo_image_surface_get_width (surface);
file_height = cairo_image_surface_get_height (surface);
height_ratio = file_height / node->alloc_height;
width_ratio = file_width / node->alloc_width;
background_image_width = cairo_image_surface_get_width (surface);
background_image_height = cairo_image_surface_get_height (surface);
*needs_background_fill = TRUE;
if ((file_width > node->alloc_width || file_height > node->alloc_height)
&& !node->background_position_set)
{
double scale_factor;
double x_offset, y_offset;
if (width_ratio > height_ratio)
{
double scaled_height;
cairo_matrix_init_identity (&matrix);
/* center vertically */
get_background_scale (node,
node->alloc_width, node->alloc_height,
background_image_width, background_image_height,
&scale_w, &scale_h);
if ((scale_w != 1) || (scale_h != 1))
cairo_matrix_scale (&matrix, 1.0/scale_w, 1.0/scale_h);
background_image_width *= scale_w;
background_image_height *= scale_h;
scale_factor = width_ratio;
scaled_height = file_height / scale_factor;
get_background_coordinates (node,
node->alloc_width, node->alloc_height,
background_image_width, background_image_height,
&x, &y);
cairo_matrix_translate (&matrix, -x, -y);
x_offset = 0.;
y_offset = - (node->alloc_height / 2. - scaled_height / 2.);
}
else
{
double scaled_width;
/* 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;
/* center horizontally */
scale_factor = height_ratio;
scaled_width = file_width / scale_factor;
y_offset = 0.;
x_offset = - (node->alloc_width / 2. - scaled_width / 2.);
}
cairo_matrix_init_scale (&matrix, scale_factor, scale_factor);
cairo_matrix_translate (&matrix, x_offset, y_offset);
cairo_pattern_set_matrix (pattern, &matrix);
/* If it's opaque, and when scaled, fills up the entire allocated
* area, then don't bother doing a background fill first
*/
if (content != CAIRO_CONTENT_COLOR_ALPHA && width_ratio == height_ratio)
*needs_background_fill = FALSE;
}
else
{
double x_offset, y_offset;
if (node->background_position_set)
{
x_offset = -node->background_position_x;
y_offset = -node->background_position_y;
}
else
{
if (node->alloc_width > file_width)
x_offset = - (node->alloc_width / 2.0 - file_width / 2.0);
else
x_offset = - (file_width / 2.0 - node->alloc_width / 2.0);
if (node->alloc_height > file_height)
y_offset = - (node->alloc_height / 2.0 - file_height / 2.0);
else
y_offset = - (file_height / 2.0 - node->alloc_height / 2.0);
}
/* If it's opaque, and when translated, fills up the entire allocated
* area, then don't bother doing a background fill first
*/
if (content != CAIRO_CONTENT_COLOR_ALPHA
&& -x_offset <= 0
&& -x_offset + file_width >= node->alloc_width
&& -y_offset <= 0
&& -y_offset + file_height >= node->alloc_height)
*needs_background_fill = FALSE;
cairo_matrix_init_translate (&matrix, x_offset, y_offset);
cairo_pattern_set_matrix (pattern, &matrix);
}
cairo_pattern_set_matrix (pattern, &matrix);
return pattern;
}

View File

@ -3,6 +3,7 @@
* st-theme-node-private.h: private structures and functions for StThemeNode
*
* Copyright 2009, 2010 Red Hat, Inc.
* Copyright 2011 Quentin "Sardem FF7" Glidic
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@ -24,6 +25,7 @@
#include <gdk/gdk.h>
#include "st-theme-node.h"
#include "st-types.h"
G_BEGIN_DECLS
@ -44,6 +46,9 @@ struct _StThemeNode {
int background_position_x;
int background_position_y;
gboolean background_position_set : 1;
StBackgroundSize background_size;
gint background_size_w;
gint background_size_h;
ClutterColor foreground_color;
ClutterColor border_color[4];

View File

@ -7,6 +7,7 @@
* Copyright 2009, 2010 Florian Müllner
* Copyright 2010 Adel Gadllah
* Copyright 2010 Giovanni Campagna
* Copyright 2011 Quentin "Sardem FF7" Glidic
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@ -1579,6 +1580,7 @@ _st_theme_node_ensure_background (StThemeNode *node)
node->background_color = TRANSPARENT_COLOR;
node->background_gradient_type = ST_GRADIENT_NONE;
node->background_position_set = FALSE;
node->background_size = ST_BACKGROUND_SIZE_AUTO;
ensure_properties (node);
@ -1606,6 +1608,7 @@ _st_theme_node_ensure_background (StThemeNode *node)
g_free (node->background_image);
node->background_image = NULL;
node->background_position_set = FALSE;
node->background_size = ST_BACKGROUND_SIZE_AUTO;
for (term = decl->value; term; term = term->next)
{
@ -1662,6 +1665,44 @@ _st_theme_node_ensure_background (StThemeNode *node)
else
node->background_position_set = TRUE;
}
else if (strcmp (property_name, "-size") == 0)
{
if (decl->value->type == TERM_IDENT)
{
if (strcmp (decl->value->content.str->stryng->str, "contain") == 0)
node->background_size = ST_BACKGROUND_SIZE_CONTAIN;
else if (strcmp (decl->value->content.str->stryng->str, "cover") == 0)
node->background_size = ST_BACKGROUND_SIZE_COVER;
else if ((strcmp (decl->value->content.str->stryng->str, "auto") == 0) && (decl->value->next) && (decl->value->next->type == TERM_NUMBER))
{
GetFromTermResult result = get_length_from_term_int (node, decl->value->next, FALSE, &node->background_size_h);
node->background_size_w = -1;
node->background_size = (result == VALUE_FOUND) ? ST_BACKGROUND_SIZE_FIXED : ST_BACKGROUND_SIZE_AUTO;
}
else
node->background_size = ST_BACKGROUND_SIZE_AUTO;
}
else if (decl->value->type == TERM_NUMBER)
{
GetFromTermResult result = get_length_from_term_int (node, decl->value, FALSE, &node->background_size_w);
if (result == VALUE_NOT_FOUND)
continue;
node->background_size = ST_BACKGROUND_SIZE_FIXED;
if ((decl->value->next) && (decl->value->next->type == TERM_NUMBER))
{
result = get_length_from_term_int (node, decl->value->next, FALSE, &node->background_size_h);
if (result == VALUE_FOUND)
continue;
}
node->background_size_h = -1;
}
else
node->background_size = ST_BACKGROUND_SIZE_AUTO;
}
else if (strcmp (property_name, "-color") == 0)
{
GetFromTermResult result;

View File

@ -49,6 +49,13 @@ typedef enum {
ST_ICON_DOCUMENT
} StIconType;
typedef enum {
ST_BACKGROUND_SIZE_AUTO,
ST_BACKGROUND_SIZE_CONTAIN,
ST_BACKGROUND_SIZE_COVER,
ST_BACKGROUND_SIZE_FIXED
} StBackgroundSize;
G_END_DECLS
#endif /* __ST_TYPES_H__ */

View File

@ -0,0 +1,89 @@
// -*- 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;
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = 1024;
stage.height = 768;
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 vbox = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(vbox);
let tbox = null;
function addTestCase(image, size, backgroundSize) {
let obin = new St.Bin({ style: 'border: 3px solid green;' });
tbox.add(obin);
let bin = new St.Bin({ style_class: 'background-image-' + image,
width: size.width,
height: size.height,
style: 'border: 1px solid transparent;'
+ 'background-size: ' + backgroundSize + ';',
x_fill: true,
y_fill: true
});
obin.set_child(bin);
bin.set_child(new St.Label({ text: backgroundSize,
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
}
function addTestLine(image, size, backgroundSizes) {
vbox.add(new St.Label({ text: image + '.svg / ' + size.width + '×' + size.height,
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
tbox = new St.BoxLayout({ style: 'spacing: 20px;' });
vbox.add(tbox);
if (backgroundSizes.length == 2)
addTestCase(image, size, "auto");
for each (let s in backgroundSizes)
addTestCase(image, size, s);
}
let size1 = { width: 200, height: 200 }
let size2 = { width: 250, height: 250 }
let size3 = { width: 100, height: 100 }
// fixed size
addTestLine('200-200', size1, ["200px 200px", "100px 100px", "100px 200px"]);
// same size
addTestLine('200-200', size1, ["contain", "cover"]);
// smaller
addTestLine('200-200', size2, ["contain", "cover"]);
// larger
addTestLine('200-200', size3, ["contain", "cover"]);
addTestLine('200-100', size1, ["contain", "cover"]);
addTestLine('200-100', size2, ["contain", "cover"]);
addTestLine('200-100', size3, ["contain", "cover"]);
addTestLine('100-200', size1, ["contain", "cover"]);
addTestLine('100-200', size2, ["contain", "cover"]);
addTestLine('100-200', size3, ["contain", "cover"]);
stage.show();
Clutter.main();
stage.destroy();

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 200" width="100" height="200">
<path
d="
M 2,2 h 96 v 196 h -96 v -196
M 8,8 h 84 v 184 h -84 v -184
"
fill="white"
stroke="blue"
stroke-width="2"
stroke-linecap="square"
/>
<path
d="
M 10,10 h 20 v 20 h -20 v -20
"
fill="green"
/>
</svg>

After

Width:  |  Height:  |  Size: 518 B

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 100" width="200" height="100">
<path
d="
M 2,2 h 196 v 96 h -196 v -96
M 8,8 h 184 v 84 h -184 v -84
"
fill="white"
stroke="blue"
stroke-width="2"
stroke-linecap="square"
/>
<path
d="
M 10,10 h 20 v 20 h -20 v -20
"
fill="green"
/>
</svg>

After

Width:  |  Height:  |  Size: 518 B

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 200" width="200" height="200">
<path
d="
M 2,2 h 196 v 196 h -196 v -196
M 8,8 h 184 v 184 h -184 v -184
"
fill="white"
stroke="blue"
stroke-width="2"
stroke-linecap="square"
/>
<path
d="
M 10,10 h 20 v 20 h -20 v -20
"
fill="green"
/>
</svg>

After

Width:  |  Height:  |  Size: 522 B

View File

@ -37,6 +37,18 @@ stage {
border-image: url('border-image.png') 16;
}
.background-image-200-200 {
background-image: url('200-200.svg');
}
.background-image-100-200 {
background-image: url('100-200.svg');
}
.background-image-200-100 {
background-image: url('200-100.svg');
}
.background-gradient {
background-gradient-start: rgba(127, 255, 127, .6);
background-gradient-end: rgba(127, 127, 255, .6);