#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 280
#define PANNABLE_MAX_WIDTH 788
#define PANNABLE_MAX_HEIGHT 378
+#define ACCEL_FACTOR 27
+#define MIN_ACCEL_THRESHOLD 40
+#define FAST_CLICK 125
G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
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 */
+ guint32 last_press_time;
gint last_type;
gboolean last_in;
gboolean moved;
gdouble vmin;
gdouble vmax;
gdouble vmax_overshooting;
+ gdouble accel_vel_x;
+ gdouble accel_vel_y;
gdouble vfast_factor;
gdouble decel;
gdouble drag_inertia;
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;
GtkAdjustment *hadjust;
GtkAdjustment *vadjust;
+ gint x_offset;
+ gint y_offset;
GtkPolicyType vscrollbar_policy;
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
};
PROP_SIZE_REQUEST_POLICY,
PROP_HADJUSTMENT,
PROP_VADJUSTMENT,
+ PROP_CENTER_ON_CHILD_FOCUS,
PROP_LAST
};
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);
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,
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
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;
"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));
"Maximum scroll velocity",
"Maximum distance the child widget should scroll "
"per 'frame', in pixels per frame.",
- 0, G_MAXDOUBLE, 500,
+ 0, G_MAXDOUBLE, 3500,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
"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));
"Minimum velocity that is considered 'fast': "
"children widgets won't receive button presses. "
"Expressed as a fraction of the maximum velocity.",
- 0, 1, 0.02,
+ 0, 1, 0.01,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
"Deceleration multiplier",
"The multiplier used when decelerating when in "
"acceleration scrolling mode.",
- 0, 1.0, 0.93,
+ 0, 1.0, 0.85,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
"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));
"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));
"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));
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",
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
area->priv = priv;
priv->moved = FALSE;
- priv->clicked = 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;
priv->overshot_dist_y = 0;
priv->overshooting_y = 0;
priv->overshooting_x = 0;
+ priv->accel_vel_x = 0;
+ priv->accel_vel_y = 0;
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;
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;
priv->child_width = 0;
priv->child_height = 0;
priv->last_in = TRUE;
+ priv->x_offset = 0;
+ priv->y_offset = 0;
+ priv->center_on_child_focus_pending = FALSE;
- 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));
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);
}
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 */
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);
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,
| 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;
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)
{
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);
HildonPannableAreaPrivate *priv;
GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
gint border_width;
+ gdouble hv, vv;
border_width = GTK_CONTAINER (widget)->border_width;
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);
gtk_widget_size_allocate (child, &child_allocation);
}
+ if (priv->vadjust->page_size >= 0) {
+ priv->accel_vel_y = MIN (priv->vmax,
+ priv->vadjust->upper/priv->vadjust->page_size*ACCEL_FACTOR);
+ priv->accel_vel_x = MIN (priv->vmax,
+ priv->hadjust->upper/priv->hadjust->page_size*ACCEL_FACTOR);
+ }
+
+ 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));
}
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);
}
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],
(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,
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],
(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,
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;
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
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)) {
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;
}
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) {
}
- 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);
}
tx, ty, mask);
if (!selected_window) {
if (tx)
- *tx = x;
+ *tx = x-wx;
if (ty)
- *ty = y;
+ *ty = y-wy;
selected_window = child;
}
} else {
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) &&
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;
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);
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->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) {
(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;
{
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);
}
gtk_widget_queue_resize (GTK_WIDGET (area));
} else {
*vel = 0.0;
+ *scroll_to = -1;
}
} else if (dist > adjust->upper - adjust->page_size) {
if (s) *s = FALSE;
gtk_widget_queue_resize (GTK_WIDGET (area));
} else {
*vel = 0.0;
+ *scroll_to = -1;
}
} else {
if ((*scroll_to) != -1) {
}
}
- 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
*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);
*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);
*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)
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;
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,
&priv->scroll_to_y, priv->vovershoot_max, &sy);
} else {
- priv->vel_y = 0;
+ priv->vel_y = 0.0;
+ priv->scroll_to_y = -1;
}
if (hscroll_visible) {
&priv->overshooting_x, &priv->overshot_dist_x,
&priv->scroll_to_x, priv->hovershoot_max, &sx);
} else {
- priv->vel_x = 0;
+ priv->vel_x = 0.0;
+ priv->scroll_to_x = -1;
}
+ 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.
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) {
+ hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
+
+ gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
+
+ if (!priv->button_pressed) {
/* Decelerate gradually when pointer is raised */
if ((!priv->overshot_dist_y) &&
(!priv->overshot_dist_x)) {
priv->vel_y = 0;
priv->idle_id = 0;
+ g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
+
return FALSE;
}
}
return FALSE;
}
- hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
-
return TRUE;
}
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);
}
}
-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;
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;
(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);
}
}
+}
- 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;
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);
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;
+ gboolean force_fast = TRUE;
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->overshot_dist_y * 0.9;
}
- 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->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 < FAST_CLICK) &&
+ ((ABS (priv->old_vel_x) > priv->vmin) ||
+ (ABS (priv->old_vel_y) > priv->vmin)) &&
+ ((ABS (priv->old_vel_x) > MIN_ACCEL_THRESHOLD) ||
+ (ABS (priv->old_vel_y) > MIN_ACCEL_THRESHOLD)))
+ {
+ gint symbol = 0;
- priv->clicked = FALSE;
+ if (priv->vel_x != 0)
+ symbol = ((priv->vel_x * priv->old_vel_x) > 0) ? 1 : -1;
- if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
- priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
+ priv->vel_x = symbol *
+ (priv->old_vel_x + ((priv->old_vel_x > 0) ? priv->accel_vel_x
+ : -priv->accel_vel_x));
- /* 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;
+ 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->accel_vel_y
+ : -priv->accel_vel_y));
+
+ force_fast = FALSE;
}
- 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;
+ 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);
}
- 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 (force_fast) {
+ if ((ABS (priv->vel_x) > MAX_SPEED_THRESHOLD) &&
+ (priv->accel_vel_x > MAX_SPEED_THRESHOLD))
+ priv->vel_x = (priv->vel_x > 0) ? priv->accel_vel_x : -priv->accel_vel_x;
+
+ if ((ABS (priv->vel_y) > MAX_SPEED_THRESHOLD) &&
+ (priv->accel_vel_y > MAX_SPEED_THRESHOLD))
+ priv->vel_y = (priv->vel_y > 0) ? priv->accel_vel_y : -priv->accel_vel_y;
+ }
+
+ if (!priv->idle_id)
+ 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;
+
+ hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
+ priv->scroll_indicator_alpha);
+
priv->last_time = event->time;
priv->last_type = 3;
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;
/* 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);
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;
}
}
}
+/* 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)
{
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)
{
fct += fct_i;
}
- priv->vel_factor = fct;
+ priv->vel_factor = fct;
}
/**
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;
}
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;
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);
}
/**
{
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));
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) {
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;
}
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;
+}