Add a function to dump CSS nodes and styles

Add a gtk_style_context_to_string function that can serialize
a CSS node or tree of nodes, optionally including CSS properties
as well.

This will be useful in writing tests.
This commit is contained in:
Matthias Clasen 2015-12-28 01:09:54 -05:00
parent a6624d803e
commit 68edc47f6a
5 changed files with 257 additions and 1 deletions

View File

@ -6244,6 +6244,8 @@ gtk_style_context_set_frame_clock
gtk_style_context_set_state
gtk_style_context_set_scale
gtk_style_context_get_scale
GtkStyleContextPrintFlags
gtk_style_context_to_string
<SUBSECTION>
GtkBorder

View File

@ -25,6 +25,8 @@
#include "gtkmarshalers.h"
#include "gtksettingsprivate.h"
#include "gtktypebuiltins.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkcsssectionprivate.h"
/*
* CSS nodes are the backbone of the GtkStyleContext implementation and
@ -1510,7 +1512,7 @@ GtkStyleProviderPrivate *
gtk_css_node_get_style_provider (GtkCssNode *cssnode)
{
GtkStyleProviderPrivate *result;
result = gtk_css_node_get_style_provider_or_null (cssnode);
if (result)
return result;
@ -1520,3 +1522,189 @@ gtk_css_node_get_style_provider (GtkCssNode *cssnode)
return GTK_STYLE_PROVIDER_PRIVATE (_gtk_settings_get_style_cascade (gtk_settings_get_default (), 1));
}
static void
append_id (GtkCssNode *cssnode,
GString *string)
{
const char *id;
id = gtk_css_node_get_id (cssnode);
if (id)
{
g_string_append (string, " id=");
g_string_append (string, id);
}
}
static void
append_visible (GtkCssNode *cssnode,
GString *string)
{
g_string_append_printf (string, " visible=%d", gtk_css_node_get_visible (cssnode));
}
static void
append_state (GtkCssNode *cssnode,
GString *string)
{
GtkStateFlags state;
state = gtk_css_node_get_state (cssnode);
if (state)
{
GFlagsClass *fclass;
gint i;
gboolean first = TRUE;
g_string_append (string, " state=");
fclass = g_type_class_ref (GTK_TYPE_STATE_FLAGS);
for (i = 0; i < fclass->n_values; i++)
{
if (state & fclass->values[i].value)
{
if (first)
first = FALSE;
else
g_string_append_c (string, '|');
g_string_append (string, fclass->values[i].value_nick);
}
}
g_type_class_unref (fclass);
}
}
static void
append_classes (GtkCssNode *cssnode,
GString *string)
{
const GQuark *classes;
guint n_classes;
classes = gtk_css_node_list_classes (cssnode, &n_classes);
if (n_classes > 0)
{
int i;
g_string_append (string, " classes=");
for (i = 0; i < n_classes; i++)
{
if (i > 0)
g_string_append_c (string, ',');
g_string_append (string, g_quark_to_string (classes[i]));
}
}
}
static gboolean
gtk_css_node_has_initial_value (GtkCssNode *cssnode,
GtkCssStyleProperty *prop)
{
GtkCssNode *parent_node;
GtkCssStyle *style, *parent_style;
GtkCssValue *value, *initial, *computed;
GtkCssProvider *provider;
gboolean is_initial;
guint id;
id = _gtk_css_style_property_get_id (prop);
style = gtk_css_node_get_style (cssnode);
value = gtk_css_style_get_value (style, id);
parent_node = gtk_css_node_get_parent (cssnode);
parent_style = parent_node ? gtk_css_node_get_style (parent_node) : NULL;
provider = gtk_css_node_get_style_provider (cssnode);
initial = _gtk_css_style_property_get_initial_value (prop);
computed = _gtk_css_value_compute (initial, id, provider, style, parent_style);
is_initial = _gtk_css_value_equal (value, computed);
_gtk_css_value_unref (computed);
return is_initial;
}
static void
append_value (GtkCssNode *cssnode,
GtkCssStyleProperty *prop,
GString *string,
guint indent)
{
GtkCssValue *value;
GtkCssStyle *style;
GtkCssSection *section;
const char *name;
guint id;
id = _gtk_css_style_property_get_id (prop);
name = _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop));
style = gtk_css_node_get_style (cssnode);
value = gtk_css_style_get_value (style, id);
g_string_append_printf (string, "%*s%s: ", indent, "", name);
_gtk_css_value_print (value, string);
section = gtk_css_style_get_section (style, id);
if (section)
{
g_string_append (string, " (");
_gtk_css_section_print (section, string);
g_string_append (string, ")");
}
g_string_append_c (string, '\n');
}
static void
append_style (GtkCssNode *cssnode,
GtkStyleContextPrintFlags flags,
GString *string,
guint indent)
{
int i;
if (!(flags & GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE))
return;
for (i = 0; i < _gtk_css_style_property_get_n_properties (); i++)
{
GtkCssStyleProperty *prop;
prop = _gtk_css_style_property_lookup_by_id (i);
if ((flags & GTK_STYLE_CONTEXT_PRINT_SHOW_INITIAL) ||
!gtk_css_node_has_initial_value (cssnode, prop))
append_value (cssnode, prop, string, indent);
}
}
void
gtk_css_node_print (GtkCssNode *cssnode,
GtkStyleContextPrintFlags flags,
GString *string,
guint indent)
{
GtkCssNode *node;
g_string_append_printf (string, "%*s", indent, "");
if (gtk_css_node_get_name (cssnode))
g_string_append (string, gtk_css_node_get_name (cssnode));
else
g_string_append (string, g_type_name (gtk_css_node_get_widget_type (cssnode)));
append_id (cssnode, string);
append_visible (cssnode, string);
append_state (cssnode, string);
append_classes (cssnode, string);
g_string_append_c (string, '\n');
append_style (cssnode, flags, string, indent + 2);
if (flags & GTK_STYLE_CONTEXT_PRINT_RECURSE)
{
for (node = gtk_css_node_get_first_child (cssnode); node; node = gtk_css_node_get_next_sibling (node))
gtk_css_node_print (node, flags, string, indent + 2);
}
}

View File

@ -171,6 +171,11 @@ GtkWidgetPath * gtk_css_node_create_widget_path (GtkCssNode *
const GtkWidgetPath * gtk_css_node_get_widget_path (GtkCssNode *cssnode);
GtkStyleProviderPrivate *gtk_css_node_get_style_provider(GtkCssNode *cssnode);
void gtk_css_node_print (GtkCssNode *cssnode,
GtkStyleContextPrintFlags flags,
GString *string,
guint indent);
G_END_DECLS
#endif /* __GTK_CSS_NODE_PRIVATE_H__ */

View File

@ -3219,3 +3219,53 @@ _gtk_style_context_is_background_opaque (GtkStyleContext *context)
corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_RIGHT_RADIUS)) &&
corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_LEFT_RADIUS)));
}
/**
* GtkStyleContextPrintFlags:
* @GTK_STYLE_CONTEXT_PRINT_RECURSE: Print the entire tree of
* CSS nodes starting at the style context's node
* @GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE: Show the values of the
* CSS properties for each node
* @GTK_STYLE_CONTEXT_PRINT_SHOW_INITIAL: Show the values of the
* CSS properties even if they match the initial value. By default,
* values are only shown if they are different from the initial
* value.
*
* Flags that modify the behavior of gtk_style_context_to_string().
* New values may be added to this enumeration.
*/
/**
* gtk_style_context_to_string:
* @context: a #GtkStyleContext
* @flags: Flags that determine what to print
*
* Converts the style context into a string representation.
*
* The string representation always includes information about
* the name, state, id, visibility and style classes of the CSS
* node that is backing @context. Depending on the flags, more
* information may be included.
*
* This function is intended for testing and debugging of the
* CSS implementation in GTK+. There are no guarantees about
* the format of the returned string, it may change.
*
* Returns: a newly allocated string representing @context
*
* Since: 3.20
*/
char *
gtk_style_context_to_string (GtkStyleContext *context,
GtkStyleContextPrintFlags flags)
{
GString *string;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
string = g_string_new ("");
gtk_css_node_print (context->priv->cssnode, flags, string, 0);
return g_string_free (string, FALSE);
}

View File

@ -1208,6 +1208,17 @@ void gtk_draw_insertion_cursor (GtkWidget *widget,
GtkTextDirection direction,
gboolean draw_arrow);
typedef enum {
GTK_STYLE_CONTEXT_PRINT_NONE = 0,
GTK_STYLE_CONTEXT_PRINT_RECURSE = 1 << 0,
GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE = 1 << 1,
GTK_STYLE_CONTEXT_PRINT_SHOW_INITIAL = 1 << 2
} GtkStyleContextPrintFlags;
GDK_AVAILABLE_IN_3_20
char * gtk_style_context_to_string (GtkStyleContext *context,
GtkStyleContextPrintFlags flags);
G_END_DECLS
#endif /* __GTK_STYLE_CONTEXT_H__ */