image: Add gtk_image_set_keep_aspect_ratio()
authorBenjamin Otte <otte@redhat.com>
Thu, 15 Mar 2018 17:10:01 +0000 (18:10 +0100)
committerBenjamin Otte <otte@redhat.com>
Fri, 16 Mar 2018 05:04:45 +0000 (06:04 +0100)
If set, the image will draw its contents while keeping their aspect
ratio. That means empty areas show up on the top/bottom or left/right.

Also move the special-case snapshotting code for icons to
GtkIconHelper. That's where it belongs.

docs/reference/gtk/gtk4-sections.txt
gtk/gtkiconhelper.c
gtk/gtkimage.c
gtk/gtkimage.h

index a653a7558abaf45fa82078b18a3d7b4bfdb617ef..9bfaf9bad547285978d4f66780c4428368bebdee 100644 (file)
@@ -1505,6 +1505,8 @@ gtk_image_set_pixel_size
 gtk_image_get_pixel_size
 gtk_image_set_icon_size
 gtk_image_get_icon_size
+gtk_image_set_keep_aspect_ratio
+gtk_image_get_keep_aspect_ratio
 <SUBSECTION Standard>
 GTK_IMAGE
 GTK_IS_IMAGE
index 5a38f2aea2dc074dcdaf516f8500e99819f51f94..c9aff693b6ca5aebbe38707d87ea5dbbb9553941 100644 (file)
@@ -249,11 +249,41 @@ gtk_icon_helper_paintable_snapshot (GdkPaintable *paintable,
   if (self->paintable == NULL)
     return;
 
-  gtk_css_style_snapshot_icon_paintable (style,
-                                         snapshot,
-                                         self->paintable,
-                                         width, height,
-                                         self->texture_is_symbolic);
+  switch (gtk_image_definition_get_storage_type (self->def))
+    {
+    case GTK_IMAGE_ICON_NAME:
+    case GTK_IMAGE_GICON:
+      {
+        double x, y, w, h;
+
+        /* Never scale up icons. */
+        w = gdk_paintable_get_intrinsic_width (self->paintable);
+        h = gdk_paintable_get_intrinsic_height (self->paintable);
+        w = MIN (w, width);
+        h = MIN (h, height);
+        x = (width - w) / 2;
+        y = (height - h) / 2;
+        gtk_snapshot_offset (snapshot, x, y);
+        gtk_css_style_snapshot_icon_paintable (style,
+                                               snapshot,
+                                               self->paintable,
+                                               w, h,
+                                               self->texture_is_symbolic);
+        gtk_snapshot_offset (snapshot, -x, -y);
+      }
+      break;
+
+    case GTK_IMAGE_TEXTURE:
+    case GTK_IMAGE_PAINTABLE:
+    case GTK_IMAGE_EMPTY:
+    default:
+      gtk_css_style_snapshot_icon_paintable (style,
+                                             snapshot,
+                                             self->paintable,
+                                             width, height,
+                                             self->texture_is_symbolic);
+      break;
+    }
 }
 
 static GdkPaintable *
index 2d4d5a0dbb250f53056e379f88abc6ca8daad6db..3d31eb3242e7ef3b0af76e8fbd21fa6f4537bf2d 100644 (file)
@@ -86,6 +86,8 @@ struct _GtkImagePrivate
 
   gchar                *filename;       /* Only used with GTK_IMAGE_SURFACE */
   gchar                *resource_path;  /* Only used with GTK_IMAGE_SURFACE */
+
+  guint keep_aspect_ratio : 1;
 };
 
 
@@ -129,6 +131,7 @@ enum
   PROP_GICON,
   PROP_RESOURCE,
   PROP_USE_FALLBACK,
+  PROP_KEEP_ASPECT_RATIO,
   NUM_PROPERTIES
 };
 
@@ -261,6 +264,19 @@ gtk_image_class_init (GtkImageClass *class)
                             FALSE,
                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
+  /**
+   * GtkImage:keep-aspect-ratio:
+   *
+   * Whether the GtkImage will render its contents trying to preserve the aspect
+   * ratio of the contents.
+   */
+  image_props[PROP_KEEP_ASPECT_RATIO] =
+      g_param_spec_boolean ("keep-aspect-ratio",
+                            P_("Keep aspect ratio"),
+                            P_("Render contents respecting the aspect ratio"),
+                            TRUE,
+                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, image_props);
 
   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_IMAGE_ACCESSIBLE);
@@ -277,6 +293,7 @@ gtk_image_init (GtkImage *image)
   gtk_widget_set_has_window (GTK_WIDGET (image), FALSE);
 
   priv->icon_helper = gtk_icon_helper_new (widget_node, GTK_WIDGET (image));
+  priv->keep_aspect_ratio = TRUE;
 }
 
 static void
@@ -336,6 +353,10 @@ gtk_image_set_property (GObject      *object,
         g_object_notify_by_pspec (object, pspec);
       break;
 
+    case PROP_KEEP_ASPECT_RATIO:
+      gtk_image_set_keep_aspect_ratio (image, g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -380,6 +401,9 @@ gtk_image_get_property (GObject     *object,
     case PROP_USE_FALLBACK:
       g_value_set_boolean (value, _gtk_icon_helper_get_use_fallback (priv->icon_helper));
       break;
+    case PROP_KEEP_ASPECT_RATIO:
+      g_value_set_boolean (value, priv->keep_aspect_ratio);
+      break;
     case PROP_STORAGE_TYPE:
       g_value_set_enum (value, _gtk_icon_helper_get_storage_type (priv->icon_helper));
       break;
@@ -1203,27 +1227,40 @@ gtk_image_snapshot (GtkWidget   *widget,
 {
   GtkImage *image = GTK_IMAGE (widget);
   GtkImagePrivate *priv = gtk_image_get_instance_private (image);
-  int x, y, width, height;
-  gint w, h, baseline;
+  double ratio;
+  int x, y, width, height, baseline;
+  double w, h;
 
   width = gtk_widget_get_width (widget);
   height = gtk_widget_get_height (widget);
+  ratio = gdk_paintable_get_intrinsic_aspect_ratio (GDK_PAINTABLE (priv->icon_helper));
 
-  if (_gtk_icon_helper_get_storage_type (priv->icon_helper) == GTK_IMAGE_PAINTABLE)
+  if (!priv->keep_aspect_ratio || ratio == 0)
     {
       gdk_paintable_snapshot (GDK_PAINTABLE (priv->icon_helper), snapshot, width, height);
     }
   else
     {
-      _gtk_icon_helper_get_size (priv->icon_helper, &w, &h);
-      x = (width - w) / 2;
+      double image_ratio = (double) width / height;
 
-      baseline = gtk_widget_get_allocated_baseline (widget);
+    if (ratio > image_ratio)
+      {
+        w = width;
+        h = width / ratio;
+      }
+    else
+      {
+        w = height * ratio;
+        h = height;
+      }
 
+      x = (width - ceil (w)) / 2;
+
+      baseline = gtk_widget_get_allocated_baseline (widget);
       if (baseline == -1)
-        y = floor(height - h) / 2;
+        y = floor(height - ceil (h)) / 2;
       else
-        y = CLAMP (baseline - h * gtk_image_get_baseline_align (image), 0, height - h);
+        y = CLAMP (baseline - h * gtk_image_get_baseline_align (image), 0, height - ceil (h));
 
       gtk_snapshot_offset (snapshot, x, y);
       gdk_paintable_snapshot (GDK_PAINTABLE (priv->icon_helper), snapshot, w, h);
@@ -1463,6 +1500,51 @@ gtk_image_get_icon_size (GtkImage *image)
   return priv->icon_size;
 }
 
+/**
+ * gtk_image_set_keep_aspect_ratio:
+ * @image: a #GtkImage
+ * @keep_aspect_ratio: whether to keep aspect ratio
+ *
+ * If set to %TRUE, the @image will render its contents according to
+ * their aspect ratio. That means that empty space may show up at the
+ * top/bottom or left/right of @image.
+ *
+ * If set to %FALSE or if the contents provide no aspect ratio, the
+ * contents will be stretched over the image's whole area.
+ */
+void
+gtk_image_set_keep_aspect_ratio (GtkImage *image,
+                                 gboolean  keep_aspect_ratio)
+{
+  GtkImagePrivate *priv = gtk_image_get_instance_private (image);
+
+  g_return_if_fail (GTK_IS_IMAGE (image));
+
+  if (priv->keep_aspect_ratio == keep_aspect_ratio)
+    return;
+
+  priv->keep_aspect_ratio = keep_aspect_ratio;
+  g_object_notify_by_pspec (G_OBJECT (image), image_props[PROP_KEEP_ASPECT_RATIO]);
+}
+
+/**
+ * gtk_image_get_keep_aspect_ratio:
+ * @image: a #GtkImage
+ *
+ * Gets the value set via gtk_image_set_keep_aspect_ratio().
+ *
+ * Returns: %TRUE if the image tries to keep the contents' aspect ratio
+ **/
+gboolean
+gtk_image_get_keep_aspect_ratio (GtkImage *image)
+{
+  GtkImagePrivate *priv = gtk_image_get_instance_private (image);
+
+  g_return_val_if_fail (GTK_IS_IMAGE (image), TRUE);
+
+  return priv->keep_aspect_ratio;
+}
+
 void
 gtk_image_get_image_size (GtkImage *image,
                           int      *width,
index f4390fab1822d3b2d99dafe4563cb74c641645af..d9fa53313644ecf7420e8fdb66afc0178f87ac1c 100644 (file)
@@ -148,6 +148,9 @@ void gtk_image_set_pixel_size     (GtkImage        *image,
 GDK_AVAILABLE_IN_ALL
 void gtk_image_set_icon_size      (GtkImage        *image,
                                    GtkIconSize      icon_size);
+GDK_AVAILABLE_IN_ALL
+void gtk_image_set_keep_aspect_ratio (GtkImage     *image,
+                                      gboolean      keep_aspect_ratio);
 
 GDK_AVAILABLE_IN_ALL
 GtkImageType gtk_image_get_storage_type (GtkImage   *image);
@@ -165,6 +168,8 @@ GDK_AVAILABLE_IN_ALL
 gint       gtk_image_get_pixel_size (GtkImage             *image);
 GDK_AVAILABLE_IN_ALL
 GtkIconSize gtk_image_get_icon_size (GtkImage             *image);
+GDK_AVAILABLE_IN_ALL
+gboolean   gtk_image_get_keep_aspect_ratio (GtkImage      *image);
 
 G_END_DECLS