diff --git a/src/st/st-theme-node-private.h b/src/st/st-theme-node-private.h index 1d7e9985f..7a2a383c0 100644 --- a/src/st/st-theme-node-private.h +++ b/src/st/st-theme-node-private.h @@ -25,6 +25,7 @@ #include #include "st-theme-node.h" +#include #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 diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c index 661789e7e..c6c6e8917 100644 --- a/src/st/st-theme-node.c +++ b/src/st/st-theme-node.c @@ -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, diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h index dfe206ec0..a193f5ae7 100644 --- a/src/st/st-theme-node.h +++ b/src/st/st-theme-node.h @@ -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); diff --git a/src/st/st-widget.c b/src/st/st-widget.c index c62b598c6..de0ed958a 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -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); diff --git a/src/st/test-theme.c b/src/st/test-theme.c index 5fa5de3cb..6c95c61d5 100644 --- a/src/st/test-theme.c +++ b/src/st/test-theme.c @@ -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); diff --git a/src/st/test-theme.css b/src/st/test-theme.css index 77ae34c19..0c0af1b06 100644 --- a/src/st/test-theme.css +++ b/src/st/test-theme.css @@ -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; +}