Add support for inline styles

Add support for passing an inline-style string when creating a
StThemeNode.

Hook this up to a new 'style' property of StWidget.

Add a test case that demonstrates using this to update font sizes
on the fly.

https://bugzilla.gnome.org/show_bug.cgi?id=595991
This commit is contained in:
Owen W. Taylor 2009-09-19 22:56:09 -04:00
parent 3c646ec516
commit 6527dbc8b7
9 changed files with 190 additions and 26 deletions

View File

@ -281,7 +281,7 @@ st_theme_context_get_root_node (StThemeContext *context)
{ {
if (context->root_node == NULL) if (context->root_node == NULL)
context->root_node = st_theme_node_new (context, NULL, context->theme, context->root_node = st_theme_node_new (context, NULL, context->theme,
G_TYPE_NONE, NULL, NULL, NULL); G_TYPE_NONE, NULL, NULL, NULL, NULL);
return context->root_node; return context->root_node;
} }

View File

@ -33,10 +33,14 @@ struct _StThemeNode {
char *element_id; char *element_id;
char *element_class; char *element_class;
char *pseudo_class; char *pseudo_class;
char *inline_style;
CRDeclaration **properties; CRDeclaration **properties;
int n_properties; int n_properties;
/* We hold onto these separately so we can destroy them on finalize */
CRDeclaration *inline_properties;
guint properties_computed : 1; guint properties_computed : 1;
guint borders_computed : 1; guint borders_computed : 1;
guint background_computed : 1; guint background_computed : 1;
@ -76,6 +80,7 @@ st_theme_node_finalize (GObject *object)
g_free (node->element_id); g_free (node->element_id);
g_free (node->element_class); g_free (node->element_class);
g_free (node->pseudo_class); g_free (node->pseudo_class);
g_free (node->inline_style);
if (node->properties) if (node->properties)
{ {
@ -84,6 +89,12 @@ st_theme_node_finalize (GObject *object)
node->n_properties = 0; node->n_properties = 0;
} }
if (node->inline_properties)
{
/* This destroys the list, not just the head of the list */
cr_declaration_destroy (node->inline_properties);
}
if (node->font_desc) if (node->font_desc)
{ {
pango_font_description_free (node->font_desc); pango_font_description_free (node->font_desc);
@ -131,7 +142,8 @@ st_theme_node_new (StThemeContext *context,
GType element_type, GType element_type,
const char *element_id, const char *element_id,
const char *element_class, const char *element_class,
const char *pseudo_class) const char *pseudo_class,
const char *inline_style)
{ {
StThemeNode *node; StThemeNode *node;
@ -156,6 +168,7 @@ st_theme_node_new (StThemeContext *context,
node->element_id = g_strdup (element_id); node->element_id = g_strdup (element_id);
node->element_class = g_strdup (element_class); node->element_class = g_strdup (element_class);
node->pseudo_class = g_strdup (pseudo_class); node->pseudo_class = g_strdup (pseudo_class);
node->inline_style = g_strdup (inline_style);
return node; return node;
} }
@ -230,11 +243,32 @@ ensure_properties (StThemeNode *node)
{ {
if (!node->properties_computed) if (!node->properties_computed)
{ {
GPtrArray *properties = NULL;
node->properties_computed = TRUE; node->properties_computed = TRUE;
if (node->theme) if (node->theme)
_st_theme_get_matched_properties (node->theme, node, properties = _st_theme_get_matched_properties (node->theme, node);
&node->properties, &node->n_properties);
if (node->inline_style)
{
CRDeclaration *cur_decl;
if (!properties)
properties = g_ptr_array_new ();
node->inline_properties = cr_declaration_parse_list_from_buf ((const guchar *)node->inline_style,
CR_UTF_8);
for (cur_decl = node->inline_properties; cur_decl; cur_decl = cur_decl->next)
g_ptr_array_add (properties, cur_decl);
}
if (properties)
{
node->n_properties = properties->len;
node->properties = (CRDeclaration **)g_ptr_array_free (properties, FALSE);
}
} }
} }

View File

@ -58,7 +58,8 @@ StThemeNode *st_theme_node_new (StThemeContext *context,
GType element_type, GType element_type,
const char *element_id, const char *element_id,
const char *element_class, const char *element_class,
const char *pseudo_class); const char *pseudo_class,
const char *inline_style);
StThemeNode *st_theme_node_get_parent (StThemeNode *node); StThemeNode *st_theme_node_get_parent (StThemeNode *node);

View File

@ -7,10 +7,8 @@
G_BEGIN_DECLS G_BEGIN_DECLS
void _st_theme_get_matched_properties (StTheme *theme, GPtrArray *_st_theme_get_matched_properties (StTheme *theme,
StThemeNode *node, StThemeNode *node);
CRDeclaration ***properties,
int *n_properties);
/* Resolve an URL from the stylesheet to a filename */ /* Resolve an URL from the stylesheet to a filename */
char *_st_theme_resolve_url (StTheme *theme, char *_st_theme_resolve_url (StTheme *theme,

View File

@ -879,18 +879,16 @@ compare_declarations (gconstpointer a,
return 0; return 0;
} }
void GPtrArray *
_st_theme_get_matched_properties (StTheme *theme, _st_theme_get_matched_properties (StTheme *theme,
StThemeNode *node, StThemeNode *node)
CRDeclaration ***properties,
int *n_properties)
{ {
enum CRStyleOrigin origin = 0; enum CRStyleOrigin origin = 0;
CRStyleSheet *sheet = NULL; CRStyleSheet *sheet = NULL;
GPtrArray *props = g_ptr_array_new (); GPtrArray *props = g_ptr_array_new ();
g_return_if_fail (ST_IS_THEME (theme)); g_return_val_if_fail (ST_IS_THEME (theme), NULL);
g_return_if_fail (ST_IS_THEME_NODE (node)); g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++)
{ {
@ -905,8 +903,7 @@ _st_theme_get_matched_properties (StTheme *theme,
* after earlier declarations */ * after earlier declarations */
g_ptr_array_sort (props, compare_declarations); g_ptr_array_sort (props, compare_declarations);
*n_properties = props->len; return props;
*properties = (CRDeclaration **) g_ptr_array_free (props, FALSE);
} }
/* Resolve an url from an url() reference in a stylesheet into an absolute /* Resolve an url from an url() reference in a stylesheet into an absolute

View File

@ -54,6 +54,7 @@ struct _StWidgetPrivate
StThemeNode *theme_node; StThemeNode *theme_node;
gchar *pseudo_class; gchar *pseudo_class;
gchar *style_class; gchar *style_class;
gchar *inline_style;
ClutterActor *border_image; ClutterActor *border_image;
ClutterActor *background_image; ClutterActor *background_image;
@ -84,6 +85,7 @@ enum
PROP_THEME, PROP_THEME,
PROP_PSEUDO_CLASS, PROP_PSEUDO_CLASS,
PROP_STYLE_CLASS, PROP_STYLE_CLASS,
PROP_STYLE,
PROP_STYLABLE, PROP_STYLABLE,
@ -126,6 +128,10 @@ st_widget_set_property (GObject *gobject,
st_widget_set_style_class_name (actor, g_value_get_string (value)); st_widget_set_style_class_name (actor, g_value_get_string (value));
break; break;
case PROP_STYLE:
st_widget_set_style (actor, g_value_get_string (value));
break;
case PROP_STYLABLE: case PROP_STYLABLE:
if (actor->priv->is_stylable != g_value_get_boolean (value)) if (actor->priv->is_stylable != g_value_get_boolean (value))
{ {
@ -171,6 +177,10 @@ st_widget_get_property (GObject *gobject,
g_value_set_string (value, priv->style_class); g_value_set_string (value, priv->style_class);
break; break;
case PROP_STYLE:
g_value_set_string (value, priv->inline_style);
break;
case PROP_STYLABLE: case PROP_STYLABLE:
g_value_set_boolean (value, priv->is_stylable); g_value_set_boolean (value, priv->is_stylable);
break; break;
@ -659,7 +669,8 @@ st_widget_get_theme_node (StWidget *widget)
G_OBJECT_TYPE (widget), G_OBJECT_TYPE (widget),
clutter_actor_get_name (CLUTTER_ACTOR (widget)), clutter_actor_get_name (CLUTTER_ACTOR (widget)),
priv->style_class, priv->style_class,
priv->pseudo_class); priv->pseudo_class,
priv->inline_style);
} }
return priv->theme_node; return priv->theme_node;
@ -763,6 +774,20 @@ st_widget_class_init (StWidgetClass *klass)
"", "",
ST_PARAM_READWRITE)); ST_PARAM_READWRITE));
/**
* StWidget:style:
*
* Inline style information for the actor as a ';'-separated list of
* CSS properties.
*/
g_object_class_install_property (gobject_class,
PROP_STYLE,
g_param_spec_string ("style",
"Style",
"Inline style string",
"",
ST_PARAM_READWRITE));
/** /**
* StWidget:theme * StWidget:theme
* *
@ -972,6 +997,53 @@ st_widget_set_style_pseudo_class (StWidget *actor,
} }
} }
/**
* st_widget_set_style:
* @actor: a #StWidget
* @style_class: (allow-none): a inline style string, or %NULL
*
* Set the inline style string for this widget. The inline style string is an
* optional ';'-separated list of CSS properties that override the style as
* determined from the stylesheets of the current theme.
*/
void
st_widget_set_style (StWidget *actor,
const gchar *style)
{
StWidgetPrivate *priv = actor->priv;
g_return_if_fail (ST_IS_WIDGET (actor));
priv = actor->priv;
if (g_strcmp0 (style, priv->inline_style))
{
g_free (priv->inline_style);
priv->inline_style = g_strdup (style);
st_widget_style_changed (actor);
g_object_notify (G_OBJECT (actor), "style");
}
}
/**
* st_widget_get_style:
* @actor: a #StWidget
*
* Get the current inline style string. See st_widget_set_style().
*
* Returns: The inline style string, or %NULL. The string is owned by the
* #StWidget and should not be modified or freed.
*/
const gchar*
st_widget_get_style (StWidget *actor)
{
g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
return actor->priv->inline_style;
}
static void static void
st_widget_name_notify (StWidget *widget, st_widget_name_notify (StWidget *widget,
GParamSpec *pspec, GParamSpec *pspec,

View File

@ -86,6 +86,9 @@ G_CONST_RETURN gchar *st_widget_get_style_pseudo_class (StWidget *actor);
void st_widget_set_style_class_name (StWidget *actor, void st_widget_set_style_class_name (StWidget *actor,
const gchar *style_class); const gchar *style_class);
G_CONST_RETURN gchar *st_widget_get_style_class_name (StWidget *actor); G_CONST_RETURN gchar *st_widget_get_style_class_name (StWidget *actor);
void st_widget_set_style (StWidget *actor,
const gchar *style);
G_CONST_RETURN gchar *st_widget_get_style (StWidget *actor);
void st_widget_set_theme (StWidget *actor, void st_widget_set_theme (StWidget *actor,
StTheme *theme); StTheme *theme);
StTheme * st_widget_get_theme (StWidget *actor); StTheme * st_widget_get_theme (StWidget *actor);

View File

@ -244,6 +244,16 @@ test_pseudo_class (void)
assert_text_decoration (group3, "group3", 0); assert_text_decoration (group3, "group3", 0);
} }
static void
test_inline_style (void)
{
test = "inline_style";
/* These properties come from the inline-style specified when creating the node */
assert_foreground_color (text3, "text3", 0x00000ffff);
assert_length ("text3", "padding-bottom", 12.,
st_theme_node_get_padding (text3, ST_SIDE_BOTTOM));
}
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
@ -263,21 +273,22 @@ main (int argc, char **argv)
root = st_theme_context_get_root_node (context); root = st_theme_context_get_root_node (context);
group1 = st_theme_node_new (context, root, NULL, group1 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_GROUP, "group1", NULL, NULL); CLUTTER_TYPE_GROUP, "group1", NULL, NULL, NULL);
text1 = st_theme_node_new (context, group1, NULL, text1 = st_theme_node_new (context, group1, NULL,
CLUTTER_TYPE_TEXT, "text1", "special-text", NULL); CLUTTER_TYPE_TEXT, "text1", "special-text", NULL, NULL);
text2 = st_theme_node_new (context, group1, NULL, text2 = st_theme_node_new (context, group1, NULL,
CLUTTER_TYPE_TEXT, "text2", NULL, NULL); CLUTTER_TYPE_TEXT, "text2", NULL, NULL, NULL);
group2 = st_theme_node_new (context, root, NULL, group2 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_GROUP, "group2", NULL, NULL); CLUTTER_TYPE_GROUP, "group2", NULL, NULL, NULL);
text3 = st_theme_node_new (context, group2, NULL, text3 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_TEXT, "text3", NULL, NULL); CLUTTER_TYPE_TEXT, "text3", NULL, NULL,
"color: #0000ff; padding-bottom: 12px;");
text4 = st_theme_node_new (context, group2, NULL, text4 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_TEXT, "text4", NULL, "visited hover"); CLUTTER_TYPE_TEXT, "text4", NULL, "visited hover", NULL);
group3 = st_theme_node_new (context, group2, NULL, group3 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_GROUP, "group3", NULL, "hover"); CLUTTER_TYPE_GROUP, "group3", NULL, "hover", NULL);
cairo_texture = st_theme_node_new (context, root, NULL, cairo_texture = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_CAIRO_TEXTURE, "cairoTexture", NULL, NULL); CLUTTER_TYPE_CAIRO_TEXTURE, "cairoTexture", NULL, NULL, NULL);
test_defaults (); test_defaults ();
test_lengths (); test_lengths ();
@ -287,6 +298,7 @@ main (int argc, char **argv)
test_background (); test_background ();
test_font (); test_font ();
test_pseudo_class (); test_pseudo_class ();
test_inline_style ();
return fail ? 1 : 0; return fail ? 1 : 0;
} }

View File

@ -0,0 +1,47 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height });
stage.add_actor(vbox);
let hbox = new St.BoxLayout({ spacing: 12 });
vbox.add(hbox);
let text = new St.Label({ text: "Styled Text" });
vbox.add (text);
let size = 24;
function update_size() {
text.style = 'font-size: ' + size + 'pt';
}
update_size();
let button = new St.Button ({ label: 'Smaller',
style: 'padding: 4px; background: #eeddcc' });
hbox.add (button);
button.connect('clicked', function() {
size /= 1.2;
update_size ();
});
let button = new St.Button ({ label: 'Bigger',
style: 'padding: 4px; background: #eeddcc' });
hbox.add (button);
button.connect('clicked', function() {
size *= 1.2;
update_size ();
});
stage.show();
Clutter.main();
stage.destroy();