Add support for colored borders
Use BigRectangle to draw the border and background if there's a border width or border radius and no border image. (Only uniform borders are supported for now with some deviations from the CSS model noted in the comments.) The background color and image parameters are removed from NbtkWidget's draw_background() method since they were not used for NbtkButton (the only current user) and the encapsulation break that they presented caused some minor problems. Add a test case for borders, and also use borders to style the buttons in the 'inline-style' test case.
This commit is contained in:
parent
e1390c7dd5
commit
4743a8e750
@ -422,13 +422,11 @@ nbtk_button_unmap (ClutterActor *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nbtk_button_draw_background (NbtkWidget *widget,
|
nbtk_button_draw_background (NbtkWidget *widget)
|
||||||
ClutterActor *background,
|
|
||||||
const ClutterColor *color)
|
|
||||||
{
|
{
|
||||||
NbtkButtonPrivate *priv;
|
NbtkButtonPrivate *priv;
|
||||||
|
|
||||||
NBTK_WIDGET_CLASS (nbtk_button_parent_class)->draw_background (widget, background, color);
|
NBTK_WIDGET_CLASS (nbtk_button_parent_class)->draw_background (widget);
|
||||||
|
|
||||||
priv = NBTK_BUTTON (widget)->priv;
|
priv = NBTK_BUTTON (widget)->priv;
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ struct _NbtkWidgetPrivate
|
|||||||
gboolean is_stylable : 1;
|
gboolean is_stylable : 1;
|
||||||
gboolean has_tooltip : 1;
|
gboolean has_tooltip : 1;
|
||||||
gboolean is_style_dirty : 1;
|
gboolean is_style_dirty : 1;
|
||||||
|
gboolean draw_bg_color : 1;
|
||||||
|
|
||||||
NbtkTooltip *tooltip;
|
NbtkTooltip *tooltip;
|
||||||
};
|
};
|
||||||
@ -352,18 +353,18 @@ nbtk_widget_allocate (ClutterActor *actor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nbtk_widget_real_draw_background (NbtkWidget *self,
|
nbtk_widget_real_draw_background (NbtkWidget *self)
|
||||||
ClutterActor *background,
|
|
||||||
const ClutterColor *color)
|
|
||||||
{
|
{
|
||||||
|
NbtkWidgetPrivate *priv = self->priv;
|
||||||
|
|
||||||
/* Default implementation just draws the background
|
/* Default implementation just draws the background
|
||||||
* colour and the image on top
|
* colour and the image on top
|
||||||
*/
|
*/
|
||||||
if (color && color->alpha != 0)
|
if (priv->draw_bg_color)
|
||||||
{
|
{
|
||||||
ClutterActor *actor = CLUTTER_ACTOR (self);
|
ClutterActor *actor = CLUTTER_ACTOR (self);
|
||||||
ClutterActorBox allocation = { 0, };
|
ClutterActorBox allocation = { 0, };
|
||||||
ClutterColor bg_color = *color;
|
ClutterColor bg_color = priv->bg_color;
|
||||||
gfloat w, h;
|
gfloat w, h;
|
||||||
|
|
||||||
bg_color.alpha = clutter_actor_get_paint_opacity (actor)
|
bg_color.alpha = clutter_actor_get_paint_opacity (actor)
|
||||||
@ -382,8 +383,8 @@ nbtk_widget_real_draw_background (NbtkWidget *self,
|
|||||||
cogl_rectangle (0, 0, w, h);
|
cogl_rectangle (0, 0, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (background)
|
if (priv->border_image)
|
||||||
clutter_actor_paint (background);
|
clutter_actor_paint (priv->border_image);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -392,9 +393,7 @@ nbtk_widget_paint (ClutterActor *self)
|
|||||||
NbtkWidgetPrivate *priv = NBTK_WIDGET (self)->priv;
|
NbtkWidgetPrivate *priv = NBTK_WIDGET (self)->priv;
|
||||||
NbtkWidgetClass *klass = NBTK_WIDGET_GET_CLASS (self);
|
NbtkWidgetClass *klass = NBTK_WIDGET_GET_CLASS (self);
|
||||||
|
|
||||||
klass->draw_background (NBTK_WIDGET (self),
|
klass->draw_background (NBTK_WIDGET (self));
|
||||||
priv->border_image,
|
|
||||||
&priv->bg_color);
|
|
||||||
|
|
||||||
if (priv->background_image != NULL)
|
if (priv->background_image != NULL)
|
||||||
clutter_actor_paint (priv->background_image);
|
clutter_actor_paint (priv->background_image);
|
||||||
@ -487,6 +486,11 @@ nbtk_widget_real_style_changed (NbtkWidget *self)
|
|||||||
gboolean relayout_needed = FALSE;
|
gboolean relayout_needed = FALSE;
|
||||||
gboolean has_changed = FALSE;
|
gboolean has_changed = FALSE;
|
||||||
ClutterColor color;
|
ClutterColor color;
|
||||||
|
guint border_width = 0;
|
||||||
|
guint border_radius = 0;
|
||||||
|
ClutterColor border_color = { 0, };
|
||||||
|
ShellSide side;
|
||||||
|
ShellCorner corner;
|
||||||
|
|
||||||
/* application has request this widget is not stylable */
|
/* application has request this widget is not stylable */
|
||||||
if (!priv->is_stylable)
|
if (!priv->is_stylable)
|
||||||
@ -498,6 +502,7 @@ nbtk_widget_real_style_changed (NbtkWidget *self)
|
|||||||
if (!clutter_color_equal (&color, &priv->bg_color))
|
if (!clutter_color_equal (&color, &priv->bg_color))
|
||||||
{
|
{
|
||||||
priv->bg_color = color;
|
priv->bg_color = color;
|
||||||
|
priv->draw_bg_color = color.alpha != 0;
|
||||||
has_changed = TRUE;
|
has_changed = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,6 +520,62 @@ nbtk_widget_real_style_changed (NbtkWidget *self)
|
|||||||
|
|
||||||
texture_cache = nbtk_texture_cache_get_default ();
|
texture_cache = nbtk_texture_cache_get_default ();
|
||||||
|
|
||||||
|
/* ShellThemeNode 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 = SHELL_SIDE_TOP; side <= SHELL_SIDE_LEFT; side++)
|
||||||
|
{
|
||||||
|
double width = shell_theme_node_get_border_width (theme_node, side);
|
||||||
|
if (width > 0.5)
|
||||||
|
{
|
||||||
|
border_width = round (width);
|
||||||
|
shell_theme_node_get_border_color (theme_node, side, &border_color);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (corner = SHELL_CORNER_TOPLEFT; corner <= SHELL_CORNER_BOTTOMLEFT; corner++)
|
||||||
|
{
|
||||||
|
double radius = shell_theme_node_get_border_radius (theme_node, corner);
|
||||||
|
if (radius > 0.5)
|
||||||
|
{
|
||||||
|
border_radius = round (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.
|
||||||
|
*
|
||||||
|
* - Things are drawn in 4 layers, from the bottom:
|
||||||
|
* Background color
|
||||||
|
* Background image
|
||||||
|
* Border color or border image
|
||||||
|
* Content
|
||||||
|
* - The background color and image extend to and are clipped by the
|
||||||
|
* edge of the border area, so will be rounded if the border is rounded.
|
||||||
|
* (CSS3 background-clip property modifies this)
|
||||||
|
* - The border image replaces what would normally be drawn by the border
|
||||||
|
* - The border image is not clipped by a rounded border-radius
|
||||||
|
* - The border radius rounds the background even if the border is
|
||||||
|
* zero width or a border image is being used.
|
||||||
|
*
|
||||||
|
* Deviations from the above as implemented here:
|
||||||
|
* - The combination of border image and a non-zero border radius is
|
||||||
|
* not supported; the background color will be drawn with square
|
||||||
|
* corners.
|
||||||
|
* - The background image is drawn above the border color or image,
|
||||||
|
* not below it.
|
||||||
|
* - We don't clip the background image to the (rounded) border area.
|
||||||
|
*
|
||||||
|
* The first two allow us always draw with no more than single border_image
|
||||||
|
* and a single background image above it.
|
||||||
|
*/
|
||||||
|
|
||||||
theme_image = shell_theme_node_get_background_theme_image (theme_node);
|
theme_image = shell_theme_node_get_background_theme_image (theme_node);
|
||||||
if (theme_image)
|
if (theme_image)
|
||||||
{
|
{
|
||||||
@ -544,6 +605,22 @@ nbtk_widget_real_style_changed (NbtkWidget *self)
|
|||||||
border_left);
|
border_left);
|
||||||
clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
|
clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
|
||||||
|
|
||||||
|
has_changed = TRUE;
|
||||||
|
relayout_needed = TRUE;
|
||||||
|
}
|
||||||
|
else if ((border_width > 0 && border_color.alpha != 0) ||
|
||||||
|
(border_radius > 0 && priv->bg_color.alpha != 0))
|
||||||
|
{
|
||||||
|
priv->draw_bg_color = FALSE;
|
||||||
|
priv->border_image = g_object_new (BIG_TYPE_RECTANGLE,
|
||||||
|
"color", &priv->bg_color,
|
||||||
|
"border-width", border_width,
|
||||||
|
"border-color", &border_color,
|
||||||
|
"corner-radius", border_radius,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
|
||||||
|
|
||||||
has_changed = TRUE;
|
has_changed = TRUE;
|
||||||
relayout_needed = TRUE;
|
relayout_needed = TRUE;
|
||||||
}
|
}
|
||||||
@ -1297,7 +1374,5 @@ nbtk_widget_draw_background (NbtkWidget *self)
|
|||||||
priv = self->priv;
|
priv = self->priv;
|
||||||
|
|
||||||
klass = NBTK_WIDGET_GET_CLASS (self);
|
klass = NBTK_WIDGET_GET_CLASS (self);
|
||||||
klass->draw_background (NBTK_WIDGET (self),
|
klass->draw_background (NBTK_WIDGET (self));
|
||||||
priv->border_image,
|
|
||||||
&priv->bg_color);
|
|
||||||
}
|
}
|
||||||
|
@ -71,9 +71,7 @@ struct _NbtkWidgetClass
|
|||||||
ClutterActorClass parent_class;
|
ClutterActorClass parent_class;
|
||||||
|
|
||||||
/* vfuncs */
|
/* vfuncs */
|
||||||
void (* draw_background) (NbtkWidget *self,
|
void (* draw_background) (NbtkWidget *self);
|
||||||
ClutterActor *background,
|
|
||||||
const ClutterColor *color);
|
|
||||||
void (* style_changed) (NbtkWidget *self);
|
void (* style_changed) (NbtkWidget *self);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
57
tests/interactive/borders.js
Normal file
57
tests/interactive/borders.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Nbtk = imports.gi.Nbtk;
|
||||||
|
|
||||||
|
const UI = imports.testcommon.ui;
|
||||||
|
|
||||||
|
UI.init();
|
||||||
|
let stage = Clutter.Stage.get_default();
|
||||||
|
stage.width = 600;
|
||||||
|
stage.height = 600;
|
||||||
|
|
||||||
|
let vbox = new Nbtk.BoxLayout({ vertical: true,
|
||||||
|
width: stage.width,
|
||||||
|
height: stage.height,
|
||||||
|
spacing: 20,
|
||||||
|
style: 'padding: 10px; background: #ffee88' });
|
||||||
|
stage.add_actor(vbox);
|
||||||
|
|
||||||
|
vbox.add(new Nbtk.Label({ text: "Hello World",
|
||||||
|
style: 'border: 1px solid black; '
|
||||||
|
+ 'padding: 5px;' }));
|
||||||
|
|
||||||
|
vbox.add(new Nbtk.Label({ text: "Hello Round World",
|
||||||
|
style: 'border: 3px solid green; '
|
||||||
|
+ 'border-radius: 8px; '
|
||||||
|
+ 'padding: 5px;' }));
|
||||||
|
|
||||||
|
vbox.add(new Nbtk.Label({ text: "Hello Background",
|
||||||
|
style: 'border: 3px solid green; '
|
||||||
|
+ 'border-radius: 8px; '
|
||||||
|
+ 'background: white; '
|
||||||
|
+ 'padding: 5px;' }));
|
||||||
|
|
||||||
|
vbox.add(new Nbtk.Label({ text: "Border, Padding, Content: 20px" }));
|
||||||
|
|
||||||
|
let b1 = new Nbtk.BoxLayout({ vertical: true,
|
||||||
|
style: 'border: 20px solid black; '
|
||||||
|
+ 'background: white; '
|
||||||
|
+ 'padding: 20px;' });
|
||||||
|
vbox.add(b1);
|
||||||
|
|
||||||
|
b1.add(new Nbtk.BoxLayout({ width: 20, height: 20,
|
||||||
|
style: 'background: black' }));
|
||||||
|
|
||||||
|
vbox.add(new Nbtk.Label({ text: "Translucent blue border",
|
||||||
|
style: 'border: 20px solid rgba(0, 0, 255, 0.2); '
|
||||||
|
+ 'background: white; '
|
||||||
|
+ 'padding: 10px;' }));
|
||||||
|
|
||||||
|
vbox.add(new Nbtk.Label({ text: "Border Image",
|
||||||
|
style_class: "border-image",
|
||||||
|
style: "padding: 10px;" }));
|
||||||
|
|
||||||
|
stage.show();
|
||||||
|
Clutter.main();
|
||||||
|
stage.destroy();
|
@ -25,16 +25,16 @@ function update_size() {
|
|||||||
}
|
}
|
||||||
update_size();
|
update_size();
|
||||||
|
|
||||||
let button = new Nbtk.Button ({ label: 'Smaller',
|
let button;
|
||||||
style: 'padding: 4px; background: #eeddcc' });
|
|
||||||
|
button = new Nbtk.Button ({ label: 'Smaller' });
|
||||||
hbox.add (button);
|
hbox.add (button);
|
||||||
button.connect('clicked', function() {
|
button.connect('clicked', function() {
|
||||||
size /= 1.2;
|
size /= 1.2;
|
||||||
update_size ();
|
update_size ();
|
||||||
});
|
});
|
||||||
|
|
||||||
let button = new Nbtk.Button ({ label: 'Bigger',
|
button = new Nbtk.Button ({ label: 'Bigger' });
|
||||||
style: 'padding: 4px; background: #eeddcc' });
|
|
||||||
hbox.add (button);
|
hbox.add (button);
|
||||||
button.connect('clicked', function() {
|
button.connect('clicked', function() {
|
||||||
size *= 1.2;
|
size *= 1.2;
|
||||||
|
BIN
tests/testcommon/border-image.png
Normal file
BIN
tests/testcommon/border-image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 981 B |
@ -31,3 +31,23 @@ stage {
|
|||||||
.monospace {
|
.monospace {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-image {
|
||||||
|
border: 15px;
|
||||||
|
-shell-background-image: url('border-image.png') 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
NbtkButton {
|
||||||
|
background: #eeddbb;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
NbtkButton:hover {
|
||||||
|
background: #ffeecc;
|
||||||
|
}
|
||||||
|
|
||||||
|
NbtkButton:active {
|
||||||
|
background: #ccbb99;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user