[gtk+/wip/window-scales: 77/84] icon factory: Support scaled icons



commit d67701fa9f7910ba98ab0a8a9d66df6e8ffa9cb7
Author: Alexander Larsson <alexl redhat com>
Date:   Tue Jun 25 13:59:51 2013 +0200

    icon factory: Support scaled icons
    
    Support setting a custom scale (or wildcarding) of a GtkIconSource, which
    will cause that icon to only be used for a particular scale. This is useful
    when you want to add two pixbufs as icon sources for two scales.
    
    We also support scales when falling back to loading icons from the
    icon theme.
    
    In order to actually render scaled icons we add gtk_icon_set_render_icon_pattern
    which renders to a cairo_pattern_t which includes whatever scaling you
    need for scaled icons.

 gtk/gtkiconfactory.c  |  270 ++++++++++++++++++++++++++++++++++++++++++-------
 gtk/gtkiconfactory.h  |   11 ++-
 gtk/gtkstylecontext.h |   12 ++
 3 files changed, 253 insertions(+), 40 deletions(-)
---
diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c
index 7dfd888..92994d3 100644
--- a/gtk/gtkiconfactory.c
+++ b/gtk/gtkiconfactory.c
@@ -172,6 +172,7 @@ struct _GtkIconSource
   GtkTextDirection direction;
   GtkStateType state;
   GtkIconSize size;
+  int scale;
 
   /* If TRUE, then the parameter is wildcarded, and the above
    * fields should be ignored. If FALSE, the parameter is
@@ -180,6 +181,7 @@ struct _GtkIconSource
   guint any_direction : 1;
   guint any_state : 1;
   guint any_size : 1;
+  guint any_scale : 1;
 };
 
 
@@ -205,10 +207,10 @@ static GtkIconSize icon_size_register_intern (const gchar *name,
                                              gint         width,
                                              gint         height);
 
-#define GTK_ICON_SOURCE_INIT(any_direction, any_state, any_size)       \
+#define GTK_ICON_SOURCE_INIT(any_direction, any_state, any_size, any_scale)    \
   { GTK_ICON_SOURCE_EMPTY, { NULL }, NULL,                             \
-   0, 0, 0,                                                            \
-   any_direction, any_state, any_size }
+   0, 0, 0, 1,                                                         \
+   any_direction, any_state, any_size, any_scale }
 
 G_DEFINE_TYPE_WITH_CODE (GtkIconFactory, gtk_icon_factory, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
@@ -482,7 +484,7 @@ register_stock_icon (GtkIconFactory *factory,
                      const gchar    *icon_name)
 {
   GtkIconSet *set = gtk_icon_set_new ();
-  GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
+  GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE, TRUE);
 
   source.type = GTK_ICON_SOURCE_STATIC_ICON_NAME;
   source.source.icon_name = (gchar *)icon_name;
@@ -499,7 +501,7 @@ register_bidi_stock_icon (GtkIconFactory *factory,
                           const gchar    *icon_name)
 {
   GtkIconSet *set = gtk_icon_set_new ();
-  GtkIconSource source = GTK_ICON_SOURCE_INIT (FALSE, TRUE, TRUE);
+  GtkIconSource source = GTK_ICON_SOURCE_INIT (FALSE, TRUE, TRUE, TRUE);
 
   source.type = GTK_ICON_SOURCE_STATIC_ICON_NAME;
   source.source.icon_name = (gchar *)icon_name;
@@ -1208,12 +1210,14 @@ static GdkPixbuf *find_in_cache     (GtkIconSet       *icon_set,
                                      GtkStyleContext  *style_context,
                                      GtkTextDirection  direction,
                                      GtkStateType      state,
-                                     GtkIconSize       size);
+                                     GtkIconSize       size,
+                                     gint              scale);
 static void       add_to_cache      (GtkIconSet       *icon_set,
                                      GtkStyleContext  *style_context,
                                      GtkTextDirection  direction,
                                      GtkStateType      state,
                                      GtkIconSize       size,
+                                     gint              scale,
                                      GdkPixbuf        *pixbuf);
 /* Clear icon set contents, drop references to all contained
  * GdkPixbuf objects and forget all GtkIconSources. Used to
@@ -1293,7 +1297,7 @@ gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf)
 {
   GtkIconSet *set;
 
-  GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
+  GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE, TRUE);
 
   g_return_val_if_fail (pixbuf != NULL, NULL);
 
@@ -1424,6 +1428,7 @@ find_best_matching_source (GtkIconSet       *icon_set,
                           GtkTextDirection  direction,
                           GtkStateType      state,
                           GtkIconSize       size,
+                          gint              scale,
                           GSList           *failed)
 {
   GtkIconSource *source;
@@ -1445,7 +1450,8 @@ find_best_matching_source (GtkIconSet       *icon_set,
 
       if ((s->any_direction || (s->direction == direction)) &&
           (s->any_state || (s->state == state)) &&
-          (s->any_size || size == (GtkIconSize)-1 || (sizes_equivalent (size, s->size))))
+          (s->any_size || size == (GtkIconSize)-1 || (sizes_equivalent (size, s->size))) &&
+          (s->any_scale || (s->scale == scale)))
         {
          if (!g_slist_find (failed, s))
            {
@@ -1492,7 +1498,8 @@ ensure_filename_pixbuf (GtkIconSet    *icon_set,
 static GdkPixbuf *
 render_icon_name_pixbuf (GtkIconSource    *icon_source,
                         GtkStyleContext  *context,
-                        GtkIconSize       size)
+                        GtkIconSize       size,
+                         gint              scale)
 {
   GdkPixbuf *pixbuf;
   GdkPixbuf *tmp_pixbuf;
@@ -1562,9 +1569,10 @@ render_icon_name_pixbuf (GtkIconSource    *icon_source,
       names[1] = icon_source->source.icon_name;
       names[2] = NULL;
 
-      info = gtk_icon_theme_choose_icon (icon_theme,
-                                         (const char **) names,
-                                         pixel_size, GTK_ICON_LOOKUP_USE_BUILTIN);
+      info = gtk_icon_theme_choose_icon_for_scale (icon_theme,
+                                                   (const char **) names,
+                                                   pixel_size, scale,
+                                                   GTK_ICON_LOOKUP_USE_BUILTIN);
       g_free (names[0]);
       if (info)
         {
@@ -1576,10 +1584,10 @@ render_icon_name_pixbuf (GtkIconSource    *icon_source,
     }
   else
     {
-      tmp_pixbuf = gtk_icon_theme_load_icon (icon_theme,
-                                             icon_source->source.icon_name,
-                                             pixel_size, 0,
-                                             &error);
+      tmp_pixbuf = gtk_icon_theme_load_icon_for_scale (icon_theme,
+                                                       icon_source->source.icon_name,
+                                                       pixel_size, scale, 0,
+                                                       &error);
     }
 
   if (!tmp_pixbuf)
@@ -1610,7 +1618,8 @@ find_and_render_icon_source (GtkIconSet       *icon_set,
                             GtkStyleContext  *context,
                             GtkTextDirection  direction,
                             GtkStateType      state,
-                            GtkIconSize       size)
+                            GtkIconSize       size,
+                            gint              scale)
 {
   GSList *failed = NULL;
   GdkPixbuf *pixbuf = NULL;
@@ -1627,7 +1636,7 @@ find_and_render_icon_source (GtkIconSet       *icon_set,
    */
   while (pixbuf == NULL)
     {
-      GtkIconSource *source = find_best_matching_source (icon_set, direction, state, size, failed);
+      GtkIconSource *source = find_best_matching_source (icon_set, direction, state, size, scale, failed);
 
       if (source == NULL)
        break;
@@ -1645,10 +1654,21 @@ find_and_render_icon_source (GtkIconSet       *icon_set,
              g_warning ("Failed to render icon");
              failed = g_slist_prepend (failed, source);
            }
+
+         if (scale != source->scale)
+           {
+             GdkPixbuf *tmp = pixbuf;
+             pixbuf = gdk_pixbuf_scale_simple (pixbuf,
+                                               gdk_pixbuf_get_width (pixbuf) * scale * source->scale,
+                                               gdk_pixbuf_get_height (pixbuf) * scale * source->scale,
+                                               GDK_INTERP_BILINEAR);
+             g_object_unref (tmp);
+           }
          break;
        case GTK_ICON_SOURCE_ICON_NAME:
        case GTK_ICON_SOURCE_STATIC_ICON_NAME:
-          pixbuf = render_icon_name_pixbuf (source, context, size);
+          pixbuf = render_icon_name_pixbuf (source, context,
+                                            size, scale);
          if (!pixbuf)
            failed = g_slist_prepend (failed, source);
          break;
@@ -1671,7 +1691,7 @@ render_fallback_image (GtkStyleContext   *context,
                        GtkIconSize        size)
 {
   /* This icon can be used for any direction/state/size */
-  static GtkIconSource fallback_source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
+  static GtkIconSource fallback_source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE, TRUE);
 
   if (fallback_source.type == GTK_ICON_SOURCE_EMPTY)
     {
@@ -1693,27 +1713,28 @@ render_fallback_image (GtkStyleContext   *context,
 }
 
 /**
- * gtk_icon_set_render_icon_pixbuf:
+ * gtk_icon_set_render_icon_pixbuf_for_scale:
  * @icon_set: a #GtkIconSet
  * @context: a #GtkStyleContext
  * @size: (type int): icon size. A size of (GtkIconSize)-1
  *        means render at the size of the source and don't scale.
+ * @scale: the window scale to render for
  *
- * Renders an icon using gtk_render_icon_pixbuf(). In most cases,
- * gtk_widget_render_icon_pixbuf() is better, since it automatically provides
- * most of the arguments from the current widget settings.  This
- * function never returns %NULL; if the icon can't be rendered
+ * Renders an icon using gtk_render_icon_pixbuf(). 
+ *
+ * This function never returns %NULL; if the icon can't be rendered
  * (perhaps because an image file fails to load), a default "missing
  * image" icon will be returned instead.
  *
  * Return value: (transfer full): a #GdkPixbuf to be displayed
  *
- * Since: 3.0
+ * Since: 3.10
  */
-GdkPixbuf *
-gtk_icon_set_render_icon_pixbuf (GtkIconSet        *icon_set,
-                                 GtkStyleContext   *context,
-                                 GtkIconSize        size)
+GdkPixbuf*
+gtk_icon_set_render_icon_pixbuf_for_scale (GtkIconSet      *icon_set,
+                                          GtkStyleContext *context,
+                                          GtkIconSize      size,
+                                          gint             scale)
 {
   GdkPixbuf *icon = NULL;
   GtkStateFlags flags = 0;
@@ -1737,26 +1758,98 @@ G_GNUC_END_IGNORE_DEPRECATIONS;
 
   if (icon_set->sources)
     {
-      icon = find_in_cache (icon_set, context, direction, state, size);
+      icon = find_in_cache (icon_set, context, direction, state, size, scale);
       if (icon)
-        {
-          g_object_ref (icon);
-          return icon;
-        }
+       return g_object_ref (icon);
     }
 
   if (icon_set->sources)
-    icon = find_and_render_icon_source (icon_set, context, direction, state, size);
+    icon = find_and_render_icon_source (icon_set, context, direction, state,
+                                        size, scale);
 
   if (icon == NULL)
     icon = render_fallback_image (context, direction, state, size);
 
-  add_to_cache (icon_set, context, direction, state, size, icon);
+  add_to_cache (icon_set, context, direction, state, size, scale, icon);
 
   return icon;
 }
 
 /**
+ * gtk_icon_set_render_icon_pixbuf:
+ * @icon_set: a #GtkIconSet
+ * @context: a #GtkStyleContext
+ * @size: (type int): icon size. A size of (GtkIconSize)-1
+ *        means render at the size of the source and don't scale.
+ *
+ * Renders an icon using gtk_render_icon_pixbuf(). In most cases,
+ * gtk_widget_render_icon_pixbuf() is better, since it automatically provides
+ * most of the arguments from the current widget settings.  This
+ * function never returns %NULL; if the icon can't be rendered
+ * (perhaps because an image file fails to load), a default "missing
+ * image" icon will be returned instead.
+ *
+ * Return value: (transfer full): a #GdkPixbuf to be displayed
+ *
+ * Since: 3.0
+ */
+GdkPixbuf *
+gtk_icon_set_render_icon_pixbuf (GtkIconSet        *icon_set,
+                                 GtkStyleContext   *context,
+                                 GtkIconSize        size)
+{
+  g_return_val_if_fail (icon_set != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+  return gtk_icon_set_render_icon_pixbuf_for_scale (icon_set, context, size, 1);
+}
+
+/**
+ * gtk_icon_set_render_icon_pattern:
+ * @icon_set: a #GtkIconSet
+ * @context: a #GtkStyleContext
+ * @size: (type int): icon size. A size of (GtkIconSize)-1
+ *        means render at the size of the source and don't scale.
+ * @scale: the window scale to render for
+ * @for_window: (allow-none): #GdkWindow to optimize drawing for, or %NULL
+ *
+ * Renders an icon using gtk_render_icon_pixbuf() and converts it to a
+ * cairo pattern. 
+ *
+ * This function never returns %NULL; if the icon can't be rendered
+ * (perhaps because an image file fails to load), a default "missing
+ * image" icon will be returned instead.
+ *
+ * Return value: (transfer full): a #cairo_pattern_t to be displayed
+ *
+ * Since: 3.10
+ */
+cairo_pattern_t *
+gtk_icon_set_render_icon_pattern  (GtkIconSet      *icon_set,
+                                  GtkStyleContext *context,
+                                  GtkIconSize      size,
+                                  gint             scale,
+                                  GdkWindow       *for_window)
+{
+  GdkPixbuf *pixbuf;
+  cairo_surface_t *surface;
+  cairo_pattern_t *pattern;
+  cairo_matrix_t matrix;
+
+  pixbuf = gtk_icon_set_render_icon_pixbuf_for_scale (icon_set, context, size, scale);
+
+  surface = gdk_cairo_pixbuf_to_surface (pixbuf, for_window);
+  g_object_unref (pixbuf);
+  pattern = cairo_pattern_create_for_surface (surface);
+  cairo_surface_destroy (surface);
+
+  cairo_matrix_init_scale (&matrix, scale, scale);
+  cairo_pattern_set_matrix (pattern, &matrix);
+
+  return pattern;
+}
+
+/**
  * gtk_icon_set_render_icon:
  * @icon_set: a #GtkIconSet
  * @style: (allow-none): a #GtkStyle associated with @widget, or %NULL
@@ -1844,7 +1937,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS;
 
 /* Order sources by their "wildness", so that "wilder" sources are
  * greater than "specific" sources; for determining ordering,
- * direction beats state beats size.
+ * direction beats state beats size beats scale.
  */
 
 static int
@@ -1865,6 +1958,10 @@ icon_source_compare (gconstpointer ap, gconstpointer bp)
     return -1;
   else if (a->any_size && !b->any_size)
     return 1;
+  else if (!a->any_scale && b->any_scale)
+    return -1;
+  else if (a->any_scale && !b->any_scale)
+    return 1;
   else
     return 0;
 }
@@ -2378,6 +2475,35 @@ gtk_icon_source_set_size_wildcarded (GtkIconSource *source,
 }
 
 /**
+ * gtk_icon_source_set_scale_wildcarded:
+ * @source: a #GtkIconSource
+ * @setting: %TRUE to wildcard the scale
+ *
+ * If the icon scale is wildcarded, this source can be used as the base
+ * image for an icon of any scale.  If the size is not wildcarded, then
+ * the scale the source applies to should be set with
+ * gtk_icon_source_set_scale() and the icon source will only be used
+ * with that specific size.
+ *
+ * #GtkIconSet prefers non-wildcarded sources (exact matches) over
+ * wildcarded sources, and will use an exact match when possible.
+ *
+ * #GtkIconSet will normally scale wildcarded source images to produce
+ * an appropriate icon at a given size, but will not change the size
+ * of source images that match exactly.
+ *
+ * Since: 3.10
+ */
+void
+gtk_icon_source_set_scale_wildcarded (GtkIconSource *source,
+                                      gboolean       setting)
+{
+  g_return_if_fail (source != NULL);
+
+  source->any_scale = setting != FALSE;
+}
+
+/**
  * gtk_icon_source_get_size_wildcarded:
  * @source: a #GtkIconSource
  *
@@ -2426,6 +2552,24 @@ gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source)
 }
 
 /**
+ * gtk_icon_source_get_scale_wildcarded:
+ * @source: a #GtkIconSource
+ *
+ * Gets the value set by gtk_icon_source_set_scale_wildcarded().
+ *
+ * Return value: %TRUE if this icon source is a base for any icon scale variant
+ *
+ * Since: 3.10
+ */
+gboolean
+gtk_icon_source_get_scale_wildcarded (const GtkIconSource *source)
+{
+  g_return_val_if_fail (source != NULL, TRUE);
+
+  return source->any_scale;
+}
+
+/**
  * gtk_icon_source_set_direction:
  * @source: a #GtkIconSource
  * @direction: text direction this source applies to
@@ -2492,6 +2636,30 @@ gtk_icon_source_set_size (GtkIconSource *source,
 }
 
 /**
+ * gtk_icon_source_set_scale:
+ * @source: a #GtkIconSource
+ * @scale: scale this source applies to
+ *
+ * Sets the scale this icon source is intended to be used
+ * with.
+ *
+ * Setting the icon scale on an icon source makes no difference
+ * if the scale is wildcarded. Therefore, you should usually
+ * call gtk_icon_source_set_scale_wildcarded() to un-wildcard it
+ * in addition to calling this function.
+ *
+ * Since: 3.10
+ */
+void
+gtk_icon_source_set_scale (GtkIconSource *source,
+                           gint           scale)
+{
+  g_return_if_fail (source != NULL);
+
+  source->scale = scale;
+}
+
+/**
  * gtk_icon_source_get_direction:
  * @source: a #GtkIconSource
  *
@@ -2544,6 +2712,25 @@ gtk_icon_source_get_size (const GtkIconSource *source)
   return source->size;
 }
 
+/**
+ * gtk_icon_source_get_scale:
+ * @source: a #GtkIconSource
+ *
+ * Obtains the scale this source applies to. The return value
+ * is only useful/meaningful if the scale is <emphasis>not</emphasis> wildcarded.
+ *
+ * Return value: scale this source matches.
+ *
+ * Since: 3.10
+ */
+gint
+gtk_icon_source_get_scale (const GtkIconSource *source)
+{
+  g_return_val_if_fail (source != NULL, 0);
+
+  return source->scale;
+}
+
 #define NUM_CACHED_ICONS 8
 
 typedef struct _CachedIcon CachedIcon;
@@ -2557,6 +2744,7 @@ struct _CachedIcon
   GtkTextDirection direction;
   GtkStateType state;
   GtkIconSize size;
+  gint scale;
 
   GdkPixbuf *pixbuf;
 };
@@ -2585,7 +2773,8 @@ find_in_cache (GtkIconSet      *icon_set,
                GtkStyleContext *style_context,
                GtkTextDirection direction,
                GtkStateType     state,
-               GtkIconSize      size)
+               GtkIconSize      size,
+               gint             scale)
 {
   GSList *tmp_list;
   GSList *prev;
@@ -2601,6 +2790,7 @@ find_in_cache (GtkIconSet      *icon_set,
       if (icon->style == style_context &&
           icon->direction == direction &&
           icon->state == state &&
+          icon->scale == scale &&
           (size == (GtkIconSize)-1 || icon->size == size))
         {
           if (prev)
@@ -2627,6 +2817,7 @@ add_to_cache (GtkIconSet      *icon_set,
               GtkTextDirection direction,
               GtkStateType     state,
               GtkIconSize      size,
+              gint             scale,
               GdkPixbuf       *pixbuf)
 {
   CachedIcon *icon;
@@ -2643,6 +2834,7 @@ add_to_cache (GtkIconSet      *icon_set,
   icon->direction = direction;
   icon->state = state;
   icon->size = size;
+  icon->scale = scale;
   icon->pixbuf = pixbuf;
   attach_to_style (icon_set, icon->style);
 
diff --git a/gtk/gtkiconfactory.h b/gtk/gtkiconfactory.h
index 8aeebdd..65848d1 100644
--- a/gtk/gtkiconfactory.h
+++ b/gtk/gtkiconfactory.h
@@ -193,12 +193,17 @@ void             gtk_icon_source_set_state_wildcarded     (GtkIconSource       *
 GDK_AVAILABLE_IN_ALL
 void             gtk_icon_source_set_size_wildcarded      (GtkIconSource       *source,
                                                            gboolean             setting);
+GDK_AVAILABLE_IN_3_10
+void             gtk_icon_source_set_scale_wildcarded     (GtkIconSource       *source,
+                                                           gboolean             setting);
 GDK_AVAILABLE_IN_ALL
 gboolean         gtk_icon_source_get_size_wildcarded      (const GtkIconSource *source);
 GDK_AVAILABLE_IN_ALL
 gboolean         gtk_icon_source_get_state_wildcarded     (const GtkIconSource *source);
 GDK_AVAILABLE_IN_ALL
 gboolean         gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source);
+GDK_AVAILABLE_IN_3_10
+gboolean         gtk_icon_source_get_scale_wildcarded     (const GtkIconSource *source);
 GDK_AVAILABLE_IN_ALL
 void             gtk_icon_source_set_direction            (GtkIconSource       *source,
                                                            GtkTextDirection     direction);
@@ -208,13 +213,17 @@ void             gtk_icon_source_set_state                (GtkIconSource       *
 GDK_AVAILABLE_IN_ALL
 void             gtk_icon_source_set_size                 (GtkIconSource       *source,
                                                            GtkIconSize          size);
+GDK_AVAILABLE_IN_3_10
+void             gtk_icon_source_set_scale                (GtkIconSource       *source,
+                                                           gint                 scale);
 GDK_AVAILABLE_IN_ALL
 GtkTextDirection gtk_icon_source_get_direction            (const GtkIconSource *source);
 GDK_AVAILABLE_IN_ALL
 GtkStateType     gtk_icon_source_get_state                (const GtkIconSource *source);
 GDK_AVAILABLE_IN_ALL
 GtkIconSize      gtk_icon_source_get_size                 (const GtkIconSource *source);
-
+GDK_AVAILABLE_IN_3_10
+gint             gtk_icon_source_get_scale                (const GtkIconSource *source);
 
 /* ignore this */
 void _gtk_icon_set_invalidate_caches (void);
diff --git a/gtk/gtkstylecontext.h b/gtk/gtkstylecontext.h
index a7719f0..f3ca674 100644
--- a/gtk/gtkstylecontext.h
+++ b/gtk/gtkstylecontext.h
@@ -879,6 +879,18 @@ GDK_AVAILABLE_IN_ALL
 GdkPixbuf  * gtk_icon_set_render_icon_pixbuf   (GtkIconSet      *icon_set,
                                                 GtkStyleContext *context,
                                                 GtkIconSize      size);
+GDK_AVAILABLE_IN_3_10
+GdkPixbuf  * gtk_icon_set_render_icon_pixbuf_for_scale  (GtkIconSet      *icon_set,
+                                                        GtkStyleContext *context,
+                                                        GtkIconSize      size,
+                                                        int              scale);
+GDK_AVAILABLE_IN_3_10
+cairo_pattern_t  *
+gtk_icon_set_render_icon_pattern               (GtkIconSet      *icon_set,
+                                               GtkStyleContext *context,
+                                               GtkIconSize      size,
+                                               int              scale,
+                                               GdkWindow       *for_window);
 
 GDK_AVAILABLE_IN_ALL
 void        gtk_style_context_set_screen (GtkStyleContext *context,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]