mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-10 19:00:08 +00:00
f165bbda57
We perform lots of gadget allocations that require allocating a GtkBuiltinIcon. One notable example is the scrollbar for a scrolled window. In the process of doing this, we often calculate baseline information that isn't necessary. With how much this code path gets exercised, its worth catching the result for the common case, which is that the font-description has not changed and we are using the default language the application was started with. This simply caches the previous result and verifies that we can reuse it with pango_font_description_hash() and a simple language check. Numbers below are scrolling through a textview with GDK_KEY_Down. Before: SELF CUMULATIVE FUNCTION [ 0.08%] [ 9.26%] gtk_builtin_icon_get_preferred_size [ 0.01%] [ 8.82%] pango_context_get_metrics [ 0.02%] [ 0.16%] gtk_widget_get_pango_context [ 0.06%] [ 0.06%] pango_context_get_language [ 0.01%] [ 0.02%] g_type_check_instance_cast [ 0.02%] [ 0.02%] strlen [ 0.02%] [ 0.02%] pango_context_get_font_description [ 0.02%] [ 0.02%] g_list_foreach [ 0.01%] [ 0.01%] gtk_css_style_get_value [ 0.01%] [ 0.01%] itemize_with_font [ 0.01%] [ 0.01%] pango_context_get_type [ 0.01%] [ 0.01%] get_base_metrics [ 0.00%] [ 0.01%] pango_font_metrics_unref [ 0.01%] [ 0.01%] g_list_free [ 0.01%] [ 0.01%] gtk_builtin_icon_get_type After: SELF CUMULATIVE FUNCTION [ 0.08%] [ 0.18%] gtk_builtin_icon_get_preferred_size [ 0.02%] [ 0.02%] pango_font_description_hash [ 0.00%] [ 0.02%] gtk_widget_get_pango_context [ 0.00%] [ 0.02%] g_object_get_qdata [ 0.00%] [ 0.02%] g_datalist_id_get_data [ 0.02%] [ 0.02%] gtk_builtin_icon_get_type [ 0.01%] [ 0.01%] pango_context_get_font_description [ 0.00%] [ 0.01%] - - kernel - - [ 0.01%] [ 0.01%] pango_context_get_language [ 0.00%] [ 0.01%] gtk_css_style_get_value [ 0.00%] [ 0.01%] gtk_css_gadget_get_style https://bugzilla.gnome.org/show_bug.cgi?id=765486
338 lines
11 KiB
C
338 lines
11 KiB
C
/*
|
|
* Copyright © 2015 Red Hat Inc.
|
|
*
|
|
* 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/>.
|
|
*
|
|
* Authors: Benjamin Otte <otte@gnome.org>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkbuiltiniconprivate.h"
|
|
|
|
#include "gtkcssnodeprivate.h"
|
|
#include "gtkcssnumbervalueprivate.h"
|
|
#include "gtkrendericonprivate.h"
|
|
|
|
/* GtkBuiltinIcon is a gadget implementation that is meant to replace
|
|
* all calls to gtk_render_ functions to render arrows, expanders, checks
|
|
* radios, handles, separators, etc. See the GtkCssImageBuiltinType
|
|
* enumeration for the full set of builtin icons that this gadget can
|
|
* render.
|
|
*
|
|
* Use gtk_builtin_icon_set_image to set which of the builtin icons
|
|
* is rendered.
|
|
*
|
|
* Use gtk_builtin_icon_set_default_size to set a non-zero default
|
|
* size for the icon. If you need to support a legacy size style property,
|
|
* use gtk_builtin_icon_set_default_size_property.
|
|
*
|
|
* Themes can override the acutal image that is used with the
|
|
* -gtk-icon-source property. If it is not specified, a builtin
|
|
* fallback is used.
|
|
*/
|
|
|
|
typedef struct _GtkBuiltinIconPrivate GtkBuiltinIconPrivate;
|
|
struct _GtkBuiltinIconPrivate {
|
|
GtkCssImageBuiltinType image_type;
|
|
int default_size;
|
|
int strikethrough;
|
|
gboolean strikethrough_valid;
|
|
char * default_size_property;
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GtkBuiltinIcon, gtk_builtin_icon, GTK_TYPE_CSS_GADGET,
|
|
G_ADD_PRIVATE (GtkBuiltinIcon))
|
|
|
|
static void
|
|
gtk_builtin_icon_get_preferred_size (GtkCssGadget *gadget,
|
|
GtkOrientation orientation,
|
|
gint for_size,
|
|
gint *minimum,
|
|
gint *natural,
|
|
gint *minimum_baseline,
|
|
gint *natural_baseline)
|
|
{
|
|
GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (gadget));
|
|
double min_size;
|
|
guint property;
|
|
|
|
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
|
property = GTK_CSS_PROPERTY_MIN_WIDTH;
|
|
else
|
|
property = GTK_CSS_PROPERTY_MIN_HEIGHT;
|
|
min_size = _gtk_css_number_value_get (gtk_css_style_get_value (gtk_css_gadget_get_style (gadget), property), 100);
|
|
if (min_size > 0.0)
|
|
{
|
|
*minimum = *natural = min_size;
|
|
}
|
|
else if (priv->default_size_property)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
/* Do it a bit more complicated here so we get warnings when
|
|
* somebody sets a non-int proerty.
|
|
*/
|
|
g_value_init (&value, G_TYPE_INT);
|
|
gtk_widget_style_get_property (gtk_css_gadget_get_owner (gadget),
|
|
priv->default_size_property,
|
|
&value);
|
|
*minimum = *natural = g_value_get_int (&value);
|
|
g_value_unset (&value);
|
|
}
|
|
else
|
|
{
|
|
*minimum = *natural = priv->default_size;
|
|
}
|
|
|
|
if (minimum_baseline)
|
|
{
|
|
if (!priv->strikethrough_valid)
|
|
{
|
|
GtkWidget *widget;
|
|
PangoContext *pango_context;
|
|
const PangoFontDescription *font_desc;
|
|
PangoFontMetrics *metrics;
|
|
|
|
widget = gtk_css_gadget_get_owner (gadget);
|
|
pango_context = gtk_widget_get_pango_context (widget);
|
|
font_desc = pango_context_get_font_description (pango_context);
|
|
|
|
metrics = pango_context_get_metrics (pango_context,
|
|
font_desc,
|
|
pango_context_get_language (pango_context));
|
|
priv->strikethrough = pango_font_metrics_get_strikethrough_position (metrics);
|
|
priv->strikethrough_valid = TRUE;
|
|
|
|
pango_font_metrics_unref (metrics);
|
|
}
|
|
|
|
*minimum_baseline = *minimum * 0.5 + PANGO_PIXELS (priv->strikethrough);
|
|
}
|
|
|
|
if (natural_baseline)
|
|
*natural_baseline = *minimum_baseline;
|
|
}
|
|
|
|
static void
|
|
gtk_builtin_icon_allocate (GtkCssGadget *gadget,
|
|
const GtkAllocation *allocation,
|
|
int baseline,
|
|
GtkAllocation *out_clip)
|
|
{
|
|
GdkRectangle icon_clip;
|
|
|
|
GTK_CSS_GADGET_CLASS (gtk_builtin_icon_parent_class)->allocate (gadget, allocation, baseline, out_clip);
|
|
|
|
gtk_css_style_render_icon_get_extents (gtk_css_gadget_get_style (gadget),
|
|
&icon_clip,
|
|
allocation->x, allocation->y,
|
|
allocation->width, allocation->height);
|
|
gdk_rectangle_union (out_clip, &icon_clip, out_clip);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_builtin_icon_draw (GtkCssGadget *gadget,
|
|
cairo_t *cr,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (gadget));
|
|
|
|
gtk_css_style_render_icon (gtk_css_gadget_get_style (gadget),
|
|
cr,
|
|
x, y,
|
|
width, height,
|
|
priv->image_type);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gtk_builtin_icon_style_changed (GtkCssGadget *gadget,
|
|
GtkCssStyleChange *change)
|
|
{
|
|
GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (gadget));
|
|
|
|
if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_FONT))
|
|
priv->strikethrough_valid = FALSE;
|
|
|
|
GTK_CSS_GADGET_CLASS (gtk_builtin_icon_parent_class)->style_changed (gadget, change);
|
|
}
|
|
|
|
static void
|
|
gtk_builtin_icon_finalize (GObject *object)
|
|
{
|
|
GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (object));
|
|
|
|
g_free (priv->default_size_property);
|
|
|
|
G_OBJECT_CLASS (gtk_builtin_icon_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gtk_builtin_icon_class_init (GtkBuiltinIconClass *klass)
|
|
{
|
|
GtkCssGadgetClass *gadget_class = GTK_CSS_GADGET_CLASS (klass);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = gtk_builtin_icon_finalize;
|
|
|
|
gadget_class->get_preferred_size = gtk_builtin_icon_get_preferred_size;
|
|
gadget_class->allocate = gtk_builtin_icon_allocate;
|
|
gadget_class->draw = gtk_builtin_icon_draw;
|
|
gadget_class->style_changed = gtk_builtin_icon_style_changed;
|
|
}
|
|
|
|
static void
|
|
gtk_builtin_icon_init (GtkBuiltinIcon *custom_gadget)
|
|
{
|
|
}
|
|
|
|
GtkCssGadget *
|
|
gtk_builtin_icon_new_for_node (GtkCssNode *node,
|
|
GtkWidget *owner)
|
|
{
|
|
return g_object_new (GTK_TYPE_BUILTIN_ICON,
|
|
"node", node,
|
|
"owner", owner,
|
|
NULL);
|
|
}
|
|
|
|
GtkCssGadget *
|
|
gtk_builtin_icon_new (const char *name,
|
|
GtkWidget *owner,
|
|
GtkCssGadget *parent,
|
|
GtkCssGadget *next_sibling)
|
|
{
|
|
GtkCssNode *node;
|
|
GtkCssGadget *result;
|
|
|
|
node = gtk_css_node_new ();
|
|
gtk_css_node_set_name (node, g_intern_string (name));
|
|
if (parent)
|
|
gtk_css_node_insert_before (gtk_css_gadget_get_node (parent),
|
|
node,
|
|
next_sibling ? gtk_css_gadget_get_node (next_sibling) : NULL);
|
|
|
|
result = gtk_builtin_icon_new_for_node (node, owner);
|
|
|
|
g_object_unref (node);
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
gtk_builtin_icon_set_image (GtkBuiltinIcon *icon,
|
|
GtkCssImageBuiltinType image)
|
|
{
|
|
GtkBuiltinIconPrivate *priv;
|
|
|
|
g_return_if_fail (GTK_IS_BUILTIN_ICON (icon));
|
|
|
|
priv = gtk_builtin_icon_get_instance_private (icon);
|
|
|
|
if (priv->image_type != image)
|
|
{
|
|
priv->image_type = image;
|
|
gtk_widget_queue_draw (gtk_css_gadget_get_owner (GTK_CSS_GADGET (icon)));
|
|
}
|
|
}
|
|
|
|
GtkCssImageBuiltinType
|
|
gtk_builtin_icon_get_image (GtkBuiltinIcon *icon)
|
|
{
|
|
GtkBuiltinIconPrivate *priv;
|
|
|
|
g_return_val_if_fail (GTK_IS_BUILTIN_ICON (icon), GTK_CSS_IMAGE_BUILTIN_NONE);
|
|
|
|
priv = gtk_builtin_icon_get_instance_private (icon);
|
|
|
|
return priv->image_type;
|
|
}
|
|
|
|
void
|
|
gtk_builtin_icon_set_default_size (GtkBuiltinIcon *icon,
|
|
int default_size)
|
|
{
|
|
GtkBuiltinIconPrivate *priv;
|
|
|
|
g_return_if_fail (GTK_IS_BUILTIN_ICON (icon));
|
|
|
|
priv = gtk_builtin_icon_get_instance_private (icon);
|
|
|
|
if (priv->default_size != default_size)
|
|
{
|
|
priv->default_size = default_size;
|
|
gtk_widget_queue_resize (gtk_css_gadget_get_owner (GTK_CSS_GADGET (icon)));
|
|
}
|
|
}
|
|
|
|
int
|
|
gtk_builtin_icon_get_default_size (GtkBuiltinIcon *icon)
|
|
{
|
|
GtkBuiltinIconPrivate *priv;
|
|
|
|
g_return_val_if_fail (GTK_IS_BUILTIN_ICON (icon), GTK_CSS_IMAGE_BUILTIN_NONE);
|
|
|
|
priv = gtk_builtin_icon_get_instance_private (icon);
|
|
|
|
return priv->default_size;
|
|
}
|
|
|
|
/**
|
|
* gtk_builtin_icon_set_default_size_property:
|
|
* @icon: icon to set the property for
|
|
* @property_name: Name of the style property
|
|
*
|
|
* Sets the name of a widget style property to use to compute the default size
|
|
* of the icon. If it is set to no %NULL, it will be used instead of the value
|
|
* set via gtk_builtin_icon_set_default_size() to set the default size of the
|
|
* icon.
|
|
*
|
|
* @property_name must refer to a style property that is of integer type.
|
|
*
|
|
* This function is intended strictly for backwards compatibility reasons.
|
|
*/
|
|
void
|
|
gtk_builtin_icon_set_default_size_property (GtkBuiltinIcon *icon,
|
|
const char *property_name)
|
|
{
|
|
GtkBuiltinIconPrivate *priv;
|
|
|
|
g_return_if_fail (GTK_IS_BUILTIN_ICON (icon));
|
|
|
|
priv = gtk_builtin_icon_get_instance_private (icon);
|
|
|
|
if (g_strcmp0 (priv->default_size_property, property_name))
|
|
{
|
|
priv->default_size_property = g_strdup (property_name);
|
|
gtk_widget_queue_resize (gtk_css_gadget_get_owner (GTK_CSS_GADGET (icon)));
|
|
}
|
|
}
|
|
|
|
const char *
|
|
gtk_builtin_icon_get_default_size_property (GtkBuiltinIcon *icon)
|
|
{
|
|
GtkBuiltinIconPrivate *priv;
|
|
|
|
g_return_val_if_fail (GTK_IS_BUILTIN_ICON (icon), NULL);
|
|
|
|
priv = gtk_builtin_icon_get_instance_private (icon);
|
|
|
|
return priv->default_size_property;
|
|
}
|