Reviewed the minimum speed conditions.
[hildon] / hildon / hildon-pannable-area.c
index 717d5ae..f3f9eff 100644 (file)
@@ -57,6 +57,7 @@
 #define SCROLL_FADE_TIMEOUT 100
 #define MOTION_EVENTS_PER_SECOND 25
 #define CURSOR_STOPPED_TIMEOUT 80
+#define MAX_SPEED_THRESHOLD 250
 #define PANNABLE_MAX_WIDTH 788
 #define PANNABLE_MAX_HEIGHT 378
 
@@ -75,7 +76,7 @@ struct _HildonPannableAreaPrivate {
   gdouble ex;          /* Used to store mouse co-ordinates of the last */
   gdouble ey;          /* motion event in acceleration mode */
   gboolean enabled;
-  gboolean clicked;
+  gboolean button_pressed;
   guint32 last_time;   /* Last event time, to stop infinite loops */
   gint last_type;
   gboolean last_in;
@@ -369,7 +370,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                        "Minimum scroll velocity",
                                                        "Minimum distance the child widget should scroll "
                                                        "per 'frame', in pixels per frame.",
-                                                       0, G_MAXDOUBLE, 20,
+                                                       0, G_MAXDOUBLE, 10,
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
 
@@ -440,7 +441,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                      "Threshold to consider a motion event an scroll",
                                                      "Amount of pixels to consider a motion event an scroll, if it is less"
                                                       "it is a click detected incorrectly by the touch screen.",
-                                                     0, G_MAXUINT, 6,
+                                                     0, G_MAXUINT, 25,
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT));
 
@@ -470,7 +471,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                      "Multiplier of the calculated speed",
                                                      "Force applied to the movement, multiplies the calculated speed of the"
                                                       "user movement the cursor in the screen",
-                                                     0, G_MAXUINT, 120,
+                                                     0, G_MAXUINT, 50,
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT));
 
@@ -510,7 +511,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                   g_param_spec_double ("scroll_time",
                                                        "Time to scroll to a position",
                                                        "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
-                                                       1.0, 20.0, 10.0,
+                                                       0.0, 20.0, 1.0,
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
 
@@ -565,15 +566,18 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                            "Pixel width used to draw the scroll indicators.",
                                            0, G_MAXUINT, 8,
                                            G_PARAM_READWRITE));
-  /**
+
+ /**
    * HildonPannableArea::horizontal-movement:
    * @hildonpannable: the object which received the signal
-   * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
-   * @initial_x: the x value of the touched point in the area when the motion started
-   * @initial_y: the y value of the touched point in the area when the motion started
+   * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
+   * @initial_x: the x coordinate of the point where the user clicked to start the movement
+   * @initial_y: the y coordinate of the point where the user clicked to start the movement
    *
    * The horizontal-movement signal is emitted when the pannable area
-   * starts a horizontal movement.
+   * detects a horizontal movement. The detection does not mean the
+   * widget is going to move (i.e. maybe the children are smaller
+   * horizontally than the screen).
    *
    * Since: 2.2
    */
@@ -592,12 +596,14 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
   /**
    * HildonPannableArea::vertical-movement:
    * @hildonpannable: the object which received the signal
-   * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
-   * @initial_x: the x value when the motion started
-   * @initial_y: the y value when the motion started
+   * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
+   * @initial_x: the x coordinate of the point where the user clicked to start the movement
+   * @initial_y: the y coordinate of the point where the user clicked to start the movement
    *
    * The vertical-movement signal is emitted when the pannable area
-   * starts a vertical movement.
+   * detects a vertical movement. The detection does not mean the
+   * widget is going to move (i.e. maybe the children are smaller
+   * vertically than the screen).
    *
    * Since: 2.2
    */
@@ -613,7 +619,6 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                  G_TYPE_DOUBLE,
                  G_TYPE_DOUBLE);
 
-
 }
 
 static void
@@ -626,7 +631,7 @@ hildon_pannable_area_init (HildonPannableArea * area)
   area->priv = priv;
 
   priv->moved = FALSE;
-  priv->clicked = FALSE;
+  priv->button_pressed = FALSE;
   priv->last_time = 0;
   priv->last_type = 0;
   priv->vscroll_visible = TRUE;
@@ -643,7 +648,8 @@ hildon_pannable_area_init (HildonPannableArea * area)
   priv->scroll_indicator_timeout = 0;
   priv->motion_event_scroll_timeout = 0;
   priv->scroll_indicator_event_interrupt = 0;
-  priv->scroll_delay_counter = priv->scrollbar_fade_delay;
+  priv->scroll_delay_counter = 0;
+  priv->scrollbar_fade_delay = 0;
   priv->scroll_to_x = -1;
   priv->scroll_to_y = -1;
   priv->first_drag = TRUE;
@@ -652,8 +658,6 @@ hildon_pannable_area_init (HildonPannableArea * area)
   priv->child_height = 0;
   priv->last_in = TRUE;
 
-  hildon_pannable_calculate_vel_factor (area);
-
   gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
 
   priv->hadjust =
@@ -811,9 +815,8 @@ hildon_pannable_area_set_property (GObject * object,
     priv->vfast_factor = g_value_get_double (value);
     break;
   case PROP_DECELERATION:
-    hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
-
     priv->decel = g_value_get_double (value);
+    hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
     break;
   case PROP_DRAG_INERTIA:
     priv->drag_inertia = g_value_get_double (value);
@@ -822,7 +825,20 @@ hildon_pannable_area_set_property (GObject * object,
     priv->sps = g_value_get_uint (value);
     break;
   case PROP_PANNING_THRESHOLD:
-    priv->panning_threshold = g_value_get_uint (value);
+    {
+      GtkSettings *settings = gtk_settings_get_default ();
+      GtkSettingsValue svalue = { NULL, { 0, }, };
+
+      priv->panning_threshold = g_value_get_uint (value);
+
+      /* insure gtk dnd is the same we are using, not allowed
+         different thresholds in the same application */
+      svalue.origin = "panning_threshold";
+      g_value_init (&svalue.value, G_TYPE_LONG);
+      g_value_set_long (&svalue.value, priv->panning_threshold);
+      gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
+      g_value_unset (&svalue.value);
+    }
     break;
   case PROP_SCROLLBAR_FADE_DELAY:
     /* convert to miliseconds */
@@ -1468,22 +1484,15 @@ static void
 hildon_pannable_area_initial_effect (GtkWidget * widget)
 {
   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
-  gboolean hscroll_visible, vscroll_visible;
-
-  if (priv->initial_hint) {
 
-    vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
-                       priv->vadjust->page_size);
-    hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
-                       priv->hadjust->page_size);
+  if (priv->vscroll_visible || priv->hscroll_visible) {
 
-    if (priv->vscroll_visible || priv->hscroll_visible) {
+    priv->scroll_indicator_event_interrupt = 0;
+    priv->scroll_delay_counter = priv->scrollbar_fade_delay;
 
-      priv->scroll_indicator_event_interrupt = 0;
-      priv->scroll_delay_counter = priv->scrollbar_fade_delay;
+    hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
 
-      hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
-    }
+    priv->initial_effect = FALSE;
   }
 }
 
@@ -1553,8 +1562,8 @@ hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
   HildonPannableAreaPrivate *priv = area->priv;
 
   /* if moving do not fade out */
-  if (((ABS (priv->vel_y)>1.0)||
-       (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
+  if (((ABS (priv->vel_y)>priv->vmin)||
+       (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
 
     return TRUE;
   }
@@ -1613,6 +1622,10 @@ hildon_pannable_area_expose_event (GtkWidget * widget,
   GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
 #endif
 
+  if (G_UNLIKELY ((priv->initial_hint) && (priv->initial_effect))) {
+    hildon_pannable_area_initial_effect (widget);
+  }
+
   if (gtk_bin_get_child (GTK_BIN (widget))) {
 
     if (priv->scroll_indicator_alpha > 0.1) {
@@ -1701,13 +1714,6 @@ hildon_pannable_area_expose_event (GtkWidget * widget,
 
   }
 
-  if (G_UNLIKELY (priv->initial_effect)) {
-
-    hildon_pannable_area_initial_effect (widget);
-
-    priv->initial_effect = FALSE;
-  }
-
   return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
 }
 
@@ -1825,7 +1831,7 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget,
   priv->scroll_to_x = -1;
   priv->scroll_to_y = -1;
 
-  if (priv->clicked && priv->child) {
+  if (priv->button_pressed && priv->child) {
     /* Widget stole focus on last click, send crossing-out event */
     synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
                    event->time, FALSE);
@@ -1845,7 +1851,7 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget,
   else
     priv->child = NULL;
 
-  priv->clicked = TRUE;
+  priv->button_pressed = TRUE;
 
   /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
   priv->vel_x = 0;
@@ -2020,7 +2026,7 @@ hildon_pannable_axis_scroll (HildonPannableArea *area,
 
     gtk_adjustment_set_value (adjust, dist);
   } else {
-    if (!priv->clicked) {
+    if (!priv->button_pressed) {
 
       /* When the overshoot has started we continue for
        * PROP_BOUNCE_STEPS more steps into the overshoot before we
@@ -2146,7 +2152,7 @@ hildon_pannable_area_timeout (HildonPannableArea * area)
     return FALSE;
   }
 
-  if (!priv->clicked) {
+  if (!priv->button_pressed) {
     /* Decelerate gradually when pointer is raised */
     if ((!priv->overshot_dist_y) &&
         (!priv->overshot_dist_x)) {
@@ -2263,7 +2269,7 @@ hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
   if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
     return TRUE;
 
-  if ((!priv->enabled) || (!priv->clicked) ||
+  if ((!priv->enabled) || (!priv->button_pressed) ||
       ((event->time == priv->last_time) && (priv->last_type == 2))) {
     gdk_window_get_pointer (widget->window, NULL, NULL, 0);
     return TRUE;
@@ -2499,7 +2505,7 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget,
 
   if  (((event->time == priv->last_time) && (priv->last_type == 3))
        || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
-       || (!priv->clicked) || (!priv->enabled) || (event->button != 1))
+       || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
     return TRUE;
 
   priv->scroll_indicator_event_interrupt = 0;
@@ -2558,15 +2564,15 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget,
     }
   }
 
-  if ((ABS (priv->vel_y) > 1.0)||
-      (ABS (priv->vel_x) > 1.0)) {
+  if ((ABS (priv->vel_y) > priv->vmin)||
+      (ABS (priv->vel_x) > priv->vmin)) {
     priv->scroll_indicator_alpha = 1.0;
   }
 
   hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
                                             priv->scroll_indicator_alpha);
 
-  priv->clicked = FALSE;
+  priv->button_pressed = FALSE;
 
   if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
       priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
@@ -2585,6 +2591,12 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget,
     if ((ABS (priv->vel_y) >= priv->vmin) ||
         (ABS (priv->vel_x) >= priv->vmin)) {
 
+      if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
+        priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
+
+      if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
+        priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
+
       if (!priv->idle_id)
         priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
                                                  (GSourceFunc)
@@ -2742,17 +2754,43 @@ hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
   GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
 }
 
+/**
+ * This method calculates a factor necessary to determine the initial distance
+ * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
+ * second, we know in how many frames 'n' we need to reach the destination
+ * point. We know that, for a distance d,
+ *
+ *   d = d_0 + d_1 + ... + d_n
+ *
+ * where d_i is the distance travelled in the i-th frame and decel_factor is
+ * the deceleration factor. This can be rewritten as
+ *
+ *   d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
+ *
+ * since the distance travelled on each frame is the distance travelled in the
+ * previous frame reduced by the deceleration factor. Reducing this and
+ * factoring d_0 out, we get
+ *
+ *   d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
+ *
+ * Since the sum is independent of the distance to be travelled, we can define
+ * vel_factor as
+ *
+ *   vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
+ *
+ * That's the gem we calculate in this method.
+ **/
 static void
 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
 {
   HildonPannableAreaPrivate *priv = self->priv;
-  gfloat fct = 0;
+  gfloat fct = 1;
   gfloat fct_i = 1;
   gint i, n;
 
   n = ceil (priv->sps * priv->scroll_time);
 
-  for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
+  for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
     fct_i *= priv->decel;
     fct += fct_i;
   }