Implement non-uniform borders (when not using corner_radius)

StTheme CSS supports different border widths for different sides.  Implement
it for StWidget by drawing the border internally.  However, we don't support
a nonzero corner-radius with nonuniform borders.

https://bugzilla.gnome.org/show_bug.cgi?id=599442
This commit is contained in:
Colin Walters 2009-10-28 12:53:26 -04:00
parent e2e513ff08
commit 4f456b9689

View File

@ -63,6 +63,7 @@ struct _StWidgetPrivate
gboolean has_tooltip : 1;
gboolean is_style_dirty : 1;
gboolean draw_bg_color : 1;
gboolean draw_border_internal : 1;
StTooltip *tooltip;
};
@ -356,25 +357,25 @@ static void
st_widget_real_draw_background (StWidget *self)
{
StWidgetPrivate *priv = self->priv;
ClutterActor *actor = CLUTTER_ACTOR (self);
ClutterActorBox allocation = { 0, };
gfloat w, h;
guint8 opacity;
clutter_actor_get_allocation_box (actor, &allocation);
w = allocation.x2 - allocation.x1;
h = allocation.y2 - allocation.y1;
opacity = clutter_actor_get_paint_opacity (actor);
/* Default implementation just draws the background
* colour and the image on top
*/
if (priv->draw_bg_color)
{
ClutterActor *actor = CLUTTER_ACTOR (self);
ClutterActorBox allocation = { 0, };
ClutterColor bg_color = priv->bg_color;
gfloat w, h;
bg_color.alpha = clutter_actor_get_paint_opacity (actor)
* bg_color.alpha
/ 255;
clutter_actor_get_allocation_box (actor, &allocation);
w = allocation.x2 - allocation.x1;
h = allocation.y2 - allocation.y1;
bg_color.alpha = opacity * bg_color.alpha / 255;
cogl_set_source_color4ub (bg_color.red,
bg_color.green,
@ -383,6 +384,76 @@ st_widget_real_draw_background (StWidget *self)
cogl_rectangle (0, 0, w, h);
}
if (priv->draw_border_internal)
{
StThemeNode *node = st_widget_get_theme_node (self);
int side;
double border_top, border_right, border_bottom, border_left;
border_top = st_theme_node_get_border_width (node, ST_SIDE_TOP);
border_right = st_theme_node_get_border_width (node, ST_SIDE_RIGHT);
border_bottom = st_theme_node_get_border_width (node, ST_SIDE_BOTTOM);
border_left = st_theme_node_get_border_width (node, ST_SIDE_LEFT);
for (side = 0; side < 4; side++)
{
ClutterColor color;
switch (side)
{
case ST_SIDE_TOP:
if (border_top <= 0)
continue;
break;
case ST_SIDE_RIGHT:
if (border_right <= 0)
continue;
break;
case ST_SIDE_BOTTOM:
if (border_bottom <= 0)
continue;
break;
case ST_SIDE_LEFT:
if (border_left <= 0)
continue;
break;
}
st_theme_node_get_border_color (node, side, &color);
color.alpha = (color.alpha * opacity) / 0xff;
cogl_set_source_color4ub (color.red,
color.green,
color.blue,
color.alpha);
/* Note top and bottom extend to the ends, left/right
* are constrained by them. See comment above about CSS
* conformance.
*/
switch (side)
{
case ST_SIDE_TOP:
cogl_rectangle (0, 0,
w, border_top);
break;
case ST_SIDE_RIGHT:
cogl_rectangle (w - border_right, border_top,
w, h - border_bottom);
break;
case ST_SIDE_BOTTOM:
cogl_rectangle (0, h - border_bottom,
w, h);
break;
case ST_SIDE_LEFT:
cogl_rectangle (0, border_top,
border_left, h - border_bottom);
break;
}
}
}
if (priv->border_image)
clutter_actor_paint (priv->border_image);
}
@ -491,6 +562,7 @@ st_widget_real_style_changed (StWidget *self)
ClutterColor border_color = { 0, };
StSide side;
StCorner corner;
gboolean uniform_border_width;
/* application has request this widget is not stylable */
if (!priv->is_stylable)
@ -520,33 +592,6 @@ st_widget_real_style_changed (StWidget *self)
texture_cache = st_texture_cache_get_default ();
/* StThemeNode supports different widths and colors for different sides
* of the border, and different radii for the different corners. We take
* the different border widths into account when positioning, but our current
* drawing code (using BigRectangle) can only handle a single width, color,
* and radius, so we arbitrarily pick the first non-zero width and radius,
* and use that.
*/
for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++)
{
double width = st_theme_node_get_border_width (theme_node, side);
if (width > 0.5)
{
border_width = (int)(0.5 + width);
st_theme_node_get_border_color (theme_node, side, &border_color);
break;
}
}
for (corner = ST_CORNER_TOPLEFT; corner <= ST_CORNER_BOTTOMLEFT; corner++)
{
double radius = st_theme_node_get_border_radius (theme_node, corner);
if (radius > 0.5)
{
border_radius = (int)(0.5 + radius);
break;
}
}
/* Rough notes about the relationship of borders and backgrounds in CSS3;
* see http://www.w3.org/TR/css3-background/ for more accurate details.
@ -565,6 +610,8 @@ st_widget_real_style_changed (StWidget *self)
* zero width or a border image is being used.
*
* Deviations from the above as implemented here:
* - Nonuniform border widths combined with a non-zero border radius result
* in the border radius being ignored
* - The combination of border image and a non-zero border radius is
* not supported; the background color will be drawn with square
* corners.
@ -576,6 +623,44 @@ st_widget_real_style_changed (StWidget *self)
* and a single background image above it.
*/
/* Check whether all border widths are the same. Also, acquire the
* first nonzero border width as well as the border color.
*/
uniform_border_width = TRUE;
border_width = st_theme_node_get_border_width (theme_node, ST_SIDE_TOP);
if (border_width > 0.5)
border_width = (int)(0.5 + border_width);
for (side = 0; side < 4; side++)
{
double width = st_theme_node_get_border_width (theme_node, side);
if (width > 0.5)
width = (int)(0.5 + width);
if (width > 0)
{
border_width = width;
st_theme_node_get_border_color (theme_node, side, &border_color);
}
if ((int)width != border_width)
{
uniform_border_width = FALSE;
break;
}
}
/* Pick the first nonzero border radius, but only if we have a uniform border. */
if (uniform_border_width)
{
for (corner = 0; corner < 4; corner++)
{
double radius = st_theme_node_get_border_radius (theme_node, corner);
if (radius > 0.5)
{
border_radius = (int)(0.5 + radius);
break;
}
}
}
border_image = st_theme_node_get_border_image (theme_node);
if (border_image)
{
@ -607,9 +692,9 @@ st_widget_real_style_changed (StWidget *self)
has_changed = TRUE;
relayout_needed = TRUE;
}
else if ((border_width > 0 && border_color.alpha != 0) ||
(border_radius > 0 && priv->bg_color.alpha != 0))
else if (border_radius > 0)
{
priv->draw_border_internal = FALSE;
priv->draw_bg_color = FALSE;
priv->border_image = g_object_new (BIG_TYPE_RECTANGLE,
"color", &priv->bg_color,
@ -623,6 +708,19 @@ st_widget_real_style_changed (StWidget *self)
has_changed = TRUE;
relayout_needed = TRUE;
}
else if (border_width > 0 && border_color.alpha != 0)
{
priv->draw_bg_color = TRUE;
priv->draw_border_internal = TRUE;
has_changed = TRUE;
relayout_needed = TRUE;
}
else if (priv->draw_border_internal)
{
priv->draw_border_internal = FALSE;
has_changed = TRUE;
relayout_needed = TRUE;
}
bg_file = st_theme_node_get_background_image (theme_node);
if (bg_file != NULL)