[gnome-photos/wip/rishi/zoom: 2/3] image-view: Support scrolling and zooming
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/zoom: 2/3] image-view: Support scrolling and zooming
- Date: Tue, 11 Apr 2017 11:45:33 +0000 (UTC)
commit 06666ddb4437b6c529b358cdb912682f75486d27
Author: Debarshi Ray <debarshir gnome org>
Date: Thu Mar 30 10:46:17 2017 +0200
image-view: Support scrolling and zooming
src/photos-image-view.c | 381 ++++++++++++++++++++++++++++++++++++++++++++---
src/photos-image-view.h | 6 +
2 files changed, 369 insertions(+), 18 deletions(-)
---
diff --git a/src/photos-image-view.c b/src/photos-image-view.c
index e7f4637..7a55a4b 100644
--- a/src/photos-image-view.c
+++ b/src/photos-image-view.c
@@ -29,6 +29,7 @@
#include "photos-gegl.h"
#include "photos-image-view.h"
#include "photos-marshalers.h"
+#include "photos-utils.h"
struct _PhotosImageView
@@ -36,8 +37,17 @@ struct _PhotosImageView
GtkDrawingArea parent_instance;
GeglBuffer *buffer;
GeglNode *node;
+ GeglRectangle bbox_zoomed_old;
+ GtkAdjustment *hadjustment;
+ GtkAdjustment *vadjustment;
+ GtkAllocation allocation_scaled_old;
+ GtkScrollablePolicy hscroll_policy;
+ GtkScrollablePolicy vscroll_policy;
cairo_region_t *bbox_region;
cairo_region_t *region;
+ gboolean best_fit;
+ gdouble height;
+ gdouble width;
gdouble x;
gdouble x_scaled;
gdouble y;
@@ -49,7 +59,11 @@ struct _PhotosImageView
enum
{
PROP_0,
+ PROP_HADJUSTMENT,
+ PROP_HSCROLL_POLICY,
PROP_NODE,
+ PROP_VADJUSTMENT,
+ PROP_VSCROLL_POLICY,
PROP_X,
PROP_Y,
PROP_ZOOM
@@ -65,7 +79,8 @@ enum
static guint signals[LAST_SIGNAL] = { 0 };
-G_DEFINE_TYPE (PhotosImageView, photos_image_view, GTK_TYPE_DRAWING_AREA);
+G_DEFINE_TYPE_WITH_CODE (PhotosImageView, photos_image_view, GTK_TYPE_DRAWING_AREA,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL));
static void photos_image_view_computed (PhotosImageView *self, GeglRectangle *rect);
@@ -111,11 +126,103 @@ photos_image_view_update_region (PhotosImageView *self)
static void
+photos_image_view_adjustment_value_changed (GtkAdjustment *adjustment, gpointer user_data)
+{
+ PhotosImageView *self = PHOTOS_IMAGE_VIEW (user_data);
+ gdouble value;
+ gint scale_factor;
+
+ g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+ g_return_if_fail (self->hadjustment == adjustment || self->vadjustment == adjustment);
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (self)))
+ return;
+
+ if (self->best_fit)
+ return;
+
+ scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+
+ if (self->hadjustment == adjustment)
+ {
+ value = gtk_adjustment_get_value (self->hadjustment);
+ self->x = value;
+ self->x_scaled = self->x * (gdouble) scale_factor;
+ }
+ else
+ {
+ value = gtk_adjustment_get_value (self->vadjustment);
+ self->y = value;
+ self->y_scaled = self->y * (gdouble) scale_factor;
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+
+static void
+photos_image_view_set_hadjustment_values (PhotosImageView *self)
+{
+ GtkAllocation allocation;
+ gdouble page_increment;
+ gdouble page_size;
+ gdouble step_increment;
+ gdouble upper;
+ gdouble value;
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &allocation);
+
+ page_size = (gdouble) allocation.width;
+ page_increment = page_size * 0.9;
+ step_increment = page_size * 0.1;
+
+ upper = MAX ((gdouble) allocation.width, self->width);
+ value = CLAMP (self->x, 0.0, upper - page_size);
+
+ gtk_adjustment_configure (self->hadjustment,
+ value,
+ 0.0,
+ upper,
+ step_increment,
+ page_increment,
+ page_size);
+}
+
+
+static void
+photos_image_view_set_vadjustment_values (PhotosImageView *self)
+{
+ GtkAllocation allocation;
+ gdouble page_increment;
+ gdouble page_size;
+ gdouble step_increment;
+ gdouble upper;
+ gdouble value;
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &allocation);
+
+ page_size = (gdouble) allocation.height;
+ page_increment = page_size * 0.9;
+ step_increment = page_size * 0.1;
+
+ upper = MAX ((gdouble) allocation.height, self->height);
+ value = CLAMP (self->y, 0.0, upper - page_size);
+
+ gtk_adjustment_configure (self->vadjustment,
+ value,
+ 0.0,
+ upper,
+ step_increment,
+ page_increment,
+ page_size);
+}
+
+
+static void
photos_image_view_update (PhotosImageView *self)
{
GdkRectangle viewport;
GeglRectangle bbox;
- gdouble zoom_scaled = 1.0;
gint scale_factor;
gint viewport_height_real;
gint viewport_width_real;
@@ -127,7 +234,7 @@ photos_image_view_update (PhotosImageView *self)
gtk_widget_get_allocation (GTK_WIDGET (self), &viewport);
- if (viewport.width < 0 || viewport.height < 0)
+ if (viewport.width <= 0 || viewport.height <= 0)
return;
bbox = *gegl_buffer_get_extent (self->buffer);
@@ -138,29 +245,64 @@ photos_image_view_update (PhotosImageView *self)
viewport_height_real = viewport.height * scale_factor;
viewport_width_real = viewport.width * scale_factor;
- if (bbox.height > viewport_height_real || bbox.width > viewport_width_real)
+ if (self->best_fit)
{
- gdouble height_ratio = bbox.height / (gdouble) viewport_height_real;
- gdouble width_ratio = bbox.width / (gdouble) viewport_width_real;
- gdouble max_ratio = MAX (height_ratio, width_ratio);
+ gdouble zoom_scaled = 1.0;
+
+ if (bbox.height > viewport_height_real || bbox.width > viewport_width_real)
+ {
+ gdouble height_ratio = bbox.height / (gdouble) viewport_height_real;
+ gdouble width_ratio = bbox.width / (gdouble) viewport_width_real;
+ gdouble max_ratio = MAX (height_ratio, width_ratio);
+
+ zoom_scaled = 1.0 / max_ratio;
- zoom_scaled = 1.0 / max_ratio;
+ bbox.width = (gint) (zoom_scaled * bbox.width + 0.5);
+ bbox.height = (gint) (zoom_scaled * bbox.height + 0.5);
+ bbox.x = (gint) (zoom_scaled * bbox.x + 0.5);
+ bbox.y = (gint) (zoom_scaled * bbox.y + 0.5);
+ }
- bbox.width = (gint) (zoom_scaled * bbox.width + 0.5);
- bbox.height = (gint) (zoom_scaled * bbox.height + 0.5);
- bbox.x = (gint) (zoom_scaled * bbox.x + 0.5);
- bbox.y = (gint) (zoom_scaled * bbox.y + 0.5);
+ self->x_scaled = (bbox.width - viewport_width_real) / 2.0 + bbox.x;
+ self->y_scaled = (bbox.height - viewport_height_real) / 2.0 + bbox.y;
+
+ self->zoom_scaled = zoom_scaled;
+ self->zoom = self->zoom_scaled / (gdouble) scale_factor;
}
+ else
+ {
+ gdouble ratio_old;
- self->zoom_scaled = zoom_scaled;
- self->zoom = self->zoom_scaled / (gdouble) scale_factor;
+ bbox.width = (gint) (self->zoom_scaled * bbox.width + 0.5);
+ bbox.height = (gint) (self->zoom_scaled * bbox.height + 0.5);
+ bbox.x = (gint) (self->zoom_scaled * bbox.x + 0.5);
+ bbox.y = (gint) (self->zoom_scaled * bbox.y + 0.5);
- /* At this point, viewport is definitely bigger than bbox. */
- self->x_scaled = (bbox.width - viewport_width_real) / 2.0 + bbox.x;
- self->y_scaled = (bbox.height - viewport_height_real) / 2.0 + bbox.y;
+ ratio_old = (self->x_scaled + self->allocation_scaled_old.width / 2.0)
+ / (gdouble) self->bbox_zoomed_old.width;
+ self->x_scaled = ratio_old * bbox.width - viewport_width_real / 2.0;
+
+ ratio_old = (self->y_scaled + self->allocation_scaled_old.height / 2.0)
+ / (gdouble) self->bbox_zoomed_old.height;
+ self->y_scaled = ratio_old * bbox.height - viewport_height_real / 2.0;
+ }
self->x = self->x_scaled / (gdouble) scale_factor;
self->y = self->y_scaled / (gdouble) scale_factor;
+
+ self->height = (gdouble) bbox.height / (gdouble) scale_factor;
+ self->width = (gdouble) bbox.width / (gdouble) scale_factor;
+
+ g_signal_handlers_block_by_func (self->hadjustment, photos_image_view_adjustment_value_changed, self);
+ g_signal_handlers_block_by_func (self->vadjustment, photos_image_view_adjustment_value_changed, self);
+
+ photos_image_view_set_hadjustment_values (self);
+ photos_image_view_set_vadjustment_values (self);
+
+ g_signal_handlers_unblock_by_func (self->hadjustment, photos_image_view_adjustment_value_changed, self);
+ g_signal_handlers_unblock_by_func (self->vadjustment, photos_image_view_adjustment_value_changed, self);
+
+ self->bbox_zoomed_old = bbox;
}
@@ -300,10 +442,93 @@ static void
photos_image_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
PhotosImageView *self = PHOTOS_IMAGE_VIEW (widget);
+ gint scale_factor;
GTK_WIDGET_CLASS (photos_image_view_parent_class)->size_allocate (widget, allocation);
photos_image_view_update (self);
+
+ scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ self->allocation_scaled_old.height = allocation->height * scale_factor;
+ self->allocation_scaled_old.width = allocation->width * scale_factor;
+}
+
+
+static void
+photos_image_view_set_hadjustment (PhotosImageView *self, GtkAdjustment *hadjustment)
+{
+ if (hadjustment != NULL && self->hadjustment == hadjustment)
+ return;
+
+ if (self->hadjustment != NULL)
+ g_signal_handlers_disconnect_by_func (self->hadjustment, photos_image_view_adjustment_value_changed,
self);
+
+ g_clear_object (&self->hadjustment);
+
+ if (hadjustment == NULL)
+ hadjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ g_signal_connect_object (hadjustment,
+ "value-changed",
+ G_CALLBACK (photos_image_view_adjustment_value_changed),
+ self,
+ 0);
+
+ self->hadjustment = g_object_ref_sink (hadjustment);
+ photos_image_view_set_hadjustment_values (self);
+
+ g_object_notify (G_OBJECT (self), "hadjustment");
+}
+
+
+static void
+photos_image_view_set_hscroll_policy (PhotosImageView *self, GtkScrollablePolicy hscroll_policy)
+{
+ if (self->hscroll_policy == hscroll_policy)
+ return;
+
+ self->hscroll_policy = hscroll_policy;
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ g_object_notify (G_OBJECT (self), "hscroll-policy");
+}
+
+
+static void
+photos_image_view_set_vadjustment (PhotosImageView *self, GtkAdjustment *vadjustment)
+{
+ if (vadjustment != NULL && self->vadjustment == vadjustment)
+ return;
+
+ if (self->vadjustment != NULL)
+ g_signal_handlers_disconnect_by_func (self->vadjustment, photos_image_view_adjustment_value_changed,
self);
+
+ g_clear_object (&self->vadjustment);
+
+ if (vadjustment == NULL)
+ vadjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ g_signal_connect_object (vadjustment,
+ "value-changed",
+ G_CALLBACK (photos_image_view_adjustment_value_changed),
+ self,
+ 0);
+
+ self->vadjustment = g_object_ref_sink (vadjustment);
+ photos_image_view_set_vadjustment_values (self);
+
+ g_object_notify (G_OBJECT (self), "vadjustment");
+}
+
+
+static void
+photos_image_view_set_vscroll_policy (PhotosImageView *self, GtkScrollablePolicy vscroll_policy)
+{
+ if (self->vscroll_policy == vscroll_policy)
+ return;
+
+ self->vscroll_policy = vscroll_policy;
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ g_object_notify (G_OBJECT (self), "vscroll-policy");
}
@@ -314,6 +539,8 @@ photos_image_view_dispose (GObject *object)
g_clear_object (&self->buffer);
g_clear_object (&self->node);
+ g_clear_object (&self->hadjustment);
+ g_clear_object (&self->vadjustment);
G_OBJECT_CLASS (photos_image_view_parent_class)->dispose (object);
}
@@ -338,10 +565,26 @@ photos_image_view_get_property (GObject *object, guint prop_id, GValue *value, G
switch (prop_id)
{
+ case PROP_HADJUSTMENT:
+ g_value_set_object (value, self->hadjustment);
+ break;
+
+ case PROP_HSCROLL_POLICY:
+ g_value_set_enum (value, self->hscroll_policy);
+ break;
+
case PROP_NODE:
g_value_set_object (value, self->node);
break;
+ case PROP_VADJUSTMENT:
+ g_value_set_object (value, self->vadjustment);
+ break;
+
+ case PROP_VSCROLL_POLICY:
+ g_value_set_enum (value, self->vscroll_policy);
+ break;
+
case PROP_X:
g_value_set_double (value, self->x);
break;
@@ -368,6 +611,24 @@ photos_image_view_set_property (GObject *object, guint prop_id, const GValue *va
switch (prop_id)
{
+ case PROP_HADJUSTMENT:
+ {
+ GtkAdjustment *hadjustment;
+
+ hadjustment = GTK_ADJUSTMENT (g_value_get_object (value));
+ photos_image_view_set_hadjustment (self, hadjustment);
+ break;
+ }
+
+ case PROP_HSCROLL_POLICY:
+ {
+ GtkScrollablePolicy hscroll_policy;
+
+ hscroll_policy = (GtkScrollablePolicy) g_value_get_enum (value);
+ photos_image_view_set_hscroll_policy (self, hscroll_policy);
+ break;
+ }
+
case PROP_NODE:
{
GeglNode *node;
@@ -377,6 +638,33 @@ photos_image_view_set_property (GObject *object, guint prop_id, const GValue *va
break;
}
+ case PROP_VADJUSTMENT:
+ {
+ GtkAdjustment *vadjustment;
+
+ vadjustment = GTK_ADJUSTMENT (g_value_get_object (value));
+ photos_image_view_set_vadjustment (self, vadjustment);
+ break;
+ }
+
+ case PROP_VSCROLL_POLICY:
+ {
+ GtkScrollablePolicy vscroll_policy;
+
+ vscroll_policy = (GtkScrollablePolicy) g_value_get_enum (value);
+ photos_image_view_set_vscroll_policy (self, vscroll_policy);
+ break;
+ }
+
+ case PROP_ZOOM:
+ {
+ gdouble zoom;
+
+ zoom = g_value_get_double (value);
+ photos_image_view_set_zoom (self, zoom);
+ break;
+ }
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -449,7 +737,7 @@ photos_image_view_class_init (PhotosImageViewClass *class)
0.0,
100.0,
1.0,
- G_PARAM_READABLE));
+ G_PARAM_READWRITE));
signals[DRAW_BACKGROUND] = g_signal_new ("draw-background",
G_TYPE_FROM_CLASS (class),
@@ -474,6 +762,11 @@ photos_image_view_class_init (PhotosImageViewClass *class)
2,
CAIRO_GOBJECT_TYPE_CONTEXT,
GDK_TYPE_RECTANGLE);
+
+ g_object_class_override_property (object_class, PROP_HADJUSTMENT, "hadjustment");
+ g_object_class_override_property (object_class, PROP_HSCROLL_POLICY, "hscroll-policy");
+ g_object_class_override_property (object_class, PROP_VADJUSTMENT, "vadjustment");
+ g_object_class_override_property (object_class, PROP_VSCROLL_POLICY, "vscroll-policy");
}
@@ -492,6 +785,14 @@ photos_image_view_new_from_node (GeglNode *node)
}
+gboolean
+photos_image_view_get_best_fit (PhotosImageView *self)
+{
+ g_return_val_if_fail (PHOTOS_IS_IMAGE_VIEW (self), FALSE);
+ return self->best_fit;
+}
+
+
GeglNode *
photos_image_view_get_node (PhotosImageView *self)
{
@@ -525,6 +826,25 @@ photos_image_view_get_zoom (PhotosImageView *self)
void
+photos_image_view_set_best_fit (PhotosImageView *self, gboolean best_fit)
+{
+ g_return_if_fail (PHOTOS_IS_IMAGE_VIEW (self));
+
+ if (self->best_fit == best_fit)
+ return;
+
+ self->best_fit = best_fit;
+
+ if (self->best_fit)
+ {
+ self->zoom = -1.0;
+ self->zoom_scaled = -1.0;
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ }
+}
+
+
+void
photos_image_view_set_node (PhotosImageView *self, GeglNode *node)
{
g_return_if_fail (PHOTOS_IS_IMAGE_VIEW (self));
@@ -539,6 +859,11 @@ photos_image_view_set_node (PhotosImageView *self, GeglNode *node)
g_signal_handlers_disconnect_by_func (self->node, photos_image_view_invalidated, self);
}
+ self->allocation_scaled_old.height = 0;
+ self->allocation_scaled_old.width = 0;
+ self->best_fit = TRUE;
+ self->height = 0.0;
+ self->width = 0.0;
g_clear_object (&self->buffer);
g_clear_object (&self->node);
g_clear_pointer (&self->bbox_region, (GDestroyNotify) cairo_region_destroy);
@@ -561,3 +886,23 @@ photos_image_view_set_node (PhotosImageView *self, GeglNode *node)
gtk_widget_queue_resize (GTK_WIDGET (self));
}
+
+
+void
+photos_image_view_set_zoom (PhotosImageView *self, gdouble zoom)
+{
+ gint scale_factor;
+
+ g_return_if_fail (PHOTOS_IS_IMAGE_VIEW (self));
+
+ if (photos_utils_equal_double (self->zoom, zoom))
+ return;
+
+ self->best_fit = FALSE;
+ self->zoom = zoom;
+
+ scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ self->zoom_scaled = self->zoom * scale_factor;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
diff --git a/src/photos-image-view.h b/src/photos-image-view.h
index 0ba8708..5cf0e82 100644
--- a/src/photos-image-view.h
+++ b/src/photos-image-view.h
@@ -33,6 +33,8 @@ GtkWidget *photos_image_view_new (void);
GtkWidget *photos_image_view_new_from_node (GeglNode *node);
+gboolean photos_image_view_get_best_fit (PhotosImageView *self);
+
GeglNode *photos_image_view_get_node (PhotosImageView *self);
gdouble photos_image_view_get_x (PhotosImageView *self);
@@ -41,8 +43,12 @@ gdouble photos_image_view_get_y (PhotosImageView *self)
gdouble photos_image_view_get_zoom (PhotosImageView *self);
+void photos_image_view_set_best_fit (PhotosImageView *self, gboolean best_fit);
+
void photos_image_view_set_node (PhotosImageView *self, GeglNode *node);
+void photos_image_view_set_zoom (PhotosImageView *self, gdouble zoom);
+
G_END_DECLS
#endif /* PHOTOS_IMAGE_VIEW_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]