diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index f55174f5a..46d9de72f 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -1054,6 +1054,54 @@ st_theme_node_paint_sliced_border_image (StThemeNode *node, cogl_handle_unref (material); } +static void +st_theme_node_paint_outline (StThemeNode *node, + const ClutterActorBox *box, + guint8 paint_opacity) + +{ + float width, height; + int outline_width; + ClutterColor outline_color, effective_outline; + + width = box->x2 - box->x1; + height = box->y2 - box->y1; + + outline_width = st_theme_node_get_outline_width (node); + if (outline_width == 0) + return; + + st_theme_node_get_outline_color (node, &outline_color); + over (&outline_color, &node->background_color, &effective_outline); + + cogl_set_source_color4ub (effective_outline.red, + effective_outline.green, + effective_outline.blue, + paint_opacity * effective_outline.alpha / 255); + + /* The outline is drawn just outside the border, which means just + * outside the allocation box. This means that in some situations + * involving clip_to_allocation or the screen edges, you won't be + * able to see the outline. In practice, it works well enough. + */ + + /* NORTH */ + cogl_rectangle (-outline_width, -outline_width, + width + outline_width, 0); + + /* EAST */ + cogl_rectangle (width, 0, + width + outline_width, height); + + /* SOUTH */ + cogl_rectangle (-outline_width, height, + width + outline_width, height + outline_width); + + /* WEST */ + cogl_rectangle (-outline_width, 0, + 0, height); +} + void st_theme_node_paint (StThemeNode *node, const ClutterActorBox *box, @@ -1121,6 +1169,8 @@ st_theme_node_paint (StThemeNode *node, else st_theme_node_paint_borders (node, box, paint_opacity); + st_theme_node_paint_outline (node, box, paint_opacity); + if (node->background_texture != COGL_INVALID_HANDLE) { ClutterActorBox background_box; diff --git a/src/st/st-theme-node-private.h b/src/st/st-theme-node-private.h index 1f9d1c304..4641df32e 100644 --- a/src/st/st-theme-node-private.h +++ b/src/st/st-theme-node-private.h @@ -24,9 +24,11 @@ struct _StThemeNode { ClutterColor foreground_color; ClutterColor border_color[4]; + ClutterColor outline_color; int border_width[4]; int border_radius[4]; + int outline_width; guint padding[4]; int width; diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c index d097da535..639c5665c 100644 --- a/src/st/st-theme-node.c +++ b/src/st/st-theme-node.c @@ -943,7 +943,7 @@ do_border_property (StThemeNode *node, if (strcmp (property_name, "") == 0) { - /* Set value for width/color/node in any order */ + /* Set value for width/color/style in any order */ CRTerm *term; for (term = decl->value; term; term = term->next) @@ -965,7 +965,6 @@ do_border_property (StThemeNode *node, } else if (strcmp (ident, "dotted") == 0 || strcmp (ident, "dashed") == 0 || - strcmp (ident, "solid") == 0 || strcmp (ident, "double") == 0 || strcmp (ident, "groove") == 0 || strcmp (ident, "ridge") == 0 || @@ -1036,6 +1035,97 @@ do_border_property (StThemeNode *node, } } +static void +do_outline_property (StThemeNode *node, + CRDeclaration *decl) +{ + const char *property_name = decl->property->stryng->str + 7; /* Skip 'outline' */ + ClutterColor color; + gboolean color_set = FALSE; + int width; + gboolean width_set = FALSE; + + if (strcmp (property_name, "") == 0) + { + /* Set value for width/color/style in any order */ + CRTerm *term; + + for (term = decl->value; term; term = term->next) + { + GetFromTermResult result; + + if (term->type == TERM_IDENT) + { + const char *ident = term->content.str->stryng->str; + if (strcmp (ident, "none") == 0 || strcmp (ident, "hidden") == 0) + { + width = 0.; + continue; + } + else if (strcmp (ident, "solid") == 0) + { + /* The only thing we support */ + continue; + } + else if (strcmp (ident, "dotted") == 0 || + strcmp (ident, "dashed") == 0 || + strcmp (ident, "double") == 0 || + strcmp (ident, "groove") == 0 || + strcmp (ident, "ridge") == 0 || + strcmp (ident, "inset") == 0 || + strcmp (ident, "outset") == 0) + { + /* Treat the same as solid */ + continue; + } + + /* Presumably a color, fall through */ + } + + if (term->type == TERM_NUMBER) + { + result = get_length_from_term_int (node, term, FALSE, &width); + if (result != VALUE_NOT_FOUND) + { + width_set = result == VALUE_FOUND; + continue; + } + } + + result = get_color_from_term (node, term, &color); + if (result != VALUE_NOT_FOUND) + { + color_set = result == VALUE_FOUND; + continue; + } + } + + } + else if (strcmp (property_name, "-color") == 0) + { + if (decl->value == NULL || decl->value->next != NULL) + return; + + if (get_color_from_term (node, decl->value, &color) == VALUE_FOUND) + /* Ignore inherit */ + color_set = TRUE; + } + else if (strcmp (property_name, "-width") == 0) + { + if (decl->value == NULL || decl->value->next != NULL) + return; + + if (get_length_from_term_int (node, decl->value, FALSE, &width) == VALUE_FOUND) + /* Ignore inherit */ + width_set = TRUE; + } + + if (color_set) + node->outline_color = color; + if (width_set) + node->outline_width = width; +} + static void do_padding_property_term (StThemeNode *node, CRTerm *term, @@ -1144,6 +1234,9 @@ _st_theme_node_ensure_geometry (StThemeNode *node) node->border_color[j] = TRANSPARENT_COLOR; } + node->outline_width = 0; + node->outline_color = TRANSPARENT_COLOR; + node->width = -1; node->height = -1; node->min_width = -1; @@ -1158,6 +1251,8 @@ _st_theme_node_ensure_geometry (StThemeNode *node) if (g_str_has_prefix (property_name, "border")) do_border_property (node, decl); + else if (g_str_has_prefix (property_name, "outline")) + do_outline_property (node, decl); else if (g_str_has_prefix (property_name, "padding")) do_padding_property (node, decl); else if (strcmp (property_name, "width") == 0) @@ -1223,6 +1318,27 @@ st_theme_node_get_border_radius (StThemeNode *node, return node->border_radius[corner]; } +int +st_theme_node_get_outline_width (StThemeNode *node) +{ + g_return_val_if_fail (ST_IS_THEME_NODE (node), 0); + + _st_theme_node_ensure_geometry (node); + + return node->outline_width; +} + +void +st_theme_node_get_outline_color (StThemeNode *node, + ClutterColor *color) +{ + g_return_if_fail (ST_IS_THEME_NODE (node)); + + _st_theme_node_ensure_geometry (node); + + *color = node->outline_color; +} + int st_theme_node_get_width (StThemeNode *node) { @@ -2539,13 +2655,13 @@ st_theme_node_get_content_box (StThemeNode *node, /** * st_theme_node_get_paint_box: * @node: a #StThemeNode - * @allocation: the box allocated to a #ClutterAlctor + * @allocation: the box allocated to a #ClutterActor * @paint_box: computed box occupied when painting the actor * - * Gets the box used to paint the actor, including the area occupied by - * properties which paint outside the actor's assigned allocation - * (currently only st-shadow). When painting @node to an offscreen buffer, - * this function can be used to determine the necessary size of the buffer. + * Gets the box used to paint the actor, including the area occupied + * by properties which paint outside the actor's assigned allocation. + * When painting @node to an offscreen buffer, this function can be + * used to determine the necessary size of the buffer. */ void st_theme_node_get_paint_box (StThemeNode *node, @@ -2553,25 +2669,30 @@ st_theme_node_get_paint_box (StThemeNode *node, ClutterActorBox *paint_box) { StShadow *shadow; + ClutterActorBox shadow_box; + int outline_width; g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_if_fail (actor_box != NULL); g_return_if_fail (paint_box != NULL); shadow = st_theme_node_get_shadow (node); - if (shadow) + outline_width = st_theme_node_get_outline_width (node); + if (!shadow && !outline_width) { - ClutterActorBox shadow_box; - - st_shadow_get_box (shadow, actor_box, &shadow_box); - - paint_box->x1 = MIN (actor_box->x1, shadow_box.x1); - paint_box->x2 = MAX (actor_box->x2, shadow_box.x2); - paint_box->y1 = MIN (actor_box->y1, shadow_box.y1); - paint_box->y2 = MAX (actor_box->y2, shadow_box.y2); + *paint_box = *actor_box; + return; } + + if (shadow) + st_shadow_get_box (shadow, actor_box, &shadow_box); else - *paint_box = *actor_box; + shadow_box = *actor_box; + + paint_box->x1 = MIN (actor_box->x1 - outline_width, shadow_box.x1); + paint_box->x2 = MAX (actor_box->x2 + outline_width, shadow_box.x2); + paint_box->y1 = MIN (actor_box->y1 - outline_width, shadow_box.y1); + paint_box->y2 = MAX (actor_box->y2 + outline_width, shadow_box.y2); } diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h index 2dfa2b2ea..dadff5974 100644 --- a/src/st/st-theme-node.h +++ b/src/st/st-theme-node.h @@ -128,6 +128,10 @@ void st_theme_node_get_border_color (StThemeNode *node, StSide side, ClutterColor *color); +int st_theme_node_get_outline_width (StThemeNode *node); +void st_theme_node_get_outline_color (StThemeNode *node, + ClutterColor *color); + double st_theme_node_get_padding (StThemeNode *node, StSide side);