[gtk+/wip/baedert/gtkimageview: 2040/2075] GtkImageView: Improvements!
- From: Timm Bäder <baedert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/baedert/gtkimageview: 2040/2075] GtkImageView: Improvements!
- Date: Sun, 12 Feb 2017 10:02:02 +0000 (UTC)
commit cb0bfb08b1c834f1cd34a341f08f4a6e299f3f8c
Author: Timm Bäder <mail baedert org>
Date: Mon Jan 18 18:22:28 2016 +0100
GtkImageView: Improvements!
gtk/gtkimageview.c | 1139 +++++++++++++++++++++++++++++++---------------------
gtk/gtkimageview.h | 67 ++--
2 files changed, 722 insertions(+), 484 deletions(-)
---
diff --git a/gtk/gtkimageview.c b/gtk/gtkimageview.c
index 5051339..fe69b33 100644
--- a/gtk/gtkimageview.c
+++ b/gtk/gtkimageview.c
@@ -19,6 +19,19 @@
#define RAD_TO_DEG(x) (((x) / (2.0 * M_PI) * 360.0))
#define TRANSITION_DURATION (150.0 * 1000.0)
+/*#define TRANSITION_DURATION (1500.0 * 1000.0)*/
+#define ANGLE_TRANSITION_MIN_DELTA (1.0)
+#define SCALE_TRANSITION_MIN_DELTA (0.01)
+
+
+/*
+ * TODO:
+ *
+ * - Angle transition from 395 to 5 does a -390deg rotation
+ * - transition from !fit-allocation to fit-allocation and back
+ *
+ */
+
typedef struct
{
@@ -30,10 +43,6 @@ typedef struct
double scale;
} State;
-
-// XXX Add gtk_image_reset_view that transitions back to scale = 1 and angle = 0.
-
-
struct _GtkImageViewPrivate
{
double scale;
@@ -48,12 +57,17 @@ struct _GtkImageViewPrivate
gboolean in_rotate : 1;
gboolean in_zoom : 1;
gboolean size_valid : 1;
+ gboolean transitions_enabled : 1;
+ gboolean in_angle_transition : 1;
+ gboolean in_scale_transition : 1;
GtkGesture *rotate_gesture;
double gesture_start_angle;
+ double visible_angle;
GtkGesture *zoom_gesture;
double gesture_start_scale;
+ double visible_scale;
/* Current anchor point, or -1/-1.
* In widget coordinates. */
@@ -76,9 +90,11 @@ struct _GtkImageViewPrivate
int animation_timeout;
/* Transitions */
- gint64 angle_transition_start;
double transition_start_angle;
- double transition_end_angle;
+ gint64 angle_transition_start;
+
+ double transition_start_scale;
+ gint64 scale_transition_start;
double cached_width;
double cached_height;
@@ -95,6 +111,7 @@ enum
PROP_ZOOM_GESTURE_ENABLED,
PROP_SNAP_ANGLE,
PROP_FIT_ALLOCATION,
+ PROP_TRANSITIONS_ENABLED,
LAST_WIDGET_PROPERTY,
PROP_HADJUSTMENT,
@@ -135,59 +152,244 @@ static void gtk_image_view_compute_bounding_box (GtkImageView *image_view,
double *width,
double *height,
double *scale_out);
+static void gtk_image_view_ensure_gestures (GtkImageView *image_view);
static inline void gtk_image_view_restrict_adjustment (GtkAdjustment *adjustment);
+static void gtk_image_view_fix_anchor (GtkImageView *image_view,
+ double anchor_x,
+ double anchor_y,
+ State *old_state);
+
/* }}} */
+static inline double
+gtk_image_view_get_real_scale (GtkImageView *image_view)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+
+ if (priv->in_zoom || priv->in_scale_transition)
+ return priv->visible_scale;
+ else
+ return priv->scale;
+}
+static inline double
+gtk_image_view_get_real_angle (GtkImageView *image_view)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+
+ if (priv->in_rotate || priv->in_angle_transition)
+ return priv->visible_angle;
+ else
+ return priv->angle;
+}
+
+static inline double
+gtk_image_view_clamp_angle (double angle)
+{
+ double new_angle = angle;
+
+ if (angle > 360.0)
+ new_angle -= (int)(angle / 360.0) * 360;
+ else if (angle < 0.0)
+ new_angle = 360.0 + (int)(angle / 360.0);
+
+ g_assert (new_angle >= 0.0);
+ g_assert (new_angle <= 360.0);
+
+ return new_angle;
+}
+
static void
gtk_image_view_get_current_state (GtkImageView *image_view,
State *state)
{
GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
- state->hvalue = gtk_adjustment_get_value (priv->hadjustment);
- state->vvalue = gtk_adjustment_get_value (priv->vadjustment);
- state->scale = priv->scale;
- state->angle = priv->angle;
- state->hupper = gtk_adjustment_get_upper (priv->hadjustment);
- state->vupper = gtk_adjustment_get_upper (priv->vadjustment);
+ if (priv->hadjustment != NULL && priv->vadjustment != NULL)
+ {
+ state->hvalue = gtk_adjustment_get_value (priv->hadjustment);
+ state->vvalue = gtk_adjustment_get_value (priv->vadjustment);
+ state->hupper = gtk_adjustment_get_upper (priv->hadjustment);
+ state->vupper = gtk_adjustment_get_upper (priv->vadjustment);
+ }
+ state->angle = gtk_image_view_get_real_angle (image_view);
+ state->scale = gtk_image_view_get_real_scale (image_view);
+}
+
+static gboolean
+gtk_image_view_transitions_enabled (GtkImageView *image_view)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+ gboolean animations_enabled;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (image_view)),
+ "gtk-enable-animations", &animations_enabled,
+ NULL);
+
+
+ return priv->transitions_enabled && animations_enabled;
+}
+
+
+static gboolean
+scale_frameclock_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock,
+ gpointer user_data)
+{
+ GtkImageView *image_view = GTK_IMAGE_VIEW (widget);
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+ State state;
+ gint64 now = gdk_frame_clock_get_frame_time (frame_clock);
+
+ double t = (now - priv->scale_transition_start) / TRANSITION_DURATION;
+ double new_scale = (priv->scale - priv->transition_start_scale) * t;
+
+ gtk_image_view_get_current_state (image_view, &state);
+
+ priv->visible_scale = priv->transition_start_scale + new_scale;
+ priv->size_valid = FALSE;
+
+ if (priv->hadjustment && priv->vadjustment)
+ {
+ GtkAllocation allocation;
+ gtk_widget_get_allocation (widget, &allocation);
+ gtk_image_view_update_adjustments (image_view);
+
+ gtk_image_view_fix_anchor (image_view,
+ allocation.width / 2,
+ allocation.height / 2,
+ &state);
+ }
+
+
+ if (priv->fit_allocation)
+ gtk_widget_queue_draw (widget);
+ else
+ gtk_widget_queue_resize (widget);
+
+ if (t >= 1.0)
+ {
+ priv->in_scale_transition = FALSE;
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+gtk_image_view_animate_to_scale (GtkImageView *image_view,
+ double new_scale)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+
+ /* Target scale is priv->scale */
+ priv->in_scale_transition = TRUE;
+ priv->visible_scale = priv->scale;
+ priv->transition_start_scale = priv->scale;
+ priv->scale_transition_start = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (GTK_WIDGET
(image_view)));
+
+ gtk_widget_add_tick_callback (GTK_WIDGET (image_view),
+ scale_frameclock_cb,
+ NULL, NULL);
}
-static gchar *
-state_str (State *s)
+static gboolean
+angle_frameclock_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock,
+ gpointer user_data)
{
- gchar *str = g_strdup_printf ("(Angle: %f, Scale: %f, hvalue: %f, vvalue: %f, hupper: %f, vupper: %f)",
- s->angle, s->scale, s->hvalue, s->vvalue, s->hupper, s->vupper);
- return str;
+ GtkImageView *image_view = GTK_IMAGE_VIEW (widget);
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+ State state;
+ gint64 now = gdk_frame_clock_get_frame_time (frame_clock);
+
+ double t = (now - priv->angle_transition_start) / TRANSITION_DURATION;
+ double new_angle = (priv->angle - priv->transition_start_angle) * t;
+
+ gtk_image_view_get_current_state (image_view, &state);
+
+ priv->visible_angle = priv->transition_start_angle + new_angle;
+ priv->size_valid = FALSE;
+
+
+ if (priv->hadjustment && priv->vadjustment)
+ {
+ GtkAllocation allocation;
+ gtk_widget_get_allocation (widget, &allocation);
+ gtk_image_view_update_adjustments (image_view);
+
+ gtk_image_view_fix_anchor (image_view,
+ allocation.width / 2,
+ allocation.height / 2,
+ &state);
+ }
+
+ if (priv->fit_allocation)
+ gtk_widget_queue_draw (widget);
+ else
+ gtk_widget_queue_resize (widget);
+
+ if (t >= 1.0)
+ {
+ priv->in_angle_transition = FALSE;
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+
+static void
+gtk_image_view_animate_to_angle (GtkImageView *image_view,
+ double target_angle)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+
+ /* Target angle is priv->angle */
+ priv->in_angle_transition = TRUE;
+ priv->visible_angle = priv->angle;
+ priv->transition_start_angle = priv->angle;
+ priv->angle_transition_start = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (GTK_WIDGET
(image_view)));
+
+ gtk_widget_add_tick_callback (GTK_WIDGET (image_view),
+ angle_frameclock_cb,
+ NULL, NULL);
}
+static void
+gtk_image_view_do_snapping (GtkImageView *image_view)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+ double new_angle = (int) ((priv->angle + 45.0) / 90.0) * 90;
+
+ g_assert (priv->snap_angle);
+
+ if (gtk_image_view_transitions_enabled (image_view))
+ gtk_image_view_animate_to_angle (image_view, new_angle);
+ priv->angle = new_angle;
+
+ /* Don't notify! */
+}
static void
free_load_task_data (LoadTaskData *data)
{
g_clear_object (&data->source);
+ g_slice_free (LoadTaskData, data);
}
- /* XXX What if the image is rotated by 45deg and the user presses outside of it?
- * I.e. the anchor point would lie outside of the image? */
-
static void
to_rotate_coords (GtkImageView *image_view,
- State *state,
+ State *state,
double in_x, double in_y,
double *out_x, double *out_y)
{
- GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
-
- g_message ("hupper: %f", gtk_adjustment_get_upper (priv->hadjustment));
- g_message ("hvalue: %f", gtk_adjustment_get_value (priv->hadjustment));
-
double cx = state->hupper / 2.0 - state->hvalue;
double cy = state->vupper / 2.0 - state->vvalue;
@@ -203,10 +405,17 @@ gtk_image_view_fix_anchor (GtkImageView *image_view,
State *old_state)
{
GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
- double hupper_delta = gtk_adjustment_get_upper (priv->hadjustment) - old_state->hupper;
- double vupper_delta = gtk_adjustment_get_upper (priv->vadjustment) - old_state->vupper;
+ double hupper_delta = gtk_adjustment_get_upper (priv->hadjustment)
+ - old_state->hupper;
+ double vupper_delta = gtk_adjustment_get_upper (priv->vadjustment)
+ - old_state->vupper;
+ double hupper_delta_scale, vupper_delta_scale;
+ double hupper_delta_angle, vupper_delta_angle;
+ double cur_scale = gtk_image_view_get_real_scale (image_view);
+ g_assert (old_state->hupper > 0);
+ g_assert (old_state->vupper > 0);
g_assert (priv->hadjustment);
g_assert (priv->vadjustment);
g_assert (priv->size_valid);
@@ -215,41 +424,15 @@ gtk_image_view_fix_anchor (GtkImageView *image_view,
g_assert (anchor_x < gtk_widget_get_allocated_width (GTK_WIDGET (image_view)));
g_assert (anchor_y < gtk_widget_get_allocated_height (GTK_WIDGET (image_view)));
-
-
- g_message ("Old State: %s", state_str (old_state));
- g_message ("New angle: %f", priv->angle);
- g_message ("New scale: %f", priv->scale);
-
- g_message ("Anchor: %f/%f", priv->anchor_x, priv->anchor_y);
- g_message ("hupper_delta: %f", hupper_delta);
- g_message ("vupper_delta: %f", vupper_delta);
-
-
-
-
- double hupper = gtk_adjustment_get_upper (priv->hadjustment);
- double hdiff_scale = (old_state->hupper / old_state->scale) * priv->scale;
-
- double hd = hdiff_scale - old_state->hupper;
-
- g_message ("hupper diff: %f", (hupper - old_state->hupper));
- g_message ("scale_diff: %f", hd);
- g_message ("angle_diff: %f", hupper_delta - hd);
-
-
/* Amount of upper change caused by scale */
- double hupper_delta_scale = ((old_state->hupper / old_state->scale) * priv->scale)
- - old_state->hupper;
- double vupper_delta_scale = ((old_state->vupper / old_state->scale) * priv->scale)
- - old_state->vupper;
+ hupper_delta_scale = ((old_state->hupper / old_state->scale) * cur_scale)
+ - old_state->hupper;
+ vupper_delta_scale = ((old_state->vupper / old_state->scale) * cur_scale)
+ - old_state->vupper;
/* Amount of upper change caused by angle */
- double hupper_delta_angle = hupper_delta - hupper_delta_scale;
- double vupper_delta_angle = vupper_delta - vupper_delta_scale;
-
-
-
+ hupper_delta_angle = hupper_delta - hupper_delta_scale;
+ vupper_delta_angle = vupper_delta - vupper_delta_scale;
/* As a first step, fix the anchor point with regard to the
* updated scale
@@ -261,8 +444,8 @@ gtk_image_view_fix_anchor (GtkImageView *image_view,
double px = anchor_x + hvalue;
double py = anchor_y + vvalue;
- double px_after = (px / old_state->scale) * priv->scale;
- double py_after = (py / old_state->scale) * priv->scale;
+ double px_after = (px / old_state->scale) * cur_scale;
+ double py_after = (py / old_state->scale) * cur_scale;
gtk_adjustment_set_value (priv->hadjustment,
hvalue + px_after - px);
@@ -280,139 +463,50 @@ gtk_image_view_fix_anchor (GtkImageView *image_view,
+ {
+ double rotate_anchor_x = 0;
+ double rotate_anchor_y = 0;
+ double anchor_angle;
+ double anchor_length;
+ double new_anchor_x, new_anchor_y;
+ double delta_x, delta_y;
+
+ /* Calculate the angle of the given anchor point relative to the
+ * bounding box center and the OLD state */
+ to_rotate_coords (image_view, old_state,
+ anchor_x, anchor_y,
+ &rotate_anchor_x, &rotate_anchor_y);
+ anchor_angle = atan2 (rotate_anchor_y, rotate_anchor_x);
+ anchor_length = sqrt ((rotate_anchor_x * rotate_anchor_x) +
+ (rotate_anchor_y * rotate_anchor_y));
+
+ /* The angle of the anchor point NOW is the old angle plus
+ * the difference between old surface angle and new surface angle */
+ anchor_angle += DEG_TO_RAD (gtk_image_view_get_real_angle (image_view)
+ - old_state->angle);
+
+ /* Calculate the position of the new anchor point, relative
+ * to the bounding box center */
+ new_anchor_x = cos (anchor_angle) * anchor_length;
+ new_anchor_y = sin (anchor_angle) * anchor_length;
+
+ /* The difference between old anchor and new anchor
+ * is what we care about... */
+ delta_x = rotate_anchor_x - new_anchor_x;
+ delta_y = rotate_anchor_y - new_anchor_y;
+
+ /* At last, make the old anchor match the new anchor */
+ gtk_adjustment_set_value (priv->hadjustment,
+ gtk_adjustment_get_value (priv->hadjustment) - delta_x);
+ gtk_adjustment_set_value (priv->vadjustment,
+ gtk_adjustment_get_value (priv->vadjustment) - delta_y);
- double rotate_anchor_x = 0;
- double rotate_anchor_y = 0;
-
- to_rotate_coords (image_view, old_state,
- anchor_x, anchor_y,
- &rotate_anchor_x, &rotate_anchor_y);
-
- g_message ("Rotate anchor coords: %f/%f", rotate_anchor_x, rotate_anchor_y);
-
- /* 1) Calculate the angle of our anchor point. */
- double anchor_angle = atan2 (rotate_anchor_y, rotate_anchor_x);
-
-
- double anchor_length = sqrt ((rotate_anchor_x * rotate_anchor_x) +
- (rotate_anchor_y * rotate_anchor_y));
- g_message ("Anchor angle: %f", RAD_TO_DEG (anchor_angle));
- g_message ("Anchor length: %f", anchor_length);
-
- /* 2) Calculate the position of our anchor point with increased angle */
- double angle_diff = priv->angle - old_state->angle;
- anchor_angle += DEG_TO_RAD (angle_diff);
-
- g_message ("anchor angle after: %f", RAD_TO_DEG (anchor_angle));
-
-
- g_message ("Angle got increased by %f", angle_diff);
- double scale_diff = priv->scale - old_state->scale;
- g_message ("Scale got increased by %f", scale_diff);
- double new_anchor_x = cos (anchor_angle) * anchor_length;
- double new_anchor_y = sin (anchor_angle) * anchor_length;
-
- g_message ("New anchor: %f, %f", new_anchor_x, new_anchor_y);
-
-
- double diff_x = rotate_anchor_x - new_anchor_x;
- double diff_y = rotate_anchor_y - new_anchor_y;
-
- g_message ("Diff: %f/%f", diff_x, diff_y);
-
- gtk_adjustment_set_value (priv->hadjustment,
- gtk_adjustment_get_value (priv->hadjustment) - diff_x);
-
-
- gtk_adjustment_set_value (priv->vadjustment,
- gtk_adjustment_get_value (priv->vadjustment) - diff_y);
-
+ }
- g_message ("-------------------------");
gtk_widget_queue_draw (GTK_WIDGET (image_view));
}
static void
-gesture_rotate_end_cb (GtkGesture *gesture,
- GdkEventSequence *sequence,
- gpointer user_data)
-{
- GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (user_data);
-
- priv->gesture_start_angle = 0.0;
- priv->in_rotate = FALSE;
-
- priv->anchor_x = -1;
- priv->anchor_y = -1;
-}
-
-static void
-gesture_rotate_cancel_cb (GtkGesture *gesture,
- GdkEventSequence *sequence,
- gpointer user_data)
-{
- GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (user_data);
- gtk_image_view_set_angle (user_data, priv->gesture_start_angle);
- priv->in_rotate = FALSE;
- priv->gesture_start_angle = 0.0;
-
- priv->anchor_x = -1;
- priv->anchor_y = -1;
-}
-
-
-static void
-gesture_angle_changed_cb (GtkGestureRotate *gesture,
- double angle,
- double delta,
- GtkWidget *widget)
-{
- GtkImageView *image_view = GTK_IMAGE_VIEW (widget);
- GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
- State old_state;
- double new_angle;
-
- if (!priv->rotate_gesture_enabled)
- {
- gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
- return;
- }
-
- if (!priv->in_rotate)
- {
- priv->in_rotate = TRUE;
- priv->gesture_start_angle = priv->angle;
- }
-
-
- new_angle = priv->gesture_start_angle + RAD_TO_DEG (delta);
-
- if (new_angle == priv->angle)
- return;
-
-
- gtk_image_view_get_current_state (image_view, &old_state);
-
- /* Don't notify */
- priv->angle = new_angle;
- priv->size_valid = FALSE;
- gtk_image_view_update_adjustments (image_view);
-
- if (priv->hadjustment && priv->vadjustment)
- gtk_image_view_fix_anchor (image_view,
- priv->anchor_x,
- priv->anchor_y,
- &old_state);
-
- // XXX Even if fit_allocation is not set, we still don't need to query a resize
- // if we are in a scrolledwindow, right?
- if (priv->fit_allocation)
- gtk_widget_queue_draw (widget);
- else
- gtk_widget_queue_resize (widget);
-}
-
-static void
gtk_image_view_compute_bounding_box (GtkImageView *image_view,
double *width,
double *height,
@@ -430,6 +524,8 @@ gtk_image_view_compute_bounding_box (GtkImageView *image_view,
double upper_right_x, upper_right_y;
double upper_left_x, upper_left_y;
double scale;
+ double angle;
+
if (priv->size_valid)
{
*width = priv->cached_width;
@@ -448,12 +544,13 @@ gtk_image_view_compute_bounding_box (GtkImageView *image_view,
}
gtk_widget_get_allocation (GTK_WIDGET (image_view), &alloc);
+ angle = gtk_image_view_get_real_angle (image_view);
image_width = cairo_image_surface_get_width (priv->image_surface);
image_height = cairo_image_surface_get_height (priv->image_surface);
- upper_right_degrees = DEG_TO_RAD (priv->angle) + atan (image_height / image_width);
- upper_left_degrees = DEG_TO_RAD (priv->angle) + atan (image_height / -image_width);
+ upper_right_degrees = DEG_TO_RAD (angle) + atan (image_height / image_width);
+ upper_left_degrees = DEG_TO_RAD (angle) + atan (image_height / -image_width);
r = sqrt ((image_width / 2.0) * (image_width / 2.0) + (image_height / 2.0) * (image_height / 2.0));
upper_right_x = r * cos (upper_right_degrees);
@@ -465,23 +562,17 @@ gtk_image_view_compute_bounding_box (GtkImageView *image_view,
bb_width = round (MAX (fabs (upper_right_x), fabs (upper_left_x)) * 2.0);
bb_height = round (MAX (fabs (upper_right_y), fabs (upper_left_y)) * 2.0);
- if (priv->scale_set)
+
+ if (priv->fit_allocation)
{
- scale = priv->scale;
+ double scale_x = (double)alloc.width / (double)bb_width;
+ double scale_y = (double)alloc.height / (double)bb_height;
+
+ scale = MIN (MIN (scale_x, scale_y), 1.0);
}
else
{
- if (priv->fit_allocation)
- {
- double scale_x = (double)alloc.width / (double)bb_width;
- double scale_y = (double)alloc.height / (double)bb_height;
-
- scale = MIN (MIN (scale_x, scale_y), 1.0);
- }
- else
- {
- scale = 1.0;
- }
+ scale = gtk_image_view_get_real_scale (image_view);
}
priv->cached_scale = scale;
@@ -490,6 +581,7 @@ gtk_image_view_compute_bounding_box (GtkImageView *image_view,
if (priv->fit_allocation)
{
+ g_assert (!priv->scale_set);
priv->scale = scale;
g_object_notify_by_pspec (G_OBJECT (image_view),
widget_props[PROP_SCALE]);
@@ -522,6 +614,7 @@ static void
gtk_image_view_update_adjustments (GtkImageView *image_view)
{
GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+ double width, height;
int widget_width = gtk_widget_get_allocated_width (GTK_WIDGET (image_view));
int widget_height = gtk_widget_get_allocated_height (GTK_WIDGET (image_view));
@@ -540,52 +633,28 @@ gtk_image_view_update_adjustments (GtkImageView *image_view)
return;
}
+ gtk_image_view_compute_bounding_box (image_view,
+ &width,
+ &height,
+ NULL);
- if (priv->fit_allocation)
- {
- if (priv->hadjustment)
- gtk_adjustment_set_upper (priv->hadjustment, widget_width);
-
- if (priv->vadjustment)
- gtk_adjustment_set_upper (priv->vadjustment, widget_height);
- }
- else
- {
- double width, height;
- gtk_image_view_compute_bounding_box (image_view,
- &width,
- &height,
- NULL);
-
- if (priv->hadjustment)
- gtk_adjustment_set_upper (priv->hadjustment, MAX (width, widget_width));
-
- if (priv->vadjustment)
- gtk_adjustment_set_upper (priv->vadjustment, MAX (height, widget_height));
- }
-
-
+ /* compute_bounding_box makes sure that the bounding box is never bigger than
+ * the widget allocation if fit-allocation is set */
if (priv->hadjustment)
{
+ gtk_adjustment_set_upper (priv->hadjustment, MAX (width, widget_width));
gtk_adjustment_set_page_size (priv->hadjustment, widget_width);
gtk_image_view_restrict_adjustment (priv->hadjustment);
}
if (priv->vadjustment)
{
+ gtk_adjustment_set_upper (priv->vadjustment, MAX (height, widget_height));
gtk_adjustment_set_page_size (priv->vadjustment, widget_height);
gtk_image_view_restrict_adjustment (priv->vadjustment);
}
}
-
-
-
-/*
- * This is basically the normal _set_scale without the
- * _fix_anchor call at the end, so we can choose the point
- * to fix.
- */
static void
gtk_image_view_set_scale_internal (GtkImageView *image_view,
double scale)
@@ -598,10 +667,9 @@ gtk_image_view_set_scale_internal (GtkImageView *image_view,
g_object_notify_by_pspec (G_OBJECT (image_view),
widget_props[PROP_SCALE]);
-
- if (!priv->scale_set)
+ if (priv->scale_set)
{
- priv->scale_set = TRUE;
+ priv->scale_set = FALSE;
g_object_notify_by_pspec (G_OBJECT (image_view),
widget_props[PROP_SCALE_SET]);
}
@@ -619,18 +687,40 @@ gtk_image_view_set_scale_internal (GtkImageView *image_view,
gtk_widget_queue_resize (GTK_WIDGET (image_view));
}
+/* Zoom Gesture {{{ */
+
+static void
+gesture_zoom_begin_cb (GtkGesture *gesture,
+ GdkEventSequence *sequence,
+ gpointer user_data)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (user_data);
+
+ if (!priv->zoom_gesture_enabled ||
+ !priv->image_surface)
+ {
+ gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+ return;
+ }
+
+ if (priv->anchor_x == -1 && priv->anchor_y == -1)
+ {
+ gtk_gesture_get_bounding_box_center (gesture,
+ &priv->anchor_x,
+ &priv->anchor_y);
+ }
+}
+
static void
gesture_zoom_end_cb (GtkGesture *gesture,
GdkEventSequence *sequence,
- gpointer user_data)
+ gpointer image_view)
{
- GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (user_data);
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
- gtk_image_view_set_scale (user_data, priv->scale);
+ gtk_image_view_set_scale_internal (image_view, priv->visible_scale);
- priv->gesture_start_scale = 0.0;
priv->in_zoom = FALSE;
-
priv->anchor_x = -1;
priv->anchor_y = -1;
}
@@ -642,17 +732,16 @@ gesture_zoom_cancel_cb (GtkGesture *gesture,
{
GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (user_data);
- gtk_image_view_set_scale (user_data, priv->gesture_start_scale);
+ if (priv->in_zoom)
+ gtk_image_view_set_scale (user_data, priv->gesture_start_scale);
- priv->gesture_start_scale = 0.0;
priv->in_zoom = FALSE;
priv->anchor_x = -1;
priv->anchor_y = -1;
}
-
static void
-gesture_scale_changed_cb (GtkGestureZoom *gesture,
+gesture_zoom_changed_cb (GtkGestureZoom *gesture,
double delta,
GtkWidget *widget)
{
@@ -661,43 +750,56 @@ gesture_scale_changed_cb (GtkGestureZoom *gesture,
State state;
double new_scale;
- if (!priv->rotate_gesture_enabled)
- {
- gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
- return;
- }
-
if (!priv->in_zoom)
{
priv->in_zoom = TRUE;
priv->gesture_start_scale = priv->scale;
}
+ if (priv->fit_allocation)
+ {
+ priv->fit_allocation = FALSE;
+ g_object_notify_by_pspec (G_OBJECT (widget),
+ widget_props[PROP_FIT_ALLOCATION]);
+ }
+
new_scale = priv->gesture_start_scale * delta;
gtk_image_view_get_current_state (image_view, &state);
- /* Don't emit */
- priv->scale = new_scale;
- gtk_image_view_update_adjustments (image_view);
+ priv->visible_scale = new_scale;
+ priv->size_valid = FALSE;
- gtk_image_view_set_scale_internal (image_view, new_scale);
+ gtk_image_view_update_adjustments (image_view);
- if (priv->hadjustment || priv->vadjustment)
+ if (priv->hadjustment != NULL && priv->vadjustment != NULL)
{
gtk_image_view_fix_anchor (image_view,
- priv->anchor_x,
- priv->anchor_y,
- &state);
+ priv->anchor_x,
+ priv->anchor_y,
+ &state);
}
+
+ gtk_widget_queue_resize (GTK_WIDGET (image_view));
}
+/* }}} */
+
+/* Rotate Gesture {{{ */
static void
-gesture_begin_cb (GtkGesture *gesture,
- GdkEventSequence *sequence,
- gpointer user_data)
+gesture_rotate_begin_cb (GtkGesture *gesture,
+ GdkEventSequence *sequence,
+ gpointer user_data)
{
GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (user_data);
+ if (!priv->rotate_gesture_enabled ||
+ !priv->image_surface)
+ {
+ gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+ return;
+ }
+
+
if (priv->anchor_x == -1 && priv->anchor_y == -1)
{
gtk_gesture_get_bounding_box_center (gesture,
@@ -706,6 +808,125 @@ gesture_begin_cb (GtkGesture *gesture,
}
}
+static void
+gesture_rotate_end_cb (GtkGesture *gesture,
+ GdkEventSequence *sequence,
+ gpointer image_view)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+
+ priv->angle = gtk_image_view_clamp_angle (priv->visible_angle);
+
+ if (priv->snap_angle)
+ {
+ /* Will update priv->angle */
+ gtk_image_view_do_snapping (image_view);
+ }
+ g_object_notify_by_pspec (image_view,
+ widget_props[PROP_ANGLE]);
+
+
+ priv->in_rotate = FALSE;
+ priv->anchor_x = -1;
+ priv->anchor_y = -1;
+}
+
+static void
+gesture_rotate_cancel_cb (GtkGesture *gesture,
+ GdkEventSequence *sequence,
+ gpointer image_view)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+
+ priv->size_valid = FALSE;
+ gtk_image_view_update_adjustments (image_view);
+
+ priv->in_rotate = FALSE;
+ priv->anchor_x = -1;
+ priv->anchor_y = -1;
+}
+
+static void
+gesture_rotate_changed_cb (GtkGestureRotate *gesture,
+ double angle,
+ double delta,
+ GtkWidget *widget)
+{
+ GtkImageView *image_view = GTK_IMAGE_VIEW (widget);
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+ State old_state;
+ double new_angle;
+
+ if (!priv->in_rotate)
+ {
+ priv->in_rotate = TRUE;
+ priv->gesture_start_angle = priv->angle;
+ }
+
+
+ new_angle = priv->gesture_start_angle + RAD_TO_DEG (delta);
+ gtk_image_view_get_current_state (image_view, &old_state);
+
+ priv->visible_angle = new_angle;
+ priv->size_valid = FALSE;
+ gtk_image_view_update_adjustments (image_view);
+
+ if (priv->hadjustment && priv->vadjustment && !priv->fit_allocation)
+ gtk_image_view_fix_anchor (image_view,
+ priv->anchor_x,
+ priv->anchor_y,
+ &old_state);
+
+ if (priv->fit_allocation)
+ gtk_widget_queue_draw (widget);
+ else
+ gtk_widget_queue_resize (widget);
+}
+/* }}} */
+
+static void
+gtk_image_view_ensure_gestures (GtkImageView *image_view)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+
+ if (priv->zoom_gesture_enabled && priv->zoom_gesture == NULL)
+ {
+ priv->zoom_gesture = gtk_gesture_zoom_new (GTK_WIDGET (image_view));
+ g_signal_connect (priv->zoom_gesture, "scale-changed",
+ (GCallback)gesture_zoom_changed_cb, image_view);
+ g_signal_connect (priv->zoom_gesture, "begin",
+ (GCallback)gesture_zoom_begin_cb, image_view);
+ g_signal_connect (priv->zoom_gesture, "end",
+ (GCallback)gesture_zoom_end_cb, image_view);
+ g_signal_connect (priv->zoom_gesture, "cancel",
+ (GCallback)gesture_zoom_cancel_cb, image_view);
+ }
+ else if (!priv->zoom_gesture_enabled && priv->zoom_gesture != NULL)
+ {
+ g_clear_object (&priv->zoom_gesture);
+ }
+
+ if (priv->rotate_gesture_enabled && priv->rotate_gesture == NULL)
+ {
+ priv->rotate_gesture = gtk_gesture_rotate_new (GTK_WIDGET (image_view));
+ /*gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->rotate_gesture),*/
+ /*GTK_PHASE_CAPTURE);*/
+ g_signal_connect (priv->rotate_gesture, "angle-changed", (GCallback)gesture_rotate_changed_cb,
image_view);
+ g_signal_connect (priv->rotate_gesture, "begin", (GCallback)gesture_rotate_begin_cb, image_view);
+ g_signal_connect (priv->rotate_gesture, "end", (GCallback)gesture_rotate_end_cb, image_view);
+ g_signal_connect (priv->rotate_gesture, "cancel", (GCallback)gesture_rotate_cancel_cb, image_view);
+
+
+ }
+ else if (!priv->rotate_gesture_enabled && priv->rotate_gesture != NULL)
+ {
+ g_clear_object (&priv->rotate_gesture);
+ }
+
+ if (priv->zoom_gesture && priv->rotate_gesture)
+ gtk_gesture_group (priv->zoom_gesture,
+ priv->rotate_gesture);
+}
static void
gtk_image_view_init (GtkImageView *image_view)
@@ -718,6 +939,8 @@ gtk_image_view_init (GtkImageView *image_view)
priv->scale = 1.0;
priv->angle = 0.0;
+ priv->visible_scale = 1.0;
+ priv->visible_angle = 0.0;
priv->snap_angle = FALSE;
priv->fit_allocation = FALSE;
priv->scale_set = FALSE;
@@ -726,22 +949,9 @@ gtk_image_view_init (GtkImageView *image_view)
priv->anchor_y = -1;
priv->rotate_gesture_enabled = TRUE;
priv->zoom_gesture_enabled = TRUE;
- priv->rotate_gesture = gtk_gesture_rotate_new (widget);
- gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->rotate_gesture),
- GTK_PHASE_CAPTURE);
- g_signal_connect (priv->rotate_gesture, "angle-changed", (GCallback)gesture_angle_changed_cb, image_view);
- g_signal_connect (priv->rotate_gesture, "begin", (GCallback)gesture_begin_cb, image_view);
- g_signal_connect (priv->rotate_gesture, "end", (GCallback)gesture_rotate_end_cb, image_view);
- g_signal_connect (priv->rotate_gesture, "cancel", (GCallback)gesture_rotate_cancel_cb, image_view);
+ priv->transitions_enabled = TRUE;
- priv->zoom_gesture = gtk_gesture_zoom_new (widget);
- g_signal_connect (priv->zoom_gesture, "scale-changed", (GCallback)gesture_scale_changed_cb, image_view);
- g_signal_connect (priv->zoom_gesture, "begin", (GCallback)gesture_begin_cb, image_view);
- g_signal_connect (priv->zoom_gesture, "end", (GCallback)gesture_zoom_end_cb, image_view);
- g_signal_connect (priv->zoom_gesture, "cancel", (GCallback)gesture_zoom_cancel_cb, image_view);
-
- gtk_gesture_group (priv->zoom_gesture,
- priv->rotate_gesture);
+ gtk_image_view_ensure_gestures (image_view);
}
@@ -800,74 +1010,6 @@ gtk_image_view_stop_animation (GtkImageView *image_view)
}
}
-
-static gboolean
-frameclock_cb (GtkWidget *widget,
- GdkFrameClock *frame_clock,
- gpointer user_data)
-{
- GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (GTK_IMAGE_VIEW (widget));
- gint64 now = gdk_frame_clock_get_frame_time (frame_clock);
-
- double t = (now - priv->angle_transition_start) / TRANSITION_DURATION;
-
- double new_angle = (priv->transition_end_angle - priv->transition_start_angle) * t;
-
- priv->angle = priv->transition_start_angle + new_angle;
-
- if (priv->fit_allocation)
- gtk_widget_queue_draw (widget);
- else
- gtk_widget_queue_resize (widget);
-
- if (t >= 1.0)
- {
- priv->angle = priv->transition_end_angle;
- return G_SOURCE_REMOVE;
- }
-
- return G_SOURCE_CONTINUE;
-}
-
-
-static void
-gtk_image_view_animate_to_angle (GtkImageView *image_view,
- double start_angle)
-{
- GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
- /* target angle is priv->angle! */
-
- priv->transition_start_angle = start_angle;
- priv->transition_end_angle = priv->angle;
- priv->angle_transition_start = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (GTK_WIDGET
(image_view)));
- gtk_widget_add_tick_callback (GTK_WIDGET (image_view), frameclock_cb, NULL, NULL);
-}
-
-static void
-gtk_image_view_do_snapping (GtkImageView *image_view,
- double angle)
-{
- GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
- int new_angle;
-
- g_assert (priv->snap_angle);
-
- /* Snap to angles of 0, 90, 180 and 270 degrees */
-
- new_angle = (int) ((angle) / 90.0) * 90;
-
- if (new_angle != priv->angle)
- {
- double old_angle = priv->angle;
- priv->angle = new_angle;
- /* XXX Make this conditional */
- gtk_image_view_animate_to_angle (image_view,
- old_angle);
- }
-
- priv->angle = new_angle;
-}
-
static gboolean
gtk_image_view_draw (GtkWidget *widget, cairo_t *ct)
{
@@ -951,7 +1093,7 @@ gtk_image_view_draw (GtkWidget *widget, cairo_t *ct)
cairo_translate (ct,
draw_x + (image_width / 2.0),
draw_y + (image_height / 2.0));
- cairo_rotate (ct, DEG_TO_RAD (priv->angle));
+ cairo_rotate (ct, DEG_TO_RAD (gtk_image_view_get_real_angle (image_view)));
cairo_translate (ct,
- draw_x - (image_width / 2.0),
- draw_y - (image_height / 2.0));
@@ -966,15 +1108,6 @@ gtk_image_view_draw (GtkWidget *widget, cairo_t *ct)
cairo_fill (ct);
cairo_restore (ct);
-
- /* XXX @debug */
- if (priv->anchor_x != -1 && priv->anchor_y != -1)
- {
- cairo_set_source_rgba (ct, 0, 1, 0, 1);
- cairo_rectangle (ct, priv->anchor_x - 1, priv->anchor_y - 1, 2, 2);
- cairo_fill (ct);
- }
-
return GDK_EVENT_PROPAGATE;
}
@@ -1085,8 +1218,7 @@ gtk_image_view_set_vscroll_policy (GtkImageView *image_view,
* @scale: The new scale value
*
* Sets the value of the #scale property. This will cause the
- * #scale-set property to be set to #TRUE as well. If the given
- * value of @scale is below zero, 0 will be set instead.
+ * #scale-set property to be set to #TRUE as well
*
* If #fit-allocation is #TRUE, it will be set to #FALSE, and @image_view
* will be resized to the image's current size, taking the new scale into
@@ -1098,25 +1230,51 @@ gtk_image_view_set_scale (GtkImageView *image_view,
{
GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
State state;
- double pointer_x;
- double pointer_y;
g_return_if_fail (GTK_IS_IMAGE_VIEW (image_view));
- g_return_if_fail (scale >= 0.0);
-
- pointer_x = gtk_widget_get_allocated_width (GTK_WIDGET (image_view)) / 2;
- pointer_y = gtk_widget_get_allocated_height (GTK_WIDGET (image_view)) / 2;
+ g_return_if_fail (scale > 0.0);
gtk_image_view_get_current_state (image_view, &state);
- gtk_image_view_set_scale_internal (image_view, scale);
+
+ if (gtk_image_view_transitions_enabled (image_view))
+ gtk_image_view_animate_to_scale (image_view, scale);
+
+ priv->scale = scale;
+ g_object_notify_by_pspec (G_OBJECT (image_view),
+ widget_props[PROP_SCALE]);
+
+ if (priv->scale_set)
+ {
+ priv->scale_set = FALSE;
+ g_object_notify_by_pspec (G_OBJECT (image_view),
+ widget_props[PROP_SCALE_SET]);
+ }
+
+ if (priv->fit_allocation)
+ {
+ priv->fit_allocation = FALSE;
+ g_object_notify_by_pspec (G_OBJECT (image_view),
+ widget_props[PROP_FIT_ALLOCATION]);
+ }
+
+ priv->size_valid = FALSE;
+ gtk_image_view_update_adjustments (image_view);
+
+ if (!priv->image_surface)
+ return;
+
if (priv->hadjustment != NULL && priv->vadjustment != NULL)
{
+ int pointer_x = gtk_widget_get_allocated_width (GTK_WIDGET (image_view)) / 2;
+ int pointer_y = gtk_widget_get_allocated_height (GTK_WIDGET (image_view)) / 2;
gtk_image_view_fix_anchor (image_view,
- pointer_x,
- pointer_y,
- &state);
+ pointer_x,
+ pointer_y,
+ &state);
}
+
+ gtk_widget_queue_resize (GTK_WIDGET (image_view));
}
double
@@ -1146,41 +1304,33 @@ gtk_image_view_set_angle (GtkImageView *image_view,
g_return_if_fail (GTK_IS_IMAGE_VIEW (image_view));
-
-
- if (angle > 360.0)
- angle -= (int)(angle / 360.0) * 360;
- else if (angle < 0.0)
- angle = 360.0 + (int)(angle / 360.0);
-
- g_assert (angle >= 0.0);
- g_assert (angle <= 360.0);
-
-
-
-
-
gtk_image_view_get_current_state (image_view, &state);
- priv->angle = angle;
- priv->size_valid = FALSE;
+ if (gtk_image_view_transitions_enabled (image_view) &&
+ ABS(gtk_image_view_clamp_angle (angle) - priv->angle) > ANGLE_TRANSITION_MIN_DELTA)
+ gtk_image_view_animate_to_angle (image_view, angle);
+ priv->angle = gtk_image_view_clamp_angle (angle);
+ priv->size_valid = FALSE;
gtk_image_view_update_adjustments (image_view);
-
g_object_notify_by_pspec (G_OBJECT (image_view),
widget_props[PROP_ANGLE]);
- // XXX Pass a width/2, height/2 anchor here.
- //
- // TODO: Would we have to document this behavior? Or make it configurable?
+ if (!priv->image_surface)
+ return;
- gtk_image_view_fix_anchor (image_view,
- priv->anchor_x,
- priv->anchor_y,
- &state);
+ if (priv->hadjustment && priv->vadjustment && !priv->fit_allocation)
+ {
+ int pointer_x = gtk_widget_get_allocated_width (GTK_WIDGET (image_view)) / 2;
+ int pointer_y = gtk_widget_get_allocated_height (GTK_WIDGET (image_view)) / 2;
+ gtk_image_view_fix_anchor (image_view,
+ pointer_x,
+ pointer_y,
+ &state);
+ }
if (priv->fit_allocation)
gtk_widget_queue_draw (GTK_WIDGET (image_view));
@@ -1206,14 +1356,15 @@ gtk_image_view_get_angle (GtkImageView *image_view)
*
* Setting #snap-angle to #TRUE will cause @image_view's angle to
* be snapped to 90° steps. Setting the #angle property will cause it to
- * be set to the lower 90° step, e.g. setting #angle to 359 will cause
- * the new value to be 270.
+ * be set to the closest 90° step, so e.g. using an angle of 40 will result
+ * in an angle of 0, using using 240 will result in 270, etc.
*/
void
gtk_image_view_set_snap_angle (GtkImageView *image_view,
gboolean snap_angle)
{
GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+
g_return_if_fail (GTK_IS_IMAGE_VIEW (image_view));
snap_angle = !!snap_angle;
@@ -1226,7 +1377,11 @@ gtk_image_view_set_snap_angle (GtkImageView *image_view,
widget_props[PROP_SNAP_ANGLE]);
if (priv->snap_angle)
- gtk_image_view_do_snapping (image_view, priv->angle);
+ {
+ gtk_image_view_do_snapping (image_view);
+ g_object_notify_by_pspec (G_OBJECT (image_view),
+ widget_props[PROP_ANGLE]);
+ }
}
gboolean
@@ -1276,7 +1431,7 @@ gtk_image_view_set_fit_allocation (GtkImageView *image_view,
g_object_notify_by_pspec (G_OBJECT (image_view),
widget_props[PROP_SCALE_SET]);
- if (!priv->fit_allocation && !priv->scale_set)
+ if (!priv->fit_allocation)
{
priv->scale = 1.0;
g_object_notify_by_pspec (G_OBJECT (image_view),
@@ -1286,7 +1441,6 @@ gtk_image_view_set_fit_allocation (GtkImageView *image_view,
gtk_image_view_update_adjustments (image_view);
gtk_widget_queue_resize (GTK_WIDGET (image_view));
- gtk_image_view_update_adjustments (image_view);
}
gboolean
@@ -1309,9 +1463,13 @@ gtk_image_view_set_rotate_gesture_enabled (GtkImageView *image_view,
rotate_gesture_enabled = !!rotate_gesture_enabled;
- priv->rotate_gesture_enabled = rotate_gesture_enabled;
- g_object_notify_by_pspec (G_OBJECT (image_view),
- widget_props[PROP_ROTATE_GESTURE_ENABLED]);
+ if (priv->rotate_gesture_enabled != rotate_gesture_enabled)
+ {
+ priv->rotate_gesture_enabled = rotate_gesture_enabled;
+ gtk_image_view_ensure_gestures (image_view);
+ g_object_notify_by_pspec (G_OBJECT (image_view),
+ widget_props[PROP_ROTATE_GESTURE_ENABLED]);
+ }
}
gboolean
@@ -1334,9 +1492,13 @@ gtk_image_view_set_zoom_gesture_enabled (GtkImageView *image_view,
zoom_gesture_enabled = !!zoom_gesture_enabled;
- priv->zoom_gesture_enabled = zoom_gesture_enabled;
- g_object_notify_by_pspec (G_OBJECT (image_view),
- widget_props[PROP_ZOOM_GESTURE_ENABLED]);
+ if (zoom_gesture_enabled != priv->zoom_gesture_enabled)
+ {
+ priv->zoom_gesture_enabled = zoom_gesture_enabled;
+ gtk_image_view_ensure_gestures (image_view);
+ g_object_notify_by_pspec (G_OBJECT (image_view),
+ widget_props[PROP_ZOOM_GESTURE_ENABLED]);
+ }
}
gboolean
@@ -1347,6 +1509,34 @@ gtk_image_view_get_zoom_gesture_enabled (GtkImageView *image_view)
return priv->zoom_gesture_enabled;
}
+
+
+
+void
+gtk_image_view_set_transitions_enabled (GtkImageView *image_view,
+ gboolean transitions_enabled)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+ g_return_if_fail (GTK_IS_IMAGE_VIEW (image_view));
+
+ transitions_enabled = !!transitions_enabled;
+
+ if (transitions_enabled != priv->transitions_enabled)
+ {
+ priv->transitions_enabled = transitions_enabled;
+ g_object_notify_by_pspec (G_OBJECT (image_view),
+ widget_props[PROP_TRANSITIONS_ENABLED]);
+ }
+}
+
+gboolean
+gtk_image_view_get_transitions_enabled (GtkImageView *image_view)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
+ g_return_val_if_fail (GTK_IS_IMAGE_VIEW (image_view), FALSE);
+
+ return priv->transitions_enabled;
+}
/* }}} */
@@ -1373,7 +1563,8 @@ gtk_image_view_realize (GtkWidget *widget)
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_SMOOTH_SCROLL_MASK |
- GDK_SCROLL_MASK;
+ GDK_SCROLL_MASK |
+ GDK_TOUCH_MASK;
attributes.wclass = GDK_INPUT_ONLY;
window = gtk_widget_get_parent_window (widget);
@@ -1390,6 +1581,21 @@ gtk_image_view_realize (GtkWidget *widget)
}
static void
+gtk_image_view_unrealize (GtkWidget *widget)
+{
+ GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (GTK_IMAGE_VIEW (widget));
+
+ if (priv->event_window)
+ {
+ gtk_widget_unregister_window (widget, priv->event_window);
+ gdk_window_destroy (priv->event_window);
+ priv->event_window = NULL;
+ }
+
+ GTK_WIDGET_CLASS (gtk_image_view_parent_class)->unrealize (widget);
+}
+
+static void
gtk_image_view_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
@@ -1482,11 +1688,11 @@ gtk_image_view_get_preferred_width (GtkWidget *widget,
GtkImageView *image_view = GTK_IMAGE_VIEW (widget);
GtkImageViewPrivate *priv = gtk_image_view_get_instance_private (image_view);
double width, height;
+
gtk_image_view_compute_bounding_box (image_view,
&width,
&height,
NULL);
-
if (priv->fit_allocation)
{
*minimal = 0;
@@ -1497,7 +1703,6 @@ gtk_image_view_get_preferred_width (GtkWidget *widget,
*minimal = width;
*natural = width;
}
-
}
@@ -1510,16 +1715,24 @@ gtk_image_view_scroll_event (GtkWidget *widget,
double new_scale = priv->scale - (0.1 * event->delta_y);
State state;
+ if (!priv->image_surface)
+ return GDK_EVENT_PROPAGATE;
+
+ if (event->state & GDK_SHIFT_MASK ||
+ event->state & GDK_CONTROL_MASK)
+ return GDK_EVENT_PROPAGATE;
+
+
gtk_image_view_get_current_state (image_view, &state);
gtk_image_view_set_scale_internal (image_view, new_scale);
- if (priv->hadjustment || priv->vadjustment)
+ if (priv->hadjustment && priv->vadjustment)
{
gtk_image_view_fix_anchor (image_view,
- event->x,
- event->y,
- &state);
+ event->x,
+ event->y,
+ &state);
}
return GDK_EVENT_STOP;
@@ -1543,8 +1756,6 @@ gtk_image_view_set_property (GObject *object,
case PROP_SCALE:
gtk_image_view_set_scale (image_view, g_value_get_double (value));
break;
- /*case PROP_SCALE_SET:*/
- /*break;*/
case PROP_ANGLE:
gtk_image_view_set_angle (image_view, g_value_get_double (value));
break;
@@ -1554,6 +1765,12 @@ gtk_image_view_set_property (GObject *object,
case PROP_FIT_ALLOCATION:
gtk_image_view_set_fit_allocation (image_view, g_value_get_boolean (value));
break;
+ case PROP_ROTATE_GESTURE_ENABLED:
+ gtk_image_view_set_rotate_gesture_enabled (image_view, g_value_get_boolean (value));
+ break;
+ case PROP_ZOOM_GESTURE_ENABLED:
+ gtk_image_view_set_zoom_gesture_enabled (image_view, g_value_get_boolean (value));
+ break;
case PROP_HADJUSTMENT:
gtk_image_view_set_hadjustment (image_view, g_value_get_object (value));
break;
@@ -1585,9 +1802,9 @@ gtk_image_view_get_property (GObject *object,
case PROP_SCALE:
g_value_set_double (value, priv->scale);
break;
- /*case PROP_SCALE_SET:*/
- /*g_value_set_boolean (value, priv->scale_set);*/
- /*break;*/
+ case PROP_SCALE_SET:
+ g_value_set_boolean (value, priv->scale_set);
+ break;
case PROP_ANGLE:
g_value_set_double (value, priv->angle);
break;
@@ -1597,6 +1814,12 @@ gtk_image_view_get_property (GObject *object,
case PROP_FIT_ALLOCATION:
g_value_set_boolean (value, priv->fit_allocation);
break;
+ case PROP_ROTATE_GESTURE_ENABLED:
+ g_value_set_boolean (value, priv->rotate_gesture_enabled);
+ break;
+ case PROP_ZOOM_GESTURE_ENABLED:
+ g_value_set_boolean (value, priv->zoom_gesture_enabled);
+ break;
case PROP_HADJUSTMENT:
g_value_set_object (value, priv->hadjustment);
break;
@@ -1650,6 +1873,7 @@ gtk_image_view_class_init (GtkImageViewClass *view_class)
widget_class->draw = gtk_image_view_draw;
widget_class->realize = gtk_image_view_realize;
+ widget_class->unrealize = gtk_image_view_unrealize;
widget_class->size_allocate = gtk_image_view_size_allocate;
widget_class->map = gtk_image_view_map;
widget_class->unmap = gtk_image_view_unmap;
@@ -1683,7 +1907,7 @@ gtk_image_view_class_init (GtkImageViewClass *view_class)
P_(""),
P_("fooar"),
FALSE,
- GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ GTK_PARAM_READABLE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkImageView:angle:
* The angle the surface gets rotated about.
@@ -1750,6 +1974,20 @@ gtk_image_view_class_init (GtkImageViewClass *view_class)
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ /**
+ * GtkImageView:transitions-enabled
+ *
+ * Whether or not certain property changes will be interpolated.
+ *
+ * Since: 3.20
+ */
+ widget_props[PROP_TRANSITIONS_ENABLED] = g_param_spec_boolean ("transitions-enabled",
+ P_("Foo"),
+ P_("fooar"),
+ TRUE,
+
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+
g_object_class_install_properties (object_class, LAST_WIDGET_PROPERTY, widget_props);
@@ -1777,7 +2015,11 @@ gtk_image_view_replace_surface (GtkImageView *image_view,
if (priv->image_surface)
cairo_surface_destroy (priv->image_surface);
- priv->scale_factor = scale_factor;
+ if (scale_factor == 0)
+ priv->scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (image_view));
+ else
+ priv->scale_factor = scale_factor;
+
priv->image_surface = surface;
priv->size_valid = FALSE;
@@ -1853,6 +2095,7 @@ gtk_image_view_replace_animation (GtkImageView *image_view,
gtk_image_view_update_surface (image_view,
gdk_pixbuf_animation_get_static_image (animation),
scale_factor);
+ g_object_unref (animation);
}
}
@@ -1868,15 +2111,15 @@ gtk_image_view_load_image_from_stream (GtkImageView *image_view,
{
GdkPixbufAnimation *result;
-
g_assert (error == NULL);
result = gdk_pixbuf_animation_new_from_stream (G_INPUT_STREAM (input_stream),
cancellable,
&error);
- g_object_unref (input_stream);
if (!error)
- gtk_image_view_replace_animation (image_view, result,scale_factor);
+ gtk_image_view_replace_animation (image_view, result, scale_factor);
+
+ g_object_unref (input_stream);
}
static void
@@ -1891,16 +2134,17 @@ gtk_image_view_load_image_contents (GTask *task,
GError *error = NULL;
GFileInputStream *in_stream;
- g_free (task_data);
in_stream = g_file_read (file, cancellable, &error);
if (error)
{
+ /* in_stream is NULL */
g_task_return_error (task, error);
return;
}
+ /* Closes and unrefs the input stream */
gtk_image_view_load_image_from_stream (image_view,
G_INPUT_STREAM (in_stream),
data->scale_factor,
@@ -1912,9 +2156,9 @@ gtk_image_view_load_image_contents (GTask *task,
}
static void
-gtk_image_view_load_from_input_stream (GTask *task,
- gpointer source_object,
- gpointer task_data,
+gtk_image_view_load_from_input_stream (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
GCancellable *cancellable)
{
GtkImageView *image_view = source_object;
@@ -1922,6 +2166,7 @@ gtk_image_view_load_from_input_stream (GTask *task,
GInputStream *in_stream = G_INPUT_STREAM (data->source);
GError *error = NULL;
+ /* Closes and unrefs the input stream */
gtk_image_view_load_image_from_stream (image_view,
in_stream,
data->scale_factor,
@@ -1946,7 +2191,7 @@ gtk_image_view_load_from_file_async (GtkImageView *image_view,
LoadTaskData *task_data;
g_return_if_fail (GTK_IS_IMAGE_VIEW (image_view));
g_return_if_fail (G_IS_FILE (file));
- g_return_if_fail (scale_factor > 0);
+ g_return_if_fail (scale_factor >= 0);
task_data = g_slice_new (LoadTaskData);
task_data->scale_factor = scale_factor;
@@ -1958,12 +2203,14 @@ gtk_image_view_load_from_file_async (GtkImageView *image_view,
g_object_unref (task);
}
-void
+gboolean
gtk_image_view_load_from_file_finish (GtkImageView *image_view,
GAsyncResult *result,
GError **error)
{
- g_return_if_fail (g_task_is_valid (result, image_view));
+ g_return_val_if_fail (g_task_is_valid (result, image_view), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
}
@@ -1981,7 +2228,7 @@ gtk_image_view_load_from_stream_async (GtkImageView *image_view,
LoadTaskData *task_data;
g_return_if_fail (GTK_IS_IMAGE_VIEW (image_view));
g_return_if_fail (G_IS_INPUT_STREAM (input_stream));
- g_return_if_fail (scale_factor > 0);
+ g_return_if_fail (scale_factor >= 0);
task_data = g_slice_new (LoadTaskData);
task_data->scale_factor = scale_factor;
@@ -1993,18 +2240,20 @@ gtk_image_view_load_from_stream_async (GtkImageView *image_view,
g_object_unref (task);
}
-void
+gboolean
gtk_image_view_load_from_stream_finish (GtkImageView *image_view,
GAsyncResult *result,
GError **error)
{
- g_return_if_fail (g_task_is_valid (result, image_view));
+ g_return_val_if_fail (g_task_is_valid (result, image_view), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
}
/*
* gtk_image_view_set_pixbuf:
* @image_view: A #GtkImageView instance
- * @pixbuf: A #GdkPixbuf instance
+ * @pixbuf: (transfer none): A #GdkPixbuf instance
* @scale_factor: The scale factor of the pixbuf. This will
* be interpreted as "the given pixbuf is supposed to be used
* with the given scale factor", i.e. if the pixbuf's scale
@@ -2020,7 +2269,7 @@ gtk_image_view_set_pixbuf (GtkImageView *image_view,
g_return_if_fail (GTK_IS_IMAGE_VIEW (image_view));
g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
- g_return_if_fail (scale_factor > 0);
+ g_return_if_fail (scale_factor >= 0);
if (priv->is_animation)
@@ -2033,25 +2282,12 @@ gtk_image_view_set_pixbuf (GtkImageView *image_view,
gtk_image_view_update_surface (image_view, pixbuf, scale_factor);
gtk_image_view_update_adjustments (image_view);
-
-
- /* XXX @debug */
- double value = gtk_adjustment_get_upper (priv->hadjustment) / 2.0 -
- gtk_adjustment_get_page_size (priv->hadjustment) / 2.0;
-
- gtk_adjustment_set_value (priv->hadjustment, value);
-
- value = gtk_adjustment_get_upper (priv->vadjustment) / 2.0 -
- gtk_adjustment_get_page_size (priv->vadjustment) / 2.0;
-
- gtk_adjustment_set_value (priv->vadjustment, value);
-
}
/**
* gtk_image_view_set_surface:
* @image_view: A #GtkImageView instance
- * @surface: (nullable): A #cairo_surface_t of type #CAIRO_SURFACE_TYPE_IMAGE, or
+ * @surface: (nullable) (transfer full): A #cairo_surface_t of type #CAIRO_SURFACE_TYPE_IMAGE, or
* %NULL to unset any internal image data. In case this is %NULL, the scale will
* be reset to 1.0.
*/
@@ -2078,6 +2314,9 @@ gtk_image_view_set_surface (GtkImageView *image_view,
priv->scale = 1.0;
g_object_notify_by_pspec (G_OBJECT (image_view),
widget_props[PROP_SCALE]);
+ priv->scale_set = FALSE;
+ g_object_notify_by_pspec (G_OBJECT (image_view),
+ widget_props[PROP_SCALE_SET]);
}
@@ -2104,7 +2343,7 @@ gtk_image_view_set_surface (GtkImageView *image_view,
/**
* gtk_image_view_set_animation:
* @image_view: A #GtkImageView instance
- * @animation: The #GdkPixbufAnimation to use
+ * @animation: (transfer full): The #GdkPixbufAnimation to use
* @scale_factor: The scale factor of the animation. This will
* be interpreted as "the given animation is supposed to be used
* with the given scale factor", i.e. if the animation's scale
@@ -2118,7 +2357,7 @@ gtk_image_view_set_animation (GtkImageView *image_view,
{
g_return_if_fail (GTK_IS_IMAGE_VIEW (image_view));
g_return_if_fail (GDK_IS_PIXBUF_ANIMATION (animation));
- g_return_if_fail (scale_factor > 0);
+ g_return_if_fail (scale_factor >= 0);
gtk_image_view_replace_animation (image_view, animation, scale_factor);
}
diff --git a/gtk/gtkimageview.h b/gtk/gtkimageview.h
index 8052589..39b3c33 100644
--- a/gtk/gtkimageview.h
+++ b/gtk/gtkimageview.h
@@ -32,26 +32,25 @@ struct _GtkImageView
struct _GtkImageViewClass
{
GtkWidgetClass parent_class;
- void (* prepare_image) (cairo_surface_t *image);
};
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
GType gtk_image_view_get_type (void) G_GNUC_CONST;
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
GtkWidget * gtk_image_view_new (void);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_set_pixbuf (GtkImageView *image_view,
const GdkPixbuf *pixbuf,
int scale_factor);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_set_surface (GtkImageView *image_view,
cairo_surface_t *surface);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_set_animation (GtkImageView *image_view,
GdkPixbufAnimation *animation,
int scale_factor);
@@ -59,18 +58,18 @@ void gtk_image_view_set_animation (GtkImageView *image_view,
/* Loading {{{ */
-GDK_AVAILABLE_IN_3_18
-void gtk_image_view_load_from_file_async (GtkImageView *image_view,
- GFile *file,
- int scale_factor,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-GDK_AVAILABLE_IN_3_18
-void gtk_image_view_load_from_file_finish (GtkImageView *image_view,
- GAsyncResult *result,
- GError **error);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
+void gtk_image_view_load_from_file_async (GtkImageView *image_view,
+ GFile *file,
+ int scale_factor,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GDK_AVAILABLE_IN_3_20
+gboolean gtk_image_view_load_from_file_finish (GtkImageView *image_view,
+ GAsyncResult *result,
+ GError **error);
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_load_from_stream_async (GtkImageView *image_view,
GInputStream *input_stream,
int scale_factor,
@@ -78,64 +77,64 @@ void gtk_image_view_load_from_stream_async (GtkImageView *image_view,
GAsyncReadyCallback callback,
gpointer user_data);
-GDK_AVAILABLE_IN_3_18
-void gtk_image_view_load_from_stream_finish (GtkImageView *image_view,
- GAsyncResult *result,
- GError **error);
+GDK_AVAILABLE_IN_3_20
+gboolean gtk_image_view_load_from_stream_finish (GtkImageView *image_view,
+ GAsyncResult *result,
+ GError **error);
/* }}} */
/* Setters/Getters {{{ */
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_set_scale (GtkImageView *image_view,
double scale);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
double gtk_image_view_get_scale (GtkImageView *image_view);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_set_angle (GtkImageView *image_view,
double angle);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
double gtk_image_view_get_angle (GtkImageView *image_view);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_set_snap_angle (GtkImageView *image_view,
gboolean snap_angle);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
gboolean gtk_image_view_get_snap_angle (GtkImageView *image_view);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_set_fit_allocation (GtkImageView *image_view,
gboolean fit_allocation);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
gboolean gtk_image_view_get_fit_allocation (GtkImageView *image_view);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_set_rotate_gesture_enabled (GtkImageView *image_view,
gboolean rotate_gesture_enabled);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
gboolean gtk_image_view_get_rotate_gesture_enabled (GtkImageView *image_view);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
void gtk_image_view_set_zoom_gesture_enabled (GtkImageView *image_view,
gboolean zoom_gesture_enabled);
-GDK_AVAILABLE_IN_3_18
+GDK_AVAILABLE_IN_3_20
gboolean gtk_image_view_get_zoom_gesture_enabled (GtkImageView *image_view);
/* }}} */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]