Instead of guesstimating the values of the scrollbars adjustments after
a change in zoom level, connect callbacks to the "changed" GtkAdjustment
event (which is emitted when the bounds or page_size of the adjustment
change, e.g. when the zoom level changes), and compute the new values
from there.

The previous adjustment values are tracked in zathura->ui.hadjustment
and zathura->ui.vadjustment (and updated by signal handlers as well), so
that the view's position can be maintained while zooming.

cb_view_hadjustment_changed() centers the page horizontally if a
"best-fit" or "width" zoom is being performed, or if "zoom-center" is
true; otherwise, it keeps the view horizontally centered around the same
area of the page.

cb_view_vadjustment_changed() always keeps the view vertically centered
around the same area of the page.
---
Here's a cleaned-up version of my previous RFC patch, with comments and (IMO)
better organized code.

Let me know what you (by which I mean any of you) think.
---
 adjustment.c |   55 +++++++++++++++++++++++++++++++++++++++++++
 adjustment.h |   49 +++++++++++++++++++++++++++++++++++++++
 callbacks.c  |   73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 callbacks.h  |   47 +++++++++++++++++++++++++++++++++++++
 marks.c      |    2 --
 shortcuts.c  |   20 +++++++---------
 utils.c      |   36 -----------------------------
 utils.h      |   16 -------------
 zathura.c    |   43 ++++++++++++++++++++++++++--------
 zathura.h    |    3 +++
 10 files changed, 269 insertions(+), 75 deletions(-)
 create mode 100644 adjustment.c
 create mode 100644 adjustment.h

diff --git a/adjustment.c b/adjustment.c
new file mode 100644
index 0000000..f61ea8f
--- /dev/null
+++ b/adjustment.c
@@ -0,0 +1,55 @@
+/* See LICENSE file for license and copyright information */
+
+#include "adjustment.h"
+#include "utils.h"
+
+GtkAdjustment*
+zathura_adjustment_clone(GtkAdjustment* adjustment)
+{
+  gdouble value          = gtk_adjustment_get_value(adjustment);
+  gdouble lower          = gtk_adjustment_get_lower(adjustment);
+  gdouble upper          = gtk_adjustment_get_upper(adjustment);
+  gdouble step_increment = gtk_adjustment_get_step_increment(adjustment);
+  gdouble page_increment = gtk_adjustment_get_page_increment(adjustment);
+  gdouble page_size      = gtk_adjustment_get_page_size(adjustment);
+
+  return GTK_ADJUSTMENT(gtk_adjustment_new(value, lower, upper, step_increment,
+        page_increment, page_size));
+}
+
+void
+zathura_adjustment_set_value(GtkAdjustment* adjustment, gdouble value)
+{
+  gtk_adjustment_set_value(adjustment,
+                           MAX(gtk_adjustment_get_lower(adjustment),
+                               MIN(gtk_adjustment_get_upper(adjustment) -
+                                   gtk_adjustment_get_page_size(adjustment),
+                                   value)));
+}
+
+gdouble
+zathura_adjustment_get_ratio(GtkAdjustment* adjustment)
+{
+  gdouble lower     = gtk_adjustment_get_lower(adjustment);
+  gdouble upper     = gtk_adjustment_get_upper(adjustment);
+  gdouble page_size = gtk_adjustment_get_page_size(adjustment);
+  gdouble value     = gtk_adjustment_get_value(adjustment);
+
+  return (value - lower + page_size / 2.0) / (upper - lower);
+}
+
+void
+zathura_adjustment_set_value_from_ratio(GtkAdjustment* adjustment,
+                                        gdouble ratio)
+{
+  if (ratio == 0.0)
+    return;
+
+  gdouble lower = gtk_adjustment_get_lower(adjustment);
+  gdouble upper = gtk_adjustment_get_upper(adjustment);
+  gdouble page_size = gtk_adjustment_get_page_size(adjustment);
+
+  gdouble value = (upper - lower) * ratio + lower - page_size / 2.0;
+
+  zathura_adjustment_set_value(adjustment, value);
+}
diff --git a/adjustment.h b/adjustment.h
new file mode 100644
index 0000000..63b4f88
--- /dev/null
+++ b/adjustment.h
@@ -0,0 +1,49 @@
+/* See LICENSE file for license and copyright information */
+
+#ifndef ZATHURA_ADJUSTMENT_H
+#define ZATHURA_ADJUSTMENT_H
+
+#include <gtk/gtk.h>
+
+/* Clone a GtkAdjustment
+ *
+ * Creates a new adjustment with the same value, lower and upper bounds, step
+ * and page increments and page_size as the original adjustment.
+ *
+ * @param adjustment Adjustment instance to be cloned
+ * @return Pointer to the new adjustment
+ */
+GtkAdjustment* zathura_adjustment_clone(GtkAdjustment* adjustment);
+
+/**
+ * Set the adjustment value while enforcing its limits
+ *
+ * @param adjustment Adjustment instance
+ * @param value Adjustment value
+ */
+void zathura_adjustment_set_value(GtkAdjustment* adjustment, gdouble value);
+
+/**
+ * Compute the adjustment ratio
+ *
+ * That is, the ratio between the length from the lower bound to the middle of
+ * the slider, and the total length of the scrollbar.
+ *
+ * @param adjustment Scrollbar adjustment
+ * @return Adjustment ratio
+ */
+gdouble zathura_adjustment_get_ratio(GtkAdjustment* adjustment);
+
+/**
+ * Set the adjustment value from ratio
+ *
+ * The ratio is usually obtained from a previous call to
+ * zathura_adjustment_get_ratio().
+ *
+ * @param adjustment Adjustment instance
+ * @param ratio Ratio from which the adjustment value will be set
+ */
+void zathura_adjustment_set_value_from_ratio(GtkAdjustment* adjustment,
+                                             gdouble ratio);
+
+#endif /* ZATHURA_ADJUSTMENT_H */
diff --git a/callbacks.c b/callbacks.c
index b72ebad..9bd4806 100644
--- a/callbacks.c
+++ b/callbacks.c
@@ -18,6 +18,7 @@
 #include "shortcuts.h"
 #include "page-widget.h"
 #include "page.h"
+#include "adjustment.h"
 
 gboolean
 cb_destroy(GtkWidget* UNUSED(widget), zathura_t* zathura)
@@ -109,6 +110,78 @@ cb_view_vadjustment_value_changed(GtkAdjustment* 
GIRARA_UNUSED(adjustment), gpoi
 }
 
 void
+cb_view_hadjustment_changed(GtkAdjustment* adjustment, gpointer data)
+{
+  zathura_t* zathura = data;
+  g_return_if_fail(zathura != NULL);
+
+  zathura_adjust_mode_t adjust_mode =
+    zathura_document_get_adjust_mode(zathura->document);
+
+  gdouble lower, upper, page_size, value, ratio;
+  bool zoom_center = false;
+
+  switch (adjust_mode) {
+    center:
+    case ZATHURA_ADJUST_BESTFIT:
+    case ZATHURA_ADJUST_WIDTH:
+      lower = gtk_adjustment_get_lower(adjustment);
+      upper = gtk_adjustment_get_upper(adjustment);
+      page_size = gtk_adjustment_get_page_size(adjustment);
+      value = ((upper - lower) - page_size) / 2.0;
+      zathura_adjustment_set_value(adjustment, value);
+      break;
+    default:
+      girara_setting_get(zathura->ui.session, "zoom-center", &zoom_center);
+      if (zoom_center)
+        goto center;
+
+      ratio = zathura_adjustment_get_ratio(zathura->ui.hadjustment);
+      zathura_adjustment_set_value_from_ratio(adjustment, ratio);
+      break;
+  }
+}
+
+void
+cb_view_vadjustment_changed(GtkAdjustment* adjustment, gpointer data)
+{
+  zathura_t* zathura = data;
+  g_return_if_fail(zathura != NULL);
+
+  double ratio = zathura_adjustment_get_ratio(zathura->ui.vadjustment);
+  zathura_adjustment_set_value_from_ratio(adjustment, ratio);
+}
+
+void
+cb_adjustment_track_value(GtkAdjustment* adjustment, gpointer data)
+{
+  GtkAdjustment* tracker = data;
+
+  gdouble lower = gtk_adjustment_get_lower(adjustment);
+  gdouble upper = gtk_adjustment_get_upper(adjustment);
+  if (lower != gtk_adjustment_get_lower(tracker) ||
+      upper != gtk_adjustment_get_upper(tracker))
+    return;
+
+  gdouble value = gtk_adjustment_get_value(adjustment);
+  gtk_adjustment_set_value(tracker, value);
+}
+
+void
+cb_adjustment_track_bounds(GtkAdjustment* adjustment, gpointer data)
+{
+  GtkAdjustment* tracker = data;
+  gdouble value = gtk_adjustment_get_value(adjustment);
+  gdouble lower = gtk_adjustment_get_lower(adjustment);
+  gdouble upper = gtk_adjustment_get_upper(adjustment);
+  gdouble page_size = gtk_adjustment_get_page_size(adjustment);
+  gtk_adjustment_set_value(tracker, value);
+  gtk_adjustment_set_lower(tracker, lower);
+  gtk_adjustment_set_upper(tracker, upper);
+  gtk_adjustment_set_page_size(tracker, page_size);
+}
+
+void
 cb_pages_per_row_value_changed(girara_session_t* session, const char* 
UNUSED(name), girara_setting_type_t UNUSED(type), void* value, void* 
UNUSED(data))
 {
   g_return_if_fail(value != NULL);
diff --git a/callbacks.h b/callbacks.h
index 4ce2e2e..d879732 100644
--- a/callbacks.h
+++ b/callbacks.h
@@ -35,6 +35,53 @@ void cb_buffer_changed(girara_session_t* session);
  * @param data NULL
  */
 void cb_view_vadjustment_value_changed(GtkAdjustment *adjustment, gpointer 
data);
+
+/**
+ * This function gets called when the bounds or the page_size of the horizontal
+ * scrollbar change (e.g. when the zoom level is changed).
+ *
+ * It adjusts the value of the horizontal scrollbar, possibly based on its
+ * previous adjustment, stored in the tracking adjustment
+ * zathura->ui.hadjustment.
+ *
+ * @param adjustment The horizontal adjustment of a gtkScrolledWindow
+ * @param data The zathura instance
+ */
+void cb_view_hadjustment_changed(GtkAdjustment *adjustment, gpointer data);
+
+/**
+ * This function gets called when the bounds or the page_size of the vertical
+ * scrollbar change (e.g. when the zoom level is changed).
+ *
+ * It adjusts the value of the vertical scrollbar based on its previous
+ * adjustment, stored in the tracking adjustment zathura->ui.hadjustment.
+ *
+ * @param adjustment The vertical adjustment of a gtkScrolledWindow
+ * @param data The zathura instance
+ */
+void cb_view_vadjustment_changed(GtkAdjustment *adjustment, gpointer data);
+
+/* This function gets called when the value of the adjustment changes.
+ *
+ * It updates the value of the tracking adjustment, only if the bounds of the
+ * adjustment have not changed (if they did change,
+ * cb_adjustment_track_bounds() will take care of updating everything).
+ *
+ * @param adjustment The adjustment instance
+ * @param data The tracking adjustment instance
+ */
+void cb_adjustment_track_value(GtkAdjustment* adjustment, gpointer data);
+
+/* This function gets called when the bounds or the page_size of the adjustment
+ * change.
+ *
+ * It updates the value, bounds and page_size of the tracking adjustment.
+ *
+ * @param adjustment The adjustment instance
+ * @param data The tracking adjustment instance
+ */
+void cb_adjustment_track_bounds(GtkAdjustment* adjustment, gpointer data);
+
 /**
  * This function gets called when the value of the "pages-per-row"
  * variable changes
diff --git a/marks.c b/marks.c
index d5e4d47..0fac33d 100644
--- a/marks.c
+++ b/marks.c
@@ -235,9 +235,7 @@ mark_evaluate(zathura_t* zathura, int key)
   /* search for existing mark */
   GIRARA_LIST_FOREACH(zathura->global.marks, zathura_mark_t*, iter, mark)
   if (mark != NULL && mark->key == key) {
-    double old_scale = zathura_document_get_scale(zathura->document);
     zathura_document_set_scale(zathura->document, mark->scale);
-    readjust_view_after_zooming(zathura, old_scale, true);
     render_all(zathura);
 
     position_set_delayed(zathura, mark->position_x, mark->position_y);
diff --git a/shortcuts.c b/shortcuts.c
index a3348af..4a15d44 100644
--- a/shortcuts.c
+++ b/shortcuts.c
@@ -17,6 +17,7 @@
 #include "page.h"
 #include "print.h"
 #include "page-widget.h"
+#include "adjustment.h"
 
 /* Helper function; see sc_display_link and sc_follow. */
 static bool
@@ -105,7 +106,6 @@ sc_adjust_window(girara_session_t* session, 
girara_argument_t* argument,
     goto error_ret;
   }
 
-  float old_zoom = zathura_document_get_scale(zathura->document);
   zathura_document_set_adjust_mode(zathura->document, argument->n);
   if (argument->n == ZATHURA_ADJUST_NONE) {
     /* there is nothing todo */
@@ -180,9 +180,6 @@ sc_adjust_window(girara_session_t* session, 
girara_argument_t* argument,
     goto error_ret;
   }
 
-  /* keep position */
-  readjust_view_after_zooming(zathura, old_zoom, false);
-
   /* re-render all pages */
   render_all(zathura);
 
@@ -375,8 +372,10 @@ sc_mouse_scroll(girara_session_t* session, 
girara_argument_t* argument, girara_e
         return false;
       }
 
-      set_adjustment(x_adj, gtk_adjustment_get_value(x_adj) - (event->x - x));
-      set_adjustment(y_adj, gtk_adjustment_get_value(y_adj) - (event->y - y));
+      zathura_adjustment_set_value(x_adj,
+          gtk_adjustment_get_value(x_adj) - (event->x - x));
+      zathura_adjustment_set_value(y_adj,
+          gtk_adjustment_get_value(y_adj) - (event->y - y));
       break;
 
       /* unhandled events */
@@ -698,7 +697,7 @@ sc_scroll(girara_session_t* session, girara_argument_t* 
argument,
     }
   }
 
-  set_adjustment(adjustment, new_value);
+  zathura_adjustment_set_value(adjustment, new_value);
 
   return false;
 }
@@ -818,14 +817,14 @@ sc_search(girara_session_t* session, girara_argument_t* 
argument,
 
     GtkAdjustment* view_vadjustment = 
gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
     int y = offset.y - gtk_adjustment_get_page_size(view_vadjustment) / 2 + 
rectangle.y1;
-    set_adjustment(view_vadjustment, y);
+    zathura_adjustment_set_value(view_vadjustment, y);
 
     bool search_hadjust = true;
     girara_setting_get(session, "search-hadjust", &search_hadjust);
     if (search_hadjust == true) {
       GtkAdjustment* view_hadjustment = 
gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
       int x = offset.x - gtk_adjustment_get_page_size(view_hadjustment) / 2 + 
rectangle.x1;
-      set_adjustment(view_hadjustment, x);
+      zathura_adjustment_set_value(view_hadjustment, x);
     }
   }
 
@@ -1214,9 +1213,6 @@ sc_zoom(girara_session_t* session, girara_argument_t* 
argument, girara_event_t*
     zathura_document_set_scale(zathura->document, zoom_max);
   }
 
-  /* keep position */
-  readjust_view_after_zooming(zathura, old_zoom, true);
-
   render_all(zathura);
 
   return false;
diff --git a/utils.c b/utils.c
index fd052e5..26a07cd 100644
--- a/utils.c
+++ b/utils.c
@@ -248,13 +248,6 @@ error_ret:
   return rectangle;
 }
 
-void
-set_adjustment(GtkAdjustment* adjustment, gdouble value)
-{
-  gtk_adjustment_set_value(adjustment, 
MAX(gtk_adjustment_get_lower(adjustment),
-                           MIN(gtk_adjustment_get_upper(adjustment) - 
gtk_adjustment_get_page_size(adjustment), value)));
-}
-
 double
 page_calc_height_width(zathura_page_t* page, unsigned int* page_height, 
unsigned int* page_width, bool rotate)
 {
@@ -333,35 +326,6 @@ zathura_page_get_widget(zathura_t* zathura, 
zathura_page_t* page)
 }
 
 void
-readjust_view_after_zooming(zathura_t *zathura, float old_zoom, bool delay)
-{
-  if (zathura == NULL || zathura->document == NULL) {
-    return;
-  }
-
-  GtkScrolledWindow *window = 
GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view);
-  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(window);
-  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(window);
-
-  double scale = zathura_document_get_scale(zathura->document);
-  gdouble valx = gtk_adjustment_get_value(hadjustment) / old_zoom * scale;
-  gdouble valy = gtk_adjustment_get_value(vadjustment) / old_zoom * scale;
-
-  bool zoom_center = false;
-  girara_setting_get(zathura->ui.session, "zoom-center", &zoom_center);
-  if (zoom_center) {
-    valx += gtk_adjustment_get_page_size(hadjustment) * (scale / old_zoom - 1) 
/ 2;
-  }
-
-  if (delay == true) {
-    position_set_delayed(zathura, valx, valy);
-  } else {
-    set_adjustment(hadjustment, valx);
-    set_adjustment(vadjustment, valy);
-  }
-}
-
-void
 document_draw_search_results(zathura_t* zathura, bool value)
 {
   if (zathura == NULL || zathura->document == NULL || zathura->pages == NULL) {
diff --git a/utils.h b/utils.h
index b480ab8..ce6baf6 100644
--- a/utils.h
+++ b/utils.h
@@ -87,13 +87,6 @@ zathura_rectangle_t rotate_rectangle(zathura_rectangle_t 
rectangle, unsigned int
 zathura_rectangle_t recalc_rectangle(zathura_page_t* page, zathura_rectangle_t 
rectangle);
 
 /**
- * Set adjustment of a GtkAdjustment respecting its limits.
- * @param adjust the GtkAdkustment instance
- * @param value the new adjustment
- */
-void set_adjustment(GtkAdjustment* adjust, gdouble value);
-
-/**
  * Calculate the page size according to the corrent scaling and rotation if
  * desired.
  * @param page the page
@@ -133,15 +126,6 @@ void zathura_get_document_size(zathura_t* zathura,
 GtkWidget* zathura_page_get_widget(zathura_t* zathura, zathura_page_t* page);
 
 /**
- * Re-adjust view
- *
- * @param zathura Zathura instance
- * @param old_zoom Old zoom value
- * @param delay true if action should be delayed
- */
-void readjust_view_after_zooming(zathura_t* zathura, float old_zoom, bool 
delay);
-
-/**
  * Set if the search results should be drawn or not
  *
  * @param zathura Zathura instance
diff --git a/zathura.c b/zathura.c
index 7485530..8e2efa4 100644
--- a/zathura.c
+++ b/zathura.c
@@ -32,6 +32,7 @@
 #include "page.h"
 #include "page-widget.h"
 #include "plugin.h"
+#include "adjustment.h"
 
 typedef struct zathura_document_info_s {
   zathura_t* zathura;
@@ -152,11 +153,35 @@ zathura_init(zathura_t* zathura)
 
   g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "size-allocate", 
G_CALLBACK(cb_view_resized), zathura);
 
-  /* callbacks */
-  GtkAdjustment* view_vadjustment = 
gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
-  g_signal_connect(G_OBJECT(view_vadjustment), "value-changed", 
G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
-  GtkAdjustment* view_hadjustment = 
gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
-  g_signal_connect(G_OBJECT(view_hadjustment), "value-changed", 
G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
+  /* Setup hadjustment tracker */
+  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(
+      GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
+  zathura->ui.hadjustment = zathura_adjustment_clone(hadjustment);
+
+  /* Connect hadjustment signals */
+  g_signal_connect(G_OBJECT(hadjustment), "value-changed",
+      G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
+  g_signal_connect(G_OBJECT(hadjustment), "value-changed",
+      G_CALLBACK(cb_adjustment_track_value), zathura->ui.hadjustment);
+  g_signal_connect(G_OBJECT(hadjustment), "changed",
+      G_CALLBACK(cb_view_hadjustment_changed), zathura);
+  g_signal_connect(G_OBJECT(hadjustment), "changed",
+      G_CALLBACK(cb_adjustment_track_bounds), zathura->ui.hadjustment);
+
+  /* Setup vadjustment tracker */
+  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(
+      GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
+  zathura->ui.vadjustment = zathura_adjustment_clone(vadjustment);
+
+  /* Connect vadjustment signals */
+  g_signal_connect(G_OBJECT(vadjustment), "value-changed",
+      G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
+  g_signal_connect(G_OBJECT(vadjustment), "value-changed",
+      G_CALLBACK(cb_adjustment_track_value), zathura->ui.vadjustment);
+  g_signal_connect(G_OBJECT(vadjustment), "changed",
+      G_CALLBACK(cb_view_vadjustment_changed), zathura);
+  g_signal_connect(G_OBJECT(vadjustment), "changed",
+      G_CALLBACK(cb_adjustment_track_bounds), zathura->ui.vadjustment);
 
   /* page view alignment */
   zathura->ui.page_widget_alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
@@ -956,8 +981,8 @@ page_set(zathura_t* zathura, unsigned int page_id)
 
   GtkAdjustment* view_vadjustment = 
gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
   GtkAdjustment* view_hadjustment = 
gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
-  set_adjustment(view_hadjustment, offset.x);
-  set_adjustment(view_vadjustment, offset.y);
+  zathura_adjustment_set_value(view_hadjustment, offset.x);
+  zathura_adjustment_set_value(view_vadjustment, offset.y);
 
   statusbar_page_number_update(zathura);
 
@@ -1065,8 +1090,8 @@ position_set_delayed_impl(gpointer data)
   GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(window);
   GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(window);
 
-  set_adjustment(hadjustment, p->position_x);
-  set_adjustment(vadjustment, p->position_y);
+  zathura_adjustment_set_value(hadjustment, p->position_x);
+  zathura_adjustment_set_value(vadjustment, p->position_y);
 
   g_free(p);
 
diff --git a/zathura.h b/zathura.h
index 96d9610..1520d78 100644
--- a/zathura.h
+++ b/zathura.h
@@ -69,6 +69,9 @@ struct zathura_s
     GtkWidget *page_widget_alignment;
     GtkWidget *page_widget; /**< Widget that contains all rendered pages */
     GtkWidget *index; /**< Widget to show the index of the document */
+
+    GtkAdjustment *hadjustment; /**< Tracking hadjustment */
+    GtkAdjustment *vadjustment; /**< Tracking vadjustment */
   } ui;
 
   struct
-- 
1.7.10.4

_______________________________________________
zathura mailing list
zathura@lists.pwmt.org
http://lists.pwmt.org/mailman/listinfo/zathura

Reply via email to