gtk2/gtk/gtkbuiltinicon.c

338 lines
11 KiB
C
Raw Normal View History

/*
* 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"
2015-12-22 19:09:36 +00:00
/* 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;
builtinicon: avoid calculating font-metrics in vast majority of cases 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
2016-04-25 07:22:54 +00:00
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)
builtinicon: avoid calculating font-metrics in vast majority of cases 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
2016-04-25 07:22:54 +00:00
{
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;
}
builtinicon: avoid calculating font-metrics in vast majority of cases 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
2016-04-25 07:22:54 +00:00
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))
builtinicon: avoid calculating font-metrics in vast majority of cases 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
2016-04-25 07:22:54 +00:00
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;
builtinicon: avoid calculating font-metrics in vast majority of cases 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
2016-04-25 07:22:54 +00:00
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;
}