diff options
author | Benjamin Otte <otte@redhat.com> | 2018-06-09 22:48:06 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2018-06-10 02:23:02 +0200 |
commit | 6546ef345986c10857e41b3c7129e0b24985da66 (patch) | |
tree | a114f93b09388b252c2c4a06ab34ff6187fc6a6e | |
parent | 9d48a95d9a0a2087131ea28c5c4d2af60b3ac1b0 (diff) | |
download | gtk+-6546ef345986c10857e41b3c7129e0b24985da66.tar.gz |
picture: Introduce
This commit introduces GtkPicture, which is supposed to complement
GtkImage.
GtkImage will be adapted to always display an icon, while
GtkPicture displays regular imagery.
-rw-r--r-- | docs/reference/gtk/gtk4-docs.xml | 1 | ||||
-rw-r--r-- | docs/reference/gtk/gtk4-sections.txt | 34 | ||||
-rw-r--r-- | gtk/a11y/gtk-a11y-autocleanups.h | 1 | ||||
-rw-r--r-- | gtk/a11y/gtkpictureaccessible.c | 136 | ||||
-rw-r--r-- | gtk/a11y/gtkpictureaccessibleprivate.h | 34 | ||||
-rw-r--r-- | gtk/a11y/gtkwidgetaccessible.h | 2 | ||||
-rw-r--r-- | gtk/a11y/meson.build | 1 | ||||
-rw-r--r-- | gtk/gtk-a11y.h | 2 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtkpicture.c | 975 | ||||
-rw-r--r-- | gtk/gtkpicture.h | 92 | ||||
-rw-r--r-- | gtk/gtkvideo.c | 3 | ||||
-rw-r--r-- | gtk/gtkwidgetpaintable.c | 8 | ||||
-rw-r--r-- | gtk/meson.build | 2 |
14 files changed, 1285 insertions, 7 deletions
diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 2ad8229aec..9a59b2b947 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -93,6 +93,7 @@ <title>Display Widgets</title> <xi:include href="xml/gtklabel.xml" /> <xi:include href="xml/gtkimage.xml" /> + <xi:include href="xml/gtkpicture.xml" /> <xi:include href="xml/gtkspinner.xml" /> <xi:include href="xml/gtkinfobar.xml" /> <xi:include href="xml/gtkprogressbar.xml" /> diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index aef56e724e..36edc8567f 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -2050,6 +2050,40 @@ gtk_paned_get_type </SECTION> <SECTION> +<FILE>gtkpicture</FILE> +<TITLE>GtkPicture</TITLE> +GtkPicture +gtk_picture_new +gtk_picture_new_for_paintable +gtk_picture_new_for_pixbuf +gtk_picture_new_for_file +gtk_picture_new_for_filename +gtk_picture_new_for_resource +gtk_picture_set_paintable +gtk_picture_get_paintable +gtk_picture_set_pixbuf +gtk_picture_set_file +gtk_picture_get_file +gtk_picture_set_filename +gtk_picture_set_resource +gtk_picture_set_keep_aspect_ratio +gtk_picture_get_keep_aspect_ratio +gtk_picture_set_can_shrink +gtk_picture_get_can_shrink +gtk_picture_set_alternative_text +gtk_picture_get_alternative_text +<SUBSECTION Standard> +GTK_PICTURE +GTK_IS_PICTURE +GTK_TYPE_PICTURE +GTK_PICTURE_CLASS +GTK_IS_PICTURE_CLASS +GTK_PICTURE_GET_CLASS +<SUBSECTION Private> +gtk_picture_get_type +</SECTION> + +<SECTION> <FILE>gtkprogressbar</FILE> <TITLE>GtkProgressBar</TITLE> GtkProgressBar diff --git a/gtk/a11y/gtk-a11y-autocleanups.h b/gtk/a11y/gtk-a11y-autocleanups.h index 2dde2f1cef..7bc1a3bae5 100644 --- a/gtk/a11y/gtk-a11y-autocleanups.h +++ b/gtk/a11y/gtk-a11y-autocleanups.h @@ -68,7 +68,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTextViewAccessible, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkToggleButtonAccessible, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkToplevelAccessible, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkTreeViewAccessible, g_object_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkWidgetAccessible, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkWindowAccessible, g_object_unref) #endif diff --git a/gtk/a11y/gtkpictureaccessible.c b/gtk/a11y/gtkpictureaccessible.c new file mode 100644 index 0000000000..9f49eaf855 --- /dev/null +++ b/gtk/a11y/gtkpictureaccessible.c @@ -0,0 +1,136 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * 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 "gtkpictureaccessibleprivate.h" + +#include "gtkpicture.h" + +struct _GtkPictureAccessible +{ + GtkWidgetAccessible parent_instance; +}; + +struct _GtkPictureAccessibleClass +{ + GtkWidgetAccessibleClass parent_class; +}; + +static const gchar * +gtk_picture_accessible_get_image_description (AtkImage *image) +{ + GtkWidget* widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (image)); + if (widget == NULL) + return NULL; + + return gtk_picture_get_alternative_text (GTK_PICTURE (widget)); +} + +static void +gtk_picture_accessible_get_image_position (AtkImage *image, + gint *x, + gint *y, + AtkCoordType coord_type) +{ + atk_component_get_extents (ATK_COMPONENT (image), x, y, NULL, NULL, + coord_type); +} + +static void +gtk_picture_accessible_get_image_size (AtkImage *image, + gint *width, + gint *height) +{ + GtkWidget* widget; + GdkPaintable *paintable; + + *width = -1; + *height = -1; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (image)); + if (widget == NULL) + return; + + paintable = gtk_picture_get_paintable (GTK_PICTURE (widget)); + if (paintable == NULL) + return; + + *width = gdk_paintable_get_intrinsic_width (paintable); + if (*width == 0) + *width = -1; + + *height = gdk_paintable_get_intrinsic_height (paintable); + if (*height == 0) + *height = -1; +} + +static void +gtk_picture_accessible_image_init (AtkImageIface *iface) +{ + iface->get_image_description = gtk_picture_accessible_get_image_description; + iface->get_image_position = gtk_picture_accessible_get_image_position; + iface->get_image_size = gtk_picture_accessible_get_image_size; +} + +G_DEFINE_TYPE_WITH_CODE (GtkPictureAccessible, gtk_picture_accessible, GTK_TYPE_WIDGET_ACCESSIBLE, + G_IMPLEMENT_INTERFACE (ATK_TYPE_IMAGE, gtk_picture_accessible_image_init)) + +static void +gtk_picture_accessible_initialize (AtkObject *accessible, + gpointer data) +{ + ATK_OBJECT_CLASS (gtk_picture_accessible_parent_class)->initialize (accessible, data); + + accessible->role = ATK_ROLE_IMAGE; +} + +static const gchar * +gtk_picture_accessible_get_name (AtkObject *accessible) +{ + GtkWidget* widget; + const gchar *name; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)); + if (widget == NULL) + return NULL; + + name = ATK_OBJECT_CLASS (gtk_picture_accessible_parent_class)->get_name (accessible); + if (name) + return name; + + return gtk_picture_get_alternative_text (GTK_PICTURE (widget)); +} + +static void +gtk_picture_accessible_class_init (GtkPictureAccessibleClass *klass) +{ + AtkObjectClass *atkobject_class = ATK_OBJECT_CLASS (klass); + + atkobject_class->initialize = gtk_picture_accessible_initialize; + atkobject_class->get_name = gtk_picture_accessible_get_name; +} + +static void +gtk_picture_accessible_init (GtkPictureAccessible *image) +{ +} + diff --git a/gtk/a11y/gtkpictureaccessibleprivate.h b/gtk/a11y/gtkpictureaccessibleprivate.h new file mode 100644 index 0000000000..f079a350c7 --- /dev/null +++ b/gtk/a11y/gtkpictureaccessibleprivate.h @@ -0,0 +1,34 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * 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> + */ + +#ifndef __GTK_PICTURE_ACCESSIBLE_PRIVATE_H__ +#define __GTK_PICTURE_ACCESSIBLE_PRIVATE_H__ + +#include <gtk/a11y/gtkwidgetaccessible.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_PICTURE_ACCESSIBLE (gtk_picture_accessible_get_type ()) + +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GtkPictureAccessible, gtk_picture_accessible, GTK, PICTURE_ACCESSIBLE, GtkWidgetAccessible) + +G_END_DECLS + +#endif /* __GTK_PICTURE_ACCESSIBLE_PRIVATE_H__ */ diff --git a/gtk/a11y/gtkwidgetaccessible.h b/gtk/a11y/gtkwidgetaccessible.h index aa4aa8c38e..5eb9047af1 100644 --- a/gtk/a11y/gtkwidgetaccessible.h +++ b/gtk/a11y/gtkwidgetaccessible.h @@ -37,6 +37,8 @@ typedef struct _GtkWidgetAccessible GtkWidgetAccessible; typedef struct _GtkWidgetAccessibleClass GtkWidgetAccessibleClass; typedef struct _GtkWidgetAccessiblePrivate GtkWidgetAccessiblePrivate; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkWidgetAccessible, g_object_unref) + struct _GtkWidgetAccessible { GtkAccessible parent; diff --git a/gtk/a11y/meson.build b/gtk/a11y/meson.build index 7526343ce0..e1a1f86be9 100644 --- a/gtk/a11y/meson.build +++ b/gtk/a11y/meson.build @@ -31,6 +31,7 @@ a11y_sources = files([ 'gtknotebookaccessible.c', 'gtknotebookpageaccessible.c', 'gtkpanedaccessible.c', + 'gtkpictureaccessible.c', 'gtkpopoveraccessible.c', 'gtkprogressbaraccessible.c', 'gtkradiobuttonaccessible.c', diff --git a/gtk/gtk-a11y.h b/gtk/gtk-a11y.h index 4e43bf6aef..a96531b81b 100644 --- a/gtk/gtk-a11y.h +++ b/gtk/gtk-a11y.h @@ -56,8 +56,8 @@ #include <gtk/a11y/gtkmenushellaccessible.h> #include <gtk/a11y/gtknotebookaccessible.h> #include <gtk/a11y/gtknotebookpageaccessible.h> -#include <gtk/a11y/gtkpopoveraccessible.h> #include <gtk/a11y/gtkpanedaccessible.h> +#include <gtk/a11y/gtkpopoveraccessible.h> #include <gtk/a11y/gtkprogressbaraccessible.h> #include <gtk/a11y/gtkradiobuttonaccessible.h> #include <gtk/a11y/gtkradiomenuitemaccessible.h> @@ -159,6 +159,7 @@ #include <gtk/gtkpagesetup.h> #include <gtk/gtkpapersize.h> #include <gtk/gtkpaned.h> +#include <gtk/gtkpicture.h> #include <gtk/gtkpopover.h> #include <gtk/gtkpopovermenu.h> #include <gtk/gtkprintcontext.h> diff --git a/gtk/gtkpicture.c b/gtk/gtkpicture.c new file mode 100644 index 0000000000..b84ba68e29 --- /dev/null +++ b/gtk/gtkpicture.c @@ -0,0 +1,975 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * 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 "gtkpicture.h" + +#include "gtkcssnodeprivate.h" +#include "gtkcssnumbervalueprivate.h" +#include "gtkcssstyleprivate.h" +#include "gtkintl.h" +#include "gtkprivate.h" +#include "gtkscalerprivate.h" +#include "gtksnapshot.h" +#include "gtkwidgetprivate.h" + +#include "a11y/gtkpictureaccessibleprivate.h" + +/** + * SECTION:gtkpicture + * @Short_description: A widget displaying a #GdkPaintable + * @Title: GtkPicture + * @SeeAlso: #GdkPaintable, #GtkImage + * + * The #GtkPicture widget displays a #GdkPaintable. Many convenience functions + * are provided to make pictures simple to use. For example, if you want to load + * an image from a file, and then display that, there’s a convenience function + * to do this: + * |[<!-- language="C" --> + * GtkWidget *widget; + * widget = gtk_picture_new_for_filename ("myfile.png"); + * ]| + * If the file isn’t loaded successfully, the self will contain a + * “broken image” icon similar to that used in many web browsers. + * If you want to handle errors in loading the file yourself, + * for example by displaying an error message, then load the self with + * gdk_texture_new_for_file(), then create the #GtkPicture with + * gtk_picture_new_for_paintable(). + * + * Sometimes an application will want to avoid depending on external data + * files, such as image files. See the documentation of #GResource for details. + * In this case, the #GtkPicture:resource, gtk_picture_new_for_resource() and + * gtk_picture_set_resource() should be used. + * + * # CSS nodes + * + * GtkPicture has a single CSS node with the name image. + */ + +enum +{ + PROP_0, + PROP_PAINTABLE, + PROP_FILE, + PROP_ALTERNATIVE_TEXT, + PROP_KEEP_ASPECT_RATIO, + PROP_CAN_SHRINK, + NUM_PROPERTIES +}; + +struct _GtkPicture +{ + GtkWidget widget; + + GdkPaintable *paintable; + GFile *file; + + char *alternative_text; + guint keep_aspect_ratio : 1; + guint can_shrink : 1; +}; + +struct _GtkPictureClass +{ + GtkWidgetClass parent_class; +}; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +G_DEFINE_TYPE (GtkPicture, gtk_picture, GTK_TYPE_WIDGET) + +static void +gtk_picture_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkPicture *self = GTK_PICTURE (widget); + double ratio; + int x, y, width, height; + double w, h; + + if (self->paintable == NULL) + return; + + width = gtk_widget_get_width (widget); + height = gtk_widget_get_height (widget); + ratio = gdk_paintable_get_intrinsic_aspect_ratio (self->paintable); + + if (!self->keep_aspect_ratio || ratio == 0) + { + gdk_paintable_snapshot (self->paintable, snapshot, width, height); + } + else + { + double picture_ratio = (double) width / height; + + if (ratio > picture_ratio) + { + w = width; + h = width / ratio; + } + else + { + w = height * ratio; + h = height; + } + + x = (width - ceil (w)) / 2; + y = floor(height - ceil (h)) / 2; + + gtk_snapshot_offset (snapshot, x, y); + gdk_paintable_snapshot (self->paintable, snapshot, w, h); + gtk_snapshot_offset (snapshot, -x, -y); + } +} + +static GtkSizeRequestMode +gtk_picture_get_request_mode (GtkWidget *widget) +{ + return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; +} + +static void +gtk_picture_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkPicture *self = GTK_PICTURE (widget); + double min_width, min_height, nat_width, nat_height; + double default_size; + + if (self->paintable == NULL) + { + *minimum = 0; + *natural = 0; + return; + } + + default_size = _gtk_css_number_value_get (gtk_css_style_get_value (gtk_css_node_get_style (gtk_widget_get_css_node (widget)), GTK_CSS_PROPERTY_ICON_SIZE), 100); + + if (self->can_shrink) + { + min_width = min_height = 0; + } + else + { + gdk_paintable_compute_concrete_size (self->paintable, + 0, 0, + default_size, default_size, + &min_width, &min_height); + } + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + gdk_paintable_compute_concrete_size (self->paintable, + 0, + for_size < 0 ? 0 : for_size, + default_size, default_size, + &nat_width, &nat_height); + *minimum = ceil (min_width); + *natural = ceil (nat_width); + } + else + { + gdk_paintable_compute_concrete_size (self->paintable, + for_size < 0 ? 0 : for_size, + 0, + default_size, default_size, + &nat_width, &nat_height); + *minimum = ceil (min_height); + *natural = ceil (nat_height); + } +} + +static void +gtk_picture_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkPicture *self = GTK_PICTURE (object); + + switch (prop_id) + { + case PROP_PAINTABLE: + gtk_picture_set_paintable (self, g_value_get_object (value)); + break; + + case PROP_FILE: + gtk_picture_set_file (self, g_value_get_object (value)); + break; + + case PROP_ALTERNATIVE_TEXT: + gtk_picture_set_alternative_text (self, g_value_get_string (value)); + break; + + case PROP_KEEP_ASPECT_RATIO: + gtk_picture_set_keep_aspect_ratio (self, g_value_get_boolean (value)); + break; + + case PROP_CAN_SHRINK: + gtk_picture_set_can_shrink (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_picture_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkPicture *self = GTK_PICTURE (object); + + switch (prop_id) + { + case PROP_PAINTABLE: + g_value_set_object (value, self->paintable); + break; + + case PROP_FILE: + g_value_set_object (value, self->file); + break; + + case PROP_ALTERNATIVE_TEXT: + g_value_set_string (value, self->alternative_text); + break; + + case PROP_KEEP_ASPECT_RATIO: + g_value_set_boolean (value, self->keep_aspect_ratio); + break; + + case PROP_CAN_SHRINK: + g_value_set_boolean (value, self->can_shrink); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_picture_dispose (GObject *object) +{ + GtkPicture *self = GTK_PICTURE (object); + + gtk_picture_set_paintable (self, NULL); + + g_clear_object (&self->file); + + G_OBJECT_CLASS (gtk_picture_parent_class)->dispose (object); +}; + +static void +gtk_picture_class_init (GtkPictureClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + gobject_class->set_property = gtk_picture_set_property; + gobject_class->get_property = gtk_picture_get_property; + gobject_class->dispose = gtk_picture_dispose; + + widget_class->snapshot = gtk_picture_snapshot; + widget_class->get_request_mode = gtk_picture_get_request_mode; + widget_class->measure = gtk_picture_measure; + + /** + * GtkPicture:paintable: + * + * The #GdkPaintable to be displayed by this #GtkPicture. + */ + properties[PROP_PAINTABLE] = + g_param_spec_object ("paintable", + P_("Paintable"), + P_("The GdkPaintable to display"), + GDK_TYPE_PAINTABLE, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkPicture:paintable: + * + * The #GFile that is displayed or %NULL if none. + */ + properties[PROP_FILE] = + g_param_spec_object ("file", + P_("File"), + P_("File to load and display"), + G_TYPE_FILE, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkPicture:alternative-text: + * + * The alternative textual description for the picture. + */ + properties[PROP_ALTERNATIVE_TEXT] = + g_param_spec_string ("alternative-text", + P_("Alternative text"), + P_("The alternative textual description"), + NULL, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkPicture:keep-aspect-ratio: + * + * Whether the GtkPicture will render its contents trying to preserve the aspect + * ratio of the contents. + */ + properties[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); + + /** + * GtkPicture:can-shrink + * + * If the #GtkPicture can be made smaller than the self it contains. + */ + properties[PROP_CAN_SHRINK] = + g_param_spec_boolean ("can-shrink", + P_("Can shrink"), + P_("Allow self to be smaller than contents"), + TRUE, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties); + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_PICTURE_ACCESSIBLE); + gtk_widget_class_set_css_name (widget_class, I_("picture")); +} + +static void +gtk_picture_init (GtkPicture *self) +{ + gtk_widget_set_has_surface (GTK_WIDGET (self), FALSE); + + self->can_shrink = TRUE; + self->keep_aspect_ratio = TRUE; +} + +/** + * gtk_picture_new: + * + * Creates a new empty #GtkPicture widget. + * + * Returns: a newly created #GtkPicture widget. + **/ +GtkWidget* +gtk_picture_new (void) +{ + return g_object_new (GTK_TYPE_PICTURE, NULL); +} + +/** + * gtk_picture_new_for_paintable: + * @paintable: (allow-none): a #GdkPaintable, or %NULL + * + * Creates a new #GtkPicture displaying @paintable. + * + * The #GtkPicture will track changes to the @paintable and update + * its size and contents in response to it. + * + * Returns: a new #GtkPicture + **/ +GtkWidget* +gtk_picture_new_for_paintable (GdkPaintable *paintable) +{ + g_return_val_if_fail (paintable == NULL || GDK_IS_PAINTABLE (paintable), NULL); + + return g_object_new (GTK_TYPE_PICTURE, + "paintable", paintable, + NULL); +} + +/** + * gtk_picture_new_for_pixbuf: + * @pixbuf: (allow-none): a #GdkPixbuf, or %NULL + * + * Creates a new #GtkPicture displaying @pixbuf. + * + * This is a utility function that calls gtk_picture_new_for_paintable(), + * See that function for details. + * + * The pixbuf must not be modified after passing it to this function. + * + * Returns: a new #GtkPicture + **/ +GtkWidget* +gtk_picture_new_for_pixbuf (GdkPixbuf *pixbuf) +{ + GtkWidget *result; + GdkPaintable *paintable; + + g_return_val_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf), NULL); + + if (pixbuf) + paintable = GDK_PAINTABLE (gdk_texture_new_for_pixbuf (pixbuf)); + else + paintable = NULL; + + result = gtk_picture_new_for_paintable (paintable); + + if (paintable) + g_object_unref (paintable); + + return result; +} + +/** + * gtk_picture_new_for_file: + * @file: (allow-none): a #GFile + * + * Creates a new #GtkPicture displaying the given @file. If the file + * isn’t found or can’t be loaded, the resulting #GtkPicture be empty. + * + * If you need to detect failures to load the file, use + * gdk_texture_new_for_file() to load the file yourself, then create + * the #GtkPicture from the texture. + * + * Returns: a new #GtkPicture + **/ +GtkWidget* +gtk_picture_new_for_file (GFile *file) +{ + g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL); + + return g_object_new (GTK_TYPE_PICTURE, + "file", file, + NULL); +} + +/** + * gtk_picture_new_for_filename: + * @filename: (type filename) (allow-none): a filename + * + * Creates a new #GtkPicture displaying the file @filename. + * + * This is a utility function that calls gtk_picture_new_for_file(). + * See that function for details. + * + * Returns: a new #GtkPicture + **/ +GtkWidget* +gtk_picture_new_for_filename (const gchar *filename) +{ + GtkWidget *result; + GFile *file; + + if (filename) + file = g_file_new_for_path (filename); + else + file = NULL; + + result = gtk_picture_new_for_file (file); + + if (file) + g_object_unref (file); + + return result; +} + +/** + * gtk_picture_new_for_resource: + * @resource_path: (allow-none): resource path to play back + * + * Creates a new #GtkPicture displaying the file @filename. + * + * This is a utility function that calls gtk_picture_new_for_file(), + * See that function for details. + * + * Returns: a new #GtkPicture + **/ +GtkWidget * +gtk_picture_new_for_resource (const char *resource_path) +{ + GtkWidget *result; + GFile *file; + + if (resource_path) + { + char *uri, *escaped; + + escaped = g_uri_escape_string (resource_path, + G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE); + uri = g_strconcat ("resource://", escaped, NULL); + g_free (escaped); + + file = g_file_new_for_uri (uri); + g_free (uri); + } + else + { + file = NULL; + } + + result = gtk_picture_new_for_file (file); + + if (file) + g_object_unref (file); + + return result; +} + +typedef struct { + gint scale_factor; +} LoaderData; + +static void +on_loader_size_prepared (GdkPixbufLoader *loader, + gint width, + gint height, + gpointer user_data) +{ + LoaderData *loader_data = user_data; + GdkPixbufFormat *format; + + /* Let the regular icon helper code path handle non-scalable pictures */ + format = gdk_pixbuf_loader_get_format (loader); + if (!gdk_pixbuf_format_is_scalable (format)) + { + loader_data->scale_factor = 1; + return; + } + + gdk_pixbuf_loader_set_size (loader, + width * loader_data->scale_factor, + height * loader_data->scale_factor); +} + +static GdkPaintable * +load_scalable_with_loader (GFile *file, + gint scale_factor) +{ + GdkPixbufLoader *loader; + GBytes *bytes; + GdkPixbufAnimation *animation; + GdkPaintable *result, *scaler; + LoaderData loader_data; + + result = NULL; + + loader = gdk_pixbuf_loader_new (); + loader_data.scale_factor = scale_factor; + + g_signal_connect (loader, "size-prepared", G_CALLBACK (on_loader_size_prepared), &loader_data); + + bytes = g_file_load_bytes (file, NULL, NULL, NULL); + if (bytes == NULL) + goto out1; + + if (!gdk_pixbuf_loader_write_bytes (loader, bytes, NULL)) + goto out2; + + if (!gdk_pixbuf_loader_close (loader, NULL)) + goto out2; + + animation = gdk_pixbuf_loader_get_animation (loader); + if (animation == NULL) + goto out2; + + result = GDK_PAINTABLE (gdk_texture_new_for_pixbuf (gdk_pixbuf_animation_get_static_image (animation))); + scaler = gtk_scaler_new (result, loader_data.scale_factor); + g_object_unref (result); + result = scaler; + +out2: + g_bytes_unref (bytes); +out1: + g_object_unref (loader); + + return result; +} + +/** + * gtk_picture_set_file: + * @self: a #GtkPicture + * @file: (allow-none): a %GFile or %NULL + * + * Makes @self load and display @file. + * + * See gtk_picture_new_for_file() for details. + **/ +void +gtk_picture_set_file (GtkPicture *self, + GFile *file) +{ + GdkPaintable *paintable; + + g_return_if_fail (GTK_IS_PICTURE (self)); + g_return_if_fail (file == NULL || G_IS_FILE (file)); + + if (self->file == file) + return; + + g_object_freeze_notify (G_OBJECT (self)); + + g_set_object (&self->file, file); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILE]); + + paintable = load_scalable_with_loader (file, gtk_widget_get_scale_factor (GTK_WIDGET (self))); + gtk_picture_set_paintable (self, paintable); + g_object_unref (paintable); + + g_object_thaw_notify (G_OBJECT (self)); +} + +/** + * gtk_picture_get_file: + * @self: a #GtkPicture + * + * Gets the #GFile currently displayed if @self is displaying a file. + * If @self is not displaying a file, for example when gtk_picture_set_paintable() + * was used, then %NULL is returned. + * + * Returns: (nullable) (transfer none): The #GFile displayed by @self. + **/ +GFile * +gtk_picture_get_file (GtkPicture *self) +{ + g_return_val_if_fail (GTK_IS_PICTURE (self), FALSE); + + return self->file; +} + +/** + * gtk_picture_set_filename: + * @self: a #GtkPicture + * @filename: (allow-none): the filename to play + * + * Makes @self load and display the given @filename. + * + * This is a utility function that calls gtk_picture_set_file(), + **/ +void +gtk_picture_set_filename (GtkPicture *self, + const char *filename) +{ + GFile *file; + + g_return_if_fail (GTK_IS_PICTURE (self)); + + if (filename) + file = g_file_new_for_path (filename); + else + file = NULL; + + gtk_picture_set_file (self, file); + + if (file) + g_object_unref (file); +} + +/** + * gtk_picture_set_resource: + * @self: a #GtkPicture + * @resource_path: (allow-none): the resource to set + * + * Makes @self load and display the resource at the given + * @resource_path. + * + * This is a utility function that calls gtk_picture_set_file(), + **/ +void +gtk_picture_set_resource (GtkPicture *self, + const char *resource_path) +{ + GFile *file; + + g_return_if_fail (GTK_IS_PICTURE (self)); + + if (resource_path) + { + char *uri, *escaped; + + escaped = g_uri_escape_string (resource_path, + G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE); + uri = g_strconcat ("resource://", escaped, NULL); + g_free (escaped); + + file = g_file_new_for_uri (uri); + g_free (uri); + } + else + { + file = NULL; + } + + gtk_picture_set_file (self, file); + + if (file) + g_object_unref (file); +} + +/** + * gtk_picture_set_pixbuf: + * @self: a #GtkPicture + * @pixbuf: (allow-none): a #GdkPixbuf or %NULL + * + * See gtk_picture_new_for_pixbuf() for details. + * + * This is a utility function that calls gtk_picture_set_paintable(), + **/ +void +gtk_picture_set_pixbuf (GtkPicture *self, + GdkPixbuf *pixbuf) +{ + GdkTexture *texture; + + g_return_if_fail (GTK_IS_PICTURE (self)); + g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf)); + + if (pixbuf) + texture = gdk_texture_new_for_pixbuf (pixbuf); + else + texture = NULL; + + gtk_picture_set_paintable (self, GDK_PAINTABLE (texture)); + + if (texture) + g_object_unref (texture); +} + +static void +gtk_picture_paintable_invalidate_contents (GdkPaintable *paintable, + GtkPicture *self) +{ + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +static void +gtk_picture_paintable_invalidate_size (GdkPaintable *paintable, + GtkPicture *self) +{ + gtk_widget_queue_resize (GTK_WIDGET (self)); +} + +/** + * gtk_picture_set_paintable: + * @self: a #GtkPicture + * @paintable: (nullable): a #GdkPaintable or %NULL + * + * Makes @self display the given @paintable. If @paintable is %NULL, + * nothing will be displayed. + * + * See gtk_picture_new_for_paintable() for details. + **/ +void +gtk_picture_set_paintable (GtkPicture *self, + GdkPaintable *paintable) +{ + g_return_if_fail (GTK_IS_PICTURE (self)); + g_return_if_fail (paintable == NULL || GDK_IS_PAINTABLE (paintable)); + + if (self->paintable == paintable) + return; + + g_object_freeze_notify (G_OBJECT (self)); + + if (paintable) + g_object_ref (paintable); + + if (self->paintable) + { + g_signal_handlers_disconnect_by_func (self->paintable, + gtk_picture_paintable_invalidate_contents, + self); + g_signal_handlers_disconnect_by_func (self->paintable, + gtk_picture_paintable_invalidate_size, + self); + } + + self->paintable = paintable; + + if (paintable) + { + g_signal_connect (paintable, + "invalidate-contents", + G_CALLBACK (gtk_picture_paintable_invalidate_contents), + self); + g_signal_connect (paintable, + "invalidate-size", + G_CALLBACK (gtk_picture_paintable_invalidate_size), + self); + } + + gtk_widget_queue_resize (GTK_WIDGET (self)); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PAINTABLE]); + + g_object_thaw_notify (G_OBJECT (self)); +} + +/** + * gtk_picture_get_paintable: + * @self: a #GtkPicture + * + * Gets the #GdkPaintable being displayed by the #GtkPicture. + * + * Returns: (nullable) (transfer none): the displayed paintable, or %NULL if + * the picture is empty + **/ +GdkPaintable * +gtk_picture_get_paintable (GtkPicture *self) +{ + g_return_val_if_fail (GTK_IS_PICTURE (self), NULL); + + return self->paintable; +} + +/** + * gtk_picture_set_keep_aspect_ratio: + * @self: a #GtkPicture + * @keep_aspect_ratio: whether to keep aspect ratio + * + * If set to %TRUE, the @self 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 @self. + * + * If set to %FALSE or if the contents provide no aspect ratio, the + * contents will be stretched over the picture's whole area. + */ +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; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_KEEP_ASPECT_RATIO]); +} + +/** + * gtk_picture_get_keep_aspect_ratio: + * @self: a #GtkPicture + * + * Gets the value set via gtk_picture_set_keep_aspect_ratio(). + * + * Returns: %TRUE if the self tries to keep the contents' aspect ratio + **/ +gboolean +gtk_picture_get_keep_aspect_ratio (GtkPicture *self) +{ + g_return_val_if_fail (GTK_IS_PICTURE (self), TRUE); + + return self->keep_aspect_ratio; +} + +/** + * gtk_picture_set_can_shrink: + * @self: a #GtkPicture + * @can_shrink: if @self can be made smaller than its contents + * + * If set to %TRUE, the @self can be made smaller than its contents. + * The contents will then be scaled down when rendering. + * + * If you want to still force a minimum size manually, consider using + * gtk_widget_set_size_request(). + * + * Also of note is that a similar function for growing does not exist + * because the grow behavior can be controlled via + * gtk_widget_set_halign() and gtk_widget_set_valign(). + */ +void +gtk_picture_set_can_shrink (GtkPicture *self, + gboolean can_shrink) +{ + g_return_if_fail (GTK_IS_PICTURE (self)); + + if (self->can_shrink == can_shrink) + return; + + self->can_shrink = can_shrink; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CAN_SHRINK]); +} + +/** + * gtk_picture_get_can_shrink: + * @self: a #GtkPicture + * + * Gets the value set via gtk_picture_set_can_shrink(). + * + * Returns: %TRUE if the self can be made smaller than its contents + **/ +gboolean +gtk_picture_get_can_shrink (GtkPicture *self) +{ + g_return_val_if_fail (GTK_IS_PICTURE (self), FALSE); + + return self->can_shrink; +} + +/** + * gtk_picture_set_alternative_text: + * @self: a #GtkPicture + * @alternative_text: (allow-none): a textual description of the contents + * + * Sets an alternative textual description for the picture contents. + * It is equivalent to the "alt" attribute for images on websites. + * + * This text will be made available to accessibility tools. + * + * If the picture cannot be described textually, set this proeprty to %NULL. + */ +void +gtk_picture_set_alternative_text (GtkPicture *self, + const char *alternative_text) +{ + g_return_if_fail (GTK_IS_PICTURE (self)); + + if (g_str_equal (self->alternative_text, alternative_text)) + return; + + g_free (self->alternative_text); + self->alternative_text = g_strdup (alternative_text); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ALTERNATIVE_TEXT]); +} + +/** + * gtk_picture_get_alternative_text: + * @self: a #GtkPicture + * + * Gets the alternative textual description of the picture or returns %NULL if + * the picture cannot be described textually. + * + * Returns: (nullable) (transfer none): the alternative textual description + * of @self. + **/ +const char * +gtk_picture_get_alternative_text (GtkPicture *self) +{ + g_return_val_if_fail (GTK_IS_PICTURE (self), NULL); + + return self->alternative_text; +} + diff --git a/gtk/gtkpicture.h b/gtk/gtkpicture.h new file mode 100644 index 0000000000..b4076365c6 --- /dev/null +++ b/gtk/gtkpicture.h @@ -0,0 +1,92 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * 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> + */ + +#ifndef __GTK_PICTURE_H__ +#define __GTK_PICTURE_H__ + + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#include <gio/gio.h> +#include <gtk/gtkwidget.h> + + +G_BEGIN_DECLS + +#define GTK_TYPE_PICTURE (gtk_picture_get_type ()) + +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GtkPicture, gtk_picture, GTK, PICTURE, GtkWidget) + +GDK_AVAILABLE_IN_ALL +GtkWidget* gtk_picture_new (void); +GDK_AVAILABLE_IN_ALL +GtkWidget* gtk_picture_new_for_paintable (GdkPaintable *paintable); +GDK_AVAILABLE_IN_ALL +GtkWidget* gtk_picture_new_for_pixbuf (GdkPixbuf *pixbuf); +GDK_AVAILABLE_IN_ALL +GtkWidget* gtk_picture_new_for_file (GFile *file); +GDK_AVAILABLE_IN_ALL +GtkWidget* gtk_picture_new_for_filename (const gchar *filename); +GDK_AVAILABLE_IN_ALL +GtkWidget* gtk_picture_new_for_resource (const gchar *resource_path); + +GDK_AVAILABLE_IN_ALL +void gtk_picture_set_paintable (GtkPicture *self, + GdkPaintable *paintable); +GDK_AVAILABLE_IN_ALL +GdkPaintable * gtk_picture_get_paintable (GtkPicture *self); +GDK_AVAILABLE_IN_ALL +void gtk_picture_set_file (GtkPicture *self, + GFile *file); +GDK_AVAILABLE_IN_ALL +GFile * gtk_picture_get_file (GtkPicture *self); +GDK_AVAILABLE_IN_ALL +void gtk_picture_set_filename (GtkPicture *self, + const gchar *filename); +GDK_AVAILABLE_IN_ALL +void gtk_picture_set_resource (GtkPicture *self, + const gchar *resource_path); +GDK_AVAILABLE_IN_ALL +void gtk_picture_set_pixbuf (GtkPicture *self, + GdkPixbuf *pixbuf); + +GDK_AVAILABLE_IN_ALL +void gtk_picture_set_keep_aspect_ratio (GtkPicture *self, + gboolean keep_aspect_ratio); +GDK_AVAILABLE_IN_ALL +gboolean gtk_picture_get_keep_aspect_ratio (GtkPicture *self); +GDK_AVAILABLE_IN_ALL +void gtk_picture_set_can_shrink (GtkPicture *self, + gboolean can_shrink); +GDK_AVAILABLE_IN_ALL +gboolean gtk_picture_get_can_shrink (GtkPicture *self); + +GDK_AVAILABLE_IN_ALL +void gtk_picture_set_alternative_text (GtkPicture *self, + const char *alternative_text); +GDK_AVAILABLE_IN_ALL +const char * gtk_picture_get_alternative_text (GtkPicture *self); + + +G_END_DECLS + +#endif /* __GTK_PICTURE_H__ */ diff --git a/gtk/gtkvideo.c b/gtk/gtkvideo.c index 0281fbc230..f9e2eb9eb3 100644 --- a/gtk/gtkvideo.c +++ b/gtk/gtkvideo.c @@ -393,11 +393,12 @@ gtk_video_new_for_file (GFile *file) /** * gtk_video_new_for_filename: - * @filename: (allow-none): filename to play back + * @filename: (allow-none) (type filename): filename to play back * * Creates a #GtkVideo to play back the given @filename. * * This is a utility function that calls gtk_video_new_for_file(), + * See that function for details. * * Returns: a new #GtkVideo **/ diff --git a/gtk/gtkwidgetpaintable.c b/gtk/gtkwidgetpaintable.c index 5cae0afd44..a553b80bcf 100644 --- a/gtk/gtkwidgetpaintable.c +++ b/gtk/gtkwidgetpaintable.c @@ -46,11 +46,11 @@ * widget changes. * * You can of course use a GtkWidgetPaintable everywhere a - * #GdkPaintable is allowed, including using it on a #GtkImage (or one - * of its parents) that it was set on itself via gtk_image_set_from_paintable(). + * #GdkPaintable is allowed, including using it on a #GtkPicture (or one + * of its parents) that it was set on itself via gtk_picture_set_paintable(). * The paintable will take care of recursion when this happens. If you do - * this however, make sure to set the #GtkImage:can-shrink property - * to %TRUE or you might end up with an infinitely growing image. + * this however, ensure the #GtkPicture:can-shrink property is set to + * %TRUE or you might end up with an infinitely growing widget. */ struct _GtkWidgetPaintable { diff --git a/gtk/meson.build b/gtk/meson.build index 6d4084a672..e8565244b8 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -284,6 +284,7 @@ gtk_public_sources = files([ 'gtkpagesetup.c', 'gtkpaned.c', 'gtkpapersize.c', + 'gtkpicture.c', 'gtkpopover.c', 'gtkpopovermenu.c', 'gtkprintcontext.c', @@ -519,6 +520,7 @@ gtk_public_headers = files([ 'gtkpagesetup.h', 'gtkpaned.h', 'gtkpapersize.h', + 'gtkpicture.h', 'gtkpopover.h', 'gtkpopovermenu.h', 'gtkprintcontext.h', |