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
};
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;
"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));
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
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);
}
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);
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;
}
| 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;
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,
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)) {
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;
/* 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) {
(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;
*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);
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;
}
priv->vel_y = 0;
priv->idle_id = 0;
+ g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
+
return FALSE;
}
}
hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
+ gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
+
return TRUE;
}
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;
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);
/* 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 ((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->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
(GSourceFunc)
hildon_pannable_area_timeout, widget);
+ } 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;
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_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;
+}