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:
parent
3c646ec516
commit
6527dbc8b7
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
47
tests/interactive/inline-style.js
Normal file
47
tests/interactive/inline-style.js
Normal 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();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user