St: support css margin property

It's implemented similar to the padding property, but instead of taking
into account the margin values at drawing time in node-drawing, we set
the clutter actor margins in StWidget when the style is computed.

In the case that a CSS margin is not specified, we don't to set a value
of 0 to the clutter actor margin. In this manner it allows to use
Clutter margin values set in the code. However, the margins that are set
both in the code and in the CSS on the same side, the result is
unpredictable.

We avoid to set the clutter actor margin values to 0 if there's no CSS
margin values defined, so we still allow clutter actors to use margin
set in the code.

https://bugzilla.gnome.org/show_bug.cgi?id=728437
This commit is contained in:
Carlos Soriano 2014-11-11 15:04:46 +01:00
parent 5961f162fa
commit 2935fd4ffe
6 changed files with 234 additions and 0 deletions

View File

@ -25,6 +25,7 @@
#include <gdk/gdk.h>
#include "st-theme-node.h"
#include <libcroco/libcroco.h>
#include "st-types.h"
G_BEGIN_DECLS
@ -58,6 +59,7 @@ struct _StThemeNode {
int border_radius[4];
int outline_width;
guint padding[4];
guint margin[4];
int width;
int height;
@ -90,6 +92,8 @@ struct _StThemeNode {
guint background_position_set : 1;
guint background_repeat : 1;
gboolean margin_set : 4;
guint properties_computed : 1;
guint geometry_computed : 1;
guint background_computed : 1;
@ -121,6 +125,8 @@ struct _StThemeNodeClass {
void _st_theme_node_ensure_background (StThemeNode *node);
void _st_theme_node_ensure_geometry (StThemeNode *node);
void _st_theme_node_apply_margins (StThemeNode *node,
ClutterActor *actor);
G_END_DECLS

View File

@ -1657,6 +1657,100 @@ do_padding_property (StThemeNode *node,
}
}
static void
do_margin_property_term (StThemeNode *node,
CRTerm *term,
gboolean left,
gboolean right,
gboolean top,
gboolean bottom)
{
int value;
if (get_length_from_term_int (node, term, FALSE, &value) != VALUE_FOUND)
return;
if (left)
{
node->margin[ST_SIDE_LEFT] = value;
node->margin_set |= 1 << ST_SIDE_LEFT;
}
if (right)
{
node->margin[ST_SIDE_RIGHT] = value;
node->margin_set |= 1 << ST_SIDE_RIGHT;
}
if (top)
{
node->margin[ST_SIDE_TOP] = value;
node->margin_set |= 1 << ST_SIDE_TOP;
}
if (bottom)
{
node->margin[ST_SIDE_BOTTOM] = value;
node->margin_set |= 1 << ST_SIDE_BOTTOM;
}
}
static void
do_margin_property (StThemeNode *node,
CRDeclaration *decl)
{
const char *property_name = decl->property->stryng->str + 6; /* Skip 'margin' */
if (strcmp (property_name, "") == 0)
{
/* Slight deviation ... if we don't understand some of the terms and understand others,
* then we set the ones we understand and ignore the others instead of ignoring the
* whole thing
*/
if (decl->value == NULL) /* 0 values */
return;
else if (decl->value->next == NULL) /* 1 value */
{
do_margin_property_term (node, decl->value, TRUE, TRUE, TRUE, TRUE); /* left/right/top/bottom */
return;
}
else if (decl->value->next->next == NULL) /* 2 values */
{
do_margin_property_term (node, decl->value, FALSE, FALSE, TRUE, TRUE); /* top/bottom */
do_margin_property_term (node, decl->value->next, TRUE, TRUE, FALSE, FALSE); /* left/right */
}
else if (decl->value->next->next->next == NULL) /* 3 values */
{
do_margin_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); /* top */
do_margin_property_term (node, decl->value->next, TRUE, TRUE, FALSE, FALSE); /* left/right */
do_margin_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE); /* bottom */
}
else if (decl->value->next->next->next->next == NULL) /* 4 values */
{
do_margin_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE); /* top */
do_margin_property_term (node, decl->value->next, FALSE, TRUE, FALSE, FALSE); /* right */
do_margin_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE); /* bottom */
do_margin_property_term (node, decl->value->next->next->next, TRUE, FALSE, FALSE, FALSE); /* left */
}
else
{
g_warning ("Too many values for margin property");
return;
}
}
else
{
if (decl->value == NULL || decl->value->next != NULL)
return;
if (strcmp (property_name, "-left") == 0)
do_margin_property_term (node, decl->value, TRUE, FALSE, FALSE, FALSE);
else if (strcmp (property_name, "-right") == 0)
do_margin_property_term (node, decl->value, FALSE, TRUE, FALSE, FALSE);
else if (strcmp (property_name, "-top") == 0)
do_margin_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE);
else if (strcmp (property_name, "-bottom") == 0)
do_margin_property_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
}
}
static void
do_size_property (StThemeNode *node,
CRDeclaration *decl,
@ -1707,6 +1801,8 @@ _st_theme_node_ensure_geometry (StThemeNode *node)
do_outline_property (node, decl);
else if (g_str_has_prefix (property_name, "padding"))
do_padding_property (node, decl);
else if (g_str_has_prefix (property_name, "margin"))
do_margin_property (node, decl);
else if (strcmp (property_name, "width") == 0)
do_size_property (node, decl, &width);
else if (strcmp (property_name, "height") == 0)
@ -2257,6 +2353,18 @@ st_theme_node_get_padding (StThemeNode *node,
return node->padding[side];
}
double
st_theme_node_get_margin (StThemeNode *node,
StSide side)
{
g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
_st_theme_node_ensure_geometry (node);
return node->margin[side];
}
/**
* st_theme_node_get_transition_duration:
* @node: an #StThemeNode
@ -3029,6 +3137,28 @@ st_theme_node_get_vertical_padding (StThemeNode *node)
return padding;
}
void
_st_theme_node_apply_margins (StThemeNode *node,
ClutterActor *actor)
{
g_return_if_fail (ST_IS_THEME_NODE (node));
_st_theme_node_ensure_geometry (node);
// In the case that a CSS margin is not specified, we don't to set a value
// of 0 to the clutter actor margin. In this manner it allows to use Clutter
// margin values set in the code. However, the margins that are set both in
// the code and in the CSS on the same side, the result is unpredictable.
if (node->margin_set & 1 << ST_SIDE_LEFT)
clutter_actor_set_margin_left (actor, st_theme_node_get_margin(node, ST_SIDE_LEFT));
if (node->margin_set & 1 << ST_SIDE_RIGHT)
clutter_actor_set_margin_right (actor, st_theme_node_get_margin(node, ST_SIDE_RIGHT));
if (node->margin_set & 1 << ST_SIDE_TOP)
clutter_actor_set_margin_top (actor, st_theme_node_get_margin(node, ST_SIDE_TOP));
if (node->margin_set & 1 << ST_SIDE_BOTTOM)
clutter_actor_set_margin_bottom (actor, st_theme_node_get_margin(node, ST_SIDE_BOTTOM));
}
static GetFromTermResult
parse_shadow_property (StThemeNode *node,
CRDeclaration *decl,

View File

@ -208,6 +208,9 @@ double st_theme_node_get_padding (StThemeNode *node,
double st_theme_node_get_horizontal_padding (StThemeNode *node);
double st_theme_node_get_vertical_padding (StThemeNode *node);
double st_theme_node_get_margin (StThemeNode *node,
StSide side);
int st_theme_node_get_width (StThemeNode *node);
int st_theme_node_get_height (StThemeNode *node);
int st_theme_node_get_min_width (StThemeNode *node);

View File

@ -40,6 +40,7 @@
#include "st-texture-cache.h"
#include "st-theme-context.h"
#include "st-theme-node-transition.h"
#include "st-theme-node-private.h"
#include "st-widget-accessible.h"
@ -1540,6 +1541,8 @@ st_widget_recompute_style (StWidget *widget,
return;
}
_st_theme_node_apply_margins (new_theme_node, CLUTTER_ACTOR (widget));
if (!old_theme_node ||
!st_theme_node_geometry_equal (old_theme_node, new_theme_node))
clutter_actor_queue_relayout ((ClutterActor *) widget);

View File

@ -34,6 +34,9 @@ static StThemeNode *group2;
static StThemeNode *text3;
static StThemeNode *text4;
static StThemeNode *group3;
static StThemeNode *group4;
static StThemeNode *group5;
static StThemeNode *group6;
static StThemeNode *cairo_texture;
static gboolean fail;
@ -233,6 +236,19 @@ test_lengths (void)
/* 1in == 72pt == 96px, at 96dpi */
assert_length ("group1", "padding-left", 96.,
st_theme_node_get_padding (group1, ST_SIDE_LEFT));
/* 12pt == 16px at 96dpi */
assert_length ("group1", "margin-top", 16.,
st_theme_node_get_margin (group1, ST_SIDE_TOP));
/* 12px == 12px */
assert_length ("group1", "margin-right", 12.,
st_theme_node_get_margin (group1, ST_SIDE_RIGHT));
/* 2em == 32px (with a 12pt font) */
assert_length ("group1", "margin-bottom", 32.,
st_theme_node_get_margin (group1, ST_SIDE_BOTTOM));
/* 1in == 72pt == 96px, at 96dpi */
assert_length ("group1", "margin-left", 96.,
st_theme_node_get_margin (group1, ST_SIDE_LEFT));
}
static void
@ -282,6 +298,54 @@ test_padding (void)
st_theme_node_get_padding (group2, ST_SIDE_LEFT));
}
static void
test_margin (void)
{
test = "margin";
/* Test that a 4-sided margin property assigns the right margin to
* all sides */
assert_length ("group2", "margin-top", 1.,
st_theme_node_get_margin (group2, ST_SIDE_TOP));
assert_length ("group2", "margin-right", 2.,
st_theme_node_get_margin (group2, ST_SIDE_RIGHT));
assert_length ("group2", "margin-bottom", 3.,
st_theme_node_get_margin (group2, ST_SIDE_BOTTOM));
assert_length ("group2", "margin-left", 4.,
st_theme_node_get_margin (group2, ST_SIDE_LEFT));
/* Test that a 3-sided margin property assigns the right margin to
* all sides */
assert_length ("group4", "margin-top", 1.,
st_theme_node_get_margin (group4, ST_SIDE_TOP));
assert_length ("group4", "margin-right", 2.,
st_theme_node_get_margin (group4, ST_SIDE_RIGHT));
assert_length ("group4", "margin-bottom", 3.,
st_theme_node_get_margin (group4, ST_SIDE_BOTTOM));
assert_length ("group4", "margin-left", 2.,
st_theme_node_get_margin (group4, ST_SIDE_LEFT));
/* Test that a 2-sided margin property assigns the right margin to
* all sides */
assert_length ("group5", "margin-top", 1.,
st_theme_node_get_margin (group5, ST_SIDE_TOP));
assert_length ("group5", "margin-right", 2.,
st_theme_node_get_margin (group5, ST_SIDE_RIGHT));
assert_length ("group5", "margin-bottom", 1.,
st_theme_node_get_margin (group5, ST_SIDE_BOTTOM));
assert_length ("group5", "margin-left", 2.,
st_theme_node_get_margin (group5, ST_SIDE_LEFT));
/* Test that all sides have a margin of 0 when not specified */
assert_length ("group6", "margin-top", 0.,
st_theme_node_get_margin (group6, ST_SIDE_TOP));
assert_length ("group6", "margin-right", 0.,
st_theme_node_get_margin (group6, ST_SIDE_RIGHT));
assert_length ("group6", "margin-bottom", 0.,
st_theme_node_get_margin (group6, ST_SIDE_BOTTOM));
assert_length ("group6", "margin-left", 0.,
st_theme_node_get_margin (group6, ST_SIDE_LEFT));
}
static void
test_border (void)
{
@ -458,6 +522,12 @@ main (int argc, char **argv)
CLUTTER_TYPE_TEXT, "text2", NULL, NULL, NULL);
group2 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_GROUP, "group2", NULL, NULL, NULL);
group4 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_GROUP, "group4", NULL, NULL, NULL);
group5 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_GROUP, "group5", NULL, NULL, NULL);
group6 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_GROUP, "group6", NULL, NULL, NULL);
text3 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_TEXT, "text3", NULL, NULL,
"color: #0000ff; padding-bottom: 12px;");
@ -474,6 +544,7 @@ main (int argc, char **argv)
test_type_inheritance ();
test_adjacent_selector ();
test_padding ();
test_margin ();
test_border ();
test_background ();
test_font ();
@ -484,6 +555,9 @@ main (int argc, char **argv)
g_object_unref (group1);
g_object_unref (group2);
g_object_unref (group3);
g_object_unref (group4);
g_object_unref (group5);
g_object_unref (group6);
g_object_unref (text1);
g_object_unref (text2);
g_object_unref (text3);

View File

@ -7,6 +7,11 @@ stage {
padding-bottom: 2em;
padding-left: 1in;
margin: 12pt;
margin-right: 12px;
margin-bottom: 2em;
margin-left: 1in;
background: #ff0000 url('some-background.png');
}
@ -58,6 +63,7 @@ stage > #text2 {
#group2 {
background-image: url('other-background.png');
padding: 1px 2px 3px 4px;
margin: 1px 2px 3px 4px;
border: 2px solid #000000;
border-bottom: 5px solid #0000ff;
@ -75,3 +81,15 @@ ClutterText:visited, StLabel:visited {
StLabel:boxed {
border: 1px;
}
#group4 {
margin: 1px 2px 3px;
}
#group5 {
margin: 1px 2px;
}
#group6 {
padding: 5px;
}