[libhandy/wip/exalm/paginator-animate: 31/41] carousel-box: Shift position after page changes



commit 2a0a1a3580283ce2523fa0503212c10d5a291164
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Thu Dec 26 15:06:20 2019 +0500

    carousel-box: Shift position after page changes
    
    Ensure the scrolling position doesn't jump after adding, removing or
    reordering children, even during an ongoing gesture.
    
    Use the closest snap point instead of just position when determining
    whether to do the shift.
    
    Add 'position-shifted' signal, connect to it from HdyCarousel and update
    the HdySwipeTracker position accordingly.
    
    Signed-off-by: Alexander Mikhaylenko <alexm gnome org>

 src/hdy-carousel-box.c | 67 ++++++++++++++++++++++++++++++++++++++++----------
 src/hdy-carousel.c     |  9 +++++++
 src/hdy-carousel.ui    |  1 +
 3 files changed, 64 insertions(+), 13 deletions(-)
---
diff --git a/src/hdy-carousel-box.c b/src/hdy-carousel-box.c
index d8d32c15..ecf8eb97 100644
--- a/src/hdy-carousel-box.c
+++ b/src/hdy-carousel-box.c
@@ -78,6 +78,7 @@ static GParamSpec *props[LAST_PROP];
 
 enum {
   SIGNAL_ANIMATION_STOPPED,
+  SIGNAL_POSITION_SHIFTED,
   SIGNAL_LAST_SIGNAL,
 };
 static guint signals[SIGNAL_LAST_SIGNAL];
@@ -610,18 +611,29 @@ hdy_carousel_box_add (GtkContainer *container,
   hdy_carousel_box_insert (self, widget, -1);
 }
 
+static void
+shift_position (HdyCarouselBox *self,
+                gdouble         delta)
+{
+  hdy_carousel_box_set_position (self, self->position + delta);
+  g_signal_emit (self, signals[SIGNAL_POSITION_SHIFTED], 0, delta);
+}
+
 static void
 hdy_carousel_box_remove (GtkContainer *container,
                          GtkWidget    *widget)
 {
   HdyCarouselBox *self = HDY_CAROUSEL_BOX (container);
   gint index;
+  gdouble closest_point;
   HdyCarouselBoxChildInfo *info;
 
   info = find_child_info (self, widget);
   if (!info)
     return;
 
+  closest_point = hdy_carousel_box_get_closest_snap_point (self);
+
   gtk_widget_unparent (widget);
   index = g_list_index (self->children, info);
   self->children = g_list_remove (self->children, info);
@@ -629,13 +641,13 @@ hdy_carousel_box_remove (GtkContainer *container,
   if (gtk_widget_get_realized (GTK_WIDGET (container)))
     unregister_window (info, self);
 
-  free_child_info (info);
-
-  if (self->position >= index)
-    hdy_carousel_box_set_position (self, self->position - 1);
+  if (closest_point >= index)
+    shift_position (self, -1);
   else
     gtk_widget_queue_allocate (GTK_WIDGET (self));
 
+  free_child_info (info);
+
   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_N_PAGES]);
 }
 
@@ -828,6 +840,25 @@ hdy_carousel_box_class_init (HdyCarouselBoxClass *klass)
                   NULL, NULL, NULL,
                   G_TYPE_NONE,
                   0);
+
+  /**
+   * HdyCarouselBox::position-shifted:
+   * @self: The #HdyCarouselBox instance
+   * @delta: The amount to shift the position by
+   *
+   * This signal is emitted when position has been programmatically shifted.
+   *
+   * Since: 1.0
+   */
+  signals[SIGNAL_POSITION_SHIFTED] =
+    g_signal_new ("position-shifted",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  1,
+                  G_TYPE_DOUBLE);
 }
 
 static void
@@ -874,6 +905,7 @@ hdy_carousel_box_insert (HdyCarouselBox *self,
                          gint            position)
 {
   HdyCarouselBoxChildInfo *info;
+  gdouble closest_point;
 
   g_return_if_fail (HDY_IS_CAROUSEL_BOX (self));
   g_return_if_fail (GTK_IS_WIDGET (widget));
@@ -884,7 +916,11 @@ hdy_carousel_box_insert (HdyCarouselBox *self,
   if (gtk_widget_get_realized (GTK_WIDGET (self)))
     register_window (info, self);
 
+  closest_point = hdy_carousel_box_get_closest_snap_point (self);
+
   self->children = g_list_insert (self->children, info, position);
+  if (closest_point >= position && position >= 0)
+    shift_position (self, 1);
 
   gtk_widget_set_parent (widget, GTK_WIDGET (self));
 
@@ -913,29 +949,34 @@ hdy_carousel_box_reorder (HdyCarouselBox *self,
 {
   HdyCarouselBoxChildInfo *info;
   GList *link;
-  gint old_position, current_page;
+  gint old_position;
+  gdouble closest_point;
 
   g_return_if_fail (HDY_IS_CAROUSEL_BOX (self));
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
+  closest_point = hdy_carousel_box_get_closest_snap_point (self);
+
   info = find_child_info (self, widget);
   link = g_list_find (self->children, info);
   old_position = g_list_position (self->children, link);
   self->children = g_list_delete_link (self->children, link);
   if (position < 0 || position >= hdy_carousel_box_get_n_pages (self))
     link = NULL;
-  else
+  else {
+    if (position > old_position)
+      position--;
     link = g_list_nth (self->children, position);
+  }
 
   self->children = g_list_insert_before (self->children, link, info);
 
-  current_page = round (self->position);
-  if (current_page == old_position)
-    hdy_carousel_box_set_position (self, position);
-  else if (old_position > current_page && position <= current_page)
-    hdy_carousel_box_set_position (self, self->position + 1);
-  else if (old_position <= current_page && position > current_page)
-    hdy_carousel_box_set_position (self, self->position - 1);
+  if (closest_point == old_position)
+    shift_position (self, position - old_position);
+  else if (old_position > closest_point && closest_point >= position)
+    shift_position (self, 1);
+  else if (position >= closest_point && closest_point > old_position)
+    shift_position (self, -1);
 }
 
 /**
diff --git a/src/hdy-carousel.c b/src/hdy-carousel.c
index c3ef4913..f5903236 100644
--- a/src/hdy-carousel.c
+++ b/src/hdy-carousel.c
@@ -240,6 +240,14 @@ animation_stopped_cb (HdyCarousel    *self,
   g_signal_emit (self, signals[SIGNAL_PAGE_CHANGED], 0, index);
 }
 
+static void
+position_shifted_cb (HdyCarousel    *self,
+                     gdouble         delta,
+                     HdyCarouselBox *box)
+{
+  hdy_swipe_tracker_shift_position (self->tracker, delta);
+}
+
 static GdkRGBA
 get_color (GtkWidget *widget)
 {
@@ -991,6 +999,7 @@ hdy_carousel_class_init (HdyCarouselClass *klass)
   gtk_widget_class_bind_template_callback (widget_class, notify_position_cb);
   gtk_widget_class_bind_template_callback (widget_class, notify_spacing_cb);
   gtk_widget_class_bind_template_callback (widget_class, animation_stopped_cb);
+  gtk_widget_class_bind_template_callback (widget_class, position_shifted_cb);
 
   gtk_widget_class_set_css_name (widget_class, "carousel");
 }
diff --git a/src/hdy-carousel.ui b/src/hdy-carousel.ui
index dd7f7d45..87861ce7 100644
--- a/src/hdy-carousel.ui
+++ b/src/hdy-carousel.ui
@@ -21,6 +21,7 @@
             <signal name="notify::position" handler="notify_position_cb" swapped="true"/>
             <signal name="notify::spacing" handler="notify_spacing_cb" swapped="true"/>
             <signal name="animation-stopped" handler="animation_stopped_cb" swapped="true"/>
+            <signal name="position-shifted" handler="position_shifted_cb" swapped="true"/>
           </object>
         </child>
         <child>


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