[gnome-photos] application, help-overlay, preview-view: Hook up zooming to GActions



commit c7ddf601fe714c06899ea779d0de2ba364717b34
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
    
    There is no upper limit for the zoom, but we use the best-fit zoom
    level as the lower limit. Best-fit is defined as the size of the image
    in device pixels or the ImageView's allocation in device pixels,
    whichever is smaller. See commit ccf511c2d6e2dd90 for a longer
    explanation.
    
    The initial zoom-in is much bigger compared to subsequent increments,
    but zooming out always uses the smaller increments.
    
    All the above policy is implemented in PreviewView, outside the
    ImageView widget. Currently, we don't update the best-fit zoom factor
    value when the window is resized. Users with particularly large
    displays will notice that increasing the window size lets them
    zoom-out beyond the best-fit value corresponding to the bigger window.
    Similarly shrinking the window might skip the last few zoom-out steps
    and jump directly to the best-fit level. However, the zoom increments
    seem large enough for this to not be an issue in practice. Even if
    someone does encounter this oddity, it is unlikely to be a real
    annoyance.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=742662

 src/photos-application.c   |   27 +++++++++
 src/photos-help-overlay.ui |   21 +++++++
 src/photos-preview-view.c  |  136 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 184 insertions(+), 0 deletions(-)
---
diff --git a/src/photos-application.c b/src/photos-application.c
index 35505c4..5b7c604 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));
@@ -817,6 +824,8 @@ photos_application_edit_current (PhotosApplication *self)
   item = PHOTOS_BASE_ITEM (photos_base_manager_get_active_object (self->state->item_mngr));
   g_return_if_fail (item != NULL);
 
+  g_action_activate (G_ACTION (self->zoom_best_fit_action), NULL);
+
   photos_base_item_pipeline_snapshot (item);
   photos_mode_controller_set_window_mode (self->state->mode_cntrlr, PHOTOS_WINDOW_MODE_EDIT);
 }
@@ -1689,6 +1698,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 +1913,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 +1942,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 +2019,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..11990d4 100644
--- a/src/photos-preview-view.c
+++ b/src/photos-preview-view.c
@@ -39,11 +39,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 +57,7 @@ struct _PhotosPreviewView
   PhotosModeController *mode_cntrlr;
   PhotosPreviewNavButtons *nav_buttons;
   PhotosTool *current_tool;
+  gdouble zoom_best_fit;
 };
 
 struct _PhotosPreviewViewClass
@@ -71,6 +75,10 @@ enum
 G_DEFINE_TYPE (PhotosPreviewView, photos_preview_view, GTK_TYPE_BIN);
 
 
+static const gdouble ZOOM_FACTOR_1 = 2.8561;
+static const gdouble ZOOM_FACTOR_2 = 1.69;
+
+
 static GtkWidget *photos_preview_view_create_view_with_container (PhotosPreviewView *self);
 
 
@@ -230,6 +238,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;
@@ -288,6 +335,8 @@ 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);
 
   /* It has to be visible to become the visible child of self->stack. */
   gtk_widget_show_all (sw);
@@ -663,6 +712,75 @@ 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;
+  gboolean best_fit;
+  gdouble zoom;
+  gdouble zoom_factor;
+
+  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);
+
+  best_fit = photos_image_view_get_best_fit (PHOTOS_IMAGE_VIEW (view));
+  zoom_factor = best_fit ? ZOOM_FACTOR_1 : ZOOM_FACTOR_2;
+
+  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_2;
+
+  if (zoom < self->zoom_best_fit || photos_utils_equal_double (self->zoom_best_fit, zoom))
+    {
+      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 +934,23 @@ 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);
 }
 
 
@@ -868,6 +1003,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]