Commit 5c2c6591 authored by Benjamin Otte's avatar Benjamin Otte

stylecontext: Split out functionality into custom object

GtkCssNodeDeclaration is a new struct with copy-on-write semantics.

It encapsulated the properties used to define a node in the CSS tree.

The idea is to use it in various places for caching, in particular as
key in hash tables.
parent 4cbc0191
......@@ -558,6 +558,7 @@ gtk_private_h_sources = \
gtkcsskeyframesprivate.h \
gtkcsslookupprivate.h \
gtkcssmatcherprivate.h \
gtkcssnodedeclarationprivate.h \
gtkcssnumbervalueprivate.h \
gtkcssparserprivate.h \
gtkcsspositionvalueprivate.h \
......@@ -881,6 +882,7 @@ gtk_base_c_sources = \
gtkcsskeyframes.c \
gtkcsslookup.c \
gtkcssmatcher.c \
gtkcssnodedeclaration.c \
gtkcssnumbervalue.c \
gtkcssparser.c \
gtkcsspositionvalue.c \
......
/*
* Copyright © 2014 Benjamin Otte <otte@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkcssnodedeclarationprivate.h"
#include <string.h>
typedef struct _GtkRegion GtkRegion;
struct _GtkRegion
{
GQuark class_quark;
GtkRegionFlags flags;
};
struct _GtkCssNodeDeclaration {
guint refcount;
GtkJunctionSides junction_sides;
GtkStateFlags state;
guint n_classes;
guint n_regions;
/* GQuark classes[n_classes]; */
/* GtkRegion region[n_regions]; */
};
static inline GQuark *
get_classes (const GtkCssNodeDeclaration *decl)
{
return (GQuark *) (decl + 1);
}
static inline GtkRegion *
get_regions (const GtkCssNodeDeclaration *decl)
{
return (GtkRegion *) (get_classes (decl) + decl->n_classes);
}
static inline gsize
sizeof_node (guint n_classes,
guint n_regions)
{
return sizeof (GtkCssNodeDeclaration)
+ sizeof (GQuark) * n_classes
+ sizeof (GtkRegion) * n_regions;
}
static inline gsize
sizeof_this_node (GtkCssNodeDeclaration *decl)
{
return sizeof_node (decl->n_classes, decl->n_regions);
}
static void
gtk_css_node_declaration_make_writable (GtkCssNodeDeclaration **decl)
{
if ((*decl)->refcount == 1)
return;
(*decl)->refcount--;
*decl = g_memdup (*decl, sizeof_this_node (*decl));
(*decl)->refcount = 1;
}
static void
gtk_css_node_declaration_make_writable_resize (GtkCssNodeDeclaration **decl,
gsize offset,
gsize bytes_added,
gsize bytes_removed)
{
gsize old_size = sizeof_this_node (*decl);
gsize new_size = old_size + bytes_added - bytes_removed;
if ((*decl)->refcount == 1)
{
if (bytes_removed > 0 && old_size - offset - bytes_removed > 0)
memmove (((char *) *decl) + offset + bytes_removed, ((char *) *decl) + offset, old_size - offset - bytes_removed);
*decl = g_realloc (*decl, new_size);
if (bytes_added > 0 && old_size - offset > 0)
memmove (((char *) *decl) + offset + bytes_added, ((char *) *decl) + offset, old_size - offset);
}
else
{
GtkCssNodeDeclaration *old = *decl;
old->refcount--;
*decl = g_malloc (new_size);
memcpy (*decl, old, offset);
if (old_size - offset - bytes_removed > 0)
memcpy (((char *) *decl) + offset + bytes_added, ((char *) old) + offset + bytes_removed, old_size - offset - bytes_removed);
(*decl)->refcount = 1;
}
}
GtkCssNodeDeclaration *
gtk_css_node_declaration_new (void)
{
static GtkCssNodeDeclaration empty = {
1, /* need to own a ref ourselves so the copy-on-write path kicks in when people change things */
0,
0,
0,
0
};
return gtk_css_node_declaration_ref (&empty);
}
GtkCssNodeDeclaration *
gtk_css_node_declaration_ref (GtkCssNodeDeclaration *decl)
{
decl->refcount++;
return decl;
}
void
gtk_css_node_declaration_unref (GtkCssNodeDeclaration *decl)
{
decl->refcount--;
if (decl->refcount > 0)
return;
g_free (decl);
}
gboolean
gtk_css_node_declaration_set_junction_sides (GtkCssNodeDeclaration **decl,
GtkJunctionSides junction_sides)
{
if ((*decl)->junction_sides == junction_sides)
return FALSE;
gtk_css_node_declaration_make_writable (decl);
(*decl)->junction_sides = junction_sides;
return TRUE;
}
GtkJunctionSides
gtk_css_node_declaration_get_junction_sides (const GtkCssNodeDeclaration *decl)
{
return decl->junction_sides;
}
gboolean
gtk_css_node_declaration_set_state (GtkCssNodeDeclaration **decl,
GtkStateFlags state)
{
if ((*decl)->state == state)
return FALSE;
gtk_css_node_declaration_make_writable (decl);
(*decl)->state = state;
return TRUE;
}
GtkStateFlags
gtk_css_node_declaration_get_state (const GtkCssNodeDeclaration *decl)
{
return decl->state;
}
static gboolean
find_class (const GtkCssNodeDeclaration *decl,
GQuark class_quark,
guint *position)
{
gint min, max, mid;
gboolean found = FALSE;
GQuark *classes;
guint pos;
if (position)
*position = 0;
if (decl->n_classes == 0)
return FALSE;
min = 0;
max = decl->n_classes - 1;
classes = get_classes (decl);
do
{
GQuark item;
mid = (min + max) / 2;
item = classes[mid];
if (class_quark == item)
{
found = TRUE;
pos = mid;
break;
}
else if (class_quark > item)
min = pos = mid + 1;
else
{
max = mid - 1;
pos = mid;
}
}
while (min <= max);
if (position)
*position = pos;
return found;
}
gboolean
gtk_css_node_declaration_add_class (GtkCssNodeDeclaration **decl,
GQuark class_quark)
{
guint pos;
if (find_class (*decl, class_quark, &pos))
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) &get_classes (*decl)[pos] - (char *) *decl,
sizeof (GQuark),
0);
(*decl)->n_classes++;
get_classes(*decl)[pos] = class_quark;
return TRUE;
}
gboolean
gtk_css_node_declaration_remove_class (GtkCssNodeDeclaration **decl,
GQuark class_quark)
{
guint pos;
if (!find_class (*decl, class_quark, &pos))
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) &get_classes (*decl)[pos] - (char *) *decl,
0,
sizeof (GQuark));
(*decl)->n_classes--;
return TRUE;
}
gboolean
gtk_css_node_declaration_has_class (const GtkCssNodeDeclaration *decl,
GQuark class_quark)
{
return find_class (decl, class_quark, NULL);
}
GList *
gtk_css_node_declaration_list_classes (const GtkCssNodeDeclaration *decl)
{
GQuark *classes;
GList *result;
guint i;
classes = get_classes (decl);
result = NULL;
for (i = 0; i < decl->n_classes; i++)
{
result = g_list_prepend (result, GUINT_TO_POINTER (classes[i]));
}
return result;
}
static gboolean
find_region (const GtkCssNodeDeclaration *decl,
GQuark region_quark,
guint *position)
{
gint min, max, mid;
gboolean found = FALSE;
GtkRegion *regions;
guint pos;
if (position)
*position = 0;
if (decl->n_regions == 0)
return FALSE;
min = 0;
max = decl->n_regions - 1;
regions = get_regions (decl);
do
{
GQuark item;
mid = (min + max) / 2;
item = regions[mid].class_quark;
if (region_quark == item)
{
found = TRUE;
pos = mid;
break;
}
else if (region_quark > item)
min = pos = mid + 1;
else
{
max = mid - 1;
pos = mid;
}
}
while (min <= max);
if (position)
*position = pos;
return found;
}
gboolean
gtk_css_node_declaration_add_region (GtkCssNodeDeclaration **decl,
GQuark region_quark,
GtkRegionFlags flags)
{
GtkRegion *regions;
guint pos;
if (find_region (*decl, region_quark, &pos))
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) &get_regions (*decl)[pos] - (char *) *decl,
sizeof (GtkRegion),
0);
(*decl)->n_regions++;
regions = get_regions(*decl);
regions[pos].class_quark = region_quark;
regions[pos].flags = flags;
return TRUE;
}
gboolean
gtk_css_node_declaration_remove_region (GtkCssNodeDeclaration **decl,
GQuark region_quark)
{
guint pos;
if (!find_region (*decl, region_quark, &pos))
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) &get_regions (*decl)[pos] - (char *) *decl,
0,
sizeof (GtkRegion));
(*decl)->n_regions--;
return TRUE;
}
gboolean
gtk_css_node_declaration_has_region (const GtkCssNodeDeclaration *decl,
GQuark region_quark,
GtkRegionFlags *flags_return)
{
guint pos;
if (!find_region (decl, region_quark, &pos))
{
if (flags_return)
*flags_return = 0;
return FALSE;
}
if (flags_return)
*flags_return = get_regions (decl)[pos].flags;
return TRUE;
}
GList *
gtk_css_node_declaration_list_regions (const GtkCssNodeDeclaration *decl)
{
GtkRegion *regions;
GList *result;
guint i;
regions = get_regions (decl);
result = NULL;
for (i = 0; i < decl->n_regions; i++)
{
result = g_list_prepend (result, GUINT_TO_POINTER (regions[i].class_quark));
}
return result;
}
guint
gtk_css_node_declaration_hash (gconstpointer elem)
{
const GtkCssNodeDeclaration *decl = elem;
GQuark *classes;
GtkRegion *regions;
guint hash, i;
hash = 0;
classes = get_classes (decl);
for (i = 0; i < decl->n_classes; i++)
{
hash <<= 5;
hash += classes[i];
}
regions = get_regions (decl);
for (i = 0; i < decl->n_regions; i++)
{
hash <<= 5;
hash += regions[i].class_quark;
hash += regions[i].flags;
}
hash ^= ((guint) decl->junction_sides) << (sizeof (guint) * 8 - 5);
hash ^= decl->state;
return hash;
}
gboolean
gtk_css_node_declaration_equal (gconstpointer elem1,
gconstpointer elem2)
{
const GtkCssNodeDeclaration *decl1 = elem1;
const GtkCssNodeDeclaration *decl2 = elem2;
GQuark *classes1, *classes2;
GtkRegion *regions1, *regions2;
guint i;
if (decl1 == decl2)
return TRUE;
if (decl1->state != decl2->state)
return FALSE;
if (decl1->n_classes != decl2->n_classes)
return FALSE;
classes1 = get_classes (decl1);
classes2 = get_classes (decl2);
for (i = 0; i < decl1->n_classes; i++)
{
if (classes1[i] != classes2[i])
return FALSE;
}
if (decl1->n_regions != decl2->n_regions)
return FALSE;
regions1 = get_regions (decl1);
regions2 = get_regions (decl2);
for (i = 0; i < decl1->n_regions; i++)
{
if (regions1[i].class_quark != regions2[i].class_quark ||
regions1[i].flags != regions2[i].flags)
return FALSE;
}
if (decl1->junction_sides != decl2->junction_sides)
return FALSE;
return TRUE;
}
void
gtk_css_node_declaration_add_to_widget_path (const GtkCssNodeDeclaration *decl,
GtkWidgetPath *path,
guint pos)
{
GQuark *classes;
GtkRegion *regions;
guint i;
/* Set widget regions */
regions = get_regions (decl);
for (i = 0; i < decl->n_regions; i++)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gtk_widget_path_iter_add_region (path, pos,
g_quark_to_string (regions[i].class_quark),
regions[i].flags);
G_GNUC_END_IGNORE_DEPRECATIONS
}
/* Set widget classes */
classes = get_classes (decl);
for (i = 0; i < decl->n_classes; i++)
{
gtk_widget_path_iter_add_class (path, pos,
g_quark_to_string (classes[i]));
}
/* Set widget state */
gtk_widget_path_iter_set_state (path, pos, decl->state);
}
/*
* Copyright © 2014 Benjamin Otte <otte@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_CSS_NODE_DECLARATION_PRIVATE_H__
#define __GTK_CSS_NODE_DECLARATION_PRIVATE_H__
#include "gtkenums.h"
#include "gtkwidgetpath.h"
G_BEGIN_DECLS
typedef struct _GtkCssNodeDeclaration GtkCssNodeDeclaration;
GtkCssNodeDeclaration * gtk_css_node_declaration_new (void);
GtkCssNodeDeclaration * gtk_css_node_declaration_ref (GtkCssNodeDeclaration *decl);
void gtk_css_node_declaration_unref (GtkCssNodeDeclaration *decl);
gboolean gtk_css_node_declaration_set_junction_sides (GtkCssNodeDeclaration **decl,
GtkJunctionSides junction_sides);
GtkJunctionSides gtk_css_node_declaration_get_junction_sides (const GtkCssNodeDeclaration *decl);
gboolean gtk_css_node_declaration_set_state (GtkCssNodeDeclaration **decl,
GtkStateFlags flags);
GtkStateFlags gtk_css_node_declaration_get_state (const GtkCssNodeDeclaration *decl);
gboolean gtk_css_node_declaration_add_class (GtkCssNodeDeclaration **decl,
GQuark class_quark);
gboolean gtk_css_node_declaration_remove_class (GtkCssNodeDeclaration **decl,
GQuark class_quark);
gboolean gtk_css_node_declaration_has_class (const GtkCssNodeDeclaration *decl,
GQuark class_quark);
GList * gtk_css_node_declaration_list_classes (const GtkCssNodeDeclaration *decl);
gboolean gtk_css_node_declaration_add_region (GtkCssNodeDeclaration **decl,
GQuark region_quark,
GtkRegionFlags flags);
gboolean gtk_css_node_declaration_remove_region (GtkCssNodeDeclaration **decl,
GQuark region_quark);
gboolean gtk_css_node_declaration_has_region (const GtkCssNodeDeclaration *decl,
GQuark region_quark,
GtkRegionFlags *flags_return);
GList * gtk_css_node_declaration_list_regions (const GtkCssNodeDeclaration *decl);
guint gtk_css_node_declaration_hash (gconstpointer elem);
gboolean gtk_css_node_declaration_equal (gconstpointer elem1,
gconstpointer elem2);
void gtk_css_node_declaration_add_to_widget_path (const GtkCssNodeDeclaration *decl,
GtkWidgetPath *path,
guint pos);
G_END_DECLS
#endif /* __GTK_CSS_NODE_DECLARATION_PRIVATE_H__ */
<
......@@ -28,6 +28,7 @@
#include "gtkcsscornervalueprivate.h"
#include "gtkcssenumvalueprivate.h"
#include "gtkcssimagevalueprivate.h"
#include "gtkcssnodedeclarationprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkcssrgbavalueprivate.h"
#include "gtkcssshadowsvalueprivate.h"
......@@ -129,15 +130,8 @@
#define GTK_STYLE_CONTEXT_CACHED_CHANGE (GTK_CSS_CHANGE_STATE)
typedef struct GtkStyleInfo GtkStyleInfo;
typedef struct GtkRegion GtkRegion;
typedef struct PropertyValue PropertyValue;
struct GtkRegion
{
GQuark class_quark;
GtkRegionFlags flags;
};
struct PropertyValue
{
GType widget_type;
......@@ -147,10 +141,7 @@ struct PropertyValue
struct GtkStyleInfo
{
GArray *style_classes;
GArray *regions;
GtkJunctionSides junction_sides;
GtkStateFlags state_flags;
GtkCssNodeDeclaration *decl;
GtkCssComputedValues *values;
};
......@@ -306,8 +297,7 @@ style_info_new (void)
GtkStyleInfo *info;
info = g_slice_new0 (GtkStyleInfo);
info->style_classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
info->regions = g_array_new (FALSE, FALSE, sizeof (GtkRegion));
info->decl = gtk_css_node_declaration_new ();
return info;
}
......@@ -333,8 +323,7 @@ style_info_free (GtkStyleInfo *info)
{
if (info->values)
g_object_unref (info->values);
g_array_free (info->style_classes, TRUE);
g_array_free (info->regions, TRUE);
gtk_css_node_declaration_unref (info->decl);
g_slice_free (GtkStyleInfo, info);
}
......@@ -356,16 +345,7 @@ style_info_copy (GtkStyleInfo *info)
GtkStyleInfo *copy;
copy = style_info_new ();
g_array_insert_vals (copy->style_classes, 0,
info->style_classes->data,
info->style_classes->len);
g_array_insert_vals (copy->regions, 0,
info->regions->data,
info->regions->len);
copy->junction_sides = info->junction_sides;
copy->state_flags = info->state_flags;
copy->decl = gtk_css_node_declaration_ref (info->decl);
style_info_set_values (copy, info->values);
return copy;
......@@ -374,28 +354,9 @@ style_info_copy (GtkStyleInfo *info)
static guint
style_info_hash (gconstpointer elem)
{
const GtkStyleInfo *info;
guint i, hash = 0;
info = elem;
for (i = 0; i < info->style_classes->len; i++)
{
hash += g_array_index (info->style_classes, GQuark, i);
hash <<= 5;
}
const GtkStyleInfo *info = elem;
for (i = 0; i < info->regions->len; i++)
{
GtkRegion *region;
region = &g_array_index (info->regions, GtkRegion, i);
hash += region->class_quark;
hash += region->flags;
hash <<= 5;
}
return hash ^ info->state_flags;
return gtk_css_node_declaration_hash (info->decl);
}
static gboolean
......@@ -407,29 +368,7 @@ style_info_equal (gconstpointer elem1,
info1 = elem1;
info2 = elem2;
if (info1->state_flags != info2->state_flags)
return FALSE;
if (info1->junction_sides != info2->junction_sides)
return FALSE;
if (info1->style_classes->len != info2->style_classes->len)
return FALSE;
if (memcmp (info1->style_classes->data,
info2->style_classes->data,
info1->style_classes->len * sizeof (GQuark)) != 0)
return FALSE;
if (info1->regions->len != info2->regions->len)
return FALSE;
if (memcmp (info1->regions->data,
info2->regions->data,
info1->regions->len * sizeof (GtkRegion)) != 0)
return FALSE;
return TRUE;
return gtk_css_node_declaration_equal (info1->decl, info2->decl);
}
static void
......@@ -490,7 +429,7 @@ gtk_style_context_init (GtkStyleContext *style_context)