[egg-list-box/flow-box-enhancements] Add autoscrolling



commit 8674191416e02c12c30e74fd84283a83e697a0ce
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Sep 29 03:36:25 2013 -0400

    Add autoscrolling
    
    When the pointer leaves the widget during a rubberband selection,
    we scroll to enlarge the selection.

 egg-flow-box.c |  178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 178 insertions(+), 0 deletions(-)
---
diff --git a/egg-flow-box.c b/egg-flow-box.c
index 92590e2..397a38c 100644
--- a/egg-flow-box.c
+++ b/egg-flow-box.c
@@ -124,6 +124,9 @@ _egg_marshal_VOID__ENUM_INT (GClosure * closure,
 
 #define DEFAULT_MAX_CHILDREN_PER_LINE 7
 #define RUBBERBAND_START_DISTANCE 32
+#define AUTOSCROLL_FAST_DISTANCE 32
+#define AUTOSCROLL_FACTOR 20
+#define AUTOSCROLL_FACTOR_FAST 10
 
 enum {
   CHILD_ACTIVATED,
@@ -198,6 +201,10 @@ struct _EggFlowBoxPrivate {
   EggFlowBoxChild   *rubberband_last;
   gint               button_down_x;
   gint               button_down_y;
+  GdkDevice         *rubberband_device;
+
+  GtkScrollType      autoscroll_mode;
+  guint              autoscroll_id;
 };
 
 typedef struct _EggFlowBoxChildPrivate EggFlowBoxChildPrivate;
@@ -2611,6 +2618,161 @@ egg_flow_box_leave_notify_event (GtkWidget        *widget,
   return FALSE;
 }
 
+static void
+remove_autoscroll (EggFlowBox *box)
+{
+  EggFlowBoxPrivate *priv = BOX_PRIV (box);
+
+  if (priv->autoscroll_id)
+    {
+      gtk_widget_remove_tick_callback (GTK_WIDGET (box), priv->autoscroll_id);
+      priv->autoscroll_id = 0;
+    }
+
+  priv->autoscroll_mode = GTK_SCROLL_NONE;
+}
+
+static gboolean
+autoscroll_cb (GtkWidget     *widget,
+               GdkFrameClock *frame_clock,
+               gpointer       data)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (data);
+  EggFlowBoxPrivate *priv = BOX_PRIV (box);
+  GtkAdjustment *adjustment;
+  gdouble factor;
+  gdouble increment;
+  gdouble value;
+  gboolean handled;
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    adjustment = priv->vadjustment;
+  else
+    adjustment = priv->hadjustment;
+
+  switch (priv->autoscroll_mode)
+    {
+    case GTK_SCROLL_STEP_FORWARD:
+      factor = AUTOSCROLL_FACTOR;
+      break;
+    case GTK_SCROLL_STEP_BACKWARD:
+      factor = - AUTOSCROLL_FACTOR;
+      break;
+    case GTK_SCROLL_PAGE_FORWARD:
+      factor = AUTOSCROLL_FACTOR_FAST;
+      break;
+    case GTK_SCROLL_PAGE_BACKWARD:
+      factor = - AUTOSCROLL_FACTOR_FAST;
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+  increment = gtk_adjustment_get_step_increment (adjustment) / factor;
+
+  value = gtk_adjustment_get_value (adjustment);
+  value += increment;
+  gtk_adjustment_set_value (adjustment, value);
+
+  if (priv->rubberband_select)
+    {
+      gint x, y;
+      EggFlowBoxChild *child;
+
+      gdk_window_get_device_position (gtk_widget_get_window (widget),
+                                      priv->rubberband_device,
+                                      &x, &y, NULL);
+
+      child = egg_flow_box_find_child_at_pos (box, x, y);
+
+      egg_flow_box_update_prelight (box, child);
+      egg_flow_box_update_active (box, child);
+
+      if (child != NULL)
+        priv->rubberband_last = child;
+    }
+
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+add_autoscroll (EggFlowBox *box)
+{
+  EggFlowBoxPrivate *priv = BOX_PRIV (box);
+
+  if (priv->autoscroll_id != 0 ||
+      priv->autoscroll_mode == GTK_SCROLL_NONE)
+    return;
+
+  priv->autoscroll_id = gtk_widget_add_tick_callback (GTK_WIDGET (box),
+                                                      (GtkTickCallback)autoscroll_cb,
+                                                      box,
+                                                      NULL);
+}
+
+static gboolean
+get_view_rect (EggFlowBox   *box,
+               GdkRectangle *rect)
+{
+  EggFlowBoxPrivate *priv = BOX_PRIV (box);
+  GtkWidget *parent;
+  GdkWindow *view;
+
+  parent = gtk_widget_get_parent (GTK_WIDGET (box));
+  if (GTK_IS_VIEWPORT (parent))
+    {
+      view = gtk_viewport_get_view_window (GTK_VIEWPORT (parent));
+      rect->x = gtk_adjustment_get_value (priv->hadjustment);
+      rect->y = gtk_adjustment_get_value (priv->vadjustment);
+      rect->width = gdk_window_get_width (view);
+      rect->height = gdk_window_get_height (view);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+update_autoscroll_mode (EggFlowBox *box,
+                        gint        x,
+                        gint        y)
+{
+  EggFlowBoxPrivate *priv = BOX_PRIV (box);
+  GtkScrollType mode = GTK_SCROLL_NONE;
+  GdkRectangle rect;
+  gint size, pos;
+
+  if (priv->rubberband_select && get_view_rect (box, &rect))
+    {
+      if (priv->orientation == GTK_ORIENTATION_VERTICAL)
+        {
+          size = rect.width;
+          pos = x - rect.x;
+        }
+      else
+        {
+          size = rect.height;
+          pos = y - rect.y;
+        }
+
+      if (pos < 0 - AUTOSCROLL_FAST_DISTANCE)
+        mode = GTK_SCROLL_PAGE_BACKWARD;
+      else if (pos > size + AUTOSCROLL_FAST_DISTANCE)
+        mode = GTK_SCROLL_PAGE_FORWARD;
+      else if (pos < 0)
+        mode = GTK_SCROLL_STEP_BACKWARD;
+      else if (pos > size)
+        mode = GTK_SCROLL_STEP_FORWARD;
+    }
+
+  if (mode != priv->autoscroll_mode)
+    {
+      remove_autoscroll (box);
+      priv->autoscroll_mode = mode;
+      add_autoscroll (box);
+    }
+}
+
 static gboolean
 egg_flow_box_motion_notify_event (GtkWidget      *widget,
                                   GdkEventMotion *event)
@@ -2660,6 +2822,8 @@ egg_flow_box_motion_notify_event (GtkWidget      *widget,
             priv->rubberband_first = child;
           if (child != NULL)
             priv->rubberband_last = child;
+
+          update_autoscroll_mode (box, event->x, event->y);
         }
     }
 
@@ -2698,6 +2862,7 @@ egg_flow_box_button_press_event (GtkWidget      *widget,
           priv->rubberband_last = NULL;
           priv->button_down_x = event->x;
           priv->button_down_y = event->y;
+          priv->rubberband_device = gdk_event_get_device ((GdkEvent*)event);
         }
     }
 
@@ -2927,6 +3092,7 @@ egg_flow_box_button_release_event (GtkWidget      *widget,
       gtk_widget_queue_draw (GTK_WIDGET (box));
   }
 
+  remove_autoscroll (box);
   priv->track_motion = FALSE;
   if (priv->rubberband_select)
     {
@@ -2934,6 +3100,7 @@ egg_flow_box_button_release_event (GtkWidget      *widget,
       priv->rubberband_first = NULL;
       priv->rubberband_last = NULL;
       priv->rubberband_select = FALSE;
+      priv->rubberband_device = NULL;
     }
 
   return FALSE;
@@ -3669,6 +3836,16 @@ egg_flow_box_realize (GtkWidget *widget)
 }
 
 static void
+egg_flow_box_unmap (GtkWidget *widget)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (widget);
+
+  remove_autoscroll (box);
+
+  GTK_WIDGET_CLASS (egg_flow_box_parent_class)->unmap (widget);
+}
+
+static void
 egg_flow_box_finalize (GObject *obj)
 {
   EggFlowBoxPrivate *priv = BOX_PRIV (obj);
@@ -3708,6 +3885,7 @@ egg_flow_box_class_init (EggFlowBoxClass *class)
   widget_class->motion_notify_event = egg_flow_box_motion_notify_event;
   widget_class->size_allocate = egg_flow_box_size_allocate;
   widget_class->realize = egg_flow_box_realize;
+  widget_class->unmap = egg_flow_box_unmap;
   widget_class->focus = egg_flow_box_focus;
   widget_class->draw = egg_flow_box_draw;
   widget_class->button_press_event = egg_flow_box_button_press_event;


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