X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=hildon%2Fhildon-pannable-area.c;h=8cd7fcd0c64bc65ab83114db63209995de4609c8;hb=ad616175cfa334604939586aa9ecb2552078766d;hp=558e148850ff9bd6f767fea23fd46c5114c88952;hpb=894aeb233b68ab2c49c23a9956f64934500c0192;p=hildon diff --git a/hildon/hildon-pannable-area.c b/hildon/hildon-pannable-area.c index 558e148..8cd7fcd 100644 --- a/hildon/hildon-pannable-area.c +++ b/hildon/hildon-pannable-area.c @@ -56,7 +56,7 @@ #define RATIO_TOLERANCE 0.000001 #define SCROLL_FADE_TIMEOUT 100 #define MOTION_EVENTS_PER_SECOND 25 -#define CURSOR_STOPPED_TIMEOUT 80 +#define CURSOR_STOPPED_TIMEOUT 200 #define MAX_SPEED_THRESHOLD 250 #define PANNABLE_MAX_WIDTH 788 #define PANNABLE_MAX_HEIGHT 378 @@ -78,6 +78,7 @@ struct _HildonPannableAreaPrivate { gboolean enabled; gboolean button_pressed; guint32 last_time; /* Last event time, to stop infinite loops */ + guint32 last_press_time; gint last_type; gboolean last_in; gboolean moved; @@ -97,6 +98,8 @@ struct _HildonPannableAreaPrivate { guint direction_error_margin; gdouble vel_x; gdouble vel_y; + gdouble old_vel_x; + gdouble old_vel_y; GdkWindow *child; gint child_width; gint child_height; @@ -141,12 +144,18 @@ struct _HildonPannableAreaPrivate { GtkPolicyType hscrollbar_policy; GdkGC *scrollbars_gc; + GdkColor scroll_color; + + gboolean center_on_child_focus; + gboolean center_on_child_focus_pending; }; /*signals*/ enum { HORIZONTAL_MOVEMENT, VERTICAL_MOVEMENT, + PANNING_STARTED, + PANNING_FINISHED, LAST_SIGNAL }; @@ -178,6 +187,7 @@ enum { PROP_SIZE_REQUEST_POLICY, PROP_HADJUSTMENT, PROP_VADJUSTMENT, + PROP_CENTER_ON_CHILD_FOCUS, PROP_LAST }; @@ -191,6 +201,7 @@ static void hildon_pannable_area_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec); +static void hildon_pannable_area_remove_timeouts (GtkWidget * widget); static void hildon_pannable_area_dispose (GObject * object); static void hildon_pannable_area_realize (GtkWidget * widget); static void hildon_pannable_area_unrealize (GtkWidget * widget); @@ -289,6 +300,9 @@ static void hildon_pannable_area_child_mapped (GtkWidget *widget, static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child); static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child); static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self); +static void hildon_pannable_area_set_focus_child (GtkContainer *container, + GtkWidget *child); +static void hildon_pannable_area_center_on_child_focus (HildonPannableArea *area); static void @@ -321,6 +335,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass) container_class->add = hildon_pannable_area_add; container_class->remove = hildon_pannable_area_remove; + container_class->set_focus_child = hildon_pannable_area_set_focus_child; klass->horizontal_movement = NULL; klass->vertical_movement = NULL; @@ -400,7 +415,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass) "Maximum scroll velocity when overshooting", "Maximum distance the child widget should scroll " "per 'frame', in pixels per frame when it overshoots after hitting the edge.", - 0, G_MAXDOUBLE, 20, + 0, G_MAXDOUBLE, 130, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); @@ -461,7 +476,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass) "Time before starting to fade the scrollbar", "Time the scrollbar is going to be visible if the widget is not in" "action in miliseconds", - 0, G_MAXUINT, 3000, + 0, G_MAXUINT, 1000, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); @@ -569,6 +584,16 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass) GTK_TYPE_ADJUSTMENT, G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_CENTER_ON_CHILD_FOCUS, + g_param_spec_boolean ("center-on-child-focus", + "Center on the child with the focus", + "Whether to center the pannable on the child that receives the focus.", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + gtk_widget_class_install_style_property (widget_class, g_param_spec_uint ("indicator-width", @@ -629,6 +654,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 @@ -643,6 +714,7 @@ hildon_pannable_area_init (HildonPannableArea * area) priv->moved = FALSE; priv->button_pressed = FALSE; priv->last_time = 0; + priv->last_press_time = 0; priv->last_type = 0; priv->vscroll_visible = TRUE; priv->hscroll_visible = TRUE; @@ -654,6 +726,8 @@ hildon_pannable_area_init (HildonPannableArea * area) priv->idle_id = 0; priv->vel_x = 0; priv->vel_y = 0; + priv->old_vel_x = 0; + priv->old_vel_y = 0; priv->scroll_indicator_alpha = 0.0; priv->scroll_indicator_timeout = 0; priv->motion_event_scroll_timeout = 0; @@ -669,6 +743,10 @@ hildon_pannable_area_init (HildonPannableArea * area) priv->last_in = TRUE; priv->x_offset = 0; priv->y_offset = 0; + priv->center_on_child_focus_pending = FALSE; + + 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)); @@ -779,6 +857,9 @@ hildon_pannable_area_get_property (GObject * object, hildon_pannable_area_get_vadjustment (HILDON_PANNABLE_AREA (object))); break; + case PROP_CENTER_ON_CHILD_FOCUS: + g_value_set_boolean (value, priv->center_on_child_focus); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -894,6 +975,9 @@ hildon_pannable_area_set_property (GObject * object, hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object), g_value_get_enum (value)); break; + case PROP_CENTER_ON_CHILD_FOCUS: + priv->center_on_child_focus = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -906,20 +990,7 @@ hildon_pannable_area_dispose (GObject * object) HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv; GtkWidget *child = gtk_bin_get_child (GTK_BIN (object)); - if (priv->idle_id) { - g_source_remove (priv->idle_id); - priv->idle_id = 0; - } - - if (priv->scroll_indicator_timeout){ - g_source_remove (priv->scroll_indicator_timeout); - priv->scroll_indicator_timeout = 0; - } - - if (priv->motion_event_scroll_timeout){ - g_source_remove (priv->motion_event_scroll_timeout); - priv->motion_event_scroll_timeout = 0; - } + hildon_pannable_area_remove_timeouts (GTK_WIDGET (object)); if (child) { g_signal_handlers_disconnect_by_func (child, @@ -1014,6 +1085,29 @@ hildon_pannable_area_realize (GtkWidget * widget) gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]); } + +static void +hildon_pannable_area_remove_timeouts (GtkWidget * widget) +{ + HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv; + + if (priv->idle_id) { + g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0); + g_source_remove (priv->idle_id); + priv->idle_id = 0; + } + + if (priv->scroll_indicator_timeout){ + g_source_remove (priv->scroll_indicator_timeout); + priv->scroll_indicator_timeout = 0; + } + + if (priv->motion_event_scroll_timeout){ + g_source_remove (priv->motion_event_scroll_timeout); + priv->motion_event_scroll_timeout = 0; + } +} + static void hildon_pannable_area_unrealize (GtkWidget * widget) { @@ -1021,6 +1115,8 @@ hildon_pannable_area_unrealize (GtkWidget * widget) priv = HILDON_PANNABLE_AREA (widget)->priv; + hildon_pannable_area_remove_timeouts (widget); + if (priv->event_window != NULL) { gdk_window_set_user_data (priv->event_window, NULL); gdk_window_destroy (priv->event_window); @@ -1145,6 +1241,8 @@ hildon_pannable_area_size_allocate (GtkWidget * widget, if (child && GTK_WIDGET_VISIBLE (child)) { + hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget)); + hildon_pannable_area_child_allocate_calculate (widget, allocation, &child_allocation); @@ -1192,6 +1290,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); } @@ -1412,7 +1511,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], @@ -1436,14 +1535,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, @@ -1458,7 +1556,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], @@ -1483,14 +1581,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, @@ -1525,9 +1622,11 @@ hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area, if (!priv->scroll_indicator_timeout) priv->scroll_indicator_timeout = - gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT, - (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, - area); + gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20, + SCROLL_FADE_TIMEOUT, + (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, + area, + NULL); } static void @@ -1646,7 +1745,7 @@ 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)) { @@ -1789,9 +1888,9 @@ hildon_pannable_area_get_topmost (GdkWindow * window, tx, ty, mask); if (!selected_window) { if (tx) - *tx = x; + *tx = x-wx; if (ty) - *ty = y; + *ty = y-wy; selected_window = child; } } else { @@ -1842,7 +1941,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) && @@ -1851,10 +1951,11 @@ 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; + priv->last_press_time = event->time; priv->last_type = 1; priv->scroll_to_x = -1; @@ -1883,8 +1984,15 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget, priv->button_pressed = TRUE; /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */ + priv->old_vel_x = priv->vel_x; + priv->old_vel_y = priv->vel_y; 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) { @@ -1896,6 +2004,8 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget, (gpointer) & priv->child); event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event); + /* remove the reference we added with the copy */ + g_object_unref (priv->event_window); event->x = x; event->y = y; priv->cx = x; @@ -2082,7 +2192,7 @@ hildon_pannable_axis_scroll (HildonPannableArea *area, *vel *= -1; } else if ((*overshooting > 1) && (*vel < 0)) { /* we add the MIN in order to avoid very small speeds */ - *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0); + *vel = MIN (((((gdouble)*overshot_dist)*0.8) * -1), -10.0); } *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max); @@ -2098,7 +2208,7 @@ hildon_pannable_axis_scroll (HildonPannableArea *area, *vel *= -1; } else if ((*overshooting > 1) && (*vel > 0)) { /* we add the MAX in order to avoid very small speeds */ - *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0); + *vel = MAX (((((gdouble)*overshot_dist)*0.8) * -1), 10.0); } *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0); @@ -2199,6 +2309,7 @@ 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; } @@ -2235,6 +2346,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; } } @@ -2247,6 +2360,8 @@ hildon_pannable_area_timeout (HildonPannableArea * area) hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y); + gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE); + return TRUE; } @@ -2302,9 +2417,10 @@ hildon_pannable_area_motion_event_scroll (HildonPannableArea *area, priv->motion_x = 0; priv->motion_y = 0; - priv->motion_event_scroll_timeout = gdk_threads_add_timeout - ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND), - (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area); + priv->motion_event_scroll_timeout = gdk_threads_add_timeout_full + (G_PRIORITY_HIGH_IDLE + 20, + (gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND), + (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area, NULL); } } @@ -2391,6 +2507,16 @@ hildon_pannable_area_check_move (HildonPannableArea *area, 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; @@ -2399,10 +2525,11 @@ hildon_pannable_area_check_move (HildonPannableArea *area, (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) { if (!priv->idle_id) - priv->idle_id = gdk_threads_add_timeout ((gint) - (1000.0 / (gdouble) priv->sps), - (GSourceFunc) - hildon_pannable_area_timeout, area); + priv->idle_id = gdk_threads_add_timeout_full + (G_PRIORITY_HIGH_IDLE + 20, + (gint)(1000.0 / (gdouble) priv->sps), + (GSourceFunc) + hildon_pannable_area_timeout, area, NULL); } } } @@ -2540,6 +2667,8 @@ hildon_pannable_area_motion_notify_cb (GtkWidget * widget, if (priv->child) { /* Send motion notify to child */ event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event); + /* remove the reference we added with the copy */ + g_object_unref (priv->event_window); event->x = priv->cx + (event->x - priv->ix); event->y = priv->cy + (event->y - priv->iy); event->window = g_object_ref (priv->child); @@ -2578,6 +2707,7 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget, gint x, y; gdouble dx, dy; GdkWindow *child; + gboolean force_fast = TRUE; if (((event->time == priv->last_time) && (priv->last_type == 3)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL) @@ -2617,33 +2747,78 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget, /* 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; + priv->vel_y = priv->overshot_dist_y * 0.9; } 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->vel_x = priv->overshot_dist_x * 0.9; } priv->button_pressed = FALSE; + /* if widget was moving fast in the panning, increase speed even more */ + if ((event->time - priv->last_press_time < 200) && + ((ABS (priv->old_vel_x) > priv->vmin) || + (ABS (priv->old_vel_y) > priv->vmin))) + { + gint symbol = 0; + + if (priv->vel_x != 0) + symbol = ((priv->vel_x * priv->old_vel_x) > 0) ? 1 : -1; + + priv->vel_x = symbol * + (priv->old_vel_x + ((priv->old_vel_x > 0) ? priv->vmax : -priv->vmax)); + + symbol = 0; + + if (priv->vel_y != 0) + symbol = ((priv->vel_y * priv->old_vel_y) > 0) ? 1 : -1; + + priv->vel_y = symbol * + (priv->old_vel_y + ((priv->old_vel_y > 0) ? priv->vmax : -priv->vmax)); + + force_fast = FALSE; + } + if ((ABS (priv->vel_y) >= priv->vmin) || (ABS (priv->vel_x) >= priv->vmin)) { + /* we have to move because we are in overshooting position*/ + if (!priv->moved) { + gboolean result_val; + + g_signal_emit (area, + pannable_area_signals[PANNING_STARTED], + 0, &result_val); + } + priv->scroll_indicator_alpha = 1.0; - if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD) - priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax; + if (force_fast) { + 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 (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); + priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20, + (gint) (1000.0 / (gdouble) priv->sps), + (GSourceFunc) hildon_pannable_area_timeout, + widget, NULL); + } else { + if (priv->center_on_child_focus_pending) { + hildon_pannable_area_center_on_child_focus (area); + } + + if (priv->moved) + g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0); } + area->priv->center_on_child_focus_pending = FALSE; + priv->scroll_indicator_event_interrupt = 0; priv->scroll_delay_counter = priv->scrollbar_fade_delay; @@ -2663,6 +2838,8 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget, event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK); event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event); + /* remove the reference we added with the copy */ + g_object_unref (priv->event_window); event->x = x; event->y = y; @@ -2728,6 +2905,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; } @@ -2787,6 +2966,39 @@ hildon_pannable_area_add (GtkContainer *container, GtkWidget *child) } } +/* call this function if you are not panning */ +static void +hildon_pannable_area_center_on_child_focus (HildonPannableArea *area) +{ + GtkWidget *focused_child = NULL; + GtkWidget *window = NULL; + + window = gtk_widget_get_toplevel (GTK_WIDGET (area)); + + if (GTK_WIDGET_TOPLEVEL (window)) { + focused_child = gtk_window_get_focus (GTK_WINDOW (window)); + } + + if (focused_child) { + hildon_pannable_area_scroll_to_child (area, focused_child); + } +} + +static void +hildon_pannable_area_set_focus_child (GtkContainer *container, + GtkWidget *child) +{ + HildonPannableArea *area = HILDON_PANNABLE_AREA (container); + + if (!area->priv->center_on_child_focus) { + return; + } + + if (GTK_IS_WIDGET (child)) { + area->priv->center_on_child_focus_pending = TRUE; + } +} + static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child) { @@ -3012,7 +3224,9 @@ hildon_pannable_area_scroll_to (HildonPannableArea *area, g_return_if_fail (x < width || y < height); if ((x > -1)&&(hscroll_visible)) { - priv->scroll_to_x = x - priv->hadjust->page_size/2; + priv->scroll_to_x = CLAMP (x - priv->hadjust->page_size/2, + priv->hadjust->lower, + priv->hadjust->upper - priv->hadjust->page_size); dist_x = priv->scroll_to_x - priv->hadjust->value; if (dist_x == 0) { priv->scroll_to_x = -1; @@ -3024,7 +3238,9 @@ hildon_pannable_area_scroll_to (HildonPannableArea *area, } if ((y > -1)&&(vscroll_visible)) { - priv->scroll_to_y = y - priv->vadjust->page_size/2; + priv->scroll_to_y = CLAMP (y - priv->vadjust->page_size/2, + priv->vadjust->lower, + priv->vadjust->upper - priv->vadjust->page_size); dist_y = priv->scroll_to_y - priv->vadjust->value; if (dist_y == 0) { priv->scroll_to_y = -1; @@ -3035,16 +3251,17 @@ hildon_pannable_area_scroll_to (HildonPannableArea *area, priv->scroll_to_y = y; } - if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) { + if ((priv->scroll_to_y == -1) && (priv->scroll_to_x == -1)) { return; } hildon_pannable_area_launch_fade_timeout (area, 1.0); if (!priv->idle_id) - priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps), - (GSourceFunc) - hildon_pannable_area_timeout, area); + priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20, + (gint) (1000.0 / (gdouble) priv->sps), + (GSourceFunc) hildon_pannable_area_timeout, + area, NULL); } /** @@ -3136,6 +3353,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; } @@ -3372,3 +3590,44 @@ hildon_pannable_area_set_size_request_policy (HildonPannableArea *area, g_object_notify (G_OBJECT (area), "size-request-policy"); } +/** + * hildon_pannable_area_get_center_on_child_focus + * @area: A #HildonPannableArea + * + * Gets the @area #HildonPannableArea:center-on-child-focus property + * value. + * + * See #HildonPannableArea:center-on-child-focus for more information. + * + * Returns: the @area #HildonPannableArea:center-on-child-focus value + * + * Since: 2.2 + **/ +gboolean +hildon_pannable_area_get_center_on_child_focus (HildonPannableArea *area) +{ + g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE); + + return area->priv->center_on_child_focus; +} + +/** + * hildon_pannable_area_set_center_on_child_focus + * @area: A #HildonPannableArea + * @value: the new value + * + * Sets the @area #HildonPannableArea:center-on-child-focus property + * to @value. + * + * See #HildonPannableArea:center-on-child-focus for more information. + * + * Since: 2.2 + **/ +void +hildon_pannable_area_set_center_on_child_focus (HildonPannableArea *area, + gboolean value) +{ + g_return_if_fail (HILDON_IS_PANNABLE_AREA (area)); + + area->priv->center_on_child_focus = value; +}