[gnome-photos/wip/rishi/zoom: 7/11] application, help-overlay, preview-view: Hook up zooming to GActions



commit e471270dc12a0ae8aee9141c22c0fbb18437c784
Author: Debarshi Ray <debarshir gnome org>
Date:   Wed Mar 29 11:03:39 2017 +0200

    application, help-overlay, preview-view: Hook up zooming to GActions
    
    https://bugzilla.gnome.org/show_bug.cgi?id=742662

 src/photos-application.c   |   25 ++++
 src/photos-help-overlay.ui |   21 +++
 src/photos-preview-view.c  |  310 +++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 340 insertions(+), 16 deletions(-)
---
diff --git a/src/photos-application.c b/src/photos-application.c
index 35505c4..f58416c 100644
--- a/src/photos-application.c
+++ b/src/photos-application.c
@@ -111,6 +111,9 @@ struct _PhotosApplication
   GSimpleAction *set_ss_action;
   GSimpleAction *share_action;
   GSimpleAction *sharpen_action;
+  GSimpleAction *zoom_best_fit_action;
+  GSimpleAction *zoom_in_action;
+  GSimpleAction *zoom_out_action;
   GtkWidget *main_window;
   PhotosBaseManager *shr_pnt_mngr;
   PhotosCameraCache *camera_cache;
@@ -337,6 +340,9 @@ photos_application_actions_update (PhotosApplication *self)
   selection = photos_selection_controller_get_selection (self->sel_cntrlr);
   selection_mode = photos_utils_get_selection_mode ();
 
+  g_simple_action_set_enabled (self->zoom_best_fit_action, FALSE);
+  g_simple_action_set_enabled (self->zoom_out_action, FALSE);
+
   enable = (mode == PHOTOS_WINDOW_MODE_EDIT);
   g_simple_action_set_enabled (self->blacks_exposure_action, enable);
   g_simple_action_set_enabled (self->brightness_contrast_action, enable);
@@ -367,6 +373,7 @@ photos_application_actions_update (PhotosApplication *self)
   g_simple_action_set_enabled (self->gear_action, enable);
   g_simple_action_set_enabled (self->set_bg_action, enable);
   g_simple_action_set_enabled (self->set_ss_action, enable);
+  g_simple_action_set_enabled (self->zoom_in_action, enable);
 
   enable = ((load_state == PHOTOS_LOAD_STATE_FINISHED && mode == PHOTOS_WINDOW_MODE_PREVIEW)
             || (selection_mode && item != NULL));
@@ -1689,6 +1696,9 @@ photos_application_startup (GApplication *application)
   const gchar *save_accels[2] = {"<Primary>x", NULL};
   const gchar *search_accels[2] = {"<Primary>f", NULL};
   const gchar *select_all_accels[2] = {"<Primary>a", NULL};
+  const gchar *zoom_best_fit_accels[3] = {"<Primary>0", NULL};
+  const gchar *zoom_in_accels[3] = {"<Primary>plus", "<Primary>equal", NULL};
+  const gchar *zoom_out_accels[2] = {"<Primary>minus", NULL};
 
   G_APPLICATION_CLASS (photos_application_parent_class)->startup (application);
 
@@ -1901,6 +1911,15 @@ photos_application_startup (GApplication *application)
   self->sharpen_action = g_simple_action_new ("sharpen-current", G_VARIANT_TYPE_DOUBLE);
   g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (self->sharpen_action));
 
+  self->zoom_best_fit_action = g_simple_action_new ("zoom-best-fit", NULL);
+  g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (self->zoom_best_fit_action));
+
+  self->zoom_in_action = g_simple_action_new ("zoom-in", NULL);
+  g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (self->zoom_in_action));
+
+  self->zoom_out_action = g_simple_action_new ("zoom-out", NULL);
+  g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (self->zoom_out_action));
+
   g_signal_connect_swapped (self->state->mode_cntrlr,
                             "window-mode-changed",
                             G_CALLBACK (photos_application_window_mode_changed),
@@ -1921,6 +1940,9 @@ photos_application_startup (GApplication *application)
   gtk_application_set_accels_for_action (GTK_APPLICATION (self), "app.save-current", save_accels);
   gtk_application_set_accels_for_action (GTK_APPLICATION (self), "app.search", search_accels);
   gtk_application_set_accels_for_action (GTK_APPLICATION (self), "app.select-all", select_all_accels);
+  gtk_application_set_accels_for_action (GTK_APPLICATION (self), "app.zoom-best-fit", zoom_best_fit_accels);
+  gtk_application_set_accels_for_action (GTK_APPLICATION (self), "app.zoom-in", zoom_in_accels);
+  gtk_application_set_accels_for_action (GTK_APPLICATION (self), "app.zoom-out", zoom_out_accels);
 
   g_signal_connect_swapped (self->state->item_mngr,
                             "load-finished",
@@ -1995,6 +2017,9 @@ photos_application_dispose (GObject *object)
   g_clear_object (&self->set_ss_action);
   g_clear_object (&self->share_action);
   g_clear_object (&self->sharpen_action);
+  g_clear_object (&self->zoom_best_fit_action);
+  g_clear_object (&self->zoom_in_action);
+  g_clear_object (&self->zoom_out_action);
   g_clear_object (&self->shr_pnt_mngr);
   g_clear_object (&self->camera_cache);
   g_clear_object (&self->sel_cntrlr);
diff --git a/src/photos-help-overlay.ui b/src/photos-help-overlay.ui
index 5f00de8..ff09da7 100644
--- a/src/photos-help-overlay.ui
+++ b/src/photos-help-overlay.ui
@@ -146,6 +146,27 @@
             <child>
               <object class="GtkShortcutsShortcut">
                 <property name="visible">1</property>
+                <property name="title" translatable="yes" context="shortcut window">Zoom in</property>
+                <property name="accelerator">&lt;Primary&gt;plus</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkShortcutsShortcut">
+                <property name="visible">1</property>
+                <property name="title" translatable="yes" context="shortcut window">Zoom out</property>
+                <property name="accelerator">&lt;Primary&gt;minus</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkShortcutsShortcut">
+                <property name="visible">1</property>
+                <property name="title" translatable="yes" context="shortcut window">Best fit</property>
+                <property name="accelerator">&lt;Primary&gt;0</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkShortcutsShortcut">
+                <property name="visible">1</property>
                 <property name="title" translatable="yes" context="shortcut window">Delete</property>
                 <property name="accelerator">Delete</property>
               </object>
diff --git a/src/photos-preview-view.c b/src/photos-preview-view.c
index c901a4f..3951c69 100644
--- a/src/photos-preview-view.c
+++ b/src/photos-preview-view.c
@@ -25,6 +25,8 @@
 
 #include "config.h"
 
+#include <math.h>
+
 #include <glib.h>
 #include <gio/gio.h>
 #include <glib/gi18n.h>
@@ -39,11 +41,14 @@
 #include "photos-preview-view.h"
 #include "photos-search-context.h"
 #include "photos-tool.h"
+#include "photos-utils.h"
 
 
 struct _PhotosPreviewView
 {
   GtkBin parent_instance;
+  GAction *zoom_best_fit_action;
+  GAction *zoom_out_action;
   GCancellable *cancellable;
   GeglNode *node;
   GtkWidget *overlay;
@@ -54,6 +59,10 @@ struct _PhotosPreviewView
   PhotosModeController *mode_cntrlr;
   PhotosPreviewNavButtons *nav_buttons;
   PhotosTool *current_tool;
+  gboolean grabbed;
+  gdouble event_x_last;
+  gdouble event_y_last;
+  gdouble zoom_best_fit;
 };
 
 struct _PhotosPreviewViewClass
@@ -71,6 +80,9 @@ enum
 G_DEFINE_TYPE (PhotosPreviewView, photos_preview_view, GTK_TYPE_BIN);
 
 
+static const gdouble ZOOM_FACTOR = 1.20;
+
+
 static GtkWidget *photos_preview_view_create_view_with_container (PhotosPreviewView *self);
 
 
@@ -103,17 +115,53 @@ photos_preview_view_button_press_event (GtkWidget *widget, GdkEvent *event, gpoi
   current_view = photos_preview_view_get_view_from_view_container (current_view_container);
   g_return_val_if_fail (widget == current_view, GDK_EVENT_PROPAGATE);
 
-  if (self->current_tool == NULL)
+  if (self->node == NULL)
     goto out;
 
-  switch (event->button.button)
+  if (self->current_tool == NULL)
     {
-    case 1:
-      ret_val = photos_tool_left_click_event (self->current_tool, &(event->button));
-      break;
+      switch (event->button.button)
+        {
+        case 1:
+          {
+            if (photos_utils_scrolled_window_can_scroll (GTK_SCROLLED_WINDOW (current_view_container)))
+              {
+                GdkCursor *cursor = NULL;
+                GdkDisplay *display;
+                GdkWindow *window;
 
-    default:
-      break;
+                window = gtk_widget_get_window (widget);
+                display = gdk_window_get_display (window);
+                cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
+                gdk_window_set_cursor (window, cursor);
+
+                self->grabbed = TRUE;
+                self->event_x_last = event->button.x;
+                self->event_y_last = event->button.y;
+
+                ret_val = GDK_EVENT_STOP;
+
+                g_object_unref (cursor);
+              }
+
+            break;
+          }
+
+        default:
+          break;
+        }
+    }
+  else
+    {
+      switch (event->button.button)
+        {
+        case 1:
+          ret_val = photos_tool_left_click_event (self->current_tool, &(event->button));
+          break;
+
+        default:
+          break;
+        }
     }
 
  out:
@@ -133,17 +181,43 @@ photos_preview_view_button_release_event (GtkWidget *widget, GdkEvent *event, gp
   current_view = photos_preview_view_get_view_from_view_container (current_view_container);
   g_return_val_if_fail (widget == current_view, GDK_EVENT_PROPAGATE);
 
-  if (self->current_tool == NULL)
+  if (self->node == NULL)
     goto out;
 
-  switch (event->button.button)
+  if (self->current_tool == NULL)
     {
-    case 1:
-      ret_val = photos_tool_left_unclick_event (self->current_tool, &(event->button));
-      break;
+      if (!self->grabbed)
+        goto out;
 
-    default:
-      break;
+      switch (event->button.button)
+        {
+        case 1:
+          {
+            GdkWindow *window;
+
+            window = gtk_widget_get_window (widget);
+            gdk_window_set_cursor (window, NULL);
+            self->grabbed = FALSE;
+            self->event_x_last = -1.0;
+            self->event_y_last = -1.0;
+            break;
+          }
+
+        default:
+          break;
+        }
+    }
+  else
+    {
+      switch (event->button.button)
+        {
+        case 1:
+          ret_val = photos_tool_left_unclick_event (self->current_tool, &(event->button));
+          break;
+
+        default:
+          break;
+        }
     }
 
  out:
@@ -219,10 +293,32 @@ photos_preview_view_motion_notify_event (GtkWidget *widget, GdkEvent *event, gpo
   current_view = photos_preview_view_get_view_from_view_container (current_view_container);
   g_return_val_if_fail (widget == current_view, GDK_EVENT_PROPAGATE);
 
-  if (self->current_tool == NULL)
+  if (self->node == NULL)
     goto out;
 
-  ret_val = photos_tool_motion_event (self->current_tool, &(event->motion));
+  if (self->current_tool == NULL)
+    {
+      if (self->grabbed)
+        {
+          gdouble delta_x;
+          gdouble delta_y;
+
+          /* We are moving the content, not the view. Hence the
+           * deltas are inverted.
+           */
+          delta_x = self->event_x_last - event->motion.x;
+          delta_y = self->event_y_last - event->motion.y;
+
+          self->event_x_last = event->motion.x;
+          self->event_y_last = event->motion.y;
+
+          photos_utils_scrolled_window_scroll (GTK_SCROLLED_WINDOW (current_view_container), delta_x, 
delta_y);
+        }
+    }
+  else
+    {
+      ret_val = photos_tool_motion_event (self->current_tool, &(event->motion));
+    }
 
  out:
   return ret_val;
@@ -230,6 +326,45 @@ photos_preview_view_motion_notify_event (GtkWidget *widget, GdkEvent *event, gpo
 
 
 static void
+photos_preview_view_update_zoom_best_fit (PhotosPreviewView *self, PhotosImageView *view)
+{
+  GtkWidget *current_view;
+  GtkWidget *current_view_container;
+  gdouble zoom;
+
+  current_view_container = gtk_stack_get_visible_child (GTK_STACK (self->stack));
+  current_view = photos_preview_view_get_view_from_view_container (current_view_container);
+  g_return_if_fail (view == PHOTOS_IMAGE_VIEW (current_view));
+
+  zoom = photos_image_view_get_zoom (view);
+  if (!photos_image_view_get_best_fit (view) || zoom == 0.0)
+    return;
+
+  self->zoom_best_fit = zoom;
+}
+
+
+static void
+photos_preview_view_notify_best_fit (GObject *object, GParamSpec *pspec, gpointer user_data)
+{
+  PhotosPreviewView *self = PHOTOS_PREVIEW_VIEW (user_data);
+  PhotosImageView *view = PHOTOS_IMAGE_VIEW (object);
+
+  photos_preview_view_update_zoom_best_fit (self, view);
+}
+
+
+static void
+photos_preview_view_notify_zoom (GObject *object, GParamSpec *pspec, gpointer user_data)
+{
+  PhotosPreviewView *self = PHOTOS_PREVIEW_VIEW (user_data);
+  PhotosImageView *view = PHOTOS_IMAGE_VIEW (object);
+
+  photos_preview_view_update_zoom_best_fit (self, view);
+}
+
+
+static void
 photos_preview_view_navigate (PhotosPreviewView *self, gint position)
 {
   GeglNode *node;
@@ -269,6 +404,61 @@ photos_preview_view_navigate_previous (PhotosPreviewView *self)
 }
 
 
+static gboolean
+photos_preview_view_scroll_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+  PhotosPreviewView *self = PHOTOS_PREVIEW_VIEW (user_data);
+  GtkWidget *current_view;
+  GtkWidget *current_view_container;
+  gboolean ret_val = GDK_EVENT_PROPAGATE;
+  gdouble delta_abs;
+  gdouble zoom;
+  gdouble zoom_factor;
+
+  current_view_container = gtk_stack_get_visible_child (GTK_STACK (self->stack));
+  current_view = photos_preview_view_get_view_from_view_container (current_view_container);
+  g_return_val_if_fail (widget == current_view, GDK_EVENT_PROPAGATE);
+
+  if (self->node == NULL)
+    goto out;
+
+  if ((event->scroll.state & GDK_SHIFT_MASK) != 0 || (event->scroll.state & GDK_CONTROL_MASK) != 0)
+    goto out;
+
+  delta_abs = fabs (event->scroll.delta_y);
+  zoom_factor = pow (ZOOM_FACTOR, delta_abs);
+
+  zoom = photos_image_view_get_zoom (PHOTOS_IMAGE_VIEW (widget));
+
+  if (event->scroll.delta_y < 0.0)
+    {
+      zoom *= zoom_factor;
+      photos_image_view_set_zoom (PHOTOS_IMAGE_VIEW (widget), zoom);
+      g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_best_fit_action), TRUE);
+      g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_out_action), TRUE);
+    }
+  else
+    {
+      zoom /= zoom_factor;
+      if (zoom <= self->zoom_best_fit)
+        {
+          photos_image_view_set_best_fit (PHOTOS_IMAGE_VIEW (widget), TRUE);
+          g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_best_fit_action), FALSE);
+          g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_out_action), FALSE);
+        }
+      else
+        {
+          photos_image_view_set_zoom (PHOTOS_IMAGE_VIEW (widget), zoom);
+        }
+    }
+
+  ret_val = GDK_EVENT_STOP;
+
+ out:
+  return ret_val;
+}
+
+
 static GtkWidget *
 photos_preview_view_create_view_with_container (PhotosPreviewView *self)
 {
@@ -288,6 +478,9 @@ photos_preview_view_create_view_with_container (PhotosPreviewView *self)
   g_signal_connect_swapped (view, "draw-background", G_CALLBACK (photos_preview_view_draw_background), self);
   g_signal_connect_swapped (view, "draw-overlay", G_CALLBACK (photos_preview_view_draw_overlay), self);
   g_signal_connect (view, "motion-notify-event", G_CALLBACK (photos_preview_view_motion_notify_event), self);
+  g_signal_connect (view, "notify::best-fit", G_CALLBACK (photos_preview_view_notify_best_fit), self);
+  g_signal_connect (view, "notify::zoom", G_CALLBACK (photos_preview_view_notify_zoom), self);
+  g_signal_connect (view, "scroll-event", G_CALLBACK (photos_preview_view_scroll_event), self);
 
   /* It has to be visible to become the visible child of self->stack. */
   gtk_widget_show_all (sw);
@@ -663,6 +856,70 @@ photos_preview_view_window_mode_changed (PhotosPreviewView *self, PhotosWindowMo
 
 
 static void
+photos_preview_view_zoom_best_fit (PhotosPreviewView *self)
+{
+  GtkWidget *view;
+  GtkWidget *view_container;
+
+  view_container = gtk_stack_get_visible_child (GTK_STACK (self->stack));
+  view = photos_preview_view_get_view_from_view_container (view_container);
+  photos_image_view_set_best_fit (PHOTOS_IMAGE_VIEW (view), TRUE);
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_best_fit_action), FALSE);
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_out_action), FALSE);
+}
+
+
+static void
+photos_preview_view_zoom_in (PhotosPreviewView *self)
+{
+  GtkWidget *view;
+  GtkWidget *view_container;
+  gdouble zoom;
+
+  g_return_if_fail (self->zoom_best_fit > 0.0);
+
+  view_container = gtk_stack_get_visible_child (GTK_STACK (self->stack));
+  view = photos_preview_view_get_view_from_view_container (view_container);
+
+  zoom = photos_image_view_get_zoom (PHOTOS_IMAGE_VIEW (view));
+  zoom *= ZOOM_FACTOR;
+
+  photos_image_view_set_zoom (PHOTOS_IMAGE_VIEW (view), zoom);
+
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_best_fit_action), TRUE);
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_out_action), TRUE);
+}
+
+
+static void
+photos_preview_view_zoom_out (PhotosPreviewView *self)
+{
+  GtkWidget *view;
+  GtkWidget *view_container;
+  gdouble zoom;
+
+  g_return_if_fail (self->zoom_best_fit > 0.0);
+
+  view_container = gtk_stack_get_visible_child (GTK_STACK (self->stack));
+  view = photos_preview_view_get_view_from_view_container (view_container);
+
+  zoom = photos_image_view_get_zoom (PHOTOS_IMAGE_VIEW (view));
+  zoom /= ZOOM_FACTOR;
+
+  if (zoom <= self->zoom_best_fit)
+    {
+      photos_image_view_set_best_fit (PHOTOS_IMAGE_VIEW (view), TRUE);
+      g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_best_fit_action), FALSE);
+      g_simple_action_set_enabled (G_SIMPLE_ACTION (self->zoom_out_action), FALSE);
+    }
+  else
+    {
+      photos_image_view_set_zoom (PHOTOS_IMAGE_VIEW (view), zoom);
+    }
+}
+
+
+static void
 photos_preview_view_dispose (GObject *object)
 {
   PhotosPreviewView *self = PHOTOS_PREVIEW_VIEW (object);
@@ -816,6 +1073,26 @@ photos_preview_view_init (PhotosPreviewView *self)
 
   action = g_action_map_lookup_action (G_ACTION_MAP (app), "sharpen-current");
   g_signal_connect_object (action, "activate", G_CALLBACK (photos_preview_view_sharpen), self, 
G_CONNECT_SWAPPED);
+
+  self->zoom_best_fit_action = g_action_map_lookup_action (G_ACTION_MAP (app), "zoom-best-fit");
+  g_signal_connect_object (self->zoom_best_fit_action,
+                           "activate",
+                           G_CALLBACK (photos_preview_view_zoom_best_fit),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  action = g_action_map_lookup_action (G_ACTION_MAP (app), "zoom-in");
+  g_signal_connect_object (action, "activate", G_CALLBACK (photos_preview_view_zoom_in), self, 
G_CONNECT_SWAPPED);
+
+  self->zoom_out_action = g_action_map_lookup_action (G_ACTION_MAP (app), "zoom-out");
+  g_signal_connect_object (self->zoom_out_action,
+                           "activate",
+                           G_CALLBACK (photos_preview_view_zoom_out),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  self->event_x_last = -1.0;
+  self->event_y_last = -1.0;
 }
 
 
@@ -868,6 +1145,7 @@ photos_preview_view_set_node (PhotosPreviewView *self, GeglNode *node)
     return;
 
   view_container = gtk_stack_get_visible_child (GTK_STACK (self->stack));
+  self->zoom_best_fit = 0.0;
   g_clear_object (&self->node);
 
   if (node == NULL)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]