X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hildon%2Fhildon-pannable-area.c;h=21c140221b3b77f471cf27ddd0cd5843c00a5008;hb=ab90c2aebca089438622f1b63faca01fc147edf2;hp=8ac65557d7d7f6cb18bae444b830002351bc0b3a;hpb=a95023cb051103d2bb87873b6218dc8772467e9f;p=hildon diff --git a/hildon/hildon-pannable-area.c b/hildon/hildon-pannable-area.c index 8ac6555..21c1402 100644 --- a/hildon/hildon-pannable-area.c +++ b/hildon/hildon-pannable-area.c @@ -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; @@ -133,17 +134,22 @@ struct _HildonPannableAreaPrivate { GtkAdjustment *hadjust; GtkAdjustment *vadjust; + gint x_offset; + gint y_offset; GtkPolicyType vscrollbar_policy; GtkPolicyType hscrollbar_policy; GdkGC *scrollbars_gc; + GdkColor scroll_color; }; /*signals*/ enum { HORIZONTAL_MOVEMENT, VERTICAL_MOVEMENT, + PANNING_STARTED, + PANNING_FINISHED, LAST_SIGNAL }; @@ -264,6 +270,14 @@ static void hildon_pannable_area_calculate_velocity (gdouble *vel, static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area); static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area, gdouble x, gdouble y); +static void hildon_pannable_area_check_move (HildonPannableArea *area, + GdkEventMotion * event, + gdouble *x, + gdouble *y); +static void hildon_pannable_area_handle_move (HildonPannableArea *area, + GdkEventMotion * event, + gdouble *x, + gdouble *y); static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget, GdkEventMotion * event); static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget, @@ -369,7 +383,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 +454,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 +484,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 +524,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)); @@ -618,6 +632,52 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass) G_TYPE_DOUBLE, G_TYPE_DOUBLE); + /** + * HildonPannableArea::panning-started: + * @hildonpannable: the pannable area object that is going to start + * the panning + * + * This signal is emitted before the panning starts. Applications + * can return %TRUE to avoid the panning. The main difference with + * the vertical-movement and horizontal-movement signals is those + * gesture signals are launched no matter if the widget is going to + * move, this signal means the widget is going to start moving. It + * could even happen that the widget moves and there was no gesture + * (i.e. click meanwhile the pannable is overshooting). + * + * Returns: %TRUE to stop the panning launch. %FALSE to continue + * with it. + * + * Since: 2.2 + */ + pannable_area_signals[PANNING_STARTED] = + g_signal_new ("panning-started", + G_TYPE_FROM_CLASS (object_class), + 0, + 0, + NULL, NULL, + _hildon_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + /** + * HildonPannableArea::panning-finished: + * @hildonpannable: the pannable area object that finished the + * panning + * + * This signal is emitted after the kinetic panning has + * finished. + * + * Since: 2.2 + */ + pannable_area_signals[PANNING_FINISHED] = + g_signal_new ("panning-finished", + G_TYPE_FROM_CLASS (object_class), + 0, + 0, + NULL, NULL, + _hildon_marshal_VOID__VOID, + G_TYPE_NONE, 0); + } static void @@ -630,7 +690,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; @@ -647,7 +707,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; @@ -655,10 +716,11 @@ hildon_pannable_area_init (HildonPannableArea * area) priv->child_width = 0; priv->child_height = 0; priv->last_in = TRUE; + priv->x_offset = 0; + priv->y_offset = 0; - hildon_pannable_calculate_vel_factor (area); - - gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK); + gtk_style_lookup_color (GTK_WIDGET (area)->style, + "SecondaryTextColor", &priv->scroll_color); priv->hadjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); @@ -815,9 +877,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); @@ -826,7 +887,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 */ @@ -885,6 +959,7 @@ hildon_pannable_area_dispose (GObject * object) GtkWidget *child = gtk_bin_get_child (GTK_BIN (object)); if (priv->idle_id) { + g_signal_emit (object, pannable_area_signals[PANNING_FINISHED], 0); g_source_remove (priv->idle_id); priv->idle_id = 0; } @@ -975,6 +1050,7 @@ hildon_pannable_area_realize (GtkWidget * widget) | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK + | GDK_POINTER_MOTION_HINT_MASK | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK; attributes.wclass = GDK_INPUT_ONLY; @@ -1099,6 +1175,7 @@ hildon_pannable_area_size_allocate (GtkWidget * widget, HildonPannableAreaPrivate *priv; GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget)); gint border_width; + gdouble hv, vv; border_width = GTK_CONTAINER (widget)->border_width; @@ -1135,18 +1212,25 @@ hildon_pannable_area_size_allocate (GtkWidget * widget, gtk_widget_size_allocate (child, &child_allocation); } + hv = priv->hadjust->value; + vv = priv->vadjust->value; + /* we have to do this after child size_allocate because page_size is * changed when we allocate the size of the children */ if (priv->overshot_dist_y < 0) { - gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper - - priv->vadjust->page_size); + priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size; } if (priv->overshot_dist_x < 0) { - gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper - - priv->hadjust->page_size); + priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size; } + if (hv != priv->hadjust->value) + gtk_adjustment_value_changed (priv->hadjust); + + if (vv != priv->vadjust->value) + gtk_adjustment_value_changed (priv->vadjust); + } else { hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget)); } @@ -1161,6 +1245,7 @@ hildon_pannable_area_style_set (GtkWidget * widget, GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)-> style_set (widget, previous_style); + gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color); gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL); } @@ -1381,7 +1466,7 @@ hildon_pannable_draw_vscroll (GtkWidget *widget, HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv; gfloat y, height; GdkColor transp_color; - GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE]; + GdkGC *gc = priv->scrollbars_gc; gdk_draw_rectangle (widget->window, widget->style->bg_gc[GTK_STATE_NORMAL], @@ -1405,14 +1490,13 @@ hildon_pannable_draw_vscroll (GtkWidget *widget, (priv->hscroll_visible ? priv->hscroll_rect.height : 0) - height); - if (priv->scroll_indicator_alpha < 1.0) { + if (priv->scroll_indicator_alpha == 1.0) { + transp_color = priv->scroll_color; + } else if (priv->scroll_indicator_alpha < 1.0) { tranparency_color (&transp_color, *back_color, *scroll_color, priv->scroll_indicator_alpha); - - gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color); - - gc = priv->scrollbars_gc; } + gdk_gc_set_rgb_fg_color (gc, &transp_color); gdk_draw_rectangle (widget->window, gc, TRUE, priv->vscroll_rect.x, y, @@ -1427,7 +1511,7 @@ hildon_pannable_draw_hscroll (GtkWidget *widget, HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv; gfloat x, width; GdkColor transp_color; - GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE]; + GdkGC *gc = priv->scrollbars_gc; gdk_draw_rectangle (widget->window, widget->style->bg_gc[GTK_STATE_INSENSITIVE], @@ -1452,14 +1536,13 @@ hildon_pannable_draw_hscroll (GtkWidget *widget, (priv->vscroll_visible ? priv->vscroll_rect.width : 0) - width); - if (priv->scroll_indicator_alpha < 1.0) { + if (priv->scroll_indicator_alpha == 1.0) { + transp_color = priv->scroll_color; + } else if (priv->scroll_indicator_alpha < 1.0) { tranparency_color (&transp_color, *back_color, *scroll_color, priv->scroll_indicator_alpha); - - gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color); - - gc = priv->scrollbars_gc; } + gdk_gc_set_rgb_fg_color (gc, &transp_color); gdk_draw_rectangle (widget->window, gc, TRUE, x, priv->hscroll_rect.y, width, @@ -1472,15 +1555,8 @@ 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) { priv->scroll_indicator_event_interrupt = 0; @@ -1518,9 +1594,17 @@ static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area, gpointer data) { - if (GTK_WIDGET_REALIZED (area)) { - HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv; + HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv; + gint xdiff, ydiff; + gint x = priv->x_offset; + gint y = priv->y_offset; + priv->x_offset = priv->hadjust->value; + xdiff = x - priv->x_offset; + priv->y_offset = priv->vadjust->value; + ydiff = y - priv->y_offset; + + if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) { hildon_pannable_area_redraw (area); if ((priv->vscroll_visible) || (priv->hscroll_visible)) { @@ -1557,8 +1641,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; } @@ -1614,9 +1698,15 @@ hildon_pannable_area_expose_event (GtkWidget * widget, GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED]; #else /* USE_CAIRO_SCROLLBARS */ GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL]; - GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE]; + GdkColor scroll_color = priv->scroll_color; #endif + if (G_UNLIKELY (priv->initial_effect)) { + hildon_pannable_area_initial_effect (widget); + + priv->initial_effect = FALSE; + } + if (gtk_bin_get_child (GTK_BIN (widget))) { if (priv->scroll_indicator_alpha > 0.1) { @@ -1705,13 +1795,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); } @@ -1811,7 +1894,8 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget, GdkEventButton * event) { gint x, y; - HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv; + HildonPannableArea *area = HILDON_PANNABLE_AREA (widget); + HildonPannableAreaPrivate *priv = area->priv; if ((!priv->enabled) || (event->button != 1) || ((event->time == priv->last_time) && @@ -1820,7 +1904,7 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget, priv->scroll_indicator_event_interrupt = 1; - hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), + hildon_pannable_area_launch_fade_timeout (area, priv->scroll_indicator_alpha); priv->last_time = event->time; @@ -1829,7 +1913,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); @@ -1849,11 +1933,16 @@ 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; priv->vel_y = 0; + if (priv->idle_id) { + g_source_remove (priv->idle_id); + priv->idle_id = 0; + g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0); + } if (priv->child) { @@ -1949,7 +2038,16 @@ hildon_pannable_area_refresh (HildonPannableArea * area) { if (GTK_WIDGET_DRAWABLE (area) && hildon_pannable_area_check_scrollbars (area)) { + HildonPannableAreaPrivate *priv = area->priv; + gtk_widget_queue_resize (GTK_WIDGET (area)); + + if ((priv->vscroll_visible) || (priv->hscroll_visible)) { + priv->scroll_indicator_event_interrupt = 0; + priv->scroll_delay_counter = area->priv->scrollbar_fade_delay; + + hildon_pannable_area_launch_fade_timeout (area, 1.0); + } } else { hildon_pannable_area_redraw (area); } @@ -2022,9 +2120,9 @@ hildon_pannable_axis_scroll (HildonPannableArea *area, } } - gtk_adjustment_set_value (adjust, dist); + adjust->value = 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 @@ -2080,7 +2178,10 @@ hildon_pannable_axis_scroll (HildonPannableArea *area, *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0); } else { *overshooting = 0; - gtk_adjustment_set_value (adjust, dist); + adjust->value = CLAMP (dist, + adjust->lower, + adjust->upper - + adjust->page_size); } if (*overshot_dist != overshot_dist_old) @@ -2096,6 +2197,7 @@ hildon_pannable_area_scroll (HildonPannableArea *area, gboolean sx, sy; HildonPannableAreaPrivate *priv = area->priv; gboolean hscroll_visible, vscroll_visible; + gdouble hv, vv; if (gtk_bin_get_child (GTK_BIN (area)) == NULL) return; @@ -2108,6 +2210,9 @@ hildon_pannable_area_scroll (HildonPannableArea *area, sx = TRUE; sy = TRUE; + hv = priv->hadjust->value; + vv = priv->vadjust->value; + if (vscroll_visible) { hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y, &priv->overshooting_y, &priv->overshot_dist_y, @@ -2124,6 +2229,12 @@ hildon_pannable_area_scroll (HildonPannableArea *area, priv->vel_x = 0; } + if (hv != priv->hadjust->value) + gtk_adjustment_value_changed (priv->hadjust); + + if (vv != priv->vadjust->value) + gtk_adjustment_value_changed (priv->vadjust); + /* If the scroll on a particular axis wasn't succesful, reset the * initial scroll position to the new mouse co-ordinate. This means * when you get to the top of the page, dragging down works immediately. @@ -2146,11 +2257,12 @@ hildon_pannable_area_timeout (HildonPannableArea * area) if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) { priv->idle_id = 0; + g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0); return FALSE; } - if (!priv->clicked) { + if (!priv->button_pressed) { /* Decelerate gradually when pointer is raised */ if ((!priv->overshot_dist_y) && (!priv->overshot_dist_x)) { @@ -2182,6 +2294,8 @@ hildon_pannable_area_timeout (HildonPannableArea * area) priv->vel_y = 0; priv->idle_id = 0; + g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0); + return FALSE; } } @@ -2255,37 +2369,20 @@ hildon_pannable_area_motion_event_scroll (HildonPannableArea *area, } } -static gboolean -hildon_pannable_area_motion_notify_cb (GtkWidget * widget, - GdkEventMotion * event) +static void +hildon_pannable_area_check_move (HildonPannableArea *area, + GdkEventMotion * event, + gdouble *x, + gdouble *y) { - HildonPannableArea *area = HILDON_PANNABLE_AREA (widget); HildonPannableAreaPrivate *priv = area->priv; - gdouble x, y; - gdouble delta; - - if (gtk_bin_get_child (GTK_BIN (widget)) == NULL) - return TRUE; - - if ((!priv->enabled) || (!priv->clicked) || - ((event->time == priv->last_time) && (priv->last_type == 2))) { - gdk_window_get_pointer (widget->window, NULL, NULL, 0); - return TRUE; - } - - if (priv->last_type == 1) { - priv->first_drag = TRUE; - } - - x = event->x - priv->x; - y = event->y - priv->y; if (priv->first_drag && (!priv->moved) && - ((ABS (x) > (priv->panning_threshold)) - || (ABS (y) > (priv->panning_threshold)))) { + ((ABS (*x) > (priv->panning_threshold)) + || (ABS (*y) > (priv->panning_threshold)))) { priv->moved = TRUE; - x = 0; - y = 0; + *x = 0; + *y = 0; if (priv->first_drag) { gboolean vscroll_visible; @@ -2355,6 +2452,16 @@ hildon_pannable_area_motion_notify_cb (GtkWidget * widget, synth_crossing (priv->child, pos_x, pos_y, event->x_root, event->y_root, event->time, FALSE); } + + if (priv->moved) { + gboolean result_val; + + g_signal_emit (area, + pannable_area_signals[PANNING_STARTED], + 0, &result_val); + + priv->moved = !result_val; + } } priv->first_drag = FALSE; @@ -2369,76 +2476,116 @@ hildon_pannable_area_motion_notify_cb (GtkWidget * widget, hildon_pannable_area_timeout, area); } } +} - if (priv->moved) { - switch (priv->mode) { - case HILDON_PANNABLE_AREA_MODE_PUSH: - /* Scroll by the amount of pixels the cursor has moved - * since the last motion event. - */ - hildon_pannable_area_motion_event_scroll (area, x, y); +static void +hildon_pannable_area_handle_move (HildonPannableArea *area, + GdkEventMotion * event, + gdouble *x, + gdouble *y) +{ + HildonPannableAreaPrivate *priv = area->priv; + gdouble delta; + + switch (priv->mode) { + case HILDON_PANNABLE_AREA_MODE_PUSH: + /* Scroll by the amount of pixels the cursor has moved + * since the last motion event. + */ + hildon_pannable_area_motion_event_scroll (area, *x, *y); + priv->x = event->x; + priv->y = event->y; + break; + case HILDON_PANNABLE_AREA_MODE_ACCEL: + /* Set acceleration relative to the initial click */ + priv->ex = event->x; + priv->ey = event->y; + priv->vel_x = ((*x > 0) ? 1 : -1) * + (((ABS (*x) / + (gdouble) GTK_WIDGET (area)->allocation.width) * + (priv->vmax - priv->vmin)) + priv->vmin); + priv->vel_y = ((*y > 0) ? 1 : -1) * + (((ABS (*y) / + (gdouble) GTK_WIDGET (area)->allocation.height) * + (priv->vmax - priv->vmin)) + priv->vmin); + break; + case HILDON_PANNABLE_AREA_MODE_AUTO: + + delta = event->time - priv->last_time; + + if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) { + gdouble dist = event->y - priv->y; + + hildon_pannable_area_calculate_velocity (&priv->vel_y, + delta, + dist, + priv->vmax, + priv->drag_inertia, + priv->force, + priv->sps); + } else { + *y = 0; + priv->vel_y = 0; + } + + if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) { + gdouble dist = event->x - priv->x; + + hildon_pannable_area_calculate_velocity (&priv->vel_x, + delta, + dist, + priv->vmax, + priv->drag_inertia, + priv->force, + priv->sps); + } else { + *x = 0; + priv->vel_x = 0; + } + + hildon_pannable_area_motion_event_scroll (area, *x, *y); + + if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) priv->x = event->x; + if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) priv->y = event->y; - break; - case HILDON_PANNABLE_AREA_MODE_ACCEL: - /* Set acceleration relative to the initial click */ - priv->ex = event->x; - priv->ey = event->y; - priv->vel_x = ((x > 0) ? 1 : -1) * - (((ABS (x) / - (gdouble) widget->allocation.width) * - (priv->vmax - priv->vmin)) + priv->vmin); - priv->vel_y = ((y > 0) ? 1 : -1) * - (((ABS (y) / - (gdouble) widget->allocation.height) * - (priv->vmax - priv->vmin)) + priv->vmin); - break; - case HILDON_PANNABLE_AREA_MODE_AUTO: - delta = event->time - priv->last_time; - - if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) { - gdouble dist = event->y - priv->y; + break; + default: + break; + } +} - hildon_pannable_area_calculate_velocity (&priv->vel_y, - delta, - dist, - priv->vmax, - priv->drag_inertia, - priv->force, - priv->sps); - } else { - y = 0; - priv->vel_y = 0; - } +static gboolean +hildon_pannable_area_motion_notify_cb (GtkWidget * widget, + GdkEventMotion * event) +{ + HildonPannableArea *area = HILDON_PANNABLE_AREA (widget); + HildonPannableAreaPrivate *priv = area->priv; + gdouble x, y; - if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) { - gdouble dist = event->x - priv->x; + if (gtk_bin_get_child (GTK_BIN (widget)) == NULL) + return TRUE; - hildon_pannable_area_calculate_velocity (&priv->vel_x, - delta, - dist, - priv->vmax, - priv->drag_inertia, - priv->force, - priv->sps); - } else { - x = 0; - priv->vel_x = 0; - } + 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; + } - hildon_pannable_area_motion_event_scroll (area, x, y); + if (priv->last_type == 1) { + priv->first_drag = TRUE; + } - if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) - priv->x = event->x; - if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) - priv->y = event->y; + x = event->x - priv->x; + y = event->y - priv->y; - break; + if (!priv->moved) { + hildon_pannable_area_check_move (area, event, &x, &y); + } - default: - break; - } + if (priv->moved) { + hildon_pannable_area_handle_move (area, event, &x, &y); } else if (priv->child) { gboolean in; gint pos_x, pos_y; @@ -2497,105 +2644,95 @@ static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget, GdkEventButton * event) { - HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv; + HildonPannableArea *area = HILDON_PANNABLE_AREA (widget); + HildonPannableAreaPrivate *priv = area->priv; gint x, y; + gdouble dx, dy; GdkWindow *child; 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; - priv->scroll_delay_counter = priv->scrollbar_fade_delay; + /* if last event was a motion-notify we have to check the movement + and launch the animation */ + if (priv->last_type == 2) { - /* move all the way to the last position */ - if (priv->motion_event_scroll_timeout) { - g_source_remove (priv->motion_event_scroll_timeout); - hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget)); - priv->motion_x = 0; - priv->motion_y = 0; - } + dx = event->x - priv->x; + dy = event->y - priv->y; - if (priv->last_type == 2) { - gdouble delta = event->time - priv->last_time; + hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy); - if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) { - gdouble dist = event->y - priv->y; + if (priv->moved) { + gdouble delta = event->time - priv->last_time; - if (ABS (dist) >= 1.0) { - hildon_pannable_area_calculate_velocity (&priv->vel_y, - delta, - dist, - priv->vmax, - priv->drag_inertia, - priv->force, - priv->sps); + hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy); - priv->motion_y = dist; + /* move all the way to the last position now */ + if (priv->motion_event_scroll_timeout) { + g_source_remove (priv->motion_event_scroll_timeout); hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget)); - } else - if (delta >= CURSOR_STOPPED_TIMEOUT) { - y = 0; - priv->vel_y = 0; - } - } + priv->motion_x = 0; + priv->motion_y = 0; + } - if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) { - gdouble dist = event->x - priv->x; + if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT)) + priv->vel_x = 0; - if (ABS (dist) >= 1.0) { - hildon_pannable_area_calculate_velocity (&priv->vel_x, - delta, - dist, - priv->vmax, - priv->drag_inertia, - priv->force, - priv->sps); - priv->motion_x = dist; - hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget)); - } else - if (delta >= CURSOR_STOPPED_TIMEOUT) { - x = 0; - priv->vel_x = 0; - } + if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT)) + priv->vel_y = 0; } } - if ((ABS (priv->vel_y) > 1.0)|| - (ABS (priv->vel_x) > 1.0)) { - priv->scroll_indicator_alpha = 1.0; + /* If overshoot has been initiated with a finger down, on release set max speed */ + if (priv->overshot_dist_y != 0) { + priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */ + priv->vel_y = priv->vmax_overshooting; } - hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), - priv->scroll_indicator_alpha); + if (priv->overshot_dist_x != 0) { + priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */ + priv->vel_x = priv->vmax_overshooting; + } - priv->clicked = FALSE; + priv->button_pressed = FALSE; - if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO || - priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) { + if ((ABS (priv->vel_y) >= priv->vmin) || + (ABS (priv->vel_x) >= priv->vmin)) { - /* If overshoot has been initiated with a finger down, on release set max speed */ - if (priv->overshot_dist_y != 0) { - priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */ - priv->vel_y = priv->vmax_overshooting; - } + /* we have to move because we are in overshooting position*/ + if (!priv->moved) { + gboolean result_val; - if (priv->overshot_dist_x != 0) { - priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */ - priv->vel_x = priv->vmax_overshooting; + g_signal_emit (area, + pannable_area_signals[PANNING_STARTED], + 0, &result_val); } - if ((ABS (priv->vel_y) >= priv->vmin) || - (ABS (priv->vel_x) >= priv->vmin)) { + priv->scroll_indicator_alpha = 1.0; - if (!priv->idle_id) - priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps), - (GSourceFunc) - hildon_pannable_area_timeout, widget); - } + 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) + hildon_pannable_area_timeout, widget); + } else { + if (priv->moved) + g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0); } + priv->scroll_indicator_event_interrupt = 0; + priv->scroll_delay_counter = priv->scrollbar_fade_delay; + + hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), + priv->scroll_indicator_alpha); + priv->last_time = event->time; priv->last_type = 3; @@ -2619,6 +2756,10 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget, /* Send synthetic leave event */ synth_crossing (priv->child, x, y, event->x_root, event->y_root, event->time, FALSE); + /* insure no click will happen for widgets that do not handle + leave-notify */ + event->x = -16384; + event->y = -16384; /* Send synthetic button release event */ ((GdkEventAny *) event)->window = g_object_ref (priv->child); gdk_event_put ((GdkEvent *) event); @@ -2670,6 +2811,8 @@ hildon_pannable_area_scroll_cb (GtkWidget *widget, gtk_widget_queue_resize (GTK_WIDGET (widget)); } + g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0); + g_source_remove (priv->idle_id); priv->idle_id = 0; } @@ -2746,17 +2889,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; } @@ -2988,6 +3157,7 @@ hildon_pannable_area_jump_to (HildonPannableArea *area, { HildonPannableAreaPrivate *priv; gint width, height; + gdouble hv, vv; g_return_if_fail (HILDON_IS_PANNABLE_AREA (area)); g_return_if_fail (GTK_WIDGET_REALIZED (area)); @@ -3004,26 +3174,33 @@ hildon_pannable_area_jump_to (HildonPannableArea *area, g_return_if_fail (x < width || y < height); + hv = priv->hadjust->value; + vv = priv->vadjust->value; + if (x != -1) { gdouble jump_to = x - priv->hadjust->page_size/2; - if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) { - jump_to = priv->hadjust->upper - priv->hadjust->page_size; - } - - gtk_adjustment_set_value (priv->hadjust, jump_to); + priv->hadjust->value = CLAMP (jump_to, + priv->hadjust->lower, + priv->hadjust->upper - + priv->hadjust->page_size); } if (y != -1) { gdouble jump_to = y - priv->vadjust->page_size/2; - if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) { - jump_to = priv->vadjust->upper - priv->vadjust->page_size; - } - - gtk_adjustment_set_value (priv->vadjust, jump_to); + priv->vadjust->value = CLAMP (jump_to, + priv->vadjust->lower, + priv->vadjust->upper - + priv->vadjust->page_size); } + if (hv != priv->hadjust->value) + gtk_adjustment_value_changed (priv->hadjust); + + if (vv != priv->vadjust->value) + gtk_adjustment_value_changed (priv->vadjust); + priv->scroll_indicator_alpha = 1.0; if (priv->scroll_indicator_timeout) { @@ -3044,6 +3221,7 @@ hildon_pannable_area_jump_to (HildonPannableArea *area, gtk_widget_queue_resize (GTK_WIDGET (area)); } + g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0); g_source_remove (priv->idle_id); priv->idle_id = 0; }