picture: Add content-fit property

It allows to specify the resize mode of the paintable inside the
GtkPicture allocation. This also deprecates the keep-aspect-ratio
property.

Fixes #5027.
This commit is contained in:
Marco Melorio 2022-07-15 18:18:26 +02:00
parent 7743f35972
commit d8a73cbd03
26 changed files with 192 additions and 45 deletions

View File

@ -330,7 +330,7 @@ start_puzzle (GdkPaintable *paintable)
x, y,
width, height);
picture = gtk_picture_new_for_paintable (piece);
gtk_picture_set_keep_aspect_ratio (GTK_PICTURE (picture), FALSE);
gtk_picture_set_content_fit (GTK_PICTURE (picture), GTK_CONTENT_FIT_FILL);
gtk_grid_attach (GTK_GRID (grid),
picture,
x, y,

View File

@ -216,7 +216,6 @@
<child>
<object class="GtkPicture" id="picture">
<property name="can-shrink">0</property>
<property name="keep-aspect-ratio">1</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>

View File

@ -1265,6 +1265,8 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
<property name="child">
<object class="GtkPicture">
<property name="file">resource:///org/gtk/WidgetFactory4/sunset.jpg</property>
<property name="content-fit">cover</property>
<property name="overflow">hidden</property>
<child>
<object class="GtkDragSource">
<signal name="prepare" handler="on_picture_drag_prepare" swapped="no"/>

View File

@ -110,6 +110,36 @@ typedef enum
GTK_BASELINE_POSITION_BOTTOM
} GtkBaselinePosition;
/**
* GtkContentFit:
* @GTK_CONTENT_FIT_FILL: Make the content fill the entire allocation,
* without taking its aspect ratio in consideration. The resulting
* content will appear as stretched if its aspect ratio is different
* from the allocation aspect ratio.
* @GTK_CONTENT_FIT_CONTAIN: Scale the content to fit the allocation,
* while taking its aspect ratio in consideration. The resulting
* content will appear as letterboxed if its aspect ratio is different
* from the allocation aspect ratio.
* @GTK_CONTENT_FIT_COVER: Cover the entire allocation, while taking
* the content aspect ratio in consideration. This can result in an overflow
* if the content aspect ratio is different from the allocation aspect ratio.
* For this reason, you may also want to set [property@Gtk.Widget:overflow]
* to %GTK_OVERFLOW_HIDDEN.
* @GTK_CONTENT_FIT_SCALE_DOWN: The content is scaled down to fit the
* allocation, if needed, otherwise its original size is used.
*
* Controls how a content should be made to fit inside an allocation.
*
* Since: 4.8
*/
typedef enum
{
GTK_CONTENT_FIT_FILL,
GTK_CONTENT_FIT_CONTAIN,
GTK_CONTENT_FIT_COVER,
GTK_CONTENT_FIT_SCALE_DOWN,
} GtkContentFit;
/**
* GtkDeleteType:
* @GTK_DELETE_CHARS: Delete characters.

View File

@ -27,6 +27,7 @@
#include "gtkintl.h"
#include "gtkprivate.h"
#include "gtksnapshot.h"
#include "gtktypebuiltins.h"
#include "gtkwidgetprivate.h"
#include "gdkpixbufutilsprivate.h"
@ -89,6 +90,7 @@ enum
PROP_ALTERNATIVE_TEXT,
PROP_KEEP_ASPECT_RATIO,
PROP_CAN_SHRINK,
PROP_CONTENT_FIT,
NUM_PROPERTIES
};
@ -100,8 +102,8 @@ struct _GtkPicture
GFile *file;
char *alternative_text;
guint keep_aspect_ratio : 1;
guint can_shrink : 1;
GtkContentFit content_fit;
};
struct _GtkPictureClass
@ -129,15 +131,38 @@ gtk_picture_snapshot (GtkWidget *widget,
height = gtk_widget_get_height (widget);
ratio = gdk_paintable_get_intrinsic_aspect_ratio (self->paintable);
if (!self->keep_aspect_ratio || ratio == 0)
if (self->content_fit == GTK_CONTENT_FIT_FILL || ratio == 0)
{
gdk_paintable_snapshot (self->paintable, snapshot, width, height);
}
else
{
double picture_ratio = (double) width / height;
int paintable_width = gdk_paintable_get_intrinsic_width (self->paintable);
int paintable_height = gdk_paintable_get_intrinsic_height (self->paintable);
if (ratio > picture_ratio)
if (self->content_fit == GTK_CONTENT_FIT_SCALE_DOWN &&
width >= paintable_width && height >= paintable_height)
{
w = paintable_width;
h = paintable_height;
}
else if (ratio > picture_ratio)
{
if (self->content_fit == GTK_CONTENT_FIT_COVER)
{
w = height * ratio;
h = height;
}
else
{
w = width;
h = width / ratio;
}
}
else
{
if (self->content_fit == GTK_CONTENT_FIT_COVER)
{
w = width;
h = width / ratio;
@ -147,6 +172,7 @@ gtk_picture_snapshot (GtkWidget *widget,
w = height * ratio;
h = height;
}
}
x = (width - ceil (w)) / 2;
y = floor(height - ceil (h)) / 2;
@ -246,13 +272,19 @@ gtk_picture_set_property (GObject *object,
break;
case PROP_KEEP_ASPECT_RATIO:
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gtk_picture_set_keep_aspect_ratio (self, g_value_get_boolean (value));
G_GNUC_END_IGNORE_DEPRECATIONS
break;
case PROP_CAN_SHRINK:
gtk_picture_set_can_shrink (self, g_value_get_boolean (value));
break;
case PROP_CONTENT_FIT:
gtk_picture_set_content_fit (self, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -282,13 +314,19 @@ gtk_picture_get_property (GObject *object,
break;
case PROP_KEEP_ASPECT_RATIO:
g_value_set_boolean (value, self->keep_aspect_ratio);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_value_set_boolean (value, gtk_picture_get_keep_aspect_ratio (self));
G_GNUC_END_IGNORE_DEPRECATIONS
break;
case PROP_CAN_SHRINK:
g_value_set_boolean (value, self->can_shrink);
break;
case PROP_CONTENT_FIT:
g_value_set_enum (value, self->content_fit);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -394,11 +432,15 @@ gtk_picture_class_init (GtkPictureClass *class)
*
* Whether the GtkPicture will render its contents trying to preserve the aspect
* ratio.
*
* Deprecated: 4.8: Use [property@Gtk.Picture:content-fit] instead.
*/
properties[PROP_KEEP_ASPECT_RATIO] =
g_param_spec_boolean ("keep-aspect-ratio", NULL, NULL,
TRUE,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
GTK_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_DEPRECATED);
/**
* GtkPicture:can-shrink: (attributes org.gtk.Property.get=gtk_picture_get_can_shrink org.gtk.Property.set=gtk_picture_set_can_shrink)
@ -410,6 +452,17 @@ gtk_picture_class_init (GtkPictureClass *class)
TRUE,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkPicture:content-fit: (attributes org.gtk.Property.get=gtk_picture_get_content_fit org.gtk.Property.set=gtk_picture_set_content_fit)
*
* How the content should be resized to fit inside the `GtkPicture`.
*/
properties[PROP_CONTENT_FIT] =
g_param_spec_enum ("content-fit", NULL, NULL,
GTK_TYPE_CONTENT_FIT,
GTK_CONTENT_FIT_CONTAIN,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
gtk_widget_class_set_css_name (widget_class, I_("picture"));
@ -420,7 +473,7 @@ static void
gtk_picture_init (GtkPicture *self)
{
self->can_shrink = TRUE;
self->keep_aspect_ratio = TRUE;
self->content_fit = GTK_CONTENT_FIT_CONTAIN;
}
/**
@ -827,21 +880,20 @@ gtk_picture_get_paintable (GtkPicture *self)
*
* If set to %FALSE or if the contents provide no aspect ratio,
* the contents will be stretched over the picture's whole area.
*
* Deprecated: 4.8: Use [method@Gtk.Picture.set_content_fit] instead. If still
* used, this method will always set the [property@Gtk.Picture:content-fit]
* property to `GTK_CONTENT_FIT_CONTAIN` if @keep_aspect_ratio is true,
* otherwise it will set it to `GTK_CONTENT_FIT_FILL`.
*/
void
gtk_picture_set_keep_aspect_ratio (GtkPicture *self,
gboolean keep_aspect_ratio)
{
g_return_if_fail (GTK_IS_PICTURE (self));
if (self->keep_aspect_ratio == keep_aspect_ratio)
return;
self->keep_aspect_ratio = keep_aspect_ratio;
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_KEEP_ASPECT_RATIO]);
if (keep_aspect_ratio)
gtk_picture_set_content_fit (self, GTK_CONTENT_FIT_CONTAIN);
else
gtk_picture_set_content_fit (self, GTK_CONTENT_FIT_FILL);
}
/**
@ -851,13 +903,17 @@ gtk_picture_set_keep_aspect_ratio (GtkPicture *self,
* Returns whether the `GtkPicture` preserves its contents aspect ratio.
*
* Returns: %TRUE if the self tries to keep the contents' aspect ratio
*
* Deprecated: 4.8: Use [method@Gtk.Picture.get_content_fit] instead. This will
* now return `FALSE` only if [property@Gtk.Picture:content-fit] is
* `GTK_CONTENT_FIT_FILL`. Returns `TRUE` otherwise.
*/
gboolean
gtk_picture_get_keep_aspect_ratio (GtkPicture *self)
{
g_return_val_if_fail (GTK_IS_PICTURE (self), TRUE);
return self->keep_aspect_ratio;
return self->content_fit != GTK_CONTENT_FIT_FILL;
}
/**
@ -908,6 +964,60 @@ gtk_picture_get_can_shrink (GtkPicture *self)
return self->can_shrink;
}
/**
* gtk_picture_set_content_fit: (attributes org.gtk.Method.set_property=content-fit)
* @self: a `GtkPicture`
* @content_fit: the content fit mode
*
* Sets how the content should be resized to fit the `GtkPicture`.
*
* See [enum@Gtk.ContentFit] for details.
*
* If you use `GTK_CONTENT_FIT_COVER`, you may also want to set the
* [property@Gtk.Widget:overflow] to `GTK_OVERFLOW_HIDDEN`, otherwise the
* paintable will overflow the widget allocation if the aspect ratio of the
* paintable is different from the one of the `GtkPicture` allocation.
*/
void
gtk_picture_set_content_fit (GtkPicture *self,
GtkContentFit content_fit)
{
g_return_if_fail (GTK_IS_PICTURE (self));
if (self->content_fit == content_fit)
return;
gboolean notify_keep_aspect_ratio = (content_fit == GTK_CONTENT_FIT_FILL ||
self->content_fit == GTK_CONTENT_FIT_FILL);
self->content_fit = content_fit;
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CONTENT_FIT]);
if (notify_keep_aspect_ratio)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_KEEP_ASPECT_RATIO]);
}
/**
* gtk_picture_get_content_fit: (attributes org.gtk.Method.get_property=content-fit)
* @self: a `GtkPicture`
*
* Returns the fit mode for the content of the `GtkPicture`.
*
* See [enum@Gtk.ContentFit] for details.
*
* Returns: the content fit mode
*/
GtkContentFit
gtk_picture_get_content_fit (GtkPicture *self)
{
g_return_val_if_fail (GTK_IS_PICTURE (self), FALSE);
return self->content_fit;
}
/**
* gtk_picture_set_alternative_text: (attributes org.gtk.Method.set_property=alternative-text)
* @self: a `GtkPicture`

View File

@ -69,10 +69,10 @@ GDK_AVAILABLE_IN_ALL
void gtk_picture_set_pixbuf (GtkPicture *self,
GdkPixbuf *pixbuf);
GDK_AVAILABLE_IN_ALL
GDK_DEPRECATED_IN_4_8_FOR(gtk_picture_set_fit_mode)
void gtk_picture_set_keep_aspect_ratio (GtkPicture *self,
gboolean keep_aspect_ratio);
GDK_AVAILABLE_IN_ALL
GDK_DEPRECATED_IN_4_8_FOR(gtk_picture_get_fit_mode)
gboolean gtk_picture_get_keep_aspect_ratio (GtkPicture *self);
GDK_AVAILABLE_IN_ALL
void gtk_picture_set_can_shrink (GtkPicture *self,
@ -80,6 +80,12 @@ void gtk_picture_set_can_shrink (GtkPicture
GDK_AVAILABLE_IN_ALL
gboolean gtk_picture_get_can_shrink (GtkPicture *self);
GDK_AVAILABLE_IN_4_8
void gtk_picture_set_content_fit (GtkPicture *self,
GtkContentFit content_fit);
GDK_AVAILABLE_IN_4_8
GtkContentFit gtk_picture_get_content_fit (GtkPicture *self);
GDK_AVAILABLE_IN_ALL
void gtk_picture_set_alternative_text (GtkPicture *self,
const char *alternative_text);

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-100x100.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-100x100.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-100x100.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-100x100.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-150x150.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-150x150.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-150x150.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-150x150.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-200x200.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-200x200.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-200x200.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-200x200.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-20x20.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-20x20.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-20x20.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-20x20.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-300x300.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-300x300.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -9,7 +9,7 @@
<child type="overlay">
<object class="GtkPicture">
<property name="paintable">green-300x300.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkPicture">
<property name="paintable">green-300x300.png</property>
<property name="keep-aspect-ratio">0</property>
<property name="content-fit">fill</property>
<property name="can-shrink">1</property>
<property name="halign">center</property>
<property name="valign">center</property>