Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b2be2f6f1 | |||
| b51662cafa | |||
| a328313885 | |||
| 8528385c80 | |||
| c7eba11400 | |||
| 2cb50c9af2 | |||
| bc5e401de4 | |||
| fb99b5eeb4 | |||
| d6e23b0924 | |||
| 42c0d95791 | |||
| 6d7ac9e87e | |||
| 607a1e8fc5 | |||
| 02eeed70bd | |||
| 849661a573 | |||
| ca84ac7a6a | |||
| 8d421b2af2 | |||
| b2776581da | |||
| f7eb9edb9b | |||
| fe87311f51 | |||
| c38159d4d7 | |||
| 075911ccfd | |||
| 20a5364a5f | |||
| 4a3037574d | |||
| fee9840d52 | |||
| 10bd4f8a72 | |||
| 1d986aa8a4 | |||
| 33790437ce | |||
| 239f52d543 | |||
| f2d71d3da5 | |||
| 61e625d9a6 | |||
| 9e3330c90b | |||
| 108c831e23 | |||
| 77f071b5ff | |||
| fb35e29a19 | |||
| b1f4497697 | |||
| 29569e6049 | |||
| c201145d02 | |||
| eb214aae74 | |||
| fbef4384aa | |||
| 88909ac9b2 | |||
| 84d9872a26 | |||
| 576ba1a03d | |||
| bb448f619c | |||
| 9827003e06 | |||
| 14100b867c | |||
| 5926e3cfec | |||
| e09d7b5456 | |||
| c9b8f866df | |||
| eea525ac89 | |||
| 76e7a9e5b7 | |||
| 5a523e500b | |||
| 33ea7850c9 | |||
| 9856a2faeb | |||
| a19c4b207c | |||
| 264739e6ad | |||
| 10b4eaa10b | |||
| be21cc53bc | |||
| cda35f142f | |||
| 5c98ea081e | |||
| 78866ca13d | |||
| d4382300b4 | |||
| 7ef01e70f7 | |||
| 10316b6ab2 | |||
| e11d9904bc | |||
| f1d33f76c7 | |||
| fe72a8192d | |||
| bf543ac8f4 | |||
| 93f7a97f01 | |||
| cc188dffa9 | |||
| 33e3b9ddbb | |||
| 8c4b0b66b8 | |||
| 2781c9544b | |||
| 4765dde5cf | |||
| 658b0e8092 | |||
| 0e5d05f8e0 | |||
| f1570c5c92 | |||
| e042c53caa | |||
| a7b6859561 | |||
| 14235c9c17 | |||
| 6a6bed8e04 | |||
| 7a07995944 | |||
| 62afe5cda6 | |||
| 95f6b7fa82 | |||
| ae2438df90 | |||
| fafb78e38e | |||
| 4068758bb8 | |||
| bb87595220 | |||
| 001a1517da | |||
| 76b285f475 | |||
| 0b796299c1 | |||
| 5d2af3e13d | |||
| 15d2f1697d | |||
| 460a54ce15 | |||
| 3ab0ccf643 | |||
| cebd79e7eb | |||
| 4e321365cf | |||
| 1b088606f7 | |||
| 7bf410f927 | |||
| 5476f09d35 | |||
| a49c9a27fb | |||
| e5a3cf5a36 | |||
| 59a0f62079 | |||
| fbbd6b2c2e | |||
| d77977aa62 | |||
| da35350ee3 | |||
| 40996af9c1 | |||
| 7e29b38bd4 | |||
| c1b86a5eab | |||
| 68587c203c | |||
| 80e08d1b1b | |||
| 0a05318277 | |||
| 5c187245a4 | |||
| 739059cc91 | |||
| 86e966b944 | |||
| ef08d3975c | |||
| c50007b33a | |||
| 9f20e16683 | |||
| 4f2ce2f7b6 | |||
| a404a56254 | |||
| 7a59b443ee | |||
| 127bfd71d7 | |||
| 35341511b9 | |||
| 65888ac515 | |||
| 313f51d1f8 | |||
| 5ddf0f4e5f | |||
| 5e726db14e | |||
| 705dde471f | |||
| d83640dc34 | |||
| ed20c3255b |
+5
-1
@@ -935,7 +935,7 @@ if test "x$enable_x11_backend" = xyes; then
|
||||
have_base_x_pc=true
|
||||
X_PACKAGES="$X_PACKAGES x11 xext"
|
||||
x_libs="`$PKG_CONFIG --libs x11 xext`"
|
||||
X_CFLAGS="`$PKG_CONFIG --cflags x11 xext`"
|
||||
X_CFLAGS="`$PKG_CONFIG --cflags x11 xext` -DXINPUT2_1_USE_UNSTABLE_PROTOCOL -DXINPUT2_2_USE_UNSTABLE_PROTOCOL"
|
||||
|
||||
# Strip out any .la files that pkg-config might give us (this happens
|
||||
# with -uninstalled.pc files)
|
||||
@@ -1126,6 +1126,10 @@ if test "x$enable_x11_backend" = xyes; then
|
||||
AC_DEFINE(XINPUT_2, 1, [Define to 1 if XInput 2.0 is available]),
|
||||
X_EXTENSIONS="$X_EXTENSIONS XInput")
|
||||
|
||||
gtk_save_LIBS="$LIBS"
|
||||
LIBS="$LIBS -lXi"
|
||||
AC_CHECK_FUNC(XIAllowTouchEvents, AC_DEFINE(XINPUT_2_2, 1, [Define to 1 if XInput 2.2 is available]))
|
||||
LIBS="$gtk_save_LIBS"
|
||||
else
|
||||
AC_DEFINE(XINPUT_NONE, 1,
|
||||
[Define to 1 if no XInput should be used])
|
||||
|
||||
@@ -29,6 +29,7 @@ demos = \
|
||||
links.c \
|
||||
list_store.c \
|
||||
menus.c \
|
||||
multitouch.c \
|
||||
offscreen_window.c \
|
||||
offscreen_window2.c \
|
||||
overlay.c \
|
||||
|
||||
@@ -0,0 +1,698 @@
|
||||
/* Multitouch
|
||||
*
|
||||
* Demonstrates some general multitouch event handling,
|
||||
* using GdkTouchCluster in order to get grouped motion
|
||||
* events for the touches within a cluster. Each of the
|
||||
* created rectangles has one of those GdkTouchCluster
|
||||
* objects.
|
||||
*
|
||||
* Touch events are also enabled on additional widgets,
|
||||
* enabling simultaneous touch interaction on those. Not
|
||||
* all widgets are prepared for multitouch interaction,
|
||||
* as there are constraints that not all widgets may
|
||||
* apply to.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "demo-common.h"
|
||||
|
||||
#define RECT_BORDER_WIDTH 6
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
static GtkWidget *area = NULL;
|
||||
static GtkWidget *red = NULL;
|
||||
static GtkWidget *green = NULL;
|
||||
static GtkWidget *blue = NULL;
|
||||
static GtkWidget *alpha = NULL;
|
||||
|
||||
static GQueue *shapes = NULL;
|
||||
|
||||
typedef struct {
|
||||
GdkTouchCluster *cluster;
|
||||
GdkRGBA color;
|
||||
|
||||
gdouble angle;
|
||||
gdouble zoom;
|
||||
|
||||
gdouble center_x;
|
||||
gdouble center_y;
|
||||
|
||||
gdouble x;
|
||||
gdouble y;
|
||||
gdouble width;
|
||||
gdouble height;
|
||||
|
||||
gdouble base_zoom;
|
||||
gdouble base_angle;
|
||||
gdouble initial_distance;
|
||||
gdouble initial_angle;
|
||||
|
||||
GdkPoint points[4];
|
||||
} ShapeInfo;
|
||||
|
||||
static void
|
||||
calculate_rotated_point (gdouble angle,
|
||||
gdouble zoom,
|
||||
gdouble center_x,
|
||||
gdouble center_y,
|
||||
gdouble point_x,
|
||||
gdouble point_y,
|
||||
gdouble *ret_x,
|
||||
gdouble *ret_y)
|
||||
{
|
||||
gdouble distance, xd, yd, ang;
|
||||
|
||||
if (angle == 0)
|
||||
{
|
||||
*ret_x = point_x;
|
||||
*ret_y = point_y;
|
||||
return;
|
||||
}
|
||||
|
||||
xd = center_x - point_x;
|
||||
yd = center_y - point_y;
|
||||
|
||||
if (xd == 0 && yd == 0)
|
||||
{
|
||||
*ret_x = center_x;
|
||||
*ret_y = center_y;
|
||||
return;
|
||||
}
|
||||
|
||||
distance = sqrt ((xd * xd) + (yd * yd));
|
||||
distance *= zoom;
|
||||
|
||||
ang = atan2 (xd, yd);
|
||||
|
||||
/* Invert angle */
|
||||
ang = (2 * G_PI) - ang;
|
||||
|
||||
/* Shift it 270° */
|
||||
ang += 3 * (G_PI / 2);
|
||||
|
||||
/* And constraint it to 0°-360° */
|
||||
ang = fmod (ang, 2 * G_PI);
|
||||
ang += angle;
|
||||
|
||||
*ret_x = center_x + (distance * cos (ang));
|
||||
*ret_y = center_y + (distance * sin (ang));
|
||||
}
|
||||
|
||||
static void
|
||||
shape_info_allocate_input_rect (ShapeInfo *info)
|
||||
{
|
||||
gint width, height, i;
|
||||
|
||||
width = info->width;
|
||||
height = info->height;
|
||||
|
||||
/* Top/left */
|
||||
info->points[0].x = info->x - info->center_x;
|
||||
info->points[0].y = info->y - info->center_y;
|
||||
|
||||
/* Top/right */
|
||||
info->points[1].x = info->x - info->center_x + width;
|
||||
info->points[1].y = info->y - info->center_y;
|
||||
|
||||
/* Bottom/right */
|
||||
info->points[2].x = info->x - info->center_x + width;
|
||||
info->points[2].y = info->y - info->center_y + height;
|
||||
|
||||
/* Bottom/left */
|
||||
info->points[3].x = info->x - info->center_x;
|
||||
info->points[3].y = info->y - info->center_y + height;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
gdouble ret_x, ret_y;
|
||||
|
||||
calculate_rotated_point (info->angle,
|
||||
info->zoom,
|
||||
info->x,
|
||||
info->y,
|
||||
(gdouble) info->points[i].x,
|
||||
(gdouble) info->points[i].y,
|
||||
&ret_x,
|
||||
&ret_y);
|
||||
|
||||
info->points[i].x = (gint) ret_x;
|
||||
info->points[i].y = (gint) ret_y;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shape_info_bounding_rect (ShapeInfo *info,
|
||||
GdkRectangle *rect)
|
||||
{
|
||||
gint i, left, right, top, bottom;
|
||||
|
||||
left = top = G_MAXINT;
|
||||
right = bottom = 0;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (info->points[i].x < left)
|
||||
left = info->points[i].x;
|
||||
|
||||
if (info->points[i].x > right)
|
||||
right = info->points[i].x;
|
||||
|
||||
if (info->points[i].y < top)
|
||||
top = info->points[i].y;
|
||||
|
||||
if (info->points[i].y > bottom)
|
||||
bottom = info->points[i].y;
|
||||
}
|
||||
|
||||
rect->x = left - 20;
|
||||
rect->y = top - 20;
|
||||
rect->width = right - left + 40;
|
||||
rect->height = bottom - top + 40;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
shape_info_point_in (ShapeInfo *info,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
GdkPoint *left, *right, *top, *bottom;
|
||||
gint i;
|
||||
|
||||
left = right = top = bottom = NULL;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
GdkPoint *p = &info->points[i];
|
||||
|
||||
if (!left ||
|
||||
p->x < left->x ||
|
||||
(p->x == left->x && p->y > left->y))
|
||||
left = p;
|
||||
|
||||
if (!right ||
|
||||
p->x > right->x ||
|
||||
(p->x == right->x && p->y < right->y))
|
||||
right = p;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
GdkPoint *p = &info->points[i];
|
||||
|
||||
if (p == left || p == right)
|
||||
continue;
|
||||
|
||||
if (!top ||
|
||||
p->y < top->y)
|
||||
top = p;
|
||||
|
||||
if (!bottom ||
|
||||
p->y > bottom->y)
|
||||
bottom = p;
|
||||
}
|
||||
|
||||
g_assert (left && right && top && bottom);
|
||||
|
||||
if (x < left->x ||
|
||||
x > right->x ||
|
||||
y < top->y ||
|
||||
y > bottom->y)
|
||||
return FALSE;
|
||||
|
||||
/* Check whether point is above the sides
|
||||
* between leftmost and topmost, and
|
||||
* topmost and rightmost corners.
|
||||
*/
|
||||
if (x <= top->x)
|
||||
{
|
||||
if (left->y - ((left->y - top->y) * (((gdouble) x - left->x) / (top->x - left->x))) > y)
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (top->y + ((right->y - top->y) * (((gdouble) x - top->x) / (right->x - top->x))) > y)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Check whether point is below the sides
|
||||
* between leftmost and bottom, and
|
||||
* bottom and rightmost corners.
|
||||
*/
|
||||
if (x <= bottom->x)
|
||||
{
|
||||
if (left->y + ((bottom->y - left->y) * (((gdouble) x - left->x) / (bottom->x - left->x))) < y)
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bottom->y - ((bottom->y - right->y) * (((gdouble) x - bottom->x) / (right->x - bottom->x))) < y)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static ShapeInfo *
|
||||
shape_info_new (gdouble x,
|
||||
gdouble y,
|
||||
gdouble width,
|
||||
gdouble height,
|
||||
GdkRGBA *color)
|
||||
{
|
||||
ShapeInfo *info;
|
||||
|
||||
info = g_slice_new0 (ShapeInfo);
|
||||
info->cluster = NULL;
|
||||
info->color = *color;
|
||||
|
||||
info->x = x;
|
||||
info->y = y;
|
||||
info->width = width;
|
||||
info->height = height;
|
||||
|
||||
info->angle = 0;
|
||||
info->zoom = 1;
|
||||
|
||||
info->base_zoom = 1;
|
||||
info->base_angle = 0;
|
||||
info->initial_distance = 0;
|
||||
info->initial_angle = 0;
|
||||
|
||||
shape_info_allocate_input_rect (info);
|
||||
|
||||
g_queue_push_tail (shapes, info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static void
|
||||
shape_info_free (ShapeInfo *info)
|
||||
{
|
||||
g_slice_free (ShapeInfo, info);
|
||||
}
|
||||
|
||||
static void
|
||||
shape_info_draw (cairo_t *cr,
|
||||
ShapeInfo *info)
|
||||
{
|
||||
cairo_save (cr);
|
||||
|
||||
cairo_translate (cr,
|
||||
info->points[0].x + RECT_BORDER_WIDTH / 2,
|
||||
info->points[0].y + RECT_BORDER_WIDTH / 2);
|
||||
|
||||
cairo_scale (cr, info->zoom, info->zoom);
|
||||
cairo_rotate (cr, info->angle);
|
||||
|
||||
cairo_rectangle (cr, 0, 0,
|
||||
info->width - RECT_BORDER_WIDTH,
|
||||
info->height - RECT_BORDER_WIDTH);
|
||||
gdk_cairo_set_source_rgba (cr, &info->color);
|
||||
cairo_fill_preserve (cr);
|
||||
|
||||
cairo_set_line_width (cr, RECT_BORDER_WIDTH);
|
||||
cairo_set_source_rgb (cr, 0, 0, 0);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
shape_update_scales (ShapeInfo *info)
|
||||
{
|
||||
gtk_range_set_value (GTK_RANGE (red), info->color.red);
|
||||
gtk_range_set_value (GTK_RANGE (green), info->color.green);
|
||||
gtk_range_set_value (GTK_RANGE (blue), info->color.blue);
|
||||
gtk_range_set_value (GTK_RANGE (alpha), info->color.alpha);
|
||||
}
|
||||
|
||||
static void
|
||||
range_value_changed_cb (GtkRange *range,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GdkRectangle rect;
|
||||
ShapeInfo *shape;
|
||||
gdouble value;
|
||||
|
||||
widget = GTK_WIDGET (range);
|
||||
shape = g_queue_peek_head (shapes);
|
||||
|
||||
if (!shape)
|
||||
return;
|
||||
|
||||
value = gtk_range_get_value (range);
|
||||
|
||||
if (widget == red)
|
||||
shape->color.red = value;
|
||||
else if (widget == green)
|
||||
shape->color.green = value;
|
||||
else if (widget == blue)
|
||||
shape->color.blue = value;
|
||||
else if (widget == alpha)
|
||||
shape->color.alpha = value;
|
||||
|
||||
shape_info_bounding_rect (shape, &rect);
|
||||
gdk_window_invalidate_rect (gtk_widget_get_window (area),
|
||||
&rect, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
draw_cb (GtkWidget *widget,
|
||||
cairo_t *cr,
|
||||
gpointer user_data)
|
||||
{
|
||||
GList *l;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
cairo_set_source_rgb (cr, 1, 1, 1);
|
||||
cairo_paint (cr);
|
||||
|
||||
for (l = shapes->tail; l; l = l->prev)
|
||||
shape_info_draw (cr, l->data);
|
||||
|
||||
cairo_restore (cr);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
button_press_cb (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShapeInfo *shape = NULL;
|
||||
guint touch_id;
|
||||
|
||||
if (gdk_event_get_touch_id (event, &touch_id))
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = shapes->tail; l; l = l->prev)
|
||||
{
|
||||
ShapeInfo *info = l->data;
|
||||
|
||||
if (shape_info_point_in (info,
|
||||
(gint) event->button.x,
|
||||
(gint) event->button.y))
|
||||
shape = info;
|
||||
}
|
||||
|
||||
if (!shape)
|
||||
return FALSE;
|
||||
|
||||
/* Put on top */
|
||||
g_queue_remove (shapes, shape);
|
||||
g_queue_push_head (shapes, shape);
|
||||
|
||||
shape_update_scales (shape);
|
||||
|
||||
if (!shape->cluster)
|
||||
shape->cluster = gdk_window_create_touch_cluster (gtk_widget_get_window (widget),
|
||||
gdk_event_get_device (event));
|
||||
else if (gdk_touch_cluster_get_n_touches (shape->cluster) == 0)
|
||||
{
|
||||
/* Only change cluster device if there were no touches */
|
||||
gdk_touch_cluster_set_device (shape->cluster,
|
||||
gdk_event_get_device (event));
|
||||
}
|
||||
|
||||
gdk_touch_cluster_add_touch (shape->cluster, touch_id);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
multitouch_cb (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShapeInfo *info = NULL;
|
||||
gboolean new_center = FALSE;
|
||||
gboolean new_position = FALSE;
|
||||
gdouble event_x, event_y;
|
||||
cairo_region_t *region;
|
||||
GdkRectangle rect;
|
||||
GList *l;
|
||||
|
||||
for (l = shapes->head; l; l = l->next)
|
||||
{
|
||||
ShapeInfo *shape = l->data;
|
||||
|
||||
if (event->multitouch.group == shape->cluster)
|
||||
{
|
||||
info = shape;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!info)
|
||||
return FALSE;
|
||||
|
||||
shape_info_bounding_rect (info, &rect);
|
||||
region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) &rect);
|
||||
|
||||
if (event->multitouch.n_events == 1)
|
||||
{
|
||||
/* Update center if we just got to
|
||||
* this situation from either way */
|
||||
if (event->type == GDK_MULTITOUCH_ADDED ||
|
||||
event->type == GDK_MULTITOUCH_REMOVED)
|
||||
new_center = TRUE;
|
||||
|
||||
event_x = event->multitouch.events[0]->x;
|
||||
event_y = event->multitouch.events[0]->y;
|
||||
new_position = TRUE;
|
||||
}
|
||||
else if (event->multitouch.n_events == 2)
|
||||
{
|
||||
gdouble distance, angle;
|
||||
|
||||
gdk_events_get_center ((GdkEvent *) event->multitouch.events[0],
|
||||
(GdkEvent *) event->multitouch.events[1],
|
||||
&event_x, &event_y);
|
||||
|
||||
gdk_events_get_distance ((GdkEvent *) event->multitouch.events[0],
|
||||
(GdkEvent *) event->multitouch.events[1],
|
||||
&distance);
|
||||
|
||||
gdk_events_get_angle ((GdkEvent *) event->multitouch.events[0],
|
||||
(GdkEvent *) event->multitouch.events[1],
|
||||
&angle);
|
||||
|
||||
if (event->type == GDK_MULTITOUCH_ADDED)
|
||||
{
|
||||
/* Second touch was just added, update base zoom/angle */
|
||||
info->base_zoom = info->zoom;
|
||||
info->base_angle = info->angle;
|
||||
info->initial_angle = angle;
|
||||
info->initial_distance = distance;
|
||||
new_center = TRUE;
|
||||
}
|
||||
|
||||
info->zoom = MAX (info->base_zoom * (distance / info->initial_distance), 1.0);
|
||||
info->angle = info->base_angle + (angle - info->initial_angle);
|
||||
new_position = TRUE;
|
||||
}
|
||||
|
||||
if (new_center)
|
||||
{
|
||||
gdouble origin_x, origin_y;
|
||||
|
||||
origin_x = info->x - info->center_x;
|
||||
origin_y = info->y - info->center_y;
|
||||
|
||||
calculate_rotated_point (- info->angle,
|
||||
1 / info->zoom,
|
||||
info->x - origin_x,
|
||||
info->y - origin_y,
|
||||
event_x - origin_x,
|
||||
event_y - origin_y,
|
||||
&info->center_x,
|
||||
&info->center_y);
|
||||
}
|
||||
|
||||
if (new_position)
|
||||
{
|
||||
info->x = event_x;
|
||||
info->y = event_y;
|
||||
}
|
||||
|
||||
shape_info_allocate_input_rect (info);
|
||||
|
||||
shape_info_bounding_rect (info, &rect);
|
||||
cairo_region_union_rectangle (region, (cairo_rectangle_int_t *) &rect);
|
||||
gdk_window_invalidate_region (gtk_widget_get_window (widget), region, FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
window_destroyed_cb (GtkWidget *widget,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_queue_foreach (shapes, (GFunc) shape_info_free, NULL);
|
||||
g_queue_free (shapes);
|
||||
|
||||
shapes = NULL;
|
||||
window = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
new_rectangle_clicked_cb (GtkButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
GdkRectangle rect;
|
||||
ShapeInfo *info;
|
||||
GdkRGBA color;
|
||||
|
||||
color.red = color.green = color.blue = color.alpha = 0.5;
|
||||
info = shape_info_new (0, 0, 100, 150, &color);
|
||||
|
||||
shape_info_bounding_rect (info, &rect);
|
||||
gdk_window_invalidate_rect (gtk_widget_get_window (area), &rect, FALSE);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
create_drawing_area (void)
|
||||
{
|
||||
area = gtk_drawing_area_new ();
|
||||
|
||||
gtk_widget_add_events (area,
|
||||
GDK_TOUCH_MASK |
|
||||
GDK_POINTER_MOTION_MASK |
|
||||
GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK);
|
||||
|
||||
gtk_widget_set_size_request (area, 600, 600);
|
||||
|
||||
g_signal_connect (area, "draw",
|
||||
G_CALLBACK (draw_cb), NULL);
|
||||
g_signal_connect (area, "button-press-event",
|
||||
G_CALLBACK (button_press_cb), NULL);
|
||||
g_signal_connect (area, "multitouch-event",
|
||||
G_CALLBACK (multitouch_cb), NULL);
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
create_scale (void)
|
||||
{
|
||||
GtkWidget *scale;
|
||||
|
||||
scale = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, 0, 1, 0.01);
|
||||
gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
|
||||
|
||||
gtk_widget_set_vexpand (scale, TRUE);
|
||||
gtk_widget_set_margin_left (scale, 15);
|
||||
gtk_widget_set_margin_right (scale, 15);
|
||||
|
||||
gtk_widget_add_events (scale, GDK_TOUCH_MASK);
|
||||
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (range_value_changed_cb), NULL);
|
||||
return scale;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
create_window (void)
|
||||
{
|
||||
GtkWidget *grid, *label, *button;
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Multitouch demo");
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (window_destroyed_cb), NULL);
|
||||
|
||||
grid = gtk_grid_new ();
|
||||
gtk_container_add (GTK_CONTAINER (window), grid);
|
||||
|
||||
area = create_drawing_area ();
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
area, 0, 0, 1, 3);
|
||||
gtk_widget_set_hexpand (area, TRUE);
|
||||
gtk_widget_set_vexpand (area, TRUE);
|
||||
|
||||
/* "red" label/scale */
|
||||
label = gtk_label_new ("Red");
|
||||
gtk_widget_set_vexpand (label, FALSE);
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
label, 1, 0, 1, 1);
|
||||
|
||||
red = create_scale ();
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
red, 1, 1, 1, 1);
|
||||
|
||||
/* "green" label/scale */
|
||||
label = gtk_label_new ("Green");
|
||||
gtk_widget_set_vexpand (label, FALSE);
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
label, 2, 0, 1, 1);
|
||||
|
||||
green = create_scale ();
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
green, 2, 1, 1, 1);
|
||||
|
||||
/* "blue" label/scale */
|
||||
label = gtk_label_new ("Blue");
|
||||
gtk_widget_set_vexpand (label, FALSE);
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
label, 3, 0, 1, 1);
|
||||
|
||||
blue = create_scale ();
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
blue, 3, 1, 1, 1);
|
||||
|
||||
/* "alpha" label/scale */
|
||||
label = gtk_label_new ("Alpha");
|
||||
gtk_widget_set_vexpand (label, FALSE);
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
label, 4, 0, 1, 1);
|
||||
|
||||
alpha = create_scale ();
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
alpha, 4, 1, 1, 1);
|
||||
|
||||
/* button */
|
||||
button = gtk_button_new_from_stock (GTK_STOCK_NEW);
|
||||
gtk_widget_add_events (button, GDK_TOUCH_MASK);
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
button, 1, 2, 4, 1);
|
||||
gtk_widget_set_vexpand (button, FALSE);
|
||||
|
||||
g_signal_connect (button, "clicked",
|
||||
G_CALLBACK (new_rectangle_clicked_cb), NULL);
|
||||
|
||||
gtk_widget_show_all (grid);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_multitouch (GtkWidget *do_widget)
|
||||
{
|
||||
if (!shapes)
|
||||
shapes = g_queue_new ();
|
||||
|
||||
if (!window)
|
||||
window = create_window ();
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
{
|
||||
gtk_widget_destroy (window);
|
||||
window = NULL;
|
||||
|
||||
g_queue_foreach (shapes, (GFunc) shape_info_free, NULL);
|
||||
g_queue_free (shapes);
|
||||
shapes = NULL;
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
<xi:include href="xml/windows.xml" />
|
||||
<xi:include href="xml/events.xml" />
|
||||
<xi:include href="xml/event_structs.xml" />
|
||||
<xi:include href="xml/touchcluster.xml" />
|
||||
<xi:include href="xml/keys.xml" />
|
||||
<xi:include href="xml/selections.xml" />
|
||||
<xi:include href="xml/dnd.xml" />
|
||||
|
||||
@@ -781,11 +781,13 @@ gdk_event_get_root_coords
|
||||
gdk_event_get_scroll_direction
|
||||
gdk_event_get_state
|
||||
gdk_event_get_time
|
||||
gdk_event_get_touch_id
|
||||
gdk_event_request_motions
|
||||
gdk_events_get_angle
|
||||
gdk_events_get_center
|
||||
gdk_events_get_distance
|
||||
gdk_event_triggers_context_menu
|
||||
gdk_event_get_touch_id
|
||||
|
||||
<SUBSECTION>
|
||||
gdk_event_handler_set
|
||||
@@ -833,6 +835,7 @@ GdkEventWindowState
|
||||
GdkEventSetting
|
||||
GdkEventOwnerChange
|
||||
GdkEventGrabBroken
|
||||
GdkEventMultiTouch
|
||||
|
||||
<SUBSECTION>
|
||||
GdkScrollDirection
|
||||
@@ -860,6 +863,29 @@ gdk_event_get_type
|
||||
gdk_owner_change_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<TITLE>Multitouch</TITLE>
|
||||
<FILE>touchcluster</FILE>
|
||||
GdkTouchCluster
|
||||
gdk_touch_cluster_add_touch
|
||||
gdk_touch_cluster_remove_touch
|
||||
gdk_touch_cluster_remove_all
|
||||
gdk_touch_cluster_set_device
|
||||
gdk_touch_cluster_get_device
|
||||
gdk_touch_cluster_get_touches
|
||||
|
||||
<SUBSECTION>
|
||||
gdk_window_create_touch_cluster
|
||||
gdk_window_remove_touch_cluster
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GDK_TYPE_TOUCH_CLUSTER
|
||||
|
||||
<SUBSECTION Private>
|
||||
gdk_touch_cluster_get_type
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<TITLE>Cursors</TITLE>
|
||||
<FILE>cursors</FILE>
|
||||
|
||||
@@ -9,5 +9,6 @@ gdk_display_manager_get_type
|
||||
gdk_drag_context_get_type
|
||||
gdk_keymap_get_type
|
||||
gdk_screen_get_type
|
||||
gdk_touch_cluster_get_type
|
||||
gdk_visual_get_type
|
||||
gdk_window_get_type
|
||||
|
||||
@@ -119,6 +119,7 @@ content_files = \
|
||||
running.sgml \
|
||||
building.sgml \
|
||||
compiling.sgml \
|
||||
device-interaction-patterns.xml \
|
||||
drawing-model.xml \
|
||||
glossary.xml \
|
||||
migrating-2to3.xml \
|
||||
@@ -142,6 +143,7 @@ content_files = \
|
||||
overview.xml
|
||||
|
||||
expand_content_files = \
|
||||
device-interaction-patterns.xml \
|
||||
drawing-model.xml \
|
||||
getting_started.xml \
|
||||
glossary.xml \
|
||||
|
||||
@@ -0,0 +1,578 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
|
||||
]>
|
||||
<chapter id="gtk-device-interaction-patterns">
|
||||
<title>Multitouch and other device interaction patterns</title>
|
||||
|
||||
<para>
|
||||
Depending on the platform, GTK+ is able to handle a wide range of input
|
||||
devices. Those are offered to applications in a 2-level hierarchy, with
|
||||
virtual devices (or master devices) representing the visual cursors
|
||||
displayed in the screen, which are each controlled by a number of physical
|
||||
devices (or slave devices). Those devices can respectively be retrieved
|
||||
from an input event with gdk_event_get_device() and
|
||||
gdk_event_get_source_device().
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In X11, GTK+ uses XInput2 for input events, which caters for a fully dynamic
|
||||
device hierarchy, and support for multiple virtual pointer/keyboard pairs.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Listing and modifying the device hierarchy</title>
|
||||
<programlisting>
|
||||
carlos@sacarino:~$ xinput list
|
||||
⎡ Virtual core pointer id=2 [master pointer (3)]
|
||||
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
|
||||
⎜ ↳ Wacom ISDv4 E6 Pen stylus id=10 [slave pointer (2)]
|
||||
⎜ ↳ Wacom ISDv4 E6 Finger touch id=11 [slave pointer (2)]
|
||||
⎜ ↳ SynPS/2 Synaptics TouchPad id=13 [slave pointer (2)]
|
||||
⎜ ↳ TPPS/2 IBM TrackPoint id=14 [slave pointer (2)]
|
||||
⎜ ↳ Wacom ISDv4 E6 Pen eraser id=16 [slave pointer (2)]
|
||||
⎣ Virtual core keyboard id=3 [master keyboard (2)]
|
||||
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
|
||||
↳ Power Button id=6 [slave keyboard (3)]
|
||||
↳ Video Bus id=7 [slave keyboard (3)]
|
||||
↳ Sleep Button id=8 [slave keyboard (3)]
|
||||
↳ Integrated Camera id=9 [slave keyboard (3)]
|
||||
↳ AT Translated Set 2 keyboard id=12 [slave keyboard (3)]
|
||||
↳ ThinkPad Extra Buttons id=15 [slave keyboard (3)]
|
||||
|
||||
carlos@sacarino:~$ xinput create-master eek
|
||||
carlos@sacarino:~$ xinput list
|
||||
...
|
||||
⎡ eek pointer id=17 [master pointer (18)]
|
||||
⎜ ↳ eek XTEST pointer id=19 [slave pointer (17)]
|
||||
⎣ eek keyboard id=18 [master keyboard (17)]
|
||||
↳ eek XTEST keyboard id=20 [slave keyboard (18)]
|
||||
|
||||
carlos@sacarino:~$ xinput reattach 10 17
|
||||
carlos@sacarino:~$ xinput list
|
||||
...
|
||||
⎡ eek pointer id=17 [master pointer (18)]
|
||||
⎜ ↳ Wacom ISDv4 E6 Pen stylus id=10 [slave pointer (17)]
|
||||
⎜ ↳ eek XTEST pointer id=19 [slave pointer (17)]
|
||||
⎣ eek keyboard id=18 [master keyboard (17)]
|
||||
↳ eek XTEST keyboard id=20 [slave keyboard (18)]
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
Anytime a virtual device is added or removed, or a physical device
|
||||
is attached to another virtual device, or left floating (detached
|
||||
from any virtual device), #GdkDeviceManager will emit the corresponding
|
||||
#GdkDeviceManager::device-added, #GdkDeviceManager::device-removed, or
|
||||
#GdkDeviceManager::device-changed signals.
|
||||
</para>
|
||||
|
||||
<section id="gtk-device-patterns-client-pointer">
|
||||
<title>The client pointer</title>
|
||||
|
||||
<para>
|
||||
In X11, Under the presence of multiple virtual pointers, GDK and XInput2
|
||||
use the "client pointer" principle to allow several legacy applications
|
||||
to interact simultaneously with different virtual pointer/keyboard pairs,
|
||||
it would be usually set by the window manager for a focused window, so
|
||||
different application windows could operate on different client pointers.
|
||||
gdk_device_manager_get_client_pointer() may be called to get the client
|
||||
pointer #GdkDevice
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Under the hood, X11 uses the client pointer (or its paired keyboard) to
|
||||
satisfy core calls such as XGrabPointer/Keyboard, XQueryPointer and
|
||||
others that have been superseded by XInput2.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In platforms without multidevice features, gdk_device_manager_get_client_pointer()
|
||||
will return the only virtual pointer available.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="gtk-device-patterns-simple">
|
||||
<title>Simple device handling</title>
|
||||
|
||||
<para>
|
||||
There are applications that could have little gain in knowing about
|
||||
multiple devices, although there are situations where a device could
|
||||
be needed (i.e. popping up a menu on the pointer coordinates).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For such applications, the client pointer may be a good enough
|
||||
approximation for these operations. Under the presence of multiple
|
||||
device pairs, this gives a behavior that is most similar to that
|
||||
of legacy applications (i.e. gtk+2).
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Getting the client pointer and keyboard</title>
|
||||
<programlisting>
|
||||
GdkDisplay *display;
|
||||
GdkDeviceManager *device_manager;
|
||||
GdkDevice *client_pointer, client_keyboard;
|
||||
|
||||
display = gdk_display_get_default ();
|
||||
device_manager = gdk_display_get_device_manager (display);
|
||||
client_pointer = gdk_device_manager_get_client_pointer (device_manager);
|
||||
|
||||
/* Or if we need a keyboard too */
|
||||
client_keyboard = gdk_device_get_associated_device (client_pointer);
|
||||
</programlisting>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
<section id="gtk-device-patterns-multiple-devices">
|
||||
<title>Dealing with multiple devices</title>
|
||||
|
||||
<para>
|
||||
There may be several usecases to deal with multiple devices, including,
|
||||
but not limited to:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
Retrieving advanced information from an input device: i.e. stylus pressure/tilt
|
||||
in drawing applications.
|
||||
</listitem>
|
||||
<listitem>
|
||||
Receiving events from a dedicated input device: i.e. joysticks in games.
|
||||
</listitem>
|
||||
<listitem>
|
||||
Collaborative interfaces, handling simultaneous input from multiple users.
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
However, the patterns to make them work are very similar.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Event handling</title>
|
||||
|
||||
<para>
|
||||
Each device will emit its own event stream, this means
|
||||
that you will need to check the GdkEvent you get in
|
||||
your event handlers
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Reacting differently to devices</title>
|
||||
<programlisting>
|
||||
static gboolean
|
||||
my_widget_motion_notify (GtkWidget *widget,
|
||||
GdkEventMotion *event)
|
||||
{
|
||||
GdkDevice *device, *source_device;
|
||||
|
||||
device = gdk_event_get_device ((GdkEvent *) event);
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
|
||||
g_print ("Motion event by '%s', coming from HW device '%s'\n",
|
||||
gdk_device_get_name (device),
|
||||
gdk_device_get_name (source_device));
|
||||
|
||||
/* Handle touch devices differently */
|
||||
if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
|
||||
{
|
||||
...
|
||||
}
|
||||
else
|
||||
{
|
||||
...
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
The mechanism above could also be used for fine grained event discarding
|
||||
(i.e. so rubberband selection doesn't jump to another pointer entering
|
||||
the widget for example)
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Reacting differently to devices</title>
|
||||
<programlisting>
|
||||
static gboolean
|
||||
my_widget_button_press (Gtkwidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
GET_PRIV(widget)->current_pointer = gdk_event_get_device ((GdkEvent *) event);
|
||||
...
|
||||
}
|
||||
|
||||
static gboolean
|
||||
my_widget_button_release (Gtkwidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
GET_PRIV(widget)->current_pointer = NULL;
|
||||
...
|
||||
}
|
||||
|
||||
static gboolean
|
||||
my_widget_motion_notify (Gtkwidget *widget,
|
||||
GdkEventMotion *event)
|
||||
{
|
||||
if (gdk_event_get_device (event) !=
|
||||
GET_PRIV(widget)->current_pointer)
|
||||
return FALSE;
|
||||
|
||||
...
|
||||
}
|
||||
</programlisting>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Grabs</title>
|
||||
|
||||
<para>
|
||||
Grabs are a mechanism to coerce a device into sending events to
|
||||
a window, but with multidevice there's an other side of the coin,
|
||||
how other devices are supposed to interact while the grab is in
|
||||
effect.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The GdkGrabOwnership enum passed to gdk_device_grab() may be used
|
||||
to block other devices' interaction. %GDK_OWNERSHIP_NONE applies
|
||||
no restrictions, allowing other devices to interact, even with
|
||||
the grab window. %GDK_OWNERSHIP_WINDOW blocks other devices from
|
||||
interacting with the grab window, but they'll still be able to
|
||||
interact with the rest of the application, whereas
|
||||
%GDK_OWNERSHIP_APPLICATION will render the whole application
|
||||
insensitive to input from other devices. Different devices may
|
||||
have simultaneous grabs on the same or different windows.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Grabbing as a result of an input event</title>
|
||||
<programlisting>
|
||||
gboolean
|
||||
my_widget_button_press (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
GdkDevice *pointer, *keyboard;
|
||||
|
||||
pointer = gdk_event_get_device ((GdkEvent *) event);
|
||||
keyboard = gdk_device_get_associated_device (pointer);
|
||||
|
||||
/* Grab both keyboard/pointer, other devices will be
|
||||
* unable to interact with the widget window meanwhile
|
||||
*/
|
||||
gdk_device_grab (pointer,
|
||||
gtk_widget_get_window (widget),
|
||||
GDK_OWNERSHIP_WINDOW,
|
||||
...);
|
||||
gdk_device_grab (keyboard,
|
||||
gtk_widget_get_window (widget),
|
||||
GDK_OWNERSHIP_WINDOW,
|
||||
...);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
For GTK+ grabs, there's only a boolean value, equivalent to
|
||||
%GDK_OWNERSHIP_NONE and %GDK_OWNERSHIP_WINDOW, but the mechanism
|
||||
is quite similar.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once the device is grabbed, there may be different situations
|
||||
that could break the grabs, so the widget needs to listen to
|
||||
#GdkGrabBrokenEvent and the #GtkWidget::grab-notify signal to
|
||||
handle these situations.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Handling broken grabs</title>
|
||||
<programlisting>
|
||||
static gboolean
|
||||
my_widget_grab_broken (GtkWidget *widget,
|
||||
GdkEventGrabBroken *event)
|
||||
{
|
||||
MyWidgetPrivate *priv = GET_PRIV (widget);
|
||||
|
||||
if (gdk_event_get_device (event) == priv->grab_pointer)
|
||||
{
|
||||
/* Undo state */
|
||||
...
|
||||
priv->grab_pointer = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
my_widget_grab_notify (GtkWidget *widget,
|
||||
gboolean was_grabbed)
|
||||
{
|
||||
MyWidgetPrivate *priv = GET_PRIV (widget);
|
||||
|
||||
if (gtk_widget_device_is_shadowed (widget, priv->grab_device))
|
||||
{
|
||||
/* Device was "shadowed" by another widget's grab,
|
||||
* release and undo state
|
||||
*/
|
||||
...
|
||||
priv->grab_pointer = NULL;
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Handling multipointer</title>
|
||||
|
||||
<para>
|
||||
Widgets do react by default to every virtual device, although
|
||||
by default they are set in a compatibility mode that makes them
|
||||
behave better with multiple pointers, without necessarily
|
||||
being multipointer aware.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This compatibility mode most notably disables per-device
|
||||
enter/leave events, so these are stacked, and the crossing
|
||||
events are only emitted when the first pointer enters the
|
||||
window, and after the last pointer leaves it. This behavior
|
||||
is controlled through gtk_widget_set_support_multidevice()
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Reading device axis values</title>
|
||||
|
||||
<para>
|
||||
Button and motion events provide further information about
|
||||
the device axes' current state. Note the device axes are
|
||||
hardware and driver dependent, therefore the set of axes
|
||||
is not set in stone, although there are a few more common ones.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Getting to know the axes provided by a device</title>
|
||||
<programlisting>
|
||||
carlos@sacarino:~$ xinput list "Wacom ISDv4 E6 Pen stylus" |grep "Label"
|
||||
Label: Abs X
|
||||
Label: Abs Y
|
||||
Label: Abs Pressure
|
||||
Label: Abs Tilt X
|
||||
Label: Abs Tilt Y
|
||||
Label: Abs Wheel
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Getting an axis value</title>
|
||||
<programlisting>
|
||||
gboolean
|
||||
my_widget_motion_notify (GtkWidget *widget,
|
||||
GdkEventMotion *event)
|
||||
{
|
||||
GdkAtom *label_atom;
|
||||
gdouble pressure;
|
||||
|
||||
label_atom = gdk_atom_intern_static_string ("Abs Pressure");
|
||||
gdk_device_get_axis_value (gdk_event_get_device ((GdkEvent *) event),
|
||||
event->axes, label_atom, &pressure);
|
||||
|
||||
/* Do something with pressure */
|
||||
...
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
All pointer devices report axes information, master and slave. to
|
||||
achieve this, master pointers modify their list of axes at runtime
|
||||
to reflect those of the currently routed slave, emitting
|
||||
#GdkDevice::changed as the routed slave device changes.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Dealing with slave (or floating) devices</title>
|
||||
|
||||
<para>
|
||||
By default, GTK+ listens to all master devices, and typically
|
||||
all slave devices will be attached to a master device. so
|
||||
gdk_event_get_source_device() is the recommended way to deal
|
||||
with the physical device triggering the event.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In more specialized setups, some devices could be floating
|
||||
(i.e. tablets that don't route events through any virtual
|
||||
pointer, but are expected to interact with drawing applications).
|
||||
In that case, such specialized applications could want to interact
|
||||
directly with the device. To do so, the device must be enabled,
|
||||
and the widget wanting its events needs to add the event mask.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Enabling events for a slave device</title>
|
||||
<programlisting>
|
||||
GdkDevice *device;
|
||||
|
||||
/* Gets the first device found with the given GdkInputSource */
|
||||
device = get_device (gtk_widget_get_display (widget),
|
||||
GDK_SOURCE_PEN);
|
||||
gdk_device_set_mode (device, GDK_MODE_SCREEN);
|
||||
gtk_widget_add_device_events (widget, device,
|
||||
GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK |
|
||||
GDK_POINTER_MOTION_MASK);
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
After these calls, the widget would specifically receive events
|
||||
from the physical device, regardless of it being floating or
|
||||
connected to a master device. In this second case, and if you
|
||||
want exclusive control of the device, you can temporarily detach
|
||||
the stylus device from its master by doing a GDK grab on it.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For events coming directly from slave devices, both
|
||||
gdk_event_get_device() and gdk_event_get_source_device() will
|
||||
return the same device of type %GDK_DEVICE_TYPE_SLAVE or
|
||||
%GDK_DEVICE_TYPE_FLOATING.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
This is less useful than it used to be in GTK+2/XInput1, at least
|
||||
for attached slaves, as there is gdk_event_get_source_device(),
|
||||
and master devices' events provide axes information.
|
||||
</note>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="gtk-device-patterns-multitouch-widgets">
|
||||
<title>Multitouch in GTK+ widgets</title>
|
||||
|
||||
<para>
|
||||
Since version 3.4, GTK+ offers support for multitouch devices through a new
|
||||
set of events and higher level tools like #GdkTouchCluster and
|
||||
#GtkGesturesInterpreter.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the widget does not have %GDK_TOUCH_MASK set in the event mask, it will
|
||||
only be allowed to interact with the touch emulating pointer events, and will
|
||||
only receive pointer events.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the widget does have %GDK_TOUCH_MASK enabled, it will be able to receive
|
||||
events of type %GDK_TOUCH_PRESS, %GDK_TOUCH_RELEASE and %GDK_TOUCH_MOTION,
|
||||
which will be respectively emitted through the #GtkWidget::button-press-event,
|
||||
#GtkWidget::button-release-event and #GtkWidget::motion-notify-event signals.
|
||||
There may be multiple, simultaneous sequences of events, those will be
|
||||
recognized and referenced by their touch ID. See gdk_event_get_touch_id().
|
||||
</para>
|
||||
|
||||
<para>
|
||||
#GtkWidget<!-- -->s may create GdkTouchCluster<!-- -->s via
|
||||
gdk_window_create_touch_cluster(), those may be used to group
|
||||
touch events together, which are notified through #GdkEventMultitouch,
|
||||
this event will be emitted in #GtkWidget<!-- -->s through the
|
||||
#GtkWidget::multitouch-event signal.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Widgets may also handle gestures being performed on them,
|
||||
gtk_widget_enable_gesture() and gtk_widget_disable_gesture() are
|
||||
provided as a simple API, although widgets may also create a
|
||||
#GtkGesturesInterpreter and feed it events directly.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="gtk-device-patterns-multitouch">
|
||||
<title>Multitouch across a widget hierarchy</title>
|
||||
|
||||
<para>
|
||||
Fully touch driven applications might not want to confine multitouch
|
||||
operations within a single widget, but rather offer simultaneous
|
||||
interaction with multiple widgets.
|
||||
</para>
|
||||
<para>
|
||||
GTK+ is able to provide such experience, although it does not enable
|
||||
%GDK_TOUCH_MASK by default on its stock widgets. If a widget meets the
|
||||
following requirements, it is ready to be used in a multitouch UI:
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
The widget handles #GtkWidget::button-press-event, #GtkWidget::button-release-event
|
||||
and #GtkWidget::motion-notify-event, and does something meaningful while the button 1
|
||||
is pressed. If any explicit check on the event type being %GDK_BUTTON_PRESS,
|
||||
%GDK_BUTTON_RELEASE or %GDK_MOTION_NOTIFY is performed, the event types
|
||||
%GDK_TOUCH_PRESS, %GDK_TOUCH_RELEASE or %GDK_TOUCH_MOTION also need to be handled.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The widget relies on the implicit grab as long as the button press/touch is active,
|
||||
GDK or GTK+ grabs would break the implicit grabs other touch sequences may have on
|
||||
other widgets.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The widget does not require (or opts out) keyboard interaction while a touch is
|
||||
active on it. Touch interaction does not necessarily bring the keyboard focus with it.
|
||||
</listitem>
|
||||
<listitem>
|
||||
If the widget is only meant to interact with one touch sequence at a time (i.e.
|
||||
buttons), it has to be able to discern and reject operations on any later touch
|
||||
sequence as long as the touch it is interacting with remains active.
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
If a widget meets those requirements, enabling %GDK_TOUCH_MASK on it will suffice
|
||||
to make it handle multitouch events in a way that doesn't disrupt other touch
|
||||
operations.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Enabling touch events on a widget</title>
|
||||
<programlisting>
|
||||
gtk_widget_add_events (widget, GDK_TOUCH_MASK);
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<note>
|
||||
Not all GTK+ stock widgets are immediately suitable for handling touch
|
||||
events, there could be even design reasons on some of those which render
|
||||
them unsuitable.
|
||||
</note>
|
||||
</section>
|
||||
|
||||
<section id="gtk-device-patterns-recommendations">
|
||||
<title>Recommendations</title>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
Device operations often come up as a result of input events, favor
|
||||
gdk_event_get_device() and gtk_get_current_event_device() before
|
||||
gdk_device_manager_get_client_pointer().
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
Store the devices the widget is currently interacting with, handle
|
||||
GdkEventGrabBroken and #GtkWidget::grab-notify to undo/nullify these.
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</section>
|
||||
</chapter>
|
||||
@@ -68,6 +68,12 @@
|
||||
<xi:include href="xml/gtkstyle.xml" />
|
||||
</part>
|
||||
|
||||
<part id="multitouch-and-multidevice">
|
||||
<title>Interacting with input devices</title>
|
||||
<xi:include href="xml/device-interaction-patterns.xml" />
|
||||
<xi:include href="xml/gtkgesturesinterpreter.xml" />
|
||||
</part>
|
||||
|
||||
<part id="gtkobjects">
|
||||
<title>GTK+ Widgets and Objects</title>
|
||||
|
||||
|
||||
@@ -1579,6 +1579,51 @@ GtkFramePrivate
|
||||
gtk_frame_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkgesturesinterpreter</FILE>
|
||||
<TITLE>GtkGesturesInterpreter</TITLE>
|
||||
GtkGesturesInterpreter
|
||||
GtkGestureType
|
||||
gtk_gestures_interpreter_new
|
||||
gtk_gestures_interpreter_add_gesture
|
||||
gtk_gestures_interpreter_remove_gesture
|
||||
gtk_gestures_interpreter_feed_event
|
||||
gtk_gestures_interpreter_finish
|
||||
gtk_gestures_interpreter_get_n_active_strokes
|
||||
|
||||
<SUBSECTION Gestures>
|
||||
GtkGestureStroke
|
||||
gtk_gesture_stroke_new
|
||||
gtk_gesture_stroke_copy
|
||||
gtk_gesture_stroke_free
|
||||
gtk_gesture_stroke_append_vector
|
||||
gtk_gesture_stroke_get_n_vectors
|
||||
gtk_gesture_stroke_get_vector
|
||||
GtkGesture
|
||||
GtkGestureFlags
|
||||
gtk_gesture_new
|
||||
gtk_gesture_copy
|
||||
gtk_gesture_free
|
||||
gtk_gesture_add_stroke
|
||||
gtk_gesture_get_n_strokes
|
||||
gtk_gesture_get_stroke
|
||||
gtk_gesture_get_flags
|
||||
gtk_gesture_register
|
||||
gtk_gesture_register_static
|
||||
gtk_gesture_lookup
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GTK_GESTURES_INTERPRETER
|
||||
GTK_IS_GESTURES_INTERPRETER
|
||||
GTK_TYPE_GESTURES_INTERPRETER
|
||||
GTK_IS_GESTURES_INTERPRETER_CLASS
|
||||
GTK_GESTURES_INTERPRETER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_gestures_interpreter_get_type
|
||||
gtk_gesture_stroke_get_type
|
||||
gtk_gesture_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkhandlebox</FILE>
|
||||
<TITLE>GtkHandleBox</TITLE>
|
||||
@@ -2939,6 +2984,9 @@ gtk_scrolled_window_get_min_content_width
|
||||
gtk_scrolled_window_set_min_content_width
|
||||
gtk_scrolled_window_get_min_content_height
|
||||
gtk_scrolled_window_set_min_content_height
|
||||
GtkKineticScrollingFlags
|
||||
gtk_scrolled_window_set_kinetic_scrolling
|
||||
gtk_scrolled_window_get_kinetic_scrolling
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GTK_SCROLLED_WINDOW
|
||||
@@ -5028,6 +5076,7 @@ GtkAllocation
|
||||
GtkSelectionData
|
||||
GtkWidgetAuxInfo
|
||||
GtkWidgetHelpType
|
||||
GtkCapturedEventFlags
|
||||
gtk_widget_new
|
||||
gtk_widget_destroy
|
||||
gtk_widget_in_destruction
|
||||
@@ -5213,6 +5262,7 @@ gtk_widget_get_mapped
|
||||
gtk_widget_get_requisition
|
||||
gtk_widget_device_is_shadowed
|
||||
gtk_widget_get_modifier_mask
|
||||
gtk_widget_release_captured_events
|
||||
|
||||
<SUBSECTION>
|
||||
gtk_widget_get_path
|
||||
@@ -5262,6 +5312,10 @@ gtk_widget_set_vexpand_set
|
||||
gtk_widget_queue_compute_expand
|
||||
gtk_widget_compute_expand
|
||||
|
||||
<SUBSECTION Gestures>
|
||||
gtk_widget_enable_gesture
|
||||
gtk_widget_disable_gesture
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GTK_WIDGET
|
||||
GTK_IS_WIDGET
|
||||
|
||||
@@ -77,6 +77,7 @@ gtk_font_chooser_widget_get_type
|
||||
gtk_font_selection_dialog_get_type
|
||||
gtk_font_selection_get_type
|
||||
gtk_frame_get_type
|
||||
gtk_gestures_interpreter_get_type
|
||||
gtk_grid_get_type
|
||||
gtk_handle_box_get_type
|
||||
gtk_hbox_get_type
|
||||
|
||||
@@ -88,6 +88,7 @@ gdk_public_h_sources = \
|
||||
gdkselection.h \
|
||||
gdktestutils.h \
|
||||
gdkthreads.h \
|
||||
gdktouchcluster.h \
|
||||
gdktypes.h \
|
||||
gdkvisual.h \
|
||||
gdkwindow.h
|
||||
@@ -130,6 +131,7 @@ gdk_c_sources = \
|
||||
gdkrgba.c \
|
||||
gdkscreen.c \
|
||||
gdkselection.c \
|
||||
gdktouchcluster.c \
|
||||
gdkvisual.c \
|
||||
gdkwindow.c \
|
||||
gdkwindowimpl.c
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include <gdk/gdkselection.h>
|
||||
#include <gdk/gdktestutils.h>
|
||||
#include <gdk/gdkthreads.h>
|
||||
#include <gdk/gdktouchcluster.h>
|
||||
#include <gdk/gdktypes.h>
|
||||
#include <gdk/gdkvisual.h>
|
||||
#include <gdk/gdkwindow.h>
|
||||
|
||||
@@ -168,6 +168,7 @@ gdk_event_get_scroll_direction
|
||||
gdk_event_get_source_device
|
||||
gdk_event_get_state
|
||||
gdk_event_get_time
|
||||
gdk_event_get_touch_id
|
||||
gdk_event_get_type
|
||||
gdk_event_handler_set
|
||||
gdk_event_mask_get_type
|
||||
@@ -327,6 +328,11 @@ gdk_threads_enter
|
||||
gdk_threads_init
|
||||
gdk_threads_leave
|
||||
gdk_threads_set_lock_functions
|
||||
gdk_touch_cluster_add_touch
|
||||
gdk_touch_cluster_get_device
|
||||
gdk_touch_cluster_get_type G_GNUC_CONST
|
||||
gdk_touch_cluster_list_touches
|
||||
gdk_touch_cluster_remove_touch
|
||||
gdk_unicode_to_keyval
|
||||
gdk_utf8_to_string_target
|
||||
gdk_visibility_state_get_type
|
||||
|
||||
+3
-1
@@ -61,6 +61,7 @@ typedef enum
|
||||
* of a stylus on a graphics tablet.
|
||||
* @GDK_SOURCE_CURSOR: the device is a graphics tablet "puck" or similar device.
|
||||
* @GDK_SOURCE_KEYBOARD: the device is a keyboard.
|
||||
* @GDK_SOURCE_TOUCH: the device is a touch capable device.
|
||||
*
|
||||
* An enumeration describing the type of an input device in general terms.
|
||||
*/
|
||||
@@ -70,7 +71,8 @@ typedef enum
|
||||
GDK_SOURCE_PEN,
|
||||
GDK_SOURCE_ERASER,
|
||||
GDK_SOURCE_CURSOR,
|
||||
GDK_SOURCE_KEYBOARD
|
||||
GDK_SOURCE_KEYBOARD,
|
||||
GDK_SOURCE_TOUCH
|
||||
} GdkInputSource;
|
||||
|
||||
/**
|
||||
|
||||
+135
-13
@@ -188,6 +188,7 @@ gdk_display_init (GdkDisplay *display)
|
||||
display->double_click_time = 250;
|
||||
display->double_click_distance = 5;
|
||||
|
||||
display->touch_implicit_grabs = g_array_new (FALSE, FALSE, sizeof (GdkTouchGrabInfo));
|
||||
display->device_grabs = g_hash_table_new (NULL, NULL);
|
||||
display->motion_hint_info = g_hash_table_new_full (NULL, NULL, NULL,
|
||||
(GDestroyNotify) g_free);
|
||||
@@ -236,6 +237,8 @@ gdk_display_finalize (GObject *object)
|
||||
NULL);
|
||||
g_hash_table_destroy (display->device_grabs);
|
||||
|
||||
g_array_free (display->touch_implicit_grabs, TRUE);
|
||||
|
||||
g_hash_table_destroy (display->motion_hint_info);
|
||||
g_hash_table_destroy (display->pointers_info);
|
||||
g_hash_table_destroy (display->multiple_click_info);
|
||||
@@ -694,6 +697,81 @@ _gdk_display_add_device_grab (GdkDisplay *display,
|
||||
return info;
|
||||
}
|
||||
|
||||
static void
|
||||
_gdk_display_break_touch_grabs (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
GdkWindow *new_grab_window)
|
||||
{
|
||||
guint i = 0;
|
||||
|
||||
while (i < display->touch_implicit_grabs->len)
|
||||
{
|
||||
GdkTouchGrabInfo *info;
|
||||
|
||||
info = &g_array_index (display->touch_implicit_grabs,
|
||||
GdkTouchGrabInfo, i);
|
||||
|
||||
if (info->device == device &&
|
||||
info->window != new_grab_window)
|
||||
{
|
||||
generate_grab_broken_event (GDK_WINDOW (info->window),
|
||||
device, TRUE, new_grab_window);
|
||||
_gdk_window_finish_touch_id (info->window, device, info->touch_id);
|
||||
g_array_remove_index_fast (display->touch_implicit_grabs, i);
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_display_add_touch_grab (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
guint touch_id,
|
||||
GdkWindow *window,
|
||||
GdkWindow *native_window,
|
||||
GdkEventMask event_mask,
|
||||
unsigned long serial,
|
||||
guint32 time)
|
||||
{
|
||||
GdkTouchGrabInfo info;
|
||||
|
||||
info.device = device;
|
||||
info.touch_id = touch_id;
|
||||
info.window = g_object_ref (window);
|
||||
info.native_window = g_object_ref (native_window);
|
||||
info.serial = serial;
|
||||
info.event_mask = event_mask;
|
||||
info.time = time;
|
||||
|
||||
g_array_append_val (display->touch_implicit_grabs, info);
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gdk_display_end_touch_grab (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
guint touch_id)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < display->touch_implicit_grabs->len; i++)
|
||||
{
|
||||
GdkTouchGrabInfo *info;
|
||||
|
||||
info = &g_array_index (display->touch_implicit_grabs,
|
||||
GdkTouchGrabInfo, i);
|
||||
|
||||
if (info->device == device &&
|
||||
info->touch_id == touch_id)
|
||||
{
|
||||
g_array_remove_index_fast (display->touch_implicit_grabs, i);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* _gdk_synthesize_crossing_events only works inside one toplevel.
|
||||
This function splits things into two calls if needed, converting the
|
||||
coordinates to the right toplevel */
|
||||
@@ -897,15 +975,25 @@ switch_to_pointer_grab (GdkDisplay *display,
|
||||
|
||||
if (grab == NULL) /* Ungrabbed, send events */
|
||||
{
|
||||
pointer_window = NULL;
|
||||
if (new_toplevel)
|
||||
{
|
||||
/* Find (possibly virtual) child window */
|
||||
pointer_window =
|
||||
_gdk_window_find_descendant_at (new_toplevel,
|
||||
x, y,
|
||||
NULL, NULL);
|
||||
}
|
||||
/* If the source device is a touch device, do not
|
||||
* propagate any enter event yet, until one is
|
||||
* synthesized when needed.
|
||||
*/
|
||||
if (source_device &&
|
||||
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
|
||||
info->need_touch_press_enter = TRUE;
|
||||
|
||||
pointer_window = NULL;
|
||||
|
||||
if (new_toplevel &&
|
||||
!info->need_touch_press_enter)
|
||||
{
|
||||
/* Find (possibly virtual) child window */
|
||||
pointer_window =
|
||||
_gdk_window_find_descendant_at (new_toplevel,
|
||||
x, y,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
if (pointer_window != last_grab->window)
|
||||
synthesize_crossing_events (display, device, source_device,
|
||||
@@ -964,12 +1052,15 @@ _gdk_display_device_grab_update (GdkDisplay *display,
|
||||
next_grab = NULL; /* Actually its not yet active */
|
||||
}
|
||||
|
||||
if (next_grab)
|
||||
_gdk_display_break_touch_grabs (display, device, next_grab->window);
|
||||
|
||||
if ((next_grab == NULL && current_grab->implicit_ungrab) ||
|
||||
(next_grab != NULL && current_grab->window != next_grab->window))
|
||||
generate_grab_broken_event (GDK_WINDOW (current_grab->window),
|
||||
(next_grab != NULL && current_grab->window != next_grab->window))
|
||||
generate_grab_broken_event (GDK_WINDOW (current_grab->window),
|
||||
device,
|
||||
current_grab->implicit,
|
||||
next_grab? next_grab->window : NULL);
|
||||
current_grab->implicit,
|
||||
next_grab? next_grab->window : NULL);
|
||||
|
||||
/* Remove old grab */
|
||||
grabs = g_list_delete_link (grabs, grabs);
|
||||
@@ -1028,6 +1119,34 @@ _gdk_display_has_device_grab (GdkDisplay *display,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GdkTouchGrabInfo *
|
||||
_gdk_display_has_touch_grab (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
guint touch_id,
|
||||
gulong serial)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < display->touch_implicit_grabs->len; i++)
|
||||
{
|
||||
GdkTouchGrabInfo *info;
|
||||
|
||||
info = &g_array_index (display->touch_implicit_grabs,
|
||||
GdkTouchGrabInfo, i);
|
||||
|
||||
if (info->device == device &&
|
||||
info->touch_id == touch_id)
|
||||
{
|
||||
if (serial >= info->serial)
|
||||
return info;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns true if last grab was ended
|
||||
* If if_child is non-NULL, end the grab only if the grabbed
|
||||
* window is the same as if_child or a descendant of it */
|
||||
@@ -1120,6 +1239,9 @@ _gdk_display_get_pointer_info (GdkDisplay *display,
|
||||
{
|
||||
GdkPointerWindowInfo *info;
|
||||
|
||||
if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
|
||||
device = gdk_device_get_associated_device (device);
|
||||
|
||||
if (G_UNLIKELY (!device))
|
||||
return NULL;
|
||||
|
||||
|
||||
@@ -60,6 +60,19 @@ typedef struct
|
||||
guint implicit : 1;
|
||||
} GdkDeviceGrabInfo;
|
||||
|
||||
/* Tracks information about a touch implicit grab on this display */
|
||||
typedef struct
|
||||
{
|
||||
GdkDevice *device;
|
||||
guint touch_id;
|
||||
|
||||
GdkWindow *window;
|
||||
GdkWindow *native_window;
|
||||
gulong serial;
|
||||
guint event_mask;
|
||||
guint32 time;
|
||||
} GdkTouchGrabInfo;
|
||||
|
||||
/* Tracks information about which window and position the pointer last was in.
|
||||
* This is useful when we need to synthesize events later.
|
||||
* Note that we track toplevel_under_pointer using enter/leave events,
|
||||
@@ -75,6 +88,8 @@ typedef struct
|
||||
gdouble toplevel_x, toplevel_y;
|
||||
guint32 state;
|
||||
guint32 button;
|
||||
GdkDevice *last_slave;
|
||||
guint need_touch_press_enter : 1;
|
||||
} GdkPointerWindowInfo;
|
||||
|
||||
typedef struct
|
||||
@@ -103,6 +118,7 @@ struct _GdkDisplay
|
||||
guint closed : 1; /* Whether this display has been closed */
|
||||
guint ignore_core_events : 1; /* Don't send core motion and button event */
|
||||
|
||||
GArray *touch_implicit_grabs;
|
||||
GHashTable *device_grabs;
|
||||
GHashTable *motion_hint_info;
|
||||
GdkDeviceManager *device_manager;
|
||||
@@ -260,6 +276,21 @@ gboolean _gdk_display_end_device_grab (GdkDisplay *display
|
||||
gboolean _gdk_display_check_grab_ownership (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
gulong serial);
|
||||
void _gdk_display_add_touch_grab (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
guint touch_id,
|
||||
GdkWindow *window,
|
||||
GdkWindow *native_window,
|
||||
GdkEventMask event_mask,
|
||||
unsigned long serial_start,
|
||||
guint32 time);
|
||||
GdkTouchGrabInfo * _gdk_display_has_touch_grab (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
guint touch_id,
|
||||
gulong serial);
|
||||
gboolean _gdk_display_end_touch_grab (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
guint touch_id);
|
||||
void _gdk_display_enable_motion_hints (GdkDisplay *display,
|
||||
GdkDevice *device);
|
||||
GdkPointerWindowInfo * _gdk_display_get_pointer_info (GdkDisplay *display,
|
||||
|
||||
+246
-4
@@ -445,6 +445,7 @@ gdk_event_new (GdkEventType type)
|
||||
switch (type)
|
||||
{
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
new_event->motion.x = 0.;
|
||||
new_event->motion.y = 0.;
|
||||
new_event->motion.x_root = 0.;
|
||||
@@ -454,6 +455,8 @@ gdk_event_new (GdkEventType type)
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
new_event->button.x = 0.;
|
||||
new_event->button.y = 0.;
|
||||
new_event->button.x_root = 0.;
|
||||
@@ -487,7 +490,31 @@ gdk_event_is_allocated (const GdkEvent *event)
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_gdk_event_set_pointer_emulated (GdkEvent *event,
|
||||
gboolean emulated)
|
||||
{
|
||||
if (gdk_event_is_allocated (event))
|
||||
{
|
||||
GdkEventPrivate *private = (GdkEventPrivate *) event;
|
||||
|
||||
if (emulated)
|
||||
private->flags |= GDK_EVENT_POINTER_EMULATED;
|
||||
else
|
||||
private->flags &= ~(GDK_EVENT_POINTER_EMULATED);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gdk_event_get_pointer_emulated (GdkEvent *event)
|
||||
{
|
||||
if (gdk_event_is_allocated (event))
|
||||
return (((GdkEventPrivate *) event)->flags & GDK_EVENT_POINTER_EMULATED) != 0;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_event_copy:
|
||||
* @event: a #GdkEvent
|
||||
@@ -558,12 +585,15 @@ gdk_event_copy (const GdkEvent *event)
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
if (event->button.axes)
|
||||
new_event->button.axes = g_memdup (event->button.axes,
|
||||
sizeof (gdouble) * gdk_device_get_n_axes (event->button.device));
|
||||
break;
|
||||
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
if (event->motion.axes)
|
||||
new_event->motion.axes = g_memdup (event->motion.axes,
|
||||
sizeof (gdouble) * gdk_device_get_n_axes (event->motion.device));
|
||||
@@ -583,6 +613,22 @@ gdk_event_copy (const GdkEvent *event)
|
||||
g_object_ref (new_event->selection.requestor);
|
||||
break;
|
||||
|
||||
case GDK_MULTITOUCH_ADDED:
|
||||
case GDK_MULTITOUCH_REMOVED:
|
||||
case GDK_MULTITOUCH_UPDATED:
|
||||
{
|
||||
GdkEventMotion **motion_events;
|
||||
guint i;
|
||||
|
||||
motion_events = g_new0 (GdkEventMotion*, event->multitouch.n_events);
|
||||
|
||||
for (i = 0; i < event->multitouch.n_events; i++)
|
||||
motion_events[i] = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event->multitouch.events[i]);
|
||||
|
||||
new_event->multitouch.events = motion_events;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -639,6 +685,8 @@ gdk_event_free (GdkEvent *event)
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
g_free (event->button.axes);
|
||||
break;
|
||||
|
||||
@@ -649,6 +697,7 @@ gdk_event_free (GdkEvent *event)
|
||||
break;
|
||||
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
g_free (event->motion.axes);
|
||||
break;
|
||||
|
||||
@@ -668,6 +717,20 @@ gdk_event_free (GdkEvent *event)
|
||||
g_object_unref (event->selection.requestor);
|
||||
break;
|
||||
|
||||
case GDK_MULTITOUCH_ADDED:
|
||||
case GDK_MULTITOUCH_REMOVED:
|
||||
case GDK_MULTITOUCH_UPDATED:
|
||||
if (event->multitouch.events)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < event->multitouch.n_events; i++)
|
||||
gdk_event_free ((GdkEvent *) event->multitouch.events[i]);
|
||||
|
||||
g_free (event->multitouch.events);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -696,11 +759,14 @@ gdk_event_get_time (const GdkEvent *event)
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
return event->motion.time;
|
||||
case GDK_BUTTON_PRESS:
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
return event->button.time;
|
||||
case GDK_SCROLL:
|
||||
return event->scroll.time;
|
||||
@@ -726,6 +792,10 @@ gdk_event_get_time (const GdkEvent *event)
|
||||
case GDK_DROP_START:
|
||||
case GDK_DROP_FINISHED:
|
||||
return event->dnd.time;
|
||||
case GDK_MULTITOUCH_ADDED:
|
||||
case GDK_MULTITOUCH_REMOVED:
|
||||
case GDK_MULTITOUCH_UPDATED:
|
||||
return event->multitouch.time;
|
||||
case GDK_CLIENT_EVENT:
|
||||
case GDK_VISIBILITY_NOTIFY:
|
||||
case GDK_CONFIGURE:
|
||||
@@ -771,12 +841,15 @@ gdk_event_get_state (const GdkEvent *event,
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
*state = event->motion.state;
|
||||
return TRUE;
|
||||
case GDK_BUTTON_PRESS:
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
*state = event->button.state;
|
||||
return TRUE;
|
||||
case GDK_SCROLL:
|
||||
@@ -790,6 +863,11 @@ gdk_event_get_state (const GdkEvent *event,
|
||||
case GDK_LEAVE_NOTIFY:
|
||||
*state = event->crossing.state;
|
||||
return TRUE;
|
||||
case GDK_MULTITOUCH_ADDED:
|
||||
case GDK_MULTITOUCH_REMOVED:
|
||||
case GDK_MULTITOUCH_UPDATED:
|
||||
*state = event->multitouch.state;
|
||||
return TRUE;
|
||||
case GDK_PROPERTY_NOTIFY:
|
||||
case GDK_VISIBILITY_NOTIFY:
|
||||
case GDK_CLIENT_EVENT:
|
||||
@@ -865,10 +943,13 @@ gdk_event_get_coords (const GdkEvent *event,
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
x = event->button.x;
|
||||
y = event->button.y;
|
||||
break;
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
x = event->motion.x;
|
||||
y = event->motion.y;
|
||||
break;
|
||||
@@ -908,6 +989,7 @@ gdk_event_get_root_coords (const GdkEvent *event,
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
x = event->motion.x_root;
|
||||
y = event->motion.y_root;
|
||||
break;
|
||||
@@ -919,6 +1001,8 @@ gdk_event_get_root_coords (const GdkEvent *event,
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
x = event->button.x_root;
|
||||
y = event->button.y_root;
|
||||
break;
|
||||
@@ -1162,7 +1246,8 @@ gdk_event_get_axis (const GdkEvent *event,
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
x = event->motion.x;
|
||||
y = event->motion.y;
|
||||
break;
|
||||
@@ -1172,6 +1257,8 @@ gdk_event_get_axis (const GdkEvent *event,
|
||||
break;
|
||||
case GDK_BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
x = event->button.x;
|
||||
y = event->button.y;
|
||||
break;
|
||||
@@ -1193,12 +1280,15 @@ gdk_event_get_axis (const GdkEvent *event,
|
||||
return TRUE;
|
||||
}
|
||||
else if (event->type == GDK_BUTTON_PRESS ||
|
||||
event->type == GDK_BUTTON_RELEASE)
|
||||
event->type == GDK_BUTTON_RELEASE ||
|
||||
event->type == GDK_TOUCH_PRESS ||
|
||||
event->type == GDK_TOUCH_RELEASE)
|
||||
{
|
||||
device = event->button.device;
|
||||
axes = event->button.axes;
|
||||
}
|
||||
else if (event->type == GDK_MOTION_NOTIFY)
|
||||
else if (event->type == GDK_MOTION_NOTIFY ||
|
||||
event->type == GDK_TOUCH_MOTION)
|
||||
{
|
||||
device = event->motion.device;
|
||||
axes = event->motion.axes;
|
||||
@@ -1235,12 +1325,15 @@ gdk_event_set_device (GdkEvent *event,
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
event->motion.device = device;
|
||||
break;
|
||||
case GDK_BUTTON_PRESS:
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
event->button.device = device;
|
||||
break;
|
||||
case GDK_SCROLL:
|
||||
@@ -1250,6 +1343,10 @@ gdk_event_set_device (GdkEvent *event,
|
||||
case GDK_PROXIMITY_OUT:
|
||||
event->proximity.device = device;
|
||||
break;
|
||||
case GDK_MULTITOUCH_ADDED:
|
||||
case GDK_MULTITOUCH_REMOVED:
|
||||
case GDK_MULTITOUCH_UPDATED:
|
||||
event->multitouch.device = device;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1282,17 +1379,24 @@ gdk_event_get_device (const GdkEvent *event)
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
return event->motion.device;
|
||||
case GDK_BUTTON_PRESS:
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
return event->button.device;
|
||||
case GDK_SCROLL:
|
||||
return event->scroll.device;
|
||||
case GDK_PROXIMITY_IN:
|
||||
case GDK_PROXIMITY_OUT:
|
||||
return event->proximity.device;
|
||||
case GDK_MULTITOUCH_ADDED:
|
||||
case GDK_MULTITOUCH_REMOVED:
|
||||
case GDK_MULTITOUCH_UPDATED:
|
||||
return event->multitouch.device;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1301,10 +1405,13 @@ gdk_event_get_device (const GdkEvent *event)
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
case GDK_BUTTON_PRESS:
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
case GDK_ENTER_NOTIFY:
|
||||
case GDK_LEAVE_NOTIFY:
|
||||
case GDK_FOCUS_CHANGE:
|
||||
@@ -1320,6 +1427,9 @@ gdk_event_get_device (const GdkEvent *event)
|
||||
case GDK_GRAB_BROKEN:
|
||||
case GDK_KEY_PRESS:
|
||||
case GDK_KEY_RELEASE:
|
||||
case GDK_MULTITOUCH_ADDED:
|
||||
case GDK_MULTITOUCH_REMOVED:
|
||||
case GDK_MULTITOUCH_UPDATED:
|
||||
{
|
||||
GdkDisplay *display;
|
||||
GdkDeviceManager *device_manager;
|
||||
@@ -1686,6 +1796,138 @@ gdk_event_get_screen (const GdkEvent *event)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_event_get_touch_id:
|
||||
* @event: a #GdkEvent
|
||||
* @touch_id: return location of the touch ID of a touch event
|
||||
*
|
||||
* If @event if of type %GDK_TOUCH_MOTION, %GDK_TOUCH_PRESS or
|
||||
* %GDK_TOUCH_RELEASE, fills in @touch_id and returns %TRUE,
|
||||
* else it returns %FALSE.
|
||||
*
|
||||
* Returns: %TRUE if the touch ID can be extracted from @event.
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
gboolean
|
||||
gdk_event_get_touch_id (const GdkEvent *event,
|
||||
guint *touch_id)
|
||||
{
|
||||
if (!event)
|
||||
return FALSE;
|
||||
|
||||
if (event->type == GDK_TOUCH_MOTION)
|
||||
{
|
||||
if (touch_id)
|
||||
*touch_id = event->motion.touch_id;
|
||||
return TRUE;
|
||||
}
|
||||
else if (event->type == GDK_TOUCH_PRESS ||
|
||||
event->type == GDK_TOUCH_RELEASE)
|
||||
{
|
||||
if (touch_id)
|
||||
*touch_id = event->button.touch_id;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (touch_id)
|
||||
*touch_id = 0;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_event_get_touch_area:
|
||||
* @event: a #GdkEvent
|
||||
*
|
||||
* This function takes a #GdkEvent coming from a touch device
|
||||
* (eg. gdk_event_get_source_device() returns a device of type
|
||||
* %GDK_SOURCE_TOUCH), and returns the area covered by the touch
|
||||
* as a #cairo_region_t. or %NULL if the device doesn't provide
|
||||
* this information, or the touch area information couldn't be
|
||||
* extracted from the event.
|
||||
*
|
||||
* <note><warning>Not all touch capable devices provide this
|
||||
* information, so provide fallbacks to this function returning
|
||||
* %NULL, even if the window receiving events is only meant
|
||||
* to react to touch events.</warning></note>
|
||||
*
|
||||
* Returns: (transfer full): the touch region, or %NULL if unavailable
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
cairo_region_t *
|
||||
gdk_event_get_touch_area (GdkEvent *event)
|
||||
{
|
||||
gdouble *axes, minor_axis, major_axis, orientation_axis;
|
||||
GdkAtom major, minor, orientation;
|
||||
GdkDevice *device;
|
||||
|
||||
g_return_val_if_fail (event != NULL, NULL);
|
||||
|
||||
device = gdk_event_get_source_device (event);
|
||||
|
||||
if (!device)
|
||||
return NULL;
|
||||
|
||||
if (event->type == GDK_MOTION_NOTIFY ||
|
||||
event->type == GDK_TOUCH_MOTION)
|
||||
axes = event->motion.axes;
|
||||
else if (event->type == GDK_BUTTON_PRESS ||
|
||||
event->type == GDK_2BUTTON_PRESS ||
|
||||
event->type == GDK_3BUTTON_PRESS ||
|
||||
event->type == GDK_BUTTON_RELEASE)
|
||||
axes = event->button.axes;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
major = gdk_atom_intern_static_string ("Abs MT Touch Major");
|
||||
minor = gdk_atom_intern_static_string ("Abs MT Touch Minor");
|
||||
orientation = gdk_atom_intern_static_string ("Abs MT Orientation");
|
||||
|
||||
if (gdk_device_get_axis_value (device, axes, major, &major_axis) &&
|
||||
gdk_device_get_axis_value (device, axes, minor, &minor_axis) &&
|
||||
gdk_device_get_axis_value (device, axes, orientation, &orientation_axis))
|
||||
{
|
||||
cairo_rectangle_int_t rect;
|
||||
GdkScreen *screen;
|
||||
gdouble x, y;
|
||||
|
||||
/* FIXME: We're assuming the device is mapped to a single screen,
|
||||
* could lead to stretched/shrinked shapes in multimonitor, although
|
||||
* that'd be an unusual setup for touchscreens.
|
||||
*/
|
||||
screen = gdk_window_get_screen (event->any.window);
|
||||
gdk_event_get_coords (event, &x, &y);
|
||||
|
||||
if (orientation_axis == 0)
|
||||
{
|
||||
/* Orientation is horizontal */
|
||||
rect.width = (gint) gdk_screen_get_width (screen) * major_axis;
|
||||
rect.height = (gint) gdk_screen_get_height (screen) * minor_axis;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Orientation is vertical */
|
||||
rect.height = (gint) gdk_screen_get_height (screen) * major_axis;
|
||||
rect.width = (gint) gdk_screen_get_width (screen) * minor_axis;
|
||||
}
|
||||
|
||||
/* Something is wrong here */
|
||||
if (rect.width == 0 ||
|
||||
rect.height == 0)
|
||||
return NULL;
|
||||
|
||||
rect.x = x - rect.width / 2;
|
||||
rect.y = y - rect.height / 2;
|
||||
|
||||
return cairo_region_create_rectangle (&rect);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_set_show_events:
|
||||
* @show_events: %TRUE to output event debugging information.
|
||||
|
||||
+104
-4
@@ -35,6 +35,7 @@
|
||||
#include <gdk/gdktypes.h>
|
||||
#include <gdk/gdkdnd.h>
|
||||
#include <gdk/gdkdevice.h>
|
||||
#include <gdk/gdktouchcluster.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -143,6 +144,7 @@ typedef struct _GdkEventDND GdkEventDND;
|
||||
typedef struct _GdkEventWindowState GdkEventWindowState;
|
||||
typedef struct _GdkEventSetting GdkEventSetting;
|
||||
typedef struct _GdkEventGrabBroken GdkEventGrabBroken;
|
||||
typedef struct _GdkEventMultiTouch GdkEventMultiTouch;
|
||||
|
||||
typedef union _GdkEvent GdkEvent;
|
||||
|
||||
@@ -263,6 +265,18 @@ typedef GdkFilterReturn (*GdkFilterFunc) (GdkXEvent *xevent,
|
||||
* was added in 2.8.
|
||||
* @GDK_DAMAGE: the content of the window has been changed. This event type
|
||||
* was added in 2.14.
|
||||
* @GDK_TOUCH_MOTION: A touch device has been updated. This event type was
|
||||
* added in 3.4.
|
||||
* @GDK_TOUCH_PRESS: A new touch stream has just started. This event type was
|
||||
* added in 3.4.
|
||||
* @GDK_TOUCH_RELEASE: A touch stream has finished. This event type was
|
||||
* added in 3.4.
|
||||
* @GDK_MULTITOUCH_ADDED: A touch ID was added to a #GdkTouchCluster. This
|
||||
* event type was added in 3.4.
|
||||
* @GDK_MULTITOUCH_UPDATED: A touch within a #GdkTouchCluster has been updated.
|
||||
* This event type was added in 3.4.
|
||||
* @GDK_MULTITOUCH_REMOVED: A touch ID was removed from a #GdkTouchCluster. This
|
||||
* event type was added in 3.4.
|
||||
* @GDK_EVENT_LAST: marks the end of the GdkEventType enumeration. Added in 2.18
|
||||
*
|
||||
* Specifies the type of the event.
|
||||
@@ -310,6 +324,12 @@ typedef enum
|
||||
GDK_OWNER_CHANGE = 34,
|
||||
GDK_GRAB_BROKEN = 35,
|
||||
GDK_DAMAGE = 36,
|
||||
GDK_TOUCH_MOTION = 37,
|
||||
GDK_TOUCH_PRESS = 38,
|
||||
GDK_TOUCH_RELEASE = 39,
|
||||
GDK_MULTITOUCH_ADDED = 40,
|
||||
GDK_MULTITOUCH_UPDATED = 41,
|
||||
GDK_MULTITOUCH_REMOVED = 42,
|
||||
GDK_EVENT_LAST /* helper variable for decls */
|
||||
} GdkEventType;
|
||||
|
||||
@@ -385,6 +405,13 @@ typedef enum
|
||||
* @GDK_CROSSING_GTK_UNGRAB: crossing because a GTK+ grab is deactivated.
|
||||
* @GDK_CROSSING_STATE_CHANGED: crossing because a GTK+ widget changed
|
||||
* state (e.g. sensitivity).
|
||||
* @GDK_CROSSING_TOUCH_PRESS: crossing because a touch device was pressed,
|
||||
* this event is synthetic as the pointer might have not left the window.
|
||||
* @GDK_CROSSING_TOUCH_RELEASE: crossing because a touch device was released.
|
||||
* this event is synthetic as the pointer might have not left the window.
|
||||
* @GDK_CROSSING_DEVICE_SWITCH: crossing because of a device switch (i.e.
|
||||
* a mouse taking control of the pointer after a touch device), this event
|
||||
* is synthetic as the pointer didn't leave the window.
|
||||
*
|
||||
* Specifies the crossing mode for #GdkEventCrossing.
|
||||
*/
|
||||
@@ -395,7 +422,10 @@ typedef enum
|
||||
GDK_CROSSING_UNGRAB,
|
||||
GDK_CROSSING_GTK_GRAB,
|
||||
GDK_CROSSING_GTK_UNGRAB,
|
||||
GDK_CROSSING_STATE_CHANGED
|
||||
GDK_CROSSING_STATE_CHANGED,
|
||||
GDK_CROSSING_TOUCH_PRESS,
|
||||
GDK_CROSSING_TOUCH_RELEASE,
|
||||
GDK_CROSSING_DEVICE_SWITCH
|
||||
} GdkCrossingMode;
|
||||
|
||||
/**
|
||||
@@ -552,8 +582,13 @@ struct _GdkEventVisibility
|
||||
* screen.
|
||||
* @y_root: the y coordinate of the pointer relative to the root of the
|
||||
* screen.
|
||||
* @touch_id: touch ID, only meaningful if event is of type %GDK_TOUCH_MOTION.
|
||||
*
|
||||
* Generated when the pointer moves.
|
||||
* Generated when the pointer/touch moves.
|
||||
*
|
||||
* If the event has a type of %GDK_TOUCH_MOTION, this event will
|
||||
* pertain to a sequence identified by gdk_event_get_touch_id().
|
||||
* With multitouch devices, there may be several ongoing sequences.
|
||||
*/
|
||||
struct _GdkEventMotion
|
||||
{
|
||||
@@ -568,12 +603,63 @@ struct _GdkEventMotion
|
||||
gint16 is_hint;
|
||||
GdkDevice *device;
|
||||
gdouble x_root, y_root;
|
||||
guint touch_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* GdkEventMultiTouch:
|
||||
* @type: the type of the event (%GDK_MULTITOUCH_ADDED, %GDK_MULTITOUCH_UPDATED
|
||||
* or %GDK_MULTITOUCH_REMOVED).
|
||||
* @window: the window which received the event.
|
||||
* @send_event: %TRUE if the event was sent explicitly (e.g. using
|
||||
* <function>XSendEvent</function>).
|
||||
* @time: the time of the event in milliseconds.
|
||||
* @state: (type GdkModifierType): a bit-mask representing the state of
|
||||
* the modifier keys (e.g. Control, Shift and Alt) and the pointer
|
||||
* buttons. See #GdkModifierType.
|
||||
* @device: the device where the event originated.
|
||||
* @group: the #GdkTouchCluster containing the touches that generated this event
|
||||
* @events: an array of events of type %GDK_TOUCH_MOTION for the touches in @group
|
||||
* @updated_touch_id: the touch ID that caused this event to be generated
|
||||
* @n_events: the number of events in @events
|
||||
* @n_updated_event: the index in @events of the event corresponding to
|
||||
* @updated_touch_id, or -1 for %GDK_MULTITOUCH_REMOVED events.
|
||||
*
|
||||
* Used for multitouch events. The @type field will be one of
|
||||
* %GDK_MULTITOUCH_ADDED, %GDK_MULTITOUCH_UPDATED or
|
||||
* %GDK_MULTITOUCH_REMOVED.
|
||||
*
|
||||
* Multitouch events group the events from the touches in a
|
||||
* #GdkTouchCluster, so one of these events is generated
|
||||
* whenever a touch ID generates a new event, or a touch ID
|
||||
* is added or removed.
|
||||
*
|
||||
* For any given touch ID, %GDK_MULTITOUCH_ADDED and
|
||||
* %GDK_MULTITOUCH_REMOVED events are always paired,
|
||||
* with any number of %GDK_MULTITOUCH_UPDATED
|
||||
* events in between. The minimum event stream is an
|
||||
* added/removed pair.
|
||||
*/
|
||||
struct _GdkEventMultiTouch
|
||||
{
|
||||
GdkEventType type;
|
||||
GdkWindow *window;
|
||||
gint8 send_event;
|
||||
guint32 time;
|
||||
guint state;
|
||||
GdkDevice *device;
|
||||
GdkTouchCluster *group;
|
||||
GdkEventMotion **events;
|
||||
guint updated_touch_id;
|
||||
gint8 n_events;
|
||||
gint8 n_updated_event;
|
||||
};
|
||||
|
||||
/**
|
||||
* GdkEventButton:
|
||||
* @type: the type of the event (%GDK_BUTTON_PRESS, %GDK_2BUTTON_PRESS,
|
||||
* %GDK_3BUTTON_PRESS or %GDK_BUTTON_RELEASE).
|
||||
* %GDK_3BUTTON_PRESS, %GDK_BUTTON_RELEASE, %GDK_TOUCH_PRESS or
|
||||
* %GDK_TOUCH_RELEASE).
|
||||
* @window: the window which received the event.
|
||||
* @send_event: %TRUE if the event was sent explicitly (e.g. using
|
||||
* <function>XSendEvent</function>).
|
||||
@@ -594,10 +680,13 @@ struct _GdkEventMotion
|
||||
* screen.
|
||||
* @y_root: the y coordinate of the pointer relative to the root of the
|
||||
* screen.
|
||||
* @touch_id: touch ID, only meaningful if event is of type %GDK_TOUCH_PRESS
|
||||
* or %GDK_TOUCH_RELEASE.
|
||||
*
|
||||
* Used for button press and button release events. The
|
||||
* @type field will be one of %GDK_BUTTON_PRESS,
|
||||
* %GDK_2BUTTON_PRESS, %GDK_3BUTTON_PRESS, and %GDK_BUTTON_RELEASE.
|
||||
* %GDK_2BUTTON_PRESS, %GDK_3BUTTON_PRESS, %GDK_BUTTON_RELEASE,
|
||||
* %GDK_TOUCH_PRESS and %GDK_TOUCH_RELEASE.
|
||||
*
|
||||
* Double and triple-clicks result in a sequence of events being received.
|
||||
* For double-clicks the order of events will be:
|
||||
@@ -629,6 +718,11 @@ struct _GdkEventMotion
|
||||
* For a double click to occur, the second button press must occur within
|
||||
* 1/4 of a second of the first. For a triple click to occur, the third
|
||||
* button press must also occur within 1/2 second of the first button press.
|
||||
*
|
||||
* If the event has a type of %GDK_TOUCH_PRESS or %GDK_TOUCH_RELEASE,
|
||||
* this event will pertain to a sequence identified by
|
||||
* gdk_event_get_touch_id(). With multitouch devices, there may be
|
||||
* several ongoing sequences.
|
||||
*/
|
||||
struct _GdkEventButton
|
||||
{
|
||||
@@ -643,6 +737,7 @@ struct _GdkEventButton
|
||||
guint button;
|
||||
GdkDevice *device;
|
||||
gdouble x_root, y_root;
|
||||
guint touch_id;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1086,6 +1181,7 @@ union _GdkEvent
|
||||
GdkEventWindowState window_state;
|
||||
GdkEventSetting setting;
|
||||
GdkEventGrabBroken grab_broken;
|
||||
GdkEventMultiTouch multitouch;
|
||||
};
|
||||
|
||||
GType gdk_event_get_type (void) G_GNUC_CONST;
|
||||
@@ -1150,6 +1246,10 @@ void gdk_event_set_screen (GdkEvent *event,
|
||||
GdkScreen *screen);
|
||||
GdkScreen *gdk_event_get_screen (const GdkEvent *event);
|
||||
|
||||
gboolean gdk_event_get_touch_id (const GdkEvent *event,
|
||||
guint *touch_id);
|
||||
cairo_region_t * gdk_event_get_touch_area (GdkEvent *event);
|
||||
|
||||
void gdk_set_show_events (gboolean show_events);
|
||||
gboolean gdk_get_show_events (void);
|
||||
|
||||
|
||||
+20
-1
@@ -150,7 +150,13 @@ typedef enum
|
||||
/* Following flag is set for events on the event queue during
|
||||
* translation and cleared afterwards.
|
||||
*/
|
||||
GDK_EVENT_PENDING = 1 << 0
|
||||
GDK_EVENT_PENDING = 1 << 0,
|
||||
|
||||
/* The following flag is set for:
|
||||
* 1) touch events emulating pointer events
|
||||
* 2) pointer events being emulated by a touch sequence.
|
||||
*/
|
||||
GDK_EVENT_POINTER_EMULATED = 1 << 1
|
||||
} GdkEventFlags;
|
||||
|
||||
struct _GdkEventPrivate
|
||||
@@ -260,6 +266,12 @@ struct _GdkWindow
|
||||
gulong device_changed_handler_id;
|
||||
|
||||
guint num_offscreen_children;
|
||||
|
||||
/* Store of latest per-touch events, keys are
|
||||
* GdkDevices, values are hashtables of touchID/info
|
||||
*/
|
||||
GHashTable *touch_event_tracker;
|
||||
GList *touch_clusters;
|
||||
};
|
||||
|
||||
#define GDK_WINDOW_TYPE(d) (((GDK_WINDOW (d)))->window_type)
|
||||
@@ -275,6 +287,10 @@ GdkEvent* _gdk_event_unqueue (GdkDisplay *display);
|
||||
void _gdk_event_filter_unref (GdkWindow *window,
|
||||
GdkEventFilter *filter);
|
||||
|
||||
void _gdk_event_set_pointer_emulated (GdkEvent *event,
|
||||
gboolean emulated);
|
||||
gboolean _gdk_event_get_pointer_emulated (GdkEvent *event);
|
||||
|
||||
void _gdk_event_emit (GdkEvent *event);
|
||||
GList* _gdk_event_queue_find_first (GdkDisplay *display);
|
||||
void _gdk_event_queue_remove_link (GdkDisplay *display,
|
||||
@@ -318,6 +334,9 @@ gboolean _gdk_window_update_viewable (GdkWindow *window);
|
||||
|
||||
void _gdk_window_process_updates_recurse (GdkWindow *window,
|
||||
cairo_region_t *expose_region);
|
||||
gboolean _gdk_window_finish_touch_id (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
guint touch_id);
|
||||
|
||||
void _gdk_screen_close (GdkScreen *screen);
|
||||
|
||||
|
||||
@@ -0,0 +1,448 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2011 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdktouchcluster.h"
|
||||
#include "gdkintl.h"
|
||||
|
||||
/**
|
||||
* SECTION:touchcluster
|
||||
* @Short_description: Multitouch handling
|
||||
* @Title: Multitouch
|
||||
* @See_also: #GdkEventMultiTouch
|
||||
*
|
||||
* #GdkTouchCluster is an object that gathers touch IDs from a
|
||||
* #GdkDevice, in order to send #GdkEventMultiTouch events
|
||||
* whenever a touch ID that is contained in the cluster sends
|
||||
* an event.
|
||||
*
|
||||
* #GdkTouchCluster<!-- -->s are always associated to a window,
|
||||
* you need to create them through gdk_window_create_touch_cluster(),
|
||||
* and free them through gdk_window_remove_touch_cluster().
|
||||
*
|
||||
* Touch IDs from devices can be obtained from %GDK_TOUCH_PRESS,
|
||||
* %GDK_TOUCH_MOTION or %GDK_TOUCH_RELEASE events through
|
||||
* gdk_event_get_touch_id(), and then be added via
|
||||
* gdk_touch_cluster_add_touch(). Note that touch IDs are
|
||||
* very transient, and they must be dealt with as such.
|
||||
* touch IDs must not be stored after a GDK_TOUCH_RELEASE,
|
||||
* and should always be retrieved from the events being
|
||||
* currently received.
|
||||
*
|
||||
* <example>
|
||||
* <title>Adding touch IDs to a cluster in a GTK+ widget</title>
|
||||
* <programlisting>
|
||||
* static gboolean
|
||||
* widget_button_press (GtkWidget *widget,
|
||||
* GdkEvent *event)
|
||||
* {
|
||||
* guint touch_id;
|
||||
*
|
||||
* if (gdk_event_get_touch_id (event, &touch_id))
|
||||
* {
|
||||
* /<!-- -->* It is a touch event, delegate processing
|
||||
* * to the multitouch event handler
|
||||
* *<!-- -->/
|
||||
* gdk_touch_cluster_add_touch (priv->touch_cluster, touch_id);
|
||||
* return TRUE;
|
||||
* }
|
||||
*
|
||||
* /<!-- -->* Normal button processing *<!-- -->/
|
||||
* ...
|
||||
* }
|
||||
* </programlisting>
|
||||
* </example>
|
||||
*
|
||||
* Anytime a touch ID is within a cluster, no %GDK_TOUCH_PRESS,
|
||||
* %GDK_TOUCH_MOTION or %GDK_TOUCH_RELEASE events will happen
|
||||
* for the individual touch. The event will be available instead
|
||||
* as part of the #GdkMultitouchEvent that will be emitted. This
|
||||
* will hold true until gdk_touch_cluster_remove_touch() is
|
||||
* called for it. Note that GTK+ will automatically take a
|
||||
* touch ID out of any cluster if %GDK_TOUCH_RELEASE is gotten
|
||||
* internally.
|
||||
*
|
||||
* <example>
|
||||
* <title>Typical multitouch event handler</title>
|
||||
* <programlisting>
|
||||
* static gboolean
|
||||
* widget_multitouch_event (GtkWidget *widget,
|
||||
* GdkEvent *event)
|
||||
* {
|
||||
* if (event->type == GDK_MULTITOUCH_ADDED ||
|
||||
* event->type == GDK_MULTITOUCH_REMOVED)
|
||||
* {
|
||||
* /<!-- -->* Update control mode based
|
||||
* * on the current number of touches
|
||||
* *<!-- -->/
|
||||
* priv->control_mode = update_control_mode (event->multitouch.n_events);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* /<!-- -->* A touch ID in the cluster has updated
|
||||
* * its coordinates, update widget based on the
|
||||
* * current control mode.
|
||||
* *<!-- -->/
|
||||
* update_view (widget, priv->control_mode,
|
||||
* event->multitouch.events,
|
||||
* event->multitouch.n_events);
|
||||
* }
|
||||
*
|
||||
* return TRUE;
|
||||
* }
|
||||
* </programlisting>
|
||||
* </example>
|
||||
*/
|
||||
|
||||
typedef struct GdkTouchClusterPrivate GdkTouchClusterPrivate;
|
||||
|
||||
struct GdkTouchClusterPrivate
|
||||
{
|
||||
GdkDevice *device;
|
||||
GArray *touches;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_DEVICE
|
||||
};
|
||||
|
||||
enum {
|
||||
TOUCH_ADDED,
|
||||
TOUCH_REMOVED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals [LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void gdk_touch_cluster_finalize (GObject *object);
|
||||
static void gdk_touch_cluster_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void gdk_touch_cluster_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GdkTouchCluster, gdk_touch_cluster, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gdk_touch_cluster_class_init (GdkTouchClusterClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gdk_touch_cluster_finalize;
|
||||
object_class->get_property = gdk_touch_cluster_get_property;
|
||||
object_class->set_property = gdk_touch_cluster_set_property;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_DEVICE,
|
||||
g_param_spec_object ("device",
|
||||
P_("Device"),
|
||||
P_("Device attached to the cluster"),
|
||||
GDK_TYPE_DEVICE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
signals[TOUCH_ADDED] =
|
||||
g_signal_new (g_intern_static_string ("touch-added"),
|
||||
G_TYPE_FROM_CLASS (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GdkTouchClusterClass, touch_added),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
signals[TOUCH_REMOVED] =
|
||||
g_signal_new (g_intern_static_string ("touch-removed"),
|
||||
G_TYPE_FROM_CLASS (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GdkTouchClusterClass, touch_removed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
|
||||
g_type_class_add_private (object_class, sizeof (GdkTouchClusterPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_touch_cluster_init (GdkTouchCluster *cluster)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
|
||||
priv = cluster->priv = G_TYPE_INSTANCE_GET_PRIVATE (cluster,
|
||||
GDK_TYPE_TOUCH_CLUSTER,
|
||||
GdkTouchClusterPrivate);
|
||||
priv->touches = g_array_new (FALSE, FALSE, sizeof (guint));
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_touch_cluster_finalize (GObject *object)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
|
||||
priv = GDK_TOUCH_CLUSTER (object)->priv;
|
||||
g_array_free (priv->touches, TRUE);
|
||||
|
||||
G_OBJECT_CLASS (gdk_touch_cluster_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_touch_cluster_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DEVICE:
|
||||
gdk_touch_cluster_set_device (GDK_TOUCH_CLUSTER (object),
|
||||
g_value_get_object (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_touch_cluster_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
|
||||
priv = GDK_TOUCH_CLUSTER (object)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DEVICE:
|
||||
g_value_set_object (value, priv->device);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_touch_cluster_add_touch:
|
||||
* @cluster: a #GdkTouchCluster
|
||||
* @touch_id: a touch ID from a touch event
|
||||
*
|
||||
* Adds a touch ID to @cluster, so it will generate a
|
||||
* %GDK_MULTITOUCH_ADDED event, followed by %GDK_MULTITOUCH_UPDATED
|
||||
* events whenever this touch ID is updated.
|
||||
*
|
||||
* If @touch_id already pertained to another #GdkTouchCluster, it
|
||||
* will be removed from it, generating a %GDK_MULTITOUCH_REMOVED
|
||||
* for that another cluster.
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
void
|
||||
gdk_touch_cluster_add_touch (GdkTouchCluster *cluster,
|
||||
guint touch_id)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (GDK_IS_TOUCH_CLUSTER (cluster));
|
||||
|
||||
priv = cluster->priv;
|
||||
|
||||
for (i = 0; i < priv->touches->len; i++)
|
||||
{
|
||||
if (touch_id == g_array_index (priv->touches, guint, i))
|
||||
return;
|
||||
}
|
||||
|
||||
g_array_append_val (priv->touches, touch_id);
|
||||
g_signal_emit (cluster, signals [TOUCH_ADDED], 0, touch_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_touch_cluster_remove_touch:
|
||||
* @cluster: a #GdkTouchCluster
|
||||
* @touch_id: a touch ID from a touch event
|
||||
*
|
||||
* Removes a touch ID from @cluster, generating a %GDK_MULTITOUCH_REMOVED
|
||||
* event for @cluster, and causing any further input from @touch_id
|
||||
* to be reported trough %GDK_TOUCH_MOTION events.
|
||||
*
|
||||
* <note><para>
|
||||
* Note that GTK+ automatically removes a touch ID from any cluster
|
||||
* if a %GDK_TOUCH_RELEASE event is gotten internally.
|
||||
* </para></note>
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
void
|
||||
gdk_touch_cluster_remove_touch (GdkTouchCluster *cluster,
|
||||
guint touch_id)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (GDK_IS_TOUCH_CLUSTER (cluster));
|
||||
|
||||
priv = cluster->priv;
|
||||
|
||||
for (i = 0; i < priv->touches->len; i++)
|
||||
{
|
||||
if (touch_id == g_array_index (priv->touches, guint, i))
|
||||
{
|
||||
g_array_remove_index_fast (priv->touches, i);
|
||||
g_signal_emit (cluster, signals [TOUCH_REMOVED], 0, touch_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_touch_cluster_remove_all:
|
||||
* @cluster: a #GdkTouchCluster
|
||||
*
|
||||
* Removes all touch IDs from @cluster.
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
void
|
||||
gdk_touch_cluster_remove_all (GdkTouchCluster *cluster)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
guint touch_id;
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (GDK_IS_TOUCH_CLUSTER (cluster));
|
||||
|
||||
priv = cluster->priv;
|
||||
|
||||
for (i = priv->touches->len - 1; i >= 0; i--)
|
||||
{
|
||||
touch_id = g_array_index (priv->touches, guint, i);
|
||||
g_signal_emit (cluster, signals [TOUCH_REMOVED], 0, touch_id);
|
||||
g_array_remove_index_fast (priv->touches, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gdk_touch_cluster_get_touches:
|
||||
* @cluster: a #GdkTouchCluster
|
||||
* @length: return location for the number of touches returned
|
||||
*
|
||||
* Returns the list of touches as an array of @guint.
|
||||
*
|
||||
* Returns: (transfer full) (array zero-terminated=0 length=length) (element-type uint): A list of touch IDs.
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
guint *
|
||||
gdk_touch_cluster_get_touches (GdkTouchCluster *cluster,
|
||||
gint *len)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_TOUCH_CLUSTER (cluster), NULL);
|
||||
|
||||
priv = cluster->priv;
|
||||
|
||||
if (len)
|
||||
*len = (gint) priv->touches->len;
|
||||
|
||||
return g_memdup (priv->touches->data,
|
||||
sizeof (guint) * priv->touches->len);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_touch_cluster_get_n_touches:
|
||||
* @cluster: a #GdkTouchCluster
|
||||
*
|
||||
* Returns the number of touches contained in @cluster.
|
||||
*
|
||||
* Returns: The number of touches.
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
gint
|
||||
gdk_touch_cluster_get_n_touches (GdkTouchCluster *cluster)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_TOUCH_CLUSTER (cluster), 0);
|
||||
|
||||
priv = cluster->priv;
|
||||
return (gint) priv->touches->len;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_touch_cluster_set_device:
|
||||
* @cluster: a #GdkTouchCluster
|
||||
* @device: a #GdkDevice
|
||||
*
|
||||
* Sets the current device associated to @cluster, all contained
|
||||
* touch IDs must pertain to this device. As a consequence,
|
||||
* gdk_touch_cluster_remove_all() will be called on @cluster
|
||||
* if the current device changes.
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
void
|
||||
gdk_touch_cluster_set_device (GdkTouchCluster *cluster,
|
||||
GdkDevice *device)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
|
||||
g_return_if_fail (GDK_IS_TOUCH_CLUSTER (cluster));
|
||||
g_return_if_fail (!device || GDK_IS_DEVICE (device));
|
||||
|
||||
priv = cluster->priv;
|
||||
|
||||
if (priv->device != device)
|
||||
gdk_touch_cluster_remove_all (cluster);
|
||||
|
||||
priv->device = device;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_touch_cluster_get_device:
|
||||
* @cluster: a #GdkTouchCluster
|
||||
*
|
||||
* Returns the slave/floating device this touch cluster pertains to,
|
||||
* only touch IDs from this device can be included in @cluster.
|
||||
* the #GdkDevice will typically have the %GDK_SOURCE_TOUCH input source.
|
||||
*
|
||||
* Returns: (transfer none): The #GdkDevice generating the contained touch IDs
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
GdkDevice *
|
||||
gdk_touch_cluster_get_device (GdkTouchCluster *cluster)
|
||||
{
|
||||
GdkTouchClusterPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_TOUCH_CLUSTER (cluster), NULL);
|
||||
|
||||
priv = cluster->priv;
|
||||
return priv->device;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2011 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_TOUCH_CLUSTER_H__
|
||||
#define __GDK_TOUCH_CLUSTER_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gdk/gdkdevice.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDK_TYPE_TOUCH_CLUSTER (gdk_touch_cluster_get_type ())
|
||||
#define GDK_TOUCH_CLUSTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_TOUCH_CLUSTER, GdkTouchCluster))
|
||||
#define GDK_IS_TOUCH_CLUSTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_TOUCH_CLUSTER))
|
||||
|
||||
typedef struct _GdkTouchCluster GdkTouchCluster;
|
||||
typedef struct _GdkTouchClusterClass GdkTouchClusterClass;
|
||||
|
||||
struct _GdkTouchCluster
|
||||
{
|
||||
GObject parent_instance;
|
||||
gpointer priv;
|
||||
};
|
||||
|
||||
struct _GdkTouchClusterClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* touch_added) (GdkTouchCluster *cluster,
|
||||
guint touch_id);
|
||||
void (* touch_removed) (GdkTouchCluster *cluster,
|
||||
guint touch_id);
|
||||
|
||||
gpointer padding[16];
|
||||
};
|
||||
|
||||
GType gdk_touch_cluster_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void gdk_touch_cluster_add_touch (GdkTouchCluster *cluster,
|
||||
guint touch_id);
|
||||
void gdk_touch_cluster_remove_touch (GdkTouchCluster *cluster,
|
||||
guint touch_id);
|
||||
void gdk_touch_cluster_remove_all (GdkTouchCluster *cluster);
|
||||
|
||||
guint * gdk_touch_cluster_get_touches (GdkTouchCluster *cluster,
|
||||
gint *length);
|
||||
gint gdk_touch_cluster_get_n_touches (GdkTouchCluster *cluster);
|
||||
|
||||
void gdk_touch_cluster_set_device (GdkTouchCluster *cluster,
|
||||
GdkDevice *device);
|
||||
GdkDevice * gdk_touch_cluster_get_device (GdkTouchCluster *cluster);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_TOUCH_CLUSTER_H__ */
|
||||
+10
-1
@@ -350,6 +350,7 @@ typedef enum
|
||||
* @GDK_SUBSTRUCTURE_MASK: receive events about window configuration changes of
|
||||
* child windows
|
||||
* @GDK_SCROLL_MASK: receive scroll events
|
||||
* @GDK_TOUCH_MASK: receive (multi)touch events
|
||||
* @GDK_ALL_EVENTS_MASK: the combination of all the above event masks.
|
||||
*
|
||||
* A set of bit-flags to indicate which events a window is to receive.
|
||||
@@ -365,6 +366,13 @@ typedef enum
|
||||
* some of which are marked as a hint (the is_hint member is %TRUE).
|
||||
* To receive more motion events after a motion hint event, the application
|
||||
* needs to asks for more, by calling gdk_event_request_motions().
|
||||
*
|
||||
* If %GDK_TOUCH_MASK is enabled, the window will receive (multi)touch events
|
||||
* from touch-enabled devices. Those will come as sequences #GdkEventMotion
|
||||
* with type %GDK_TOUCH_MOTION, enclosed by 2 #GdkEventButton events with
|
||||
* type %GDK_TOUCH_PRESS / %GDK_TOUCH_RELEASE. gdk_event_get_touch_id() will
|
||||
* return the touch ID on those events, so different sequences may be
|
||||
* distinguished.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
@@ -389,7 +397,8 @@ typedef enum
|
||||
GDK_PROXIMITY_OUT_MASK = 1 << 19,
|
||||
GDK_SUBSTRUCTURE_MASK = 1 << 20,
|
||||
GDK_SCROLL_MASK = 1 << 21,
|
||||
GDK_ALL_EVENTS_MASK = 0x3FFFFE
|
||||
GDK_TOUCH_MASK = 1 << 22,
|
||||
GDK_ALL_EVENTS_MASK = 0x3FFFFF
|
||||
} GdkEventMask;
|
||||
|
||||
/**
|
||||
|
||||
+723
-74
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <gdk/gdktypes.h>
|
||||
#include <gdk/gdkevents.h>
|
||||
#include <gdk/gdktouchcluster.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -878,6 +879,12 @@ void gdk_window_set_support_multidevice (GdkWindow *window,
|
||||
gboolean support_multidevice);
|
||||
gboolean gdk_window_get_support_multidevice (GdkWindow *window);
|
||||
|
||||
/* Multitouch support */
|
||||
GdkTouchCluster * gdk_window_create_touch_cluster (GdkWindow *window,
|
||||
GdkDevice *device);
|
||||
void gdk_window_remove_touch_cluster (GdkWindow *window,
|
||||
GdkTouchCluster *cluster);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_WINDOW_H__ */
|
||||
|
||||
@@ -7,6 +7,8 @@ libgdkx11includedir = $(includedir)/gtk-3.0/gdk/x11
|
||||
AM_CPPFLAGS = \
|
||||
-DG_LOG_DOMAIN=\"Gdk\" \
|
||||
-DGDK_COMPILATION \
|
||||
-DXINPUT2_2_USE_UNSTABLE_PROTOCOL \
|
||||
-DXINPUT2_1_USE_UNSTABLE_PROTOCOL \
|
||||
-I$(top_srcdir) \
|
||||
-I$(top_srcdir)/gdk \
|
||||
-I$(top_builddir)/gdk \
|
||||
|
||||
+31
-5
@@ -388,6 +388,7 @@ gdk_x11_device_xi2_grab (GdkDevice *device,
|
||||
guint32 time_)
|
||||
{
|
||||
GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (device);
|
||||
GdkX11DeviceManagerXI2 *device_manager_xi2;
|
||||
GdkDisplay *display;
|
||||
XIEventMask mask;
|
||||
Window xwindow;
|
||||
@@ -395,6 +396,7 @@ gdk_x11_device_xi2_grab (GdkDevice *device,
|
||||
gint status;
|
||||
|
||||
display = gdk_device_get_display (device);
|
||||
device_manager_xi2 = GDK_X11_DEVICE_MANAGER_XI2 (gdk_display_get_device_manager (display));
|
||||
|
||||
/* FIXME: confine_to is actually unused */
|
||||
|
||||
@@ -409,7 +411,9 @@ gdk_x11_device_xi2_grab (GdkDevice *device,
|
||||
}
|
||||
|
||||
mask.deviceid = device_xi2->device_id;
|
||||
mask.mask = _gdk_x11_device_xi2_translate_event_mask (event_mask, &mask.mask_len);
|
||||
mask.mask = _gdk_x11_device_xi2_translate_event_mask (device_manager_xi2,
|
||||
event_mask,
|
||||
&mask.mask_len);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
|
||||
@@ -625,10 +629,17 @@ gdk_x11_device_xi2_select_window_events (GdkDevice *device,
|
||||
GdkEventMask event_mask)
|
||||
{
|
||||
GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (device);
|
||||
GdkX11DeviceManagerXI2 *device_manager_xi2;
|
||||
GdkDisplay *display;
|
||||
XIEventMask evmask;
|
||||
|
||||
display = gdk_device_get_display (device);
|
||||
device_manager_xi2 = GDK_X11_DEVICE_MANAGER_XI2 (gdk_display_get_device_manager (display));
|
||||
|
||||
evmask.deviceid = device_xi2->device_id;
|
||||
evmask.mask = _gdk_x11_device_xi2_translate_event_mask (event_mask, &evmask.mask_len);
|
||||
evmask.mask = _gdk_x11_device_xi2_translate_event_mask (device_manager_xi2,
|
||||
event_mask,
|
||||
&evmask.mask_len);
|
||||
|
||||
XISelectEvents (GDK_WINDOW_XDISPLAY (window),
|
||||
GDK_WINDOW_XID (window),
|
||||
@@ -638,10 +649,14 @@ gdk_x11_device_xi2_select_window_events (GdkDevice *device,
|
||||
}
|
||||
|
||||
guchar *
|
||||
_gdk_x11_device_xi2_translate_event_mask (GdkEventMask event_mask,
|
||||
gint *len)
|
||||
_gdk_x11_device_xi2_translate_event_mask (GdkX11DeviceManagerXI2 *device_manager_xi2,
|
||||
GdkEventMask event_mask,
|
||||
gint *len)
|
||||
{
|
||||
guchar *mask;
|
||||
gint minor;
|
||||
|
||||
g_object_get (device_manager_xi2, "minor", &minor, NULL);
|
||||
|
||||
*len = XIMaskLen (XI_LASTEVENT);
|
||||
mask = g_new0 (guchar, *len);
|
||||
@@ -690,6 +705,17 @@ _gdk_x11_device_xi2_translate_event_mask (GdkEventMask event_mask,
|
||||
XISetMask (mask, XI_FocusOut);
|
||||
}
|
||||
|
||||
#ifdef XINPUT_2_2
|
||||
/* XInput 2.2 includes multitouch support */
|
||||
if (minor >= 2 &&
|
||||
event_mask & GDK_TOUCH_MASK)
|
||||
{
|
||||
XISetMask (mask, XI_TouchBegin);
|
||||
XISetMask (mask, XI_TouchUpdate);
|
||||
XISetMask (mask, XI_TouchEnd);
|
||||
}
|
||||
#endif /* XINPUT_2_2 */
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
@@ -742,7 +768,7 @@ _gdk_x11_device_xi2_translate_state (XIModifierState *mods_state,
|
||||
{
|
||||
gint group;
|
||||
|
||||
group = group_state->base + group_state->latched + group_state->locked;
|
||||
group = group_state->base | group_state->latched | group_state->locked;
|
||||
|
||||
/* FIXME: do we need the XKB complications for this ? */
|
||||
group = CLAMP(group, 0, 3);
|
||||
|
||||
@@ -54,7 +54,11 @@ _gdk_x11_device_manager_new (GdkDisplay *display)
|
||||
int major, minor;
|
||||
|
||||
major = 2;
|
||||
#ifdef XINPUT_2_2
|
||||
minor = 2;
|
||||
#else
|
||||
minor = 0;
|
||||
#endif /* XINPUT_2_2 */
|
||||
|
||||
if (!_gdk_disable_multidevice &&
|
||||
XIQueryVersion (xdisplay, &major, &minor) != BadRequest)
|
||||
@@ -66,6 +70,8 @@ _gdk_x11_device_manager_new (GdkDisplay *display)
|
||||
device_manager_xi2 = g_object_new (GDK_TYPE_X11_DEVICE_MANAGER_XI2,
|
||||
"display", display,
|
||||
"opcode", opcode,
|
||||
"major", major,
|
||||
"minor", minor,
|
||||
NULL);
|
||||
|
||||
return GDK_DEVICE_MANAGER (device_manager_xi2);
|
||||
|
||||
+166
-61
@@ -29,6 +29,7 @@
|
||||
#include "gdkprivate-x11.h"
|
||||
#include "gdkintl.h"
|
||||
#include "gdkkeysyms.h"
|
||||
#include "gdkinternals.h"
|
||||
|
||||
#ifdef XINPUT_2
|
||||
|
||||
@@ -49,6 +50,8 @@ struct _GdkX11DeviceManagerXI2
|
||||
GList *devices;
|
||||
|
||||
gint opcode;
|
||||
gint major;
|
||||
gint minor;
|
||||
};
|
||||
|
||||
struct _GdkX11DeviceManagerXI2Class
|
||||
@@ -96,7 +99,9 @@ static GdkWindow * gdk_x11_device_manager_xi2_get_window (GdkEventTra
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_OPCODE
|
||||
PROP_OPCODE,
|
||||
PROP_MAJOR,
|
||||
PROP_MINOR
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -120,6 +125,20 @@ gdk_x11_device_manager_xi2_class_init (GdkX11DeviceManagerXI2Class *klass)
|
||||
P_("Opcode for XInput2 requests"),
|
||||
0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MAJOR,
|
||||
g_param_spec_int ("major",
|
||||
P_("Major"),
|
||||
P_("Major version number"),
|
||||
0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_MINOR,
|
||||
g_param_spec_int ("minor",
|
||||
P_("Minor"),
|
||||
P_("Minor version number"),
|
||||
0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -148,8 +167,10 @@ _gdk_x11_device_manager_xi2_select_events (GdkDeviceManager *device_manager,
|
||||
static void
|
||||
translate_valuator_class (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
XIValuatorClassInfo *info,
|
||||
gint n_valuator)
|
||||
Atom valuator_label,
|
||||
gdouble min,
|
||||
gdouble max,
|
||||
gdouble resolution)
|
||||
{
|
||||
static gboolean initialized = FALSE;
|
||||
static Atom label_atoms [GDK_AXIS_LAST] = { 0 };
|
||||
@@ -170,24 +191,19 @@ translate_valuator_class (GdkDisplay *display,
|
||||
|
||||
for (i = GDK_AXIS_IGNORE; i < GDK_AXIS_LAST; i++)
|
||||
{
|
||||
if (label_atoms[i] == info->label)
|
||||
if (label_atoms[i] == valuator_label)
|
||||
{
|
||||
use = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->label != None)
|
||||
label = gdk_x11_xatom_to_atom_for_display (display, info->label);
|
||||
if (valuator_label != None)
|
||||
label = gdk_x11_xatom_to_atom_for_display (display, valuator_label);
|
||||
else
|
||||
label = GDK_NONE;
|
||||
|
||||
_gdk_device_add_axis (device,
|
||||
label,
|
||||
use,
|
||||
info->min,
|
||||
info->max,
|
||||
info->resolution);
|
||||
_gdk_device_add_axis (device, label, use, min, max, resolution);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -196,7 +212,7 @@ translate_device_classes (GdkDisplay *display,
|
||||
XIAnyClassInfo **classes,
|
||||
guint n_classes)
|
||||
{
|
||||
gint i, n_valuator = 0;
|
||||
gint i;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (device));
|
||||
|
||||
@@ -218,10 +234,14 @@ translate_device_classes (GdkDisplay *display,
|
||||
}
|
||||
break;
|
||||
case XIValuatorClass:
|
||||
translate_valuator_class (display, device,
|
||||
(XIValuatorClassInfo *) class_info,
|
||||
n_valuator);
|
||||
n_valuator++;
|
||||
{
|
||||
XIValuatorClassInfo *valuator_info = (XIValuatorClassInfo *) class_info;
|
||||
translate_valuator_class (display, device,
|
||||
valuator_info->label,
|
||||
valuator_info->min,
|
||||
valuator_info->max,
|
||||
valuator_info->resolution);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Ignore */
|
||||
@@ -232,6 +252,27 @@ translate_device_classes (GdkDisplay *display,
|
||||
g_object_thaw_notify (G_OBJECT (device));
|
||||
}
|
||||
|
||||
static gint
|
||||
count_device_touches (XIAnyClassInfo **classes,
|
||||
guint n_classes)
|
||||
{
|
||||
#ifdef XINPUT_2_2
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < n_classes; i++)
|
||||
{
|
||||
XITouchClassInfo *valuator_info = (XITouchClassInfo *) classes[i];
|
||||
|
||||
if (valuator_info->type != XITouchClass)
|
||||
continue;
|
||||
|
||||
return valuator_info->num_touches;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static GdkDevice *
|
||||
create_device (GdkDeviceManager *device_manager,
|
||||
GdkDisplay *display,
|
||||
@@ -244,6 +285,9 @@ create_device (GdkDeviceManager *device_manager,
|
||||
|
||||
if (dev->use == XIMasterKeyboard || dev->use == XISlaveKeyboard)
|
||||
input_source = GDK_SOURCE_KEYBOARD;
|
||||
else if (dev->use == XISlavePointer &&
|
||||
count_device_touches (dev->classes, dev->num_classes) > 0)
|
||||
input_source = GDK_SOURCE_TOUCH;
|
||||
else
|
||||
{
|
||||
gchar *tmp_name;
|
||||
@@ -254,6 +298,10 @@ create_device (GdkDeviceManager *device_manager,
|
||||
input_source = GDK_SOURCE_ERASER;
|
||||
else if (strstr (tmp_name, "cursor"))
|
||||
input_source = GDK_SOURCE_CURSOR;
|
||||
else if (strstr (tmp_name, "finger") ||
|
||||
(strstr (tmp_name, "touch") &&
|
||||
!strstr (tmp_name, "touchpad")))
|
||||
input_source = GDK_SOURCE_TOUCH;
|
||||
else if (strstr (tmp_name, "wacom") ||
|
||||
strstr (tmp_name, "pen"))
|
||||
input_source = GDK_SOURCE_PEN;
|
||||
@@ -408,6 +456,8 @@ gdk_x11_device_manager_xi2_constructed (GObject *object)
|
||||
display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object));
|
||||
xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
||||
|
||||
g_assert (device_manager->major == 2);
|
||||
|
||||
masters = g_hash_table_new (NULL, NULL);
|
||||
slaves = g_hash_table_new (NULL, NULL);
|
||||
|
||||
@@ -533,6 +583,12 @@ gdk_x11_device_manager_xi2_set_property (GObject *object,
|
||||
case PROP_OPCODE:
|
||||
device_manager->opcode = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_MAJOR:
|
||||
device_manager->major = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_MINOR:
|
||||
device_manager->minor = g_value_get_int (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -554,6 +610,12 @@ gdk_x11_device_manager_xi2_get_property (GObject *object,
|
||||
case PROP_OPCODE:
|
||||
g_value_set_int (value, device_manager->opcode);
|
||||
break;
|
||||
case PROP_MAJOR:
|
||||
g_value_set_int (value, device_manager->major);
|
||||
break;
|
||||
case PROP_MINOR:
|
||||
g_value_set_int (value, device_manager->minor);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -823,6 +885,11 @@ get_event_window (GdkEventTranslator *translator,
|
||||
case XI_ButtonPress:
|
||||
case XI_ButtonRelease:
|
||||
case XI_Motion:
|
||||
#ifdef XINPUT_2_2
|
||||
case XI_TouchUpdate:
|
||||
case XI_TouchBegin:
|
||||
case XI_TouchEnd:
|
||||
#endif /* XINPUT_2_2 */
|
||||
{
|
||||
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
|
||||
|
||||
@@ -1043,56 +1110,55 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
break;
|
||||
case XI_ButtonPress:
|
||||
case XI_ButtonRelease:
|
||||
#ifdef XINPUT_2_2
|
||||
case XI_TouchBegin:
|
||||
case XI_TouchEnd:
|
||||
#endif /* XINPUT_2_2 */
|
||||
{
|
||||
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
|
||||
GdkDevice *source_device;
|
||||
|
||||
switch (xev->detail)
|
||||
if (ev->evtype == XI_ButtonPress &&
|
||||
(xev->detail >= 4 && xev->detail <= 7))
|
||||
{
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
/* Button presses of button 4-7 are scroll events */
|
||||
if (ev->evtype == XI_ButtonPress)
|
||||
{
|
||||
event->scroll.type = GDK_SCROLL;
|
||||
/* Button presses of button 4-7 are scroll events */
|
||||
event->scroll.type = GDK_SCROLL;
|
||||
|
||||
if (xev->detail == 4)
|
||||
event->scroll.direction = GDK_SCROLL_UP;
|
||||
else if (xev->detail == 5)
|
||||
event->scroll.direction = GDK_SCROLL_DOWN;
|
||||
else if (xev->detail == 6)
|
||||
event->scroll.direction = GDK_SCROLL_LEFT;
|
||||
else
|
||||
event->scroll.direction = GDK_SCROLL_RIGHT;
|
||||
if (xev->detail == 4)
|
||||
event->scroll.direction = GDK_SCROLL_UP;
|
||||
else if (xev->detail == 5)
|
||||
event->scroll.direction = GDK_SCROLL_DOWN;
|
||||
else if (xev->detail == 6)
|
||||
event->scroll.direction = GDK_SCROLL_LEFT;
|
||||
else
|
||||
event->scroll.direction = GDK_SCROLL_RIGHT;
|
||||
|
||||
event->scroll.window = window;
|
||||
event->scroll.time = xev->time;
|
||||
event->scroll.x = (gdouble) xev->event_x;
|
||||
event->scroll.y = (gdouble) xev->event_y;
|
||||
event->scroll.x_root = (gdouble) xev->root_x;
|
||||
event->scroll.y_root = (gdouble) xev->root_y;
|
||||
event->scroll.window = window;
|
||||
event->scroll.time = xev->time;
|
||||
event->scroll.x = (gdouble) xev->event_x;
|
||||
event->scroll.y = (gdouble) xev->event_y;
|
||||
event->scroll.x_root = (gdouble) xev->root_x;
|
||||
event->scroll.y_root = (gdouble) xev->root_y;
|
||||
|
||||
event->scroll.device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->deviceid));
|
||||
event->scroll.device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->deviceid));
|
||||
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
gdk_event_set_source_device (event, source_device);
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
gdk_event_set_source_device (event, source_device);
|
||||
|
||||
event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
|
||||
break;
|
||||
}
|
||||
/* Button presses of button 4-7 are scroll events, so ignore the release */
|
||||
else if (ev->evtype == XI_ButtonRelease)
|
||||
{
|
||||
return_val = FALSE;
|
||||
break;
|
||||
}
|
||||
/* else (XI_ButtonRelease) fall thru */
|
||||
default:
|
||||
event->button.type = (ev->evtype == XI_ButtonPress) ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE;
|
||||
event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef XINPUT_2_2
|
||||
if (ev->evtype == XI_TouchBegin)
|
||||
event->button.type = GDK_TOUCH_PRESS;
|
||||
else if (ev->evtype == XI_TouchEnd)
|
||||
event->button.type = GDK_TOUCH_RELEASE;
|
||||
else
|
||||
#endif /* XINPUT_2_2 */
|
||||
event->button.type = (ev->evtype == XI_ButtonPress) ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE;
|
||||
|
||||
event->button.window = window;
|
||||
event->button.time = xev->time;
|
||||
@@ -1124,9 +1190,25 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
}
|
||||
|
||||
event->button.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
|
||||
event->button.button = xev->detail;
|
||||
|
||||
if (ev->evtype == XI_TouchBegin)
|
||||
event->button.state |= GDK_BUTTON1_MASK;
|
||||
|
||||
#ifdef XINPUT_2_2
|
||||
if (ev->evtype == XI_TouchBegin ||
|
||||
ev->evtype == XI_TouchEnd)
|
||||
{
|
||||
event->button.button = 1;
|
||||
event->button.touch_id = xev->detail;
|
||||
}
|
||||
else
|
||||
#endif /* XINPUT_2_2 */
|
||||
event->button.button = xev->detail;
|
||||
}
|
||||
|
||||
if (xev->flags & (XIPointerEmulated | XITouchEmulatingPointer))
|
||||
_gdk_event_set_pointer_emulated (event, TRUE);
|
||||
|
||||
if (return_val == FALSE)
|
||||
break;
|
||||
|
||||
@@ -1142,11 +1224,25 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
break;
|
||||
}
|
||||
case XI_Motion:
|
||||
#ifdef XINPUT_2_2
|
||||
case XI_TouchUpdate:
|
||||
#endif /* XINPUT_2_2 */
|
||||
{
|
||||
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
|
||||
GdkDevice *source_device;
|
||||
|
||||
event->motion.type = GDK_MOTION_NOTIFY;
|
||||
if (ev->evtype == XI_Motion)
|
||||
{
|
||||
event->motion.touch_id = 0;
|
||||
event->motion.type = GDK_MOTION_NOTIFY;
|
||||
}
|
||||
#ifdef XINPUT_2_2
|
||||
else
|
||||
{
|
||||
event->motion.touch_id = xev->detail;
|
||||
event->motion.type = GDK_TOUCH_MOTION;
|
||||
}
|
||||
#endif
|
||||
|
||||
event->motion.window = window;
|
||||
|
||||
@@ -1165,6 +1261,12 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
|
||||
event->motion.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
|
||||
|
||||
if (ev->evtype == XI_TouchUpdate)
|
||||
event->motion.state |= GDK_BUTTON1_MASK;
|
||||
|
||||
if (xev->flags & (XIPointerEmulated | XITouchEmulatingPointer))
|
||||
_gdk_event_set_pointer_emulated (event, TRUE);
|
||||
|
||||
/* There doesn't seem to be motion hints in XI */
|
||||
event->motion.is_hint = FALSE;
|
||||
|
||||
@@ -1280,7 +1382,8 @@ gdk_x11_device_manager_xi2_get_handled_events (GdkEventTranslator *translator)
|
||||
GDK_BUTTON2_MOTION_MASK |
|
||||
GDK_BUTTON3_MOTION_MASK |
|
||||
GDK_BUTTON_MOTION_MASK |
|
||||
GDK_FOCUS_CHANGE_MASK);
|
||||
GDK_FOCUS_CHANGE_MASK |
|
||||
GDK_TOUCH_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1294,7 +1397,9 @@ gdk_x11_device_manager_xi2_select_window_events (GdkEventTranslator *translator,
|
||||
device_manager = GDK_DEVICE_MANAGER (translator);
|
||||
|
||||
event_mask.deviceid = XIAllMasterDevices;
|
||||
event_mask.mask = _gdk_x11_device_xi2_translate_event_mask (evmask, &event_mask.mask_len);
|
||||
event_mask.mask = _gdk_x11_device_xi2_translate_event_mask (GDK_X11_DEVICE_MANAGER_XI2 (device_manager),
|
||||
evmask,
|
||||
&event_mask.mask_len);
|
||||
|
||||
_gdk_x11_device_manager_xi2_select_events (device_manager, window, &event_mask);
|
||||
g_free (event_mask.mask);
|
||||
|
||||
@@ -1579,9 +1579,12 @@ device_grab_update_callback (GdkDisplay *display,
|
||||
gpointer data,
|
||||
gulong serial)
|
||||
{
|
||||
GdkPointerWindowInfo *pointer_info;
|
||||
GdkDevice *device = data;
|
||||
|
||||
_gdk_display_device_grab_update (display, device, NULL, serial);
|
||||
pointer_info = _gdk_display_get_pointer_info (display, device);
|
||||
_gdk_display_device_grab_update (display, device,
|
||||
pointer_info->last_slave, serial);
|
||||
}
|
||||
|
||||
#define XSERVER_TIME_IS_LATER(time1, time2) \
|
||||
|
||||
@@ -247,8 +247,9 @@ void _gdk_x11_device_xi_translate_axes (GdkDevice *device,
|
||||
#endif
|
||||
|
||||
#ifdef XINPUT_2
|
||||
guchar * _gdk_x11_device_xi2_translate_event_mask (GdkEventMask event_mask,
|
||||
gint *len);
|
||||
guchar * _gdk_x11_device_xi2_translate_event_mask (GdkX11DeviceManagerXI2 *device_manager_xi2,
|
||||
GdkEventMask event_mask,
|
||||
gint *len);
|
||||
guint _gdk_x11_device_xi2_translate_state (XIModifierState *mods_state,
|
||||
XIButtonState *buttons_state,
|
||||
XIGroupState *group_state);
|
||||
|
||||
@@ -249,6 +249,7 @@ gtk_public_h_sources = \
|
||||
gtkfontchooserdialog.h \
|
||||
gtkfontchooserwidget.h \
|
||||
gtkframe.h \
|
||||
gtkgesturesinterpreter.h \
|
||||
gtkgradient.h \
|
||||
gtkgrid.h \
|
||||
gtkiconfactory.h \
|
||||
@@ -657,6 +658,7 @@ gtk_base_c_sources = \
|
||||
gtkfontchooserutils.c \
|
||||
gtkfontchooserwidget.c \
|
||||
gtkframe.c \
|
||||
gtkgesturesinterpreter.c \
|
||||
gtkgradient.c \
|
||||
gtkgrid.c \
|
||||
gtkiconcache.c \
|
||||
|
||||
@@ -362,3 +362,9 @@ GtkCalendar.button:hover {
|
||||
border-width: 0;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.press-and-hold {
|
||||
background-color: alpha (@bg_color, 0.5);
|
||||
color: alpha (lighter (@selected_bg_color), 0.8);
|
||||
border-width: 10;
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
#include <gtk/gtkfontchooserdialog.h>
|
||||
#include <gtk/gtkfontchooserwidget.h>
|
||||
#include <gtk/gtkframe.h>
|
||||
#include <gtk/gtkgesturesinterpreter.h>
|
||||
#include <gtk/gtkgradient.h>
|
||||
#include <gtk/gtkgrid.h>
|
||||
#include <gtk/gtkiconfactory.h>
|
||||
|
||||
+38
-2
@@ -1813,7 +1813,8 @@ gtk_button_button_press (GtkWidget *widget,
|
||||
GtkButton *button;
|
||||
GtkButtonPrivate *priv;
|
||||
|
||||
if (event->type == GDK_BUTTON_PRESS)
|
||||
if (event->type == GDK_BUTTON_PRESS ||
|
||||
event->type == GDK_TOUCH_PRESS)
|
||||
{
|
||||
button = GTK_BUTTON (widget);
|
||||
priv = button->priv;
|
||||
@@ -1932,6 +1933,40 @@ gtk_real_button_pressed (GtkButton *button)
|
||||
gtk_button_update_state (button);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
touch_release_in_button (GtkButton *button)
|
||||
{
|
||||
GtkButtonPrivate *priv;
|
||||
gint width, height;
|
||||
GdkEvent *event;
|
||||
gdouble x, y;
|
||||
|
||||
priv = button->priv;
|
||||
event = gtk_get_current_event ();
|
||||
|
||||
if (!event)
|
||||
return FALSE;
|
||||
|
||||
if (event->type != GDK_TOUCH_RELEASE ||
|
||||
event->button.window != priv->event_window)
|
||||
{
|
||||
gdk_event_free (event);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gdk_event_get_coords (event, &x, &y);
|
||||
width = gdk_window_get_width (priv->event_window);
|
||||
height = gdk_window_get_height (priv->event_window);
|
||||
|
||||
gdk_event_free (event);
|
||||
|
||||
if (x >= 0 && x <= width &&
|
||||
y >= 0 && y <= height)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_real_button_released (GtkButton *button)
|
||||
{
|
||||
@@ -1944,7 +1979,8 @@ gtk_real_button_released (GtkButton *button)
|
||||
if (priv->activate_timeout)
|
||||
return;
|
||||
|
||||
if (priv->in_button)
|
||||
if (priv->in_button ||
|
||||
touch_release_in_button (button))
|
||||
gtk_button_clicked (button);
|
||||
|
||||
gtk_button_update_state (button);
|
||||
|
||||
+101
-26
@@ -147,6 +147,8 @@ struct _GtkEntryPrivate
|
||||
GtkShadowType shadow_type;
|
||||
GtkWidget *popup_menu;
|
||||
|
||||
GdkDevice *device;
|
||||
|
||||
GdkDevice *completion_device;
|
||||
GdkWindow *text_area;
|
||||
|
||||
@@ -481,6 +483,11 @@ static void gtk_entry_toggle_overwrite (GtkEntry *entry);
|
||||
static void gtk_entry_select_all (GtkEntry *entry);
|
||||
static void gtk_entry_real_activate (GtkEntry *entry);
|
||||
static gboolean gtk_entry_popup_menu (GtkWidget *widget);
|
||||
static gboolean gtk_entry_press_and_hold (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
GtkPressAndHoldAction action,
|
||||
gint x,
|
||||
gint y);
|
||||
|
||||
static void keymap_direction_changed (GdkKeymap *keymap,
|
||||
GtkEntry *entry);
|
||||
@@ -544,9 +551,13 @@ static void gtk_entry_paste (GtkEntry *entry,
|
||||
GdkAtom selection);
|
||||
static void gtk_entry_update_primary_selection (GtkEntry *entry);
|
||||
static void gtk_entry_do_popup (GtkEntry *entry,
|
||||
GdkEventButton *event);
|
||||
GdkDevice *device,
|
||||
guint32 _time,
|
||||
guint button);
|
||||
static gboolean gtk_entry_mnemonic_activate (GtkWidget *widget,
|
||||
gboolean group_cycling);
|
||||
static void gtk_entry_grab_notify (GtkWidget *widget,
|
||||
gboolean was_grabbed);
|
||||
static void gtk_entry_check_cursor_blink (GtkEntry *entry);
|
||||
static void gtk_entry_pend_cursor_blink (GtkEntry *entry);
|
||||
static void gtk_entry_reset_blink_time (GtkEntry *entry);
|
||||
@@ -691,6 +702,7 @@ gtk_entry_class_init (GtkEntryClass *class)
|
||||
widget_class->state_flags_changed = gtk_entry_state_flags_changed;
|
||||
widget_class->screen_changed = gtk_entry_screen_changed;
|
||||
widget_class->mnemonic_activate = gtk_entry_mnemonic_activate;
|
||||
widget_class->grab_notify = gtk_entry_grab_notify;
|
||||
|
||||
widget_class->drag_drop = gtk_entry_drag_drop;
|
||||
widget_class->drag_motion = gtk_entry_drag_motion;
|
||||
@@ -700,6 +712,7 @@ gtk_entry_class_init (GtkEntryClass *class)
|
||||
widget_class->drag_data_delete = gtk_entry_drag_data_delete;
|
||||
|
||||
widget_class->popup_menu = gtk_entry_popup_menu;
|
||||
widget_class->press_and_hold = gtk_entry_press_and_hold;
|
||||
|
||||
class->move_cursor = gtk_entry_move_cursor;
|
||||
class->insert_at_cursor = gtk_entry_insert_at_cursor;
|
||||
@@ -3810,7 +3823,8 @@ gtk_entry_button_press (GtkWidget *widget,
|
||||
gtk_entry_reset_blink_time (entry);
|
||||
|
||||
priv->button = event->button;
|
||||
|
||||
priv->device = gdk_event_get_device ((GdkEvent *) event);
|
||||
|
||||
if (!gtk_widget_has_focus (widget))
|
||||
{
|
||||
priv->in_click = TRUE;
|
||||
@@ -3822,8 +3836,10 @@ gtk_entry_button_press (GtkWidget *widget,
|
||||
|
||||
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
|
||||
{
|
||||
gtk_entry_do_popup (entry, event);
|
||||
gtk_entry_do_popup (entry, event->device,
|
||||
event->time, event->button);
|
||||
priv->button = 0; /* Don't wait for release, since the menu will gtk_grab_add */
|
||||
priv->device = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -4001,9 +4017,10 @@ gtk_entry_button_release (GtkWidget *widget,
|
||||
|
||||
priv->in_drag = 0;
|
||||
}
|
||||
|
||||
|
||||
priv->button = 0;
|
||||
|
||||
priv->device = NULL;
|
||||
|
||||
gtk_entry_update_primary_selection (entry);
|
||||
|
||||
return TRUE;
|
||||
@@ -4112,7 +4129,8 @@ gtk_entry_motion_notify (GtkWidget *widget,
|
||||
|
||||
priv->in_drag = FALSE;
|
||||
priv->button = 0;
|
||||
|
||||
priv->device = NULL;
|
||||
|
||||
gtk_target_list_unref (target_list);
|
||||
}
|
||||
}
|
||||
@@ -8553,6 +8571,26 @@ gtk_entry_mnemonic_activate (GtkWidget *widget,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_entry_grab_notify (GtkWidget *widget,
|
||||
gboolean was_grabbed)
|
||||
{
|
||||
GtkEntryPrivate *priv;
|
||||
|
||||
priv = GTK_ENTRY (widget)->priv;
|
||||
|
||||
if (priv->device &&
|
||||
gtk_widget_device_is_shadowed (widget, priv->device))
|
||||
{
|
||||
/* Unset button so we don't expect
|
||||
* a button release anymore
|
||||
*/
|
||||
priv->button = 0;
|
||||
priv->device = NULL;
|
||||
priv->in_drag = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
append_action_signal (GtkEntry *entry,
|
||||
GtkWidget *menu,
|
||||
@@ -8755,15 +8793,17 @@ popup_targets_received (GtkClipboard *clipboard,
|
||||
info_entry_priv->popup_menu);
|
||||
|
||||
|
||||
if (info->device)
|
||||
if (gdk_device_get_source (info->device) != GDK_SOURCE_KEYBOARD)
|
||||
gtk_menu_popup_for_device (GTK_MENU (info_entry_priv->popup_menu),
|
||||
info->device, NULL, NULL, NULL, NULL, NULL,
|
||||
info->button, info->time);
|
||||
else
|
||||
{
|
||||
gtk_menu_popup (GTK_MENU (info_entry_priv->popup_menu), NULL, NULL,
|
||||
popup_position_func, entry,
|
||||
0, gtk_get_current_event_time ());
|
||||
gtk_menu_popup_for_device (GTK_MENU (info_entry_priv->popup_menu),
|
||||
info->device, NULL, NULL,
|
||||
popup_position_func,
|
||||
entry, NULL,
|
||||
0, info->time);
|
||||
gtk_menu_shell_select_first (GTK_MENU_SHELL (info_entry_priv->popup_menu), FALSE);
|
||||
}
|
||||
}
|
||||
@@ -8771,10 +8811,12 @@ popup_targets_received (GtkClipboard *clipboard,
|
||||
g_object_unref (entry);
|
||||
g_slice_free (PopupInfo, info);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gtk_entry_do_popup (GtkEntry *entry,
|
||||
GdkEventButton *event)
|
||||
GdkDevice *device,
|
||||
guint32 _time,
|
||||
guint button)
|
||||
{
|
||||
PopupInfo *info = g_slice_new (PopupInfo);
|
||||
|
||||
@@ -8783,19 +8825,10 @@ gtk_entry_do_popup (GtkEntry *entry,
|
||||
* we get them, then we actually pop up the menu.
|
||||
*/
|
||||
info->entry = g_object_ref (entry);
|
||||
|
||||
if (event)
|
||||
{
|
||||
info->button = event->button;
|
||||
info->time = event->time;
|
||||
info->device = event->device;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->button = 0;
|
||||
info->time = gtk_get_current_event_time ();
|
||||
info->device = NULL;
|
||||
}
|
||||
|
||||
info->button = button;
|
||||
info->time = _time;
|
||||
info->device = device;
|
||||
|
||||
gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (entry), GDK_SELECTION_CLIPBOARD),
|
||||
gdk_atom_intern_static_string ("TARGETS"),
|
||||
@@ -8806,7 +8839,49 @@ gtk_entry_do_popup (GtkEntry *entry,
|
||||
static gboolean
|
||||
gtk_entry_popup_menu (GtkWidget *widget)
|
||||
{
|
||||
gtk_entry_do_popup (GTK_ENTRY (widget), NULL);
|
||||
gtk_entry_do_popup (GTK_ENTRY (widget),
|
||||
gtk_get_current_event_device (),
|
||||
gtk_get_current_event_time (),
|
||||
0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_entry_press_and_hold (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
GtkPressAndHoldAction action,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
if (action == GTK_PRESS_AND_HOLD_TRIGGER)
|
||||
gtk_entry_do_popup (GTK_ENTRY (widget),
|
||||
device, GDK_CURRENT_TIME, 1);
|
||||
else if (action == GTK_PRESS_AND_HOLD_QUERY)
|
||||
{
|
||||
GtkEntryPrivate *priv;
|
||||
GdkDevice *source_device;
|
||||
GdkEvent *event;
|
||||
|
||||
priv = GTK_ENTRY (widget)->priv;
|
||||
event = gtk_get_current_event ();
|
||||
|
||||
if (!event)
|
||||
return FALSE;
|
||||
|
||||
source_device = gdk_event_get_source_device (event);
|
||||
|
||||
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH ||
|
||||
(event->type != GDK_BUTTON_PRESS &&
|
||||
event->type != GDK_TOUCH_PRESS) ||
|
||||
event->button.window != priv->text_area)
|
||||
{
|
||||
gdk_event_free (event);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gdk_event_free (event);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -932,6 +932,78 @@ typedef enum {
|
||||
GTK_BORDER_STYLE_RIDGE
|
||||
} GtkBorderStyle;
|
||||
|
||||
/**
|
||||
* GtkCapturedEventFlags:
|
||||
* @GTK_CAPTURED_EVENT_NONE: Event goes uncaptured
|
||||
* @GTK_CAPTURED_EVENT_HANDLED: The event was handled
|
||||
* @GTK_CAPTURED_EVENT_STORE: Store for later propagation, see
|
||||
* gtk_widget_release_captured_events()
|
||||
*
|
||||
* Describes how an event in the #GtkWidget::captured-event handler
|
||||
* is handled.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_CAPTURED_EVENT_NONE = 0,
|
||||
GTK_CAPTURED_EVENT_HANDLED = 1 << 0,
|
||||
GTK_CAPTURED_EVENT_STORE = 1 << 1
|
||||
} GtkCapturedEventFlags;
|
||||
|
||||
/**
|
||||
* GtkKineticScrollingFlags:
|
||||
* @GTK_KINETIC_SCROLLING_NONE: No kinetic scrolling.
|
||||
* @GTK_KINETIC_SCROLLING_ENABLED: Kinetic scrolling is enabled.
|
||||
* @GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS: The first button
|
||||
* press is captured by the scrolled window, and then replayed
|
||||
* if the button press is meant to go to the child widget. This
|
||||
* flag should be enabled if the child widget(s) perform
|
||||
* non-reversible actions on #GtkWidget::button-press-event.
|
||||
* If the widget does not do so, and handles
|
||||
* #GtkWidget::grab-broken-event, it might be better off without
|
||||
* this flag.
|
||||
*
|
||||
* Describes the kinetic scrolling behavior of a #GtkScrolledWindow
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_KINETIC_SCROLLING_NONE = 0,
|
||||
GTK_KINETIC_SCROLLING_ENABLED = 1 << 0,
|
||||
GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS = 1 << 1
|
||||
} GtkKineticScrollingFlags;
|
||||
|
||||
/**
|
||||
* GtkGestureType:
|
||||
* @GTK_GESTURE_SWIPE_RIGHT: A swipe from left to right
|
||||
* @GTK_GESTURE_SWIPE_LEFT: A swipe from right to left
|
||||
* @GTK_GESTURE_SWIPE_UP: A swipe from bottom to top
|
||||
* @GTK_GESTURE_SWIPE_DOWN: A swipe from top to bottom
|
||||
* @GTK_GESTURE_CIRCULAR_CLOCKWISE: A circular clockwise movement
|
||||
* @GTK_GESTURE_CIRCULAR_COUNTERCLOCKWISE: A circular counterclockwise movement
|
||||
*
|
||||
* Describes the stock gestures handled by GTK+.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_GESTURE_SWIPE_RIGHT = 1,
|
||||
GTK_GESTURE_SWIPE_LEFT,
|
||||
GTK_GESTURE_SWIPE_UP,
|
||||
GTK_GESTURE_SWIPE_DOWN,
|
||||
GTK_GESTURE_CIRCULAR_CLOCKWISE,
|
||||
GTK_GESTURE_CIRCULAR_COUNTERCLOCKWISE
|
||||
} GtkGestureType;
|
||||
|
||||
/**
|
||||
* GtkGestureFlags:
|
||||
* @GTK_GESTURE_FLAG_NONE: Gesture is interpreted as-is.
|
||||
* @GTK_GESTURE_FLAG_IGNORE_INITIAL_ORIENTATION:
|
||||
* The initial orientation is ignored in comparisons,
|
||||
* so the gesture doesn't have a sense of direction,
|
||||
* this is useful for i.e. circular strokes.
|
||||
*
|
||||
* Flags accepted by gtk_gesture_new()
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_GESTURE_FLAG_NONE = 0,
|
||||
GTK_GESTURE_FLAG_IGNORE_INITIAL_ORIENTATION = 1 << 1
|
||||
} GtkGestureFlags;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,121 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 2011 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#ifndef __GTK_GESTURES_INTERPRETER_H__
|
||||
#define __GTK_GESTURES_INTERPRETER_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_GESTURE (gtk_gesture_get_type ())
|
||||
#define GTK_TYPE_GESTURE_STROKE (gtk_gesture_stroke_get_type ())
|
||||
|
||||
#define GTK_TYPE_GESTURES_INTERPRETER (gtk_gestures_interpreter_get_type ())
|
||||
#define GTK_GESTURES_INTERPRETER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GESTURES_INTERPRETER, GtkGesturesInterpreter))
|
||||
#define GTK_GESTURES_INTERPRETER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_GESTURES_INTERPRETER, GtkGesturesInterpreterClass))
|
||||
#define GTK_IS_GESTURES_INTERPRETER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GESTURES_INTERPRETER))
|
||||
#define GTK_IS_GESTURES_INTERPRETER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GTK_TYPE_GESTURES_INTERPRETER))
|
||||
#define GTK_GESTURES_INTERPRETER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GESTURES_INTERPRETER, GtkGesturesInterpreterClass))
|
||||
|
||||
typedef struct _GtkGestureStroke GtkGestureStroke;
|
||||
typedef struct _GtkGesture GtkGesture;
|
||||
|
||||
typedef struct _GtkGesturesInterpreter GtkGesturesInterpreter;
|
||||
typedef struct _GtkGesturesInterpreterClass GtkGesturesInterpreterClass;
|
||||
|
||||
struct _GtkGesturesInterpreter
|
||||
{
|
||||
GObject parent_object;
|
||||
gpointer priv;
|
||||
};
|
||||
|
||||
struct _GtkGesturesInterpreterClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* events_vectorized) (GtkGesturesInterpreter *interpreter,
|
||||
GtkGesture *gesture);
|
||||
|
||||
void (* gesture_detected) (GtkGesturesInterpreter *interpreter,
|
||||
guint gesture_id,
|
||||
gdouble confidence);
|
||||
};
|
||||
|
||||
/* Gesture stroke */
|
||||
GType gtk_gesture_stroke_get_type (void) G_GNUC_CONST;
|
||||
GtkGestureStroke * gtk_gesture_stroke_new (void);
|
||||
GtkGestureStroke * gtk_gesture_stroke_copy (const GtkGestureStroke *stroke);
|
||||
void gtk_gesture_stroke_free (GtkGestureStroke *stroke);
|
||||
void gtk_gesture_stroke_append_vector (GtkGestureStroke *stroke,
|
||||
gdouble angle,
|
||||
guint length);
|
||||
|
||||
guint gtk_gesture_stroke_get_n_vectors (const GtkGestureStroke *stroke);
|
||||
gboolean gtk_gesture_stroke_get_vector (const GtkGestureStroke *stroke,
|
||||
guint n_vector,
|
||||
gdouble *angle,
|
||||
guint *length,
|
||||
gdouble *relative_length);
|
||||
/* Gesture */
|
||||
GType gtk_gesture_get_type (void) G_GNUC_CONST;
|
||||
GtkGesture * gtk_gesture_new (const GtkGestureStroke *stroke,
|
||||
GtkGestureFlags flags);
|
||||
void gtk_gesture_add_stroke (GtkGesture *gesture,
|
||||
const GtkGestureStroke *stroke,
|
||||
gint dx,
|
||||
gint dy);
|
||||
GtkGesture * gtk_gesture_copy (const GtkGesture *gesture);
|
||||
void gtk_gesture_free (GtkGesture *gesture);
|
||||
|
||||
GtkGestureFlags gtk_gesture_get_flags (const GtkGesture *gesture);
|
||||
guint gtk_gesture_get_n_strokes (const GtkGesture *gesture);
|
||||
const GtkGestureStroke * gtk_gesture_get_stroke (const GtkGesture *gesture,
|
||||
guint n_stroke,
|
||||
gint *dx,
|
||||
gint *dy);
|
||||
|
||||
guint gtk_gesture_register (const GtkGesture *gesture);
|
||||
guint gtk_gesture_register_static (const GtkGesture *gesture);
|
||||
const GtkGesture * gtk_gesture_lookup (guint gesture_id);
|
||||
|
||||
/* Gestures interpreter */
|
||||
GType gtk_gestures_interpreter_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkGesturesInterpreter * gtk_gestures_interpreter_new (void);
|
||||
|
||||
gboolean gtk_gestures_interpreter_add_gesture (GtkGesturesInterpreter *interpreter,
|
||||
guint gesture_id);
|
||||
void gtk_gestures_interpreter_remove_gesture (GtkGesturesInterpreter *interpreter,
|
||||
guint gesture_id);
|
||||
|
||||
guint gtk_gestures_interpreter_get_n_active_strokes (GtkGesturesInterpreter *interpreter);
|
||||
|
||||
gboolean gtk_gestures_interpreter_feed_event (GtkGesturesInterpreter *interpreter,
|
||||
GdkEvent *event);
|
||||
gboolean gtk_gestures_interpreter_finish (GtkGesturesInterpreter *interpreter,
|
||||
guint *gesture_id);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_GESTURES_INTERPRETER_H__ */
|
||||
@@ -1302,6 +1302,7 @@ gtk_icon_view_realize (GtkWidget *widget)
|
||||
gtk_style_context_save (context);
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
|
||||
gtk_style_context_set_background (context, icon_view->priv->bin_window);
|
||||
gtk_style_context_set_background (context, window);
|
||||
gtk_style_context_restore (context);
|
||||
|
||||
gdk_window_show (icon_view->priv->bin_window);
|
||||
|
||||
+56
-13
@@ -437,6 +437,11 @@ static void gtk_label_hierarchy_changed (GtkWidget *widget,
|
||||
static void gtk_label_screen_changed (GtkWidget *widget,
|
||||
GdkScreen *old_screen);
|
||||
static gboolean gtk_label_popup_menu (GtkWidget *widget);
|
||||
static gboolean gtk_label_press_and_hold (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
GtkPressAndHoldAction action,
|
||||
gint x,
|
||||
gint y);
|
||||
|
||||
static void gtk_label_create_window (GtkLabel *label);
|
||||
static void gtk_label_destroy_window (GtkLabel *label);
|
||||
@@ -490,7 +495,9 @@ static void gtk_label_move_cursor (GtkLabel *label,
|
||||
static void gtk_label_copy_clipboard (GtkLabel *label);
|
||||
static void gtk_label_select_all (GtkLabel *label);
|
||||
static void gtk_label_do_popup (GtkLabel *label,
|
||||
GdkEventButton *event);
|
||||
GdkDevice *device,
|
||||
guint32 _time,
|
||||
guint button);
|
||||
static gint gtk_label_move_forward_word (GtkLabel *label,
|
||||
gint start);
|
||||
static gint gtk_label_move_backward_word (GtkLabel *label,
|
||||
@@ -585,6 +592,7 @@ gtk_label_class_init (GtkLabelClass *class)
|
||||
widget_class->drag_data_get = gtk_label_drag_data_get;
|
||||
widget_class->grab_focus = gtk_label_grab_focus;
|
||||
widget_class->popup_menu = gtk_label_popup_menu;
|
||||
widget_class->press_and_hold = gtk_label_press_and_hold;
|
||||
widget_class->focus = gtk_label_focus;
|
||||
widget_class->get_request_mode = gtk_label_get_request_mode;
|
||||
widget_class->get_preferred_width = gtk_label_get_preferred_width;
|
||||
@@ -4638,7 +4646,8 @@ gtk_label_button_press (GtkWidget *widget,
|
||||
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
|
||||
{
|
||||
info->link_clicked = 1;
|
||||
gtk_label_do_popup (label, event);
|
||||
gtk_label_do_popup (label, event->device,
|
||||
event->time, event->button);
|
||||
return TRUE;
|
||||
}
|
||||
else if (event->button == GDK_BUTTON_PRIMARY)
|
||||
@@ -4656,7 +4665,8 @@ gtk_label_button_press (GtkWidget *widget,
|
||||
|
||||
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
|
||||
{
|
||||
gtk_label_do_popup (label, event);
|
||||
gtk_label_do_popup (label, event->device,
|
||||
event->time, event->button);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -4921,6 +4931,8 @@ gtk_label_motion (GtkWidget *widget,
|
||||
if ((event->state & GDK_BUTTON1_MASK) == 0)
|
||||
return FALSE;
|
||||
|
||||
gdk_event_request_motions (event);
|
||||
|
||||
if (info->in_drag)
|
||||
{
|
||||
if (gtk_drag_check_threshold (widget,
|
||||
@@ -6170,14 +6182,43 @@ copy_link_activate_cb (GtkMenuItem *menu_item,
|
||||
static gboolean
|
||||
gtk_label_popup_menu (GtkWidget *widget)
|
||||
{
|
||||
gtk_label_do_popup (GTK_LABEL (widget), NULL);
|
||||
gtk_label_do_popup (GTK_LABEL (widget),
|
||||
gtk_get_current_event_device (),
|
||||
gtk_get_current_event_time (),
|
||||
0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_label_press_and_hold (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
GtkPressAndHoldAction action,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
if (action == GTK_PRESS_AND_HOLD_TRIGGER)
|
||||
gtk_label_do_popup (GTK_LABEL (widget),
|
||||
device, GDK_CURRENT_TIME, 1);
|
||||
else if (action == GTK_PRESS_AND_HOLD_QUERY)
|
||||
{
|
||||
GdkDevice *source_device;
|
||||
GdkEvent *event;
|
||||
|
||||
event = gtk_get_current_event ();
|
||||
source_device = gdk_event_get_source_device (event);
|
||||
|
||||
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_label_do_popup (GtkLabel *label,
|
||||
GdkEventButton *event)
|
||||
GdkDevice *device,
|
||||
guint32 _time,
|
||||
guint button)
|
||||
{
|
||||
GtkLabelPrivate *priv = label->priv;
|
||||
GtkWidget *menuitem;
|
||||
@@ -6199,7 +6240,7 @@ gtk_label_do_popup (GtkLabel *label,
|
||||
have_selection =
|
||||
priv->select_info->selection_anchor != priv->select_info->selection_end;
|
||||
|
||||
if (event)
|
||||
if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
|
||||
{
|
||||
if (priv->select_info->link_clicked)
|
||||
link = priv->select_info->active_link;
|
||||
@@ -6259,15 +6300,17 @@ gtk_label_do_popup (GtkLabel *label,
|
||||
|
||||
g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
|
||||
|
||||
if (event)
|
||||
gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
|
||||
NULL, NULL,
|
||||
event->button, event->time);
|
||||
if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
|
||||
gtk_menu_popup_for_device (GTK_MENU (menu), device,
|
||||
NULL, NULL, NULL, NULL, NULL,
|
||||
button, _time);
|
||||
else
|
||||
{
|
||||
gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
|
||||
popup_position_func, label,
|
||||
0, gtk_get_current_event_time ());
|
||||
gtk_menu_popup_for_device (GTK_MENU (menu),
|
||||
device, NULL, NULL,
|
||||
popup_position_func,
|
||||
label, NULL,
|
||||
0, _time);
|
||||
gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
+210
-83
@@ -1332,11 +1332,14 @@ rewrite_event_for_window (GdkEvent *event,
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
rewrite_events_translate (event->any.window,
|
||||
new_window,
|
||||
&event->button.x, &event->button.y);
|
||||
break;
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
rewrite_events_translate (event->any.window,
|
||||
new_window,
|
||||
&event->motion.x, &event->motion.y);
|
||||
@@ -1386,6 +1389,9 @@ rewrite_event_for_grabs (GdkEvent *event)
|
||||
case GDK_PROXIMITY_OUT:
|
||||
case GDK_KEY_PRESS:
|
||||
case GDK_KEY_RELEASE:
|
||||
case GDK_TOUCH_PRESS:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
case GDK_TOUCH_MOTION:
|
||||
display = gdk_window_get_display (event->any.window);
|
||||
device = gdk_event_get_device (event);
|
||||
|
||||
@@ -1473,6 +1479,7 @@ gtk_main_do_event (GdkEvent *event)
|
||||
{
|
||||
GtkWidget *event_widget;
|
||||
GtkWidget *grab_widget = NULL;
|
||||
GtkWidget *topmost_widget = NULL;
|
||||
GtkWindowGroup *window_group;
|
||||
GdkEvent *rewritten_event = NULL;
|
||||
GdkDevice *device;
|
||||
@@ -1532,6 +1539,14 @@ gtk_main_do_event (GdkEvent *event)
|
||||
if (!grab_widget)
|
||||
grab_widget = gtk_window_group_get_current_grab (window_group);
|
||||
|
||||
/* Find out the topmost widget where captured event propagation
|
||||
* should start, which is the widget holding the GTK+ grab
|
||||
* if any, otherwise it's left NULL and events are emitted
|
||||
* from the toplevel (or topmost parentless parent).
|
||||
*/
|
||||
if (grab_widget)
|
||||
topmost_widget = grab_widget;
|
||||
|
||||
/* If the grab widget is an ancestor of the event widget
|
||||
* then we send the event to the original event widget.
|
||||
* This is the key to implementing modality.
|
||||
@@ -1628,14 +1643,28 @@ gtk_main_do_event (GdkEvent *event)
|
||||
case GDK_WINDOW_STATE:
|
||||
case GDK_GRAB_BROKEN:
|
||||
case GDK_DAMAGE:
|
||||
gtk_widget_event (event_widget, event);
|
||||
if (!_gtk_widget_captured_event (event_widget, event))
|
||||
gtk_widget_event (event_widget, event);
|
||||
break;
|
||||
|
||||
case GDK_SCROLL:
|
||||
case GDK_BUTTON_PRESS:
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
gtk_propagate_event (grab_widget, event);
|
||||
case GDK_TOUCH_PRESS:
|
||||
if ((event->type == GDK_BUTTON_PRESS ||
|
||||
event->type == GDK_TOUCH_PRESS) &&
|
||||
event->button.button == 1)
|
||||
{
|
||||
/* Handle press and hold on the grab widget before propagating up,
|
||||
* so a parent capturing events doesn't delay nor prevent a child
|
||||
* from doing the press-and-hold action.
|
||||
*/
|
||||
_gtk_widget_press_and_hold_check_start (grab_widget, &event->button);
|
||||
}
|
||||
|
||||
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
|
||||
gtk_propagate_event (grab_widget, event);
|
||||
break;
|
||||
|
||||
case GDK_KEY_PRESS:
|
||||
@@ -1684,22 +1713,43 @@ gtk_main_do_event (GdkEvent *event)
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_PROXIMITY_IN:
|
||||
case GDK_PROXIMITY_OUT:
|
||||
gtk_propagate_event (grab_widget, event);
|
||||
case GDK_TOUCH_MOTION:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
case GDK_MULTITOUCH_ADDED:
|
||||
case GDK_MULTITOUCH_REMOVED:
|
||||
case GDK_MULTITOUCH_UPDATED:
|
||||
if ((event->type == GDK_BUTTON_RELEASE ||
|
||||
event->type == GDK_TOUCH_RELEASE) &&
|
||||
event->button.button == 1)
|
||||
_gtk_widget_press_and_hold_check_cancel (grab_widget, &event->button);
|
||||
else if (event->type == GDK_MOTION_NOTIFY ||
|
||||
event->type == GDK_TOUCH_MOTION)
|
||||
_gtk_widget_press_and_hold_check_threshold (grab_widget,
|
||||
&event->motion);
|
||||
|
||||
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
|
||||
gtk_propagate_event (grab_widget, event);
|
||||
break;
|
||||
|
||||
case GDK_ENTER_NOTIFY:
|
||||
_gtk_widget_set_device_window (event_widget,
|
||||
gdk_event_get_device (event),
|
||||
event->any.window);
|
||||
if (gtk_widget_is_sensitive (grab_widget))
|
||||
if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
|
||||
event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
|
||||
_gtk_widget_set_device_window (event_widget,
|
||||
gdk_event_get_device (event),
|
||||
event->any.window);
|
||||
if (gtk_widget_is_sensitive (grab_widget) &&
|
||||
!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
|
||||
gtk_widget_event (grab_widget, event);
|
||||
break;
|
||||
|
||||
case GDK_LEAVE_NOTIFY:
|
||||
_gtk_widget_set_device_window (event_widget,
|
||||
gdk_event_get_device (event),
|
||||
NULL);
|
||||
if (gtk_widget_is_sensitive (grab_widget))
|
||||
if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
|
||||
event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
|
||||
_gtk_widget_set_device_window (event_widget,
|
||||
gdk_event_get_device (event),
|
||||
NULL);
|
||||
if (gtk_widget_is_sensitive (grab_widget) &&
|
||||
!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
|
||||
gtk_widget_event (grab_widget, event);
|
||||
break;
|
||||
|
||||
@@ -1727,11 +1777,22 @@ gtk_main_do_event (GdkEvent *event)
|
||||
|| event->type == GDK_DRAG_ENTER
|
||||
|| event->type == GDK_GRAB_BROKEN
|
||||
|| event->type == GDK_MOTION_NOTIFY
|
||||
|| event->type == GDK_TOUCH_MOTION
|
||||
|| event->type == GDK_SCROLL)
|
||||
{
|
||||
_gtk_tooltip_handle_event (event);
|
||||
}
|
||||
|
||||
/* Handle gestures for touch events */
|
||||
if (gdk_event_get_touch_id (event, NULL))
|
||||
{
|
||||
_gtk_widget_gesture_stroke (grab_widget, event);
|
||||
|
||||
if (event->type == GDK_BUTTON_RELEASE ||
|
||||
event->type == GDK_TOUCH_RELEASE)
|
||||
_gtk_widget_gesture_finish (grab_widget);
|
||||
}
|
||||
|
||||
tmp_list = current_events;
|
||||
current_events = g_list_remove_link (current_events, tmp_list);
|
||||
g_list_free_1 (tmp_list);
|
||||
@@ -2329,6 +2390,135 @@ gtk_get_event_widget (GdkEvent *event)
|
||||
return widget;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
propagate_event_up (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
GtkWidget *topmost)
|
||||
{
|
||||
gboolean handled_event = FALSE;
|
||||
|
||||
/* Propagate event up the widget tree so that
|
||||
* parents can see the button and motion
|
||||
* events of the children.
|
||||
*/
|
||||
while (TRUE)
|
||||
{
|
||||
GtkWidget *tmp;
|
||||
|
||||
g_object_ref (widget);
|
||||
|
||||
/* Scroll events are special cased here because it
|
||||
* feels wrong when scrolling a GtkViewport, say,
|
||||
* to have children of the viewport eat the scroll
|
||||
* event
|
||||
*/
|
||||
if (!gtk_widget_is_sensitive (widget))
|
||||
handled_event = event->type != GDK_SCROLL;
|
||||
else
|
||||
handled_event = gtk_widget_event (widget, event);
|
||||
|
||||
tmp = gtk_widget_get_parent (widget);
|
||||
g_object_unref (widget);
|
||||
|
||||
if (widget == topmost)
|
||||
break;
|
||||
|
||||
widget = tmp;
|
||||
|
||||
if (handled_event || !widget)
|
||||
break;
|
||||
}
|
||||
|
||||
return handled_event;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
propagate_event_down (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
GtkWidget *topmost)
|
||||
{
|
||||
gint handled_event = FALSE;
|
||||
GList *widgets = NULL;
|
||||
GList *l;
|
||||
|
||||
widgets = g_list_prepend (widgets, g_object_ref (widget));
|
||||
while (widget && widget != topmost)
|
||||
{
|
||||
widget = gtk_widget_get_parent (widget);
|
||||
if (!widget)
|
||||
break;
|
||||
|
||||
widgets = g_list_prepend (widgets, g_object_ref (widget));
|
||||
|
||||
if (widget == topmost)
|
||||
break;
|
||||
}
|
||||
|
||||
for (l = widgets; l && !handled_event; l = g_list_next (l))
|
||||
{
|
||||
widget = (GtkWidget *)l->data;
|
||||
|
||||
if (!gtk_widget_is_sensitive (widget))
|
||||
handled_event = TRUE;
|
||||
else
|
||||
handled_event = _gtk_widget_captured_event (widget, event);
|
||||
}
|
||||
g_list_free_full (widgets, (GDestroyNotify)g_object_unref);
|
||||
|
||||
return handled_event;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
propagate_event (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
gboolean captured,
|
||||
GtkWidget *topmost)
|
||||
{
|
||||
gboolean handled_event = FALSE;
|
||||
gboolean (* propagate_func) (GtkWidget *widget, GdkEvent *event);
|
||||
|
||||
propagate_func = captured ? _gtk_widget_captured_event : gtk_widget_event;
|
||||
|
||||
if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
|
||||
{
|
||||
/* Only send key events within Window widgets to the Window
|
||||
* The Window widget will in turn pass the
|
||||
* key event on to the currently focused widget
|
||||
* for that window.
|
||||
*/
|
||||
GtkWidget *window;
|
||||
|
||||
window = gtk_widget_get_toplevel (widget);
|
||||
if (GTK_IS_WINDOW (window))
|
||||
{
|
||||
g_object_ref (widget);
|
||||
/* If there is a grab within the window, give the grab widget
|
||||
* a first crack at the key event
|
||||
*/
|
||||
if (widget != window && gtk_widget_has_grab (widget))
|
||||
handled_event = propagate_func (widget, event);
|
||||
|
||||
if (!handled_event)
|
||||
{
|
||||
window = gtk_widget_get_toplevel (widget);
|
||||
if (GTK_IS_WINDOW (window))
|
||||
{
|
||||
if (gtk_widget_is_sensitive (window))
|
||||
handled_event = propagate_func (window, event);
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (widget);
|
||||
return handled_event;
|
||||
}
|
||||
}
|
||||
|
||||
/* Other events get propagated up/down the widget tree */
|
||||
return captured ?
|
||||
propagate_event_down (widget, event, topmost) :
|
||||
propagate_event_up (widget, event, topmost);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_propagate_event:
|
||||
* @widget: a #GtkWidget
|
||||
@@ -2357,79 +2547,16 @@ void
|
||||
gtk_propagate_event (GtkWidget *widget,
|
||||
GdkEvent *event)
|
||||
{
|
||||
gint handled_event;
|
||||
|
||||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||||
g_return_if_fail (event != NULL);
|
||||
|
||||
handled_event = FALSE;
|
||||
|
||||
g_object_ref (widget);
|
||||
|
||||
if ((event->type == GDK_KEY_PRESS) ||
|
||||
(event->type == GDK_KEY_RELEASE))
|
||||
{
|
||||
/* Only send key events within Window widgets to the Window
|
||||
* The Window widget will in turn pass the
|
||||
* key event on to the currently focused widget
|
||||
* for that window.
|
||||
*/
|
||||
GtkWidget *window;
|
||||
|
||||
window = gtk_widget_get_toplevel (widget);
|
||||
if (GTK_IS_WINDOW (window))
|
||||
{
|
||||
/* If there is a grab within the window, give the grab widget
|
||||
* a first crack at the key event
|
||||
*/
|
||||
if (widget != window && gtk_widget_has_grab (widget))
|
||||
handled_event = gtk_widget_event (widget, event);
|
||||
|
||||
if (!handled_event)
|
||||
{
|
||||
window = gtk_widget_get_toplevel (widget);
|
||||
if (GTK_IS_WINDOW (window))
|
||||
{
|
||||
if (gtk_widget_is_sensitive (window))
|
||||
gtk_widget_event (window, event);
|
||||
}
|
||||
}
|
||||
|
||||
handled_event = TRUE; /* don't send to widget */
|
||||
}
|
||||
}
|
||||
|
||||
/* Other events get propagated up the widget tree
|
||||
* so that parents can see the button and motion
|
||||
* events of the children.
|
||||
*/
|
||||
if (!handled_event)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
GtkWidget *tmp;
|
||||
|
||||
/* Scroll events are special cased here because it
|
||||
* feels wrong when scrolling a GtkViewport, say,
|
||||
* to have children of the viewport eat the scroll
|
||||
* event
|
||||
*/
|
||||
if (!gtk_widget_is_sensitive (widget))
|
||||
handled_event = event->type != GDK_SCROLL;
|
||||
else
|
||||
handled_event = gtk_widget_event (widget, event);
|
||||
|
||||
tmp = gtk_widget_get_parent (widget);
|
||||
g_object_unref (widget);
|
||||
|
||||
widget = tmp;
|
||||
|
||||
if (!handled_event && widget)
|
||||
g_object_ref (widget);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
g_object_unref (widget);
|
||||
propagate_event (widget, event, FALSE, NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_propagate_captured_event (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
GtkWidget *topmost)
|
||||
{
|
||||
return propagate_event (widget, event, TRUE, topmost);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ BOOLEAN:OBJECT,BOXED,BOXED
|
||||
BOOLEAN:OBJECT,OBJECT,OBJECT
|
||||
BOOLEAN:OBJECT,STRING,STRING
|
||||
BOOLEAN:OBJECT,ENUM
|
||||
BOOLEAN:OBJECT,ENUM,INT,INT
|
||||
BOOLEAN:INT
|
||||
BOOLEAN:INT,INT
|
||||
BOOLEAN:INT,INT,INT
|
||||
@@ -47,6 +48,7 @@ BOOLEAN:BOOLEAN,BOOLEAN,BOOLEAN
|
||||
BOOLEAN:STRING
|
||||
ENUM:ENUM
|
||||
ENUM:VOID
|
||||
FLAGS:BOXED
|
||||
INT:POINTER
|
||||
OBJECT:VOID
|
||||
STRING:DOUBLE
|
||||
@@ -109,6 +111,7 @@ VOID:STRING,STRING,STRING
|
||||
VOID:STRING,INT,POINTER
|
||||
VOID:STRING,UINT,FLAGS
|
||||
VOID:STRING,UINT,FLAGS,UINT
|
||||
VOID:UINT,DOUBLE
|
||||
VOID:UINT,FLAGS,BOXED
|
||||
VOID:UINT,UINT
|
||||
VOID:UINT,STRING
|
||||
|
||||
+230
-276
@@ -110,6 +110,7 @@
|
||||
#include "gtksettings.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkdnd.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
@@ -227,12 +228,14 @@ static void gtk_menu_scroll_to (GtkMenu *menu,
|
||||
gint offset);
|
||||
static void gtk_menu_grab_notify (GtkWidget *widget,
|
||||
gboolean was_grabbed);
|
||||
static GtkCapturedEventFlags
|
||||
gtk_menu_captured_event (GtkWidget *widget,
|
||||
GdkEvent *event);
|
||||
|
||||
|
||||
static void gtk_menu_stop_scrolling (GtkMenu *menu);
|
||||
static void gtk_menu_remove_scroll_timeout (GtkMenu *menu);
|
||||
static gboolean gtk_menu_scroll_timeout (gpointer data);
|
||||
static gboolean gtk_menu_scroll_timeout_initial (gpointer data);
|
||||
static void gtk_menu_start_scrolling (GtkMenu *menu);
|
||||
|
||||
static void gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
|
||||
GtkWidget *menu_item);
|
||||
@@ -510,6 +513,7 @@ gtk_menu_class_init (GtkMenuClass *class)
|
||||
widget_class->get_preferred_width = gtk_menu_get_preferred_width;
|
||||
widget_class->get_preferred_height = gtk_menu_get_preferred_height;
|
||||
widget_class->get_preferred_height_for_width = gtk_menu_get_preferred_height_for_width;
|
||||
widget_class->captured_event = gtk_menu_captured_event;
|
||||
|
||||
container_class->remove = gtk_menu_remove;
|
||||
container_class->get_child_property = gtk_menu_get_child_property;
|
||||
@@ -1057,6 +1061,7 @@ gtk_menu_init (GtkMenu *menu)
|
||||
priv->needs_destruction_ref = TRUE;
|
||||
|
||||
priv->monitor_num = -1;
|
||||
priv->drag_start_y = -1;
|
||||
|
||||
context = gtk_widget_get_style_context (GTK_WIDGET (menu));
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENU);
|
||||
@@ -1465,7 +1470,7 @@ gtk_menu_popup_for_device (GtkMenu *menu,
|
||||
GtkMenuShell *menu_shell;
|
||||
gboolean grab_keyboard;
|
||||
GtkWidget *parent_toplevel;
|
||||
GdkDevice *keyboard, *pointer;
|
||||
GdkDevice *keyboard, *pointer, *source_device = NULL;
|
||||
|
||||
g_return_if_fail (GTK_IS_MENU (menu));
|
||||
g_return_if_fail (device == NULL || GDK_IS_DEVICE (device));
|
||||
@@ -1602,6 +1607,7 @@ gtk_menu_popup_for_device (GtkMenu *menu,
|
||||
(current_event->type != GDK_ENTER_NOTIFY))
|
||||
menu_shell->priv->ignore_enter = TRUE;
|
||||
|
||||
source_device = gdk_event_get_source_device (current_event);
|
||||
gdk_event_free (current_event);
|
||||
}
|
||||
else
|
||||
@@ -1671,17 +1677,9 @@ gtk_menu_popup_for_device (GtkMenu *menu,
|
||||
gtk_menu_scroll_to (menu, priv->scroll_offset);
|
||||
|
||||
/* if no item is selected, select the first one */
|
||||
if (!menu_shell->priv->active_menu_item)
|
||||
{
|
||||
gboolean touchscreen_mode;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
|
||||
if (touchscreen_mode)
|
||||
gtk_menu_shell_select_first (menu_shell, TRUE);
|
||||
}
|
||||
if (!menu_shell->priv->active_menu_item &&
|
||||
source_device && gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
|
||||
gtk_menu_shell_select_first (menu_shell, TRUE);
|
||||
|
||||
/* Once everything is set up correctly, map the toplevel */
|
||||
gtk_widget_show (priv->toplevel);
|
||||
@@ -3316,34 +3314,6 @@ gtk_menu_get_preferred_height_for_width (GtkWidget *widget,
|
||||
g_free (nat_heights);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
gtk_menu_button_scroll (GtkMenu *menu,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
GtkMenuPrivate *priv = menu->priv;
|
||||
|
||||
if (priv->upper_arrow_prelight || priv->lower_arrow_prelight)
|
||||
{
|
||||
gboolean touchscreen_mode;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
|
||||
if (touchscreen_mode)
|
||||
gtk_menu_handle_scrolling (menu,
|
||||
event->x_root, event->y_root,
|
||||
event->type == GDK_BUTTON_PRESS,
|
||||
FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pointer_in_menu_window (GtkWidget *widget,
|
||||
gdouble x_root,
|
||||
@@ -3380,13 +3350,16 @@ static gboolean
|
||||
gtk_menu_button_press (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
GdkDevice *source_device;
|
||||
GtkWidget *event_widget;
|
||||
GtkMenu *menu;
|
||||
|
||||
if (event->type != GDK_BUTTON_PRESS)
|
||||
return FALSE;
|
||||
|
||||
/* Don't pass down to menu shell for presses over scroll arrows
|
||||
*/
|
||||
if (gtk_menu_button_scroll (GTK_MENU (widget), event))
|
||||
return TRUE;
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
event_widget = gtk_get_event_widget ((GdkEvent *) event);
|
||||
menu = GTK_MENU (widget);
|
||||
|
||||
/* Don't pass down to menu shell if a non-menuitem part of the menu
|
||||
* was clicked. The check for the event_widget being a GtkMenuShell
|
||||
@@ -3395,10 +3368,16 @@ gtk_menu_button_press (GtkWidget *widget,
|
||||
* the menu or on its border are delivered relative to
|
||||
* menu_shell->window.
|
||||
*/
|
||||
if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
|
||||
if (GTK_IS_MENU_SHELL (event_widget) &&
|
||||
pointer_in_menu_window (widget, event->x_root, event->y_root))
|
||||
return TRUE;
|
||||
|
||||
if (GTK_IS_MENU_ITEM (event_widget) &&
|
||||
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH &&
|
||||
GTK_MENU_ITEM (event_widget)->priv->submenu != NULL &&
|
||||
!gtk_widget_is_drawable (GTK_MENU_ITEM (event_widget)->priv->submenu))
|
||||
menu->priv->ignore_button_release = TRUE;
|
||||
|
||||
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event);
|
||||
}
|
||||
|
||||
@@ -3417,11 +3396,6 @@ gtk_menu_button_release (GtkWidget *widget,
|
||||
if (event->type != GDK_BUTTON_RELEASE)
|
||||
return FALSE;
|
||||
|
||||
/* Don't pass down to menu shell for releases over scroll arrows
|
||||
*/
|
||||
if (gtk_menu_button_scroll (GTK_MENU (widget), event))
|
||||
return TRUE;
|
||||
|
||||
/* Don't pass down to menu shell if a non-menuitem part of the menu
|
||||
* was clicked (see comment in button_press()).
|
||||
*/
|
||||
@@ -3665,10 +3639,14 @@ gtk_menu_motion_notify (GtkWidget *widget,
|
||||
GtkMenu *menu;
|
||||
GtkMenuShell *menu_shell;
|
||||
GtkWidget *parent;
|
||||
GdkDevice *source_device;
|
||||
|
||||
gboolean need_enter;
|
||||
|
||||
if (GTK_IS_MENU (widget))
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
|
||||
if (GTK_IS_MENU (widget) &&
|
||||
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH)
|
||||
{
|
||||
GtkMenuPrivate *priv = GTK_MENU(widget)->priv;
|
||||
|
||||
@@ -3832,90 +3810,17 @@ gtk_menu_scroll_by (GtkMenu *menu,
|
||||
gtk_menu_scroll_to (menu, offset);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_do_timeout_scroll (GtkMenu *menu,
|
||||
gboolean touchscreen_mode)
|
||||
{
|
||||
GtkMenuPrivate *priv = menu->priv;
|
||||
gboolean upper_visible;
|
||||
gboolean lower_visible;
|
||||
|
||||
upper_visible = priv->upper_arrow_visible;
|
||||
lower_visible = priv->lower_arrow_visible;
|
||||
|
||||
gtk_menu_scroll_by (menu, priv->scroll_step);
|
||||
|
||||
if (touchscreen_mode &&
|
||||
(upper_visible != priv->upper_arrow_visible ||
|
||||
lower_visible != priv->lower_arrow_visible))
|
||||
{
|
||||
/* We are about to hide a scroll arrow while the mouse is pressed,
|
||||
* this would cause the uncovered menu item to be activated on button
|
||||
* release. Therefore we need to ignore button release here
|
||||
*/
|
||||
GTK_MENU_SHELL (menu)->priv->ignore_enter = TRUE;
|
||||
priv->ignore_button_release = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_menu_scroll_timeout (gpointer data)
|
||||
{
|
||||
GtkMenu *menu;
|
||||
gboolean touchscreen_mode;
|
||||
|
||||
menu = GTK_MENU (data);
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
|
||||
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
|
||||
gtk_menu_scroll_by (menu, menu->priv->scroll_step);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_menu_scroll_timeout_initial (gpointer data)
|
||||
{
|
||||
GtkMenu *menu;
|
||||
guint timeout;
|
||||
gboolean touchscreen_mode;
|
||||
|
||||
menu = GTK_MENU (data);
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
|
||||
"gtk-timeout-repeat", &timeout,
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
|
||||
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
|
||||
|
||||
gtk_menu_remove_scroll_timeout (menu);
|
||||
|
||||
menu->priv->scroll_timeout =
|
||||
gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout, menu);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_start_scrolling (GtkMenu *menu)
|
||||
{
|
||||
guint timeout;
|
||||
gboolean touchscreen_mode;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
|
||||
"gtk-timeout-repeat", &timeout,
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
|
||||
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
|
||||
|
||||
menu->priv->scroll_timeout =
|
||||
gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout_initial, menu);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_menu_scroll (GtkWidget *widget,
|
||||
GdkEventScroll *event)
|
||||
@@ -4039,14 +3944,9 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
|
||||
gboolean in_arrow;
|
||||
gboolean scroll_fast = FALSE;
|
||||
gint top_x, top_y;
|
||||
gboolean touchscreen_mode;
|
||||
|
||||
menu_shell = GTK_MENU_SHELL (menu);
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
|
||||
gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
|
||||
&top_x, &top_y);
|
||||
x -= top_x;
|
||||
@@ -4064,82 +3964,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
|
||||
in_arrow = TRUE;
|
||||
}
|
||||
|
||||
if (touchscreen_mode)
|
||||
priv->upper_arrow_prelight = in_arrow;
|
||||
|
||||
if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
|
||||
{
|
||||
gboolean arrow_pressed = FALSE;
|
||||
|
||||
if (priv->upper_arrow_visible && !priv->tearoff_active)
|
||||
{
|
||||
if (touchscreen_mode)
|
||||
scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
|
||||
|
||||
if (enter && in_arrow &&
|
||||
(!priv->upper_arrow_prelight ||
|
||||
priv->scroll_fast != scroll_fast))
|
||||
{
|
||||
if (enter && priv->upper_arrow_prelight)
|
||||
{
|
||||
if (priv->scroll_timeout == 0)
|
||||
{
|
||||
/* Deselect the active item so that
|
||||
* any submenus are popped down
|
||||
*/
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
priv->upper_arrow_prelight = TRUE;
|
||||
priv->scroll_fast = scroll_fast;
|
||||
|
||||
gtk_menu_remove_scroll_timeout (menu);
|
||||
priv->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
|
||||
/* Deselect the active item so that
|
||||
* any submenus are popped down
|
||||
*/
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
|
||||
if (!motion)
|
||||
{
|
||||
/* Only do stuff on click. */
|
||||
gtk_menu_start_scrolling (menu);
|
||||
arrow_pressed = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arrow_pressed = TRUE;
|
||||
}
|
||||
}
|
||||
else if (!enter)
|
||||
{
|
||||
gtk_menu_stop_scrolling (menu);
|
||||
}
|
||||
gtk_menu_remove_scroll_timeout (menu);
|
||||
priv->scroll_step = scroll_fast
|
||||
? -MENU_SCROLL_STEP2
|
||||
: -MENU_SCROLL_STEP1;
|
||||
|
||||
priv->scroll_timeout =
|
||||
gdk_threads_add_timeout (scroll_fast
|
||||
? MENU_SCROLL_TIMEOUT2
|
||||
: MENU_SCROLL_TIMEOUT1,
|
||||
gtk_menu_scroll_timeout, menu);
|
||||
}
|
||||
else /* !touchscreen_mode */
|
||||
else if (!enter && !in_arrow && priv->upper_arrow_prelight)
|
||||
{
|
||||
scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
|
||||
|
||||
if (enter && in_arrow &&
|
||||
(!priv->upper_arrow_prelight ||
|
||||
priv->scroll_fast != scroll_fast))
|
||||
{
|
||||
priv->upper_arrow_prelight = TRUE;
|
||||
priv->scroll_fast = scroll_fast;
|
||||
|
||||
/* Deselect the active item so that
|
||||
* any submenus are popped down
|
||||
*/
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
|
||||
gtk_menu_remove_scroll_timeout (menu);
|
||||
priv->scroll_step = scroll_fast
|
||||
? -MENU_SCROLL_STEP2
|
||||
: -MENU_SCROLL_STEP1;
|
||||
|
||||
priv->scroll_timeout =
|
||||
gdk_threads_add_timeout (scroll_fast
|
||||
? MENU_SCROLL_TIMEOUT2
|
||||
: MENU_SCROLL_TIMEOUT1,
|
||||
gtk_menu_scroll_timeout, menu);
|
||||
}
|
||||
else if (!enter && !in_arrow && priv->upper_arrow_prelight)
|
||||
{
|
||||
gtk_menu_stop_scrolling (menu);
|
||||
}
|
||||
gtk_menu_stop_scrolling (menu);
|
||||
}
|
||||
}
|
||||
|
||||
/* gtk_menu_start_scrolling() might have hit the top of the
|
||||
* menu, so check if the button isn't insensitive before
|
||||
/* check if the button isn't insensitive before
|
||||
* changing it to something else.
|
||||
*/
|
||||
if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
|
||||
@@ -4174,82 +4036,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
|
||||
in_arrow = TRUE;
|
||||
}
|
||||
|
||||
if (touchscreen_mode)
|
||||
priv->lower_arrow_prelight = in_arrow;
|
||||
|
||||
if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
|
||||
{
|
||||
gboolean arrow_pressed = FALSE;
|
||||
|
||||
if (priv->lower_arrow_visible && !priv->tearoff_active)
|
||||
{
|
||||
if (touchscreen_mode)
|
||||
scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
|
||||
|
||||
if (enter && in_arrow &&
|
||||
(!priv->lower_arrow_prelight ||
|
||||
priv->scroll_fast != scroll_fast))
|
||||
{
|
||||
if (enter && priv->lower_arrow_prelight)
|
||||
{
|
||||
if (priv->scroll_timeout == 0)
|
||||
{
|
||||
/* Deselect the active item so that
|
||||
* any submenus are popped down
|
||||
*/
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
priv->lower_arrow_prelight = TRUE;
|
||||
priv->scroll_fast = scroll_fast;
|
||||
|
||||
gtk_menu_remove_scroll_timeout (menu);
|
||||
priv->scroll_step = MENU_SCROLL_STEP2; /* always fast */
|
||||
/* Deselect the active item so that
|
||||
* any submenus are popped down
|
||||
*/
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
|
||||
if (!motion)
|
||||
{
|
||||
/* Only do stuff on click. */
|
||||
gtk_menu_start_scrolling (menu);
|
||||
arrow_pressed = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arrow_pressed = TRUE;
|
||||
}
|
||||
}
|
||||
else if (!enter)
|
||||
{
|
||||
gtk_menu_stop_scrolling (menu);
|
||||
}
|
||||
gtk_menu_remove_scroll_timeout (menu);
|
||||
priv->scroll_step = scroll_fast
|
||||
? MENU_SCROLL_STEP2
|
||||
: MENU_SCROLL_STEP1;
|
||||
|
||||
priv->scroll_timeout =
|
||||
gdk_threads_add_timeout (scroll_fast
|
||||
? MENU_SCROLL_TIMEOUT2
|
||||
: MENU_SCROLL_TIMEOUT1,
|
||||
gtk_menu_scroll_timeout, menu);
|
||||
}
|
||||
else /* !touchscreen_mode */
|
||||
else if (!enter && !in_arrow && priv->lower_arrow_prelight)
|
||||
{
|
||||
scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
|
||||
|
||||
if (enter && in_arrow &&
|
||||
(!priv->lower_arrow_prelight ||
|
||||
priv->scroll_fast != scroll_fast))
|
||||
{
|
||||
priv->lower_arrow_prelight = TRUE;
|
||||
priv->scroll_fast = scroll_fast;
|
||||
|
||||
/* Deselect the active item so that
|
||||
* any submenus are popped down
|
||||
*/
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
|
||||
gtk_menu_remove_scroll_timeout (menu);
|
||||
priv->scroll_step = scroll_fast
|
||||
? MENU_SCROLL_STEP2
|
||||
: MENU_SCROLL_STEP1;
|
||||
|
||||
priv->scroll_timeout =
|
||||
gdk_threads_add_timeout (scroll_fast
|
||||
? MENU_SCROLL_TIMEOUT2
|
||||
: MENU_SCROLL_TIMEOUT1,
|
||||
gtk_menu_scroll_timeout, menu);
|
||||
}
|
||||
else if (!enter && !in_arrow && priv->lower_arrow_prelight)
|
||||
{
|
||||
gtk_menu_stop_scrolling (menu);
|
||||
}
|
||||
gtk_menu_stop_scrolling (menu);
|
||||
}
|
||||
}
|
||||
|
||||
/* gtk_menu_start_scrolling() might have hit the bottom of the
|
||||
* menu, so check if the button isn't insensitive before
|
||||
/* check if the button isn't insensitive before
|
||||
* changing it to something else.
|
||||
*/
|
||||
if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
|
||||
@@ -4279,19 +4103,18 @@ gtk_menu_enter_notify (GtkWidget *widget,
|
||||
{
|
||||
GtkWidget *menu_item;
|
||||
GtkWidget *parent;
|
||||
gboolean touchscreen_mode;
|
||||
GdkDevice *source_device;
|
||||
|
||||
if (event->mode == GDK_CROSSING_GTK_GRAB ||
|
||||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
|
||||
event->mode == GDK_CROSSING_STATE_CHANGED)
|
||||
return TRUE;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (widget),
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
menu_item = gtk_get_event_widget ((GdkEvent*) event);
|
||||
if (GTK_IS_MENU (widget))
|
||||
|
||||
if (GTK_IS_MENU (widget) &&
|
||||
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH)
|
||||
{
|
||||
GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
|
||||
|
||||
@@ -4300,7 +4123,8 @@ gtk_menu_enter_notify (GtkWidget *widget,
|
||||
event->x_root, event->y_root, TRUE, TRUE);
|
||||
}
|
||||
|
||||
if (!touchscreen_mode && GTK_IS_MENU_ITEM (menu_item))
|
||||
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH &&
|
||||
GTK_IS_MENU_ITEM (menu_item))
|
||||
{
|
||||
GtkWidget *menu = gtk_widget_get_parent (menu_item);
|
||||
|
||||
@@ -4357,6 +4181,7 @@ gtk_menu_leave_notify (GtkWidget *widget,
|
||||
GtkMenu *menu;
|
||||
GtkMenuItem *menu_item;
|
||||
GtkWidget *event_widget;
|
||||
GdkDevice *source_device;
|
||||
|
||||
if (event->mode == GDK_CROSSING_GTK_GRAB ||
|
||||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
|
||||
@@ -4369,7 +4194,10 @@ gtk_menu_leave_notify (GtkWidget *widget,
|
||||
if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
|
||||
return TRUE;
|
||||
|
||||
gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
|
||||
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH)
|
||||
gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
|
||||
|
||||
event_widget = gtk_get_event_widget ((GdkEvent*) event);
|
||||
|
||||
@@ -4404,6 +4232,138 @@ gtk_menu_leave_notify (GtkWidget *widget,
|
||||
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pointer_on_menu_widget (GtkMenu *menu,
|
||||
gdouble x_root,
|
||||
gdouble y_root)
|
||||
{
|
||||
GtkMenuPrivate *priv = menu->priv;
|
||||
GtkAllocation allocation;
|
||||
gint window_x, window_y;
|
||||
|
||||
gtk_widget_get_allocation (GTK_WIDGET (menu), &allocation);
|
||||
gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
|
||||
&window_x, &window_y);
|
||||
|
||||
if (x_root >= window_x && x_root < window_x + allocation.width &&
|
||||
y_root >= window_y && y_root < window_y + allocation.height)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GtkCapturedEventFlags
|
||||
gtk_menu_captured_event (GtkWidget *widget,
|
||||
GdkEvent *event)
|
||||
{
|
||||
GdkDevice *source_device;
|
||||
GtkCapturedEventFlags flags;
|
||||
GtkMenuPrivate *priv;
|
||||
GtkMenu *menu;
|
||||
|
||||
menu = GTK_MENU (widget);
|
||||
priv = menu->priv;
|
||||
flags = GTK_CAPTURED_EVENT_NONE;
|
||||
|
||||
if (!priv->upper_arrow_visible && !priv->lower_arrow_visible)
|
||||
return flags;
|
||||
|
||||
source_device = gdk_event_get_source_device (event);
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_BUTTON_PRESS:
|
||||
if (event->button.button == 1 &&
|
||||
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH &&
|
||||
pointer_on_menu_widget (menu, event->button.x_root, event->button.y_root))
|
||||
{
|
||||
priv->drag_start_y = event->button.y_root;
|
||||
priv->initial_drag_offset = priv->scroll_offset;
|
||||
priv->drag_scroll_started = FALSE;
|
||||
}
|
||||
else
|
||||
priv->drag_start_y = -1;
|
||||
|
||||
priv->drag_already_pressed = TRUE;
|
||||
break;
|
||||
case GDK_BUTTON_RELEASE:
|
||||
if (priv->drag_scroll_started)
|
||||
{
|
||||
flags = GTK_CAPTURED_EVENT_HANDLED;
|
||||
priv->drag_scroll_started = FALSE;
|
||||
priv->drag_start_y = -1;
|
||||
priv->drag_already_pressed = FALSE;
|
||||
}
|
||||
break;
|
||||
case GDK_MOTION_NOTIFY:
|
||||
if (event->motion.state & GDK_BUTTON1_MASK &&
|
||||
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
|
||||
{
|
||||
if (!priv->drag_already_pressed)
|
||||
{
|
||||
if (pointer_on_menu_widget (menu,
|
||||
event->motion.x_root,
|
||||
event->motion.y_root))
|
||||
{
|
||||
priv->drag_start_y = event->motion.y_root;
|
||||
priv->initial_drag_offset = priv->scroll_offset;
|
||||
priv->drag_scroll_started = FALSE;
|
||||
}
|
||||
else
|
||||
priv->drag_start_y = -1;
|
||||
|
||||
priv->drag_already_pressed = TRUE;
|
||||
}
|
||||
|
||||
if (priv->drag_start_y < 0 &&
|
||||
!priv->drag_scroll_started)
|
||||
break;
|
||||
|
||||
if (priv->drag_scroll_started)
|
||||
{
|
||||
gint offset, view_height;
|
||||
GtkBorder arrow_border;
|
||||
gdouble y_diff;
|
||||
|
||||
y_diff = event->motion.y_root - priv->drag_start_y;
|
||||
offset = priv->initial_drag_offset - y_diff;
|
||||
|
||||
view_height = gdk_window_get_height (gtk_widget_get_window (widget));
|
||||
get_arrows_border (menu, &arrow_border);
|
||||
|
||||
if (priv->upper_arrow_visible)
|
||||
view_height -= arrow_border.top;
|
||||
|
||||
if (priv->lower_arrow_visible)
|
||||
view_height -= arrow_border.bottom;
|
||||
|
||||
offset = CLAMP (offset, 0, priv->requested_height - view_height);
|
||||
gtk_menu_scroll_to (menu, offset);
|
||||
|
||||
flags = GTK_CAPTURED_EVENT_HANDLED;
|
||||
}
|
||||
else if (gtk_drag_check_threshold (widget,
|
||||
0, priv->drag_start_y,
|
||||
0, event->motion.y_root))
|
||||
{
|
||||
priv->drag_scroll_started = TRUE;
|
||||
flags = GTK_CAPTURED_EVENT_HANDLED;
|
||||
gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GDK_ENTER_NOTIFY:
|
||||
case GDK_LEAVE_NOTIFY:
|
||||
if (priv->drag_scroll_started)
|
||||
flags = GTK_CAPTURED_EVENT_HANDLED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_stop_navigating_submenu (GtkMenu *menu)
|
||||
{
|
||||
@@ -4846,19 +4806,10 @@ static void
|
||||
gtk_menu_stop_scrolling (GtkMenu *menu)
|
||||
{
|
||||
GtkMenuPrivate *priv = menu->priv;
|
||||
gboolean touchscreen_mode;
|
||||
|
||||
gtk_menu_remove_scroll_timeout (menu);
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
|
||||
if (!touchscreen_mode)
|
||||
{
|
||||
priv->upper_arrow_prelight = FALSE;
|
||||
priv->lower_arrow_prelight = FALSE;
|
||||
}
|
||||
priv->upper_arrow_prelight = FALSE;
|
||||
priv->lower_arrow_prelight = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -5664,7 +5615,6 @@ gtk_menu_real_move_scroll (GtkMenu *menu,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gtk_menu_set_monitor:
|
||||
* @menu: a #GtkMenu
|
||||
@@ -5741,11 +5691,13 @@ static void
|
||||
gtk_menu_grab_notify (GtkWidget *widget,
|
||||
gboolean was_grabbed)
|
||||
{
|
||||
GtkMenu *menu;
|
||||
GtkWidget *toplevel;
|
||||
GtkWindowGroup *group;
|
||||
GtkWidget *grab;
|
||||
GdkDevice *pointer;
|
||||
|
||||
menu = GTK_MENU (widget);
|
||||
pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (widget));
|
||||
|
||||
if (!pointer ||
|
||||
@@ -5762,6 +5714,8 @@ gtk_menu_grab_notify (GtkWidget *widget,
|
||||
|
||||
if (GTK_MENU_SHELL (widget)->priv->active && !GTK_IS_MENU_SHELL (grab))
|
||||
gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
|
||||
|
||||
menu->priv->drag_scroll_started = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+12
-5
@@ -1690,13 +1690,20 @@ static void
|
||||
gtk_real_menu_item_select (GtkMenuItem *menu_item)
|
||||
{
|
||||
GtkMenuItemPrivate *priv = menu_item->priv;
|
||||
gboolean touchscreen_mode;
|
||||
GdkDevice *source_device = NULL;
|
||||
GdkEvent *current_event;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_item)),
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
current_event = gtk_get_current_event ();
|
||||
|
||||
if (!touchscreen_mode && priv->submenu &&
|
||||
if (current_event)
|
||||
{
|
||||
source_device = gdk_event_get_source_device (current_event);
|
||||
gdk_event_free (current_event);
|
||||
}
|
||||
|
||||
if ((!source_device ||
|
||||
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH) &&
|
||||
priv->submenu &&
|
||||
(!gtk_widget_get_mapped (priv->submenu) ||
|
||||
GTK_MENU (priv->submenu)->priv->tearoff_active))
|
||||
{
|
||||
|
||||
@@ -100,6 +100,8 @@ struct _GtkMenuPrivate
|
||||
guint seen_item_enter : 1;
|
||||
guint ignore_button_release : 1;
|
||||
guint no_toggle_size : 1;
|
||||
guint drag_already_pressed : 1;
|
||||
guint drag_scroll_started : 1;
|
||||
|
||||
/* info used for the table */
|
||||
guint *heights;
|
||||
@@ -126,6 +128,9 @@ struct _GtkMenuPrivate
|
||||
gint navigation_height;
|
||||
|
||||
guint navigation_timeout;
|
||||
|
||||
gdouble drag_start_y;
|
||||
gint initial_drag_offset;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
+4
-32
@@ -1084,13 +1084,11 @@ gtk_menu_shell_enter_notify (GtkWidget *widget,
|
||||
|
||||
if (!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
|
||||
{
|
||||
gboolean touchscreen_mode;
|
||||
GdkDevice *source_device;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (widget),
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
|
||||
if (touchscreen_mode)
|
||||
if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
|
||||
_gtk_menu_item_popup_submenu (menu_item, TRUE);
|
||||
}
|
||||
}
|
||||
@@ -1612,45 +1610,19 @@ gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
|
||||
GtkMenuShellPrivate *priv = menu_shell->priv;
|
||||
GtkMenuShell *parent_menu_shell = NULL;
|
||||
gboolean had_selection;
|
||||
gboolean touchscreen_mode;
|
||||
|
||||
priv->in_unselectable_item = FALSE;
|
||||
|
||||
had_selection = priv->active_menu_item != NULL;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
|
||||
"gtk-touchscreen-mode", &touchscreen_mode,
|
||||
NULL);
|
||||
|
||||
if (priv->parent_menu_shell)
|
||||
parent_menu_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case GTK_MENU_DIR_PARENT:
|
||||
if (touchscreen_mode &&
|
||||
priv->active_menu_item &&
|
||||
GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu &&
|
||||
gtk_widget_get_visible (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu))
|
||||
if (parent_menu_shell)
|
||||
{
|
||||
/* if we are on a menu item that has an open submenu but the
|
||||
* focus is not in that submenu (e.g. because it's empty or
|
||||
* has only insensitive items), close that submenu instead of
|
||||
* running into the code below which would close *this* menu.
|
||||
*/
|
||||
_gtk_menu_item_popdown_submenu (priv->active_menu_item);
|
||||
_gtk_menu_shell_update_mnemonics (menu_shell);
|
||||
}
|
||||
else if (parent_menu_shell)
|
||||
{
|
||||
if (touchscreen_mode)
|
||||
{
|
||||
/* close menu when returning from submenu. */
|
||||
_gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->priv->parent_menu_item);
|
||||
_gtk_menu_shell_update_mnemonics (parent_menu_shell);
|
||||
break;
|
||||
}
|
||||
|
||||
if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
|
||||
GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
|
||||
gtk_menu_shell_deselect (menu_shell);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "gdk/gdk.h"
|
||||
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkenums.h"
|
||||
|
||||
|
||||
#if !defined G_OS_WIN32 && !(defined GDK_WINDOWING_QUARTZ && defined QUARTZ_RELOCATION)
|
||||
@@ -159,6 +160,22 @@ _gtk_single_string_accumulator (GSignalInvocationHint *ihint,
|
||||
return continue_emission;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_captured_enum_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer dummy)
|
||||
{
|
||||
gboolean continue_emission;
|
||||
GtkCapturedEventFlags flags;
|
||||
|
||||
flags = g_value_get_flags (handler_return);
|
||||
g_value_set_flags (return_accu, flags);
|
||||
continue_emission = (flags & GTK_CAPTURED_EVENT_HANDLED) == 0;
|
||||
|
||||
return continue_emission;
|
||||
}
|
||||
|
||||
GdkModifierType
|
||||
_gtk_replace_virtual_modifiers (GdkKeymap *keymap,
|
||||
GdkModifierType modifiers)
|
||||
|
||||
@@ -59,6 +59,10 @@ gboolean _gtk_single_string_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer dummy);
|
||||
gboolean _gtk_captured_enum_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer dummy);
|
||||
|
||||
GdkModifierType _gtk_replace_virtual_modifiers (GdkKeymap *keymap,
|
||||
GdkModifierType modifiers);
|
||||
@@ -74,6 +78,10 @@ gboolean _gtk_translate_keyboard_accel_state (GdkKeymap *keymap,
|
||||
gint *level,
|
||||
GdkModifierType *consumed_modifiers);
|
||||
|
||||
gboolean _gtk_propagate_captured_event (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
GtkWidget *topmost);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_PRIVATE_H__ */
|
||||
|
||||
+21
-24
@@ -2009,15 +2009,11 @@ gtk_range_draw (GtkWidget *widget,
|
||||
GtkStateFlags widget_state;
|
||||
gint focus_line_width = 0;
|
||||
gint focus_padding = 0;
|
||||
gboolean touchscreen;
|
||||
gboolean draw_trough = TRUE;
|
||||
gboolean draw_slider = TRUE;
|
||||
GtkStyleContext *context;
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
g_object_get (gtk_widget_get_settings (widget),
|
||||
"gtk-touchscreen-mode", &touchscreen,
|
||||
NULL);
|
||||
|
||||
if (GTK_IS_SCALE (widget) &&
|
||||
gtk_adjustment_get_upper (priv->adjustment) == gtk_adjustment_get_lower (priv->adjustment))
|
||||
@@ -2279,7 +2275,7 @@ gtk_range_draw (GtkWidget *widget,
|
||||
|
||||
state &= ~(GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_ACTIVE);
|
||||
|
||||
if (!touchscreen && priv->mouse_location == MOUSE_SLIDER && !(state & GTK_STATE_FLAG_INSENSITIVE))
|
||||
if (priv->mouse_location == MOUSE_SLIDER && !(state & GTK_STATE_FLAG_INSENSITIVE))
|
||||
state |= GTK_STATE_FLAG_PRELIGHT;
|
||||
|
||||
if (priv->grab_location == MOUSE_SLIDER)
|
||||
@@ -2309,28 +2305,28 @@ gtk_range_draw (GtkWidget *widget,
|
||||
draw_stepper (range, STEPPER_A, cr,
|
||||
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
|
||||
priv->grab_location == MOUSE_STEPPER_A,
|
||||
!touchscreen && priv->mouse_location == MOUSE_STEPPER_A,
|
||||
priv->mouse_location == MOUSE_STEPPER_A,
|
||||
widget_state);
|
||||
|
||||
if (priv->has_stepper_b)
|
||||
draw_stepper (range, STEPPER_B, cr,
|
||||
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
|
||||
priv->grab_location == MOUSE_STEPPER_B,
|
||||
!touchscreen && priv->mouse_location == MOUSE_STEPPER_B,
|
||||
priv->mouse_location == MOUSE_STEPPER_B,
|
||||
widget_state);
|
||||
|
||||
if (priv->has_stepper_c)
|
||||
draw_stepper (range, STEPPER_C, cr,
|
||||
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
|
||||
priv->grab_location == MOUSE_STEPPER_C,
|
||||
!touchscreen && priv->mouse_location == MOUSE_STEPPER_C,
|
||||
priv->mouse_location == MOUSE_STEPPER_C,
|
||||
widget_state);
|
||||
|
||||
if (priv->has_stepper_d)
|
||||
draw_stepper (range, STEPPER_D, cr,
|
||||
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
|
||||
priv->grab_location == MOUSE_STEPPER_D,
|
||||
!touchscreen && priv->mouse_location == MOUSE_STEPPER_D,
|
||||
priv->mouse_location == MOUSE_STEPPER_D,
|
||||
widget_state);
|
||||
|
||||
return FALSE;
|
||||
@@ -2347,15 +2343,11 @@ range_grab_add (GtkRange *range,
|
||||
if (device == priv->grab_device)
|
||||
return;
|
||||
|
||||
if (priv->grab_device != NULL)
|
||||
{
|
||||
g_warning ("GtkRange already had a grab device, releasing device grab");
|
||||
gtk_device_grab_remove (GTK_WIDGET (range), priv->grab_device);
|
||||
}
|
||||
|
||||
/* we don't actually gdk_grab, since a button is down */
|
||||
gtk_device_grab_add (GTK_WIDGET (range), device, TRUE);
|
||||
|
||||
/* Don't perform any GDK/GTK+ grab here. Since a button
|
||||
* is down, there's an ongoing implicit grab on
|
||||
* priv->event_window, which pretty much guarantees this
|
||||
* is the only widget receiving the pointer events.
|
||||
*/
|
||||
priv->grab_location = location;
|
||||
priv->grab_button = button;
|
||||
priv->grab_device = device;
|
||||
@@ -2532,7 +2524,8 @@ gtk_range_button_press (GtkWidget *widget,
|
||||
{
|
||||
GtkRange *range = GTK_RANGE (widget);
|
||||
GtkRangePrivate *priv = range->priv;
|
||||
GdkDevice *device;
|
||||
GdkDevice *device, *source_device;
|
||||
GdkInputSource source;
|
||||
|
||||
if (!gtk_widget_has_focus (widget))
|
||||
gtk_widget_grab_focus (widget);
|
||||
@@ -2542,6 +2535,9 @@ gtk_range_button_press (GtkWidget *widget,
|
||||
return FALSE;
|
||||
|
||||
device = gdk_event_get_device ((GdkEvent *) event);
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
source = gdk_device_get_source (source_device);
|
||||
|
||||
priv->mouse_x = event->x;
|
||||
priv->mouse_y = event->y;
|
||||
|
||||
@@ -2558,7 +2554,8 @@ gtk_range_button_press (GtkWidget *widget,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (priv->mouse_location == MOUSE_TROUGH &&
|
||||
if (source != GDK_SOURCE_TOUCH &&
|
||||
priv->mouse_location == MOUSE_TROUGH &&
|
||||
event->button == GDK_BUTTON_PRIMARY)
|
||||
{
|
||||
/* button 1 steps by page increment, as with button 2 on a stepper
|
||||
@@ -2609,17 +2606,17 @@ gtk_range_button_press (GtkWidget *widget,
|
||||
return TRUE;
|
||||
}
|
||||
else if ((priv->mouse_location == MOUSE_TROUGH &&
|
||||
event->button == GDK_BUTTON_MIDDLE) ||
|
||||
(source == GDK_SOURCE_TOUCH || event->button == GDK_BUTTON_MIDDLE)) ||
|
||||
priv->mouse_location == MOUSE_SLIDER)
|
||||
{
|
||||
gboolean need_value_update = FALSE;
|
||||
|
||||
/* Any button can be used to drag the slider, but you can start
|
||||
* dragging the slider with a trough click using button 2;
|
||||
* On button 2 press, we warp the slider to mouse position,
|
||||
* then begin the slider drag.
|
||||
* On button 2 press and touch devices, we warp the slider to
|
||||
* mouse position, then begin the slider drag.
|
||||
*/
|
||||
if (event->button == GDK_BUTTON_MIDDLE)
|
||||
if (event->button == GDK_BUTTON_MIDDLE || source == GDK_SOURCE_TOUCH)
|
||||
{
|
||||
gdouble slider_low_value, slider_high_value, new_value;
|
||||
|
||||
|
||||
+1009
-12
File diff suppressed because it is too large
Load Diff
@@ -117,6 +117,9 @@ void gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *sc
|
||||
gint gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window);
|
||||
void gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
|
||||
gint height);
|
||||
void gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
|
||||
GtkKineticScrollingFlags flags);
|
||||
GtkKineticScrollingFlags gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window);
|
||||
|
||||
gint _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window);
|
||||
|
||||
|
||||
+58
-5
@@ -32,8 +32,10 @@
|
||||
#include "gtkwidget.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkcssproviderprivate.h"
|
||||
#include "gtkcssstylepropertyprivate.h"
|
||||
#include "gtkstyleproviderprivate.h"
|
||||
#include "gtksymboliccolor.h"
|
||||
#include "gtkanimationdescription.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkversion.h"
|
||||
|
||||
@@ -97,9 +99,10 @@
|
||||
*/
|
||||
|
||||
|
||||
#define DEFAULT_TIMEOUT_INITIAL 200
|
||||
#define DEFAULT_TIMEOUT_REPEAT 20
|
||||
#define DEFAULT_TIMEOUT_EXPAND 500
|
||||
#define DEFAULT_TIMEOUT_INITIAL 200
|
||||
#define DEFAULT_TIMEOUT_REPEAT 20
|
||||
#define DEFAULT_TIMEOUT_EXPAND 500
|
||||
#define DEFAULT_TIMEOUT_PRESS_AND_HOLD 800
|
||||
|
||||
typedef struct _GtkSettingsPropertyValue GtkSettingsPropertyValue;
|
||||
typedef struct _GtkSettingsValuePrivate GtkSettingsValuePrivate;
|
||||
@@ -207,7 +210,8 @@ enum {
|
||||
PROP_IM_PREEDIT_STYLE,
|
||||
PROP_IM_STATUS_STYLE,
|
||||
PROP_SHELL_SHOWS_APP_MENU,
|
||||
PROP_SHELL_SHOWS_MENUBAR
|
||||
PROP_SHELL_SHOWS_MENUBAR,
|
||||
PROP_PRESS_AND_HOLD_TIMEOUT
|
||||
};
|
||||
|
||||
/* --- prototypes --- */
|
||||
@@ -701,13 +705,16 @@ gtk_settings_class_init (GtkSettingsClass *class)
|
||||
* functionality.
|
||||
*
|
||||
* Since: 2.10
|
||||
*
|
||||
* Deprecated: 3.4. Generally the behavior touchscreen input should be
|
||||
* performed dynamically based on gdk_event_get_source_device().
|
||||
*/
|
||||
result = settings_install_property_parser (class,
|
||||
g_param_spec_boolean ("gtk-touchscreen-mode",
|
||||
P_("Enable Touchscreen Mode"),
|
||||
P_("When TRUE, there are no motion notify events delivered on this screen"),
|
||||
FALSE,
|
||||
GTK_PARAM_READWRITE),
|
||||
GTK_PARAM_READWRITE | G_PARAM_DEPRECATED),
|
||||
NULL);
|
||||
|
||||
g_assert (result == PROP_TOUCHSCREEN_MODE);
|
||||
@@ -1350,6 +1357,24 @@ gtk_settings_class_init (GtkSettingsClass *class)
|
||||
NULL);
|
||||
g_assert (result == PROP_SHELL_SHOWS_MENUBAR);
|
||||
|
||||
/**
|
||||
* GtkSettings:gtk-press-and-hold-timeout:
|
||||
*
|
||||
* The amount of time, in milliseconds, a button has to be pressed
|
||||
* before the press-and-hold signal with the trigger action is emitted.
|
||||
*
|
||||
* Since: 3.2
|
||||
*/
|
||||
result = settings_install_property_parser (class,
|
||||
g_param_spec_int ("gtk-press-and-hold-timeout",
|
||||
P_("Press And Hold Timeout"),
|
||||
P_("Timeout before press-and-hold action activates"),
|
||||
0, G_MAXINT,
|
||||
DEFAULT_TIMEOUT_PRESS_AND_HOLD,
|
||||
GTK_PARAM_READWRITE),
|
||||
NULL);
|
||||
g_assert (result == PROP_PRESS_AND_HOLD_TIMEOUT);
|
||||
|
||||
g_type_class_add_private (class, sizeof (GtkSettingsPrivate));
|
||||
}
|
||||
|
||||
@@ -1479,6 +1504,34 @@ gtk_settings_style_provider_lookup (GtkStyleProviderPrivate *provider,
|
||||
path,
|
||||
state,
|
||||
lookup);
|
||||
|
||||
/* Set animation for press and hold */
|
||||
if (gtk_widget_path_iter_has_class (path, 0, GTK_STYLE_CLASS_PRESS_AND_HOLD))
|
||||
{
|
||||
GtkAnimationDescription *anim_desc;
|
||||
static GValue anim_value = { 0 };
|
||||
GtkStyleProperty *prop;
|
||||
gint duration;
|
||||
|
||||
if (!G_IS_VALUE (&anim_value))
|
||||
{
|
||||
g_object_get (settings,
|
||||
"gtk-press-and-hold-timeout", &duration,
|
||||
NULL);
|
||||
|
||||
anim_desc = _gtk_animation_description_new (duration,
|
||||
GTK_TIMELINE_PROGRESS_LINEAR,
|
||||
FALSE);
|
||||
g_value_init (&anim_value, GTK_TYPE_ANIMATION_DESCRIPTION);
|
||||
g_value_take_boxed (&anim_value, anim_desc);
|
||||
}
|
||||
|
||||
prop = _gtk_style_property_lookup ("transition");
|
||||
|
||||
_gtk_css_lookup_set (lookup,
|
||||
_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)),
|
||||
NULL, &anim_value);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -648,6 +648,13 @@ struct _GtkStyleContextClass
|
||||
*/
|
||||
#define GTK_STYLE_CLASS_RIGHT "right"
|
||||
|
||||
/**
|
||||
* GTK_STYLE_CLASS_PRESS_AND_HOLD:
|
||||
*
|
||||
* A CSS class for the press and hold activity indicator.
|
||||
*/
|
||||
#define GTK_STYLE_CLASS_PRESS_AND_HOLD "press-and-hold"
|
||||
|
||||
/* Predefined set of widget regions */
|
||||
|
||||
/**
|
||||
|
||||
+71
-35
@@ -378,6 +378,11 @@ static void gtk_text_view_drag_data_received (GtkWidget *widget,
|
||||
guint time);
|
||||
|
||||
static gboolean gtk_text_view_popup_menu (GtkWidget *widget);
|
||||
static gboolean gtk_text_view_press_and_hold (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
GtkPressAndHoldAction action,
|
||||
gint x,
|
||||
gint y);
|
||||
|
||||
static void gtk_text_view_move_cursor (GtkTextView *text_view,
|
||||
GtkMovementStep step,
|
||||
@@ -463,7 +468,9 @@ static void gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
|
||||
gint y);
|
||||
|
||||
static void gtk_text_view_do_popup (GtkTextView *text_view,
|
||||
GdkEventButton *event);
|
||||
GdkDevice *device,
|
||||
guint32 _time,
|
||||
guint button);
|
||||
|
||||
static void cancel_pending_scroll (GtkTextView *text_view);
|
||||
static void gtk_text_view_queue_scroll (GtkTextView *text_view,
|
||||
@@ -631,7 +638,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
|
||||
widget_class->drag_data_received = gtk_text_view_drag_data_received;
|
||||
|
||||
widget_class->popup_menu = gtk_text_view_popup_menu;
|
||||
|
||||
widget_class->press_and_hold = gtk_text_view_press_and_hold;
|
||||
|
||||
container_class->add = gtk_text_view_add;
|
||||
container_class->remove = gtk_text_view_remove;
|
||||
container_class->forall = gtk_text_view_forall;
|
||||
@@ -3984,12 +3992,10 @@ gtk_text_view_realize (GtkWidget *widget)
|
||||
GtkTextView *text_view;
|
||||
GtkTextViewPrivate *priv;
|
||||
GtkStyleContext *context;
|
||||
GtkStateFlags state;
|
||||
GdkWindow *window;
|
||||
GdkWindowAttr attributes;
|
||||
gint attributes_mask;
|
||||
GSList *tmp_list;
|
||||
GdkRGBA color;
|
||||
|
||||
text_view = GTK_TEXT_VIEW (widget);
|
||||
priv = text_view->priv;
|
||||
@@ -4015,10 +4021,11 @@ gtk_text_view_realize (GtkWidget *widget)
|
||||
gdk_window_set_user_data (window, widget);
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
state = gtk_widget_get_state_flags (widget);
|
||||
|
||||
gtk_style_context_get_background_color (context, state, &color);
|
||||
gdk_window_set_background_rgba (window, &color);
|
||||
gtk_style_context_save (context);
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
|
||||
gtk_style_context_set_background (context, window);
|
||||
gtk_style_context_restore (context);
|
||||
|
||||
text_window_realize (priv->text_window, widget);
|
||||
|
||||
@@ -4118,16 +4125,14 @@ gtk_text_view_set_background (GtkTextView *text_view)
|
||||
gtk_style_context_save (context);
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
|
||||
|
||||
gtk_style_context_get_background_color (context, state, &color);
|
||||
gdk_window_set_background_rgba (priv->text_window->bin_window, &color);
|
||||
gtk_style_context_set_background (context, priv->text_window->bin_window);
|
||||
gtk_style_context_set_background (context, gtk_widget_get_window (widget));
|
||||
|
||||
gtk_style_context_restore (context);
|
||||
|
||||
/* Set lateral panes background */
|
||||
gtk_style_context_get_background_color (context, state, &color);
|
||||
|
||||
gdk_window_set_background_rgba (gtk_widget_get_window (widget), &color);
|
||||
|
||||
if (priv->left_window)
|
||||
gdk_window_set_background_rgba (priv->left_window->bin_window, &color);
|
||||
|
||||
@@ -4273,8 +4278,15 @@ gtk_text_view_grab_notify (GtkWidget *widget,
|
||||
if (priv->grab_device &&
|
||||
gtk_widget_device_is_shadowed (widget, priv->grab_device))
|
||||
{
|
||||
if (priv->drag_start_x >= 0)
|
||||
{
|
||||
priv->drag_start_x = -1;
|
||||
priv->drag_start_y = -1;
|
||||
}
|
||||
|
||||
gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget));
|
||||
gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (widget));
|
||||
priv->grab_device = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4552,7 +4564,8 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
|
||||
|
||||
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
|
||||
{
|
||||
gtk_text_view_do_popup (text_view, event);
|
||||
gtk_text_view_do_popup (text_view, event->device,
|
||||
event->time, event->button);
|
||||
return TRUE;
|
||||
}
|
||||
else if (event->button == GDK_BUTTON_PRIMARY)
|
||||
@@ -4575,6 +4588,7 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
|
||||
gtk_widget_get_modifier_mask (widget,
|
||||
GDK_MODIFIER_INTENT_EXTEND_SELECTION)))
|
||||
{
|
||||
priv->grab_device = event->device;
|
||||
priv->drag_start_x = event->x;
|
||||
priv->drag_start_y = event->y;
|
||||
priv->pending_place_cursor_button = event->button;
|
||||
@@ -8315,16 +8329,18 @@ popup_targets_received (GtkClipboard *clipboard,
|
||||
signals[POPULATE_POPUP],
|
||||
0,
|
||||
priv->popup_menu);
|
||||
|
||||
if (info->device)
|
||||
gtk_menu_popup_for_device (GTK_MENU (priv->popup_menu),
|
||||
info->device, NULL, NULL, NULL, NULL, NULL,
|
||||
info->button, info->time);
|
||||
|
||||
if (gdk_device_get_source (info->device) != GDK_SOURCE_KEYBOARD)
|
||||
gtk_menu_popup_for_device (GTK_MENU (priv->popup_menu),
|
||||
info->device, NULL, NULL, NULL, NULL, NULL,
|
||||
info->button, info->time);
|
||||
else
|
||||
{
|
||||
gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
|
||||
popup_position_func, text_view,
|
||||
0, gtk_get_current_event_time ());
|
||||
gtk_menu_popup_for_device (GTK_MENU (priv->popup_menu),
|
||||
info->device, NULL, NULL,
|
||||
popup_position_func,
|
||||
text_view, NULL,
|
||||
0, info->time);
|
||||
gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
|
||||
}
|
||||
}
|
||||
@@ -8335,7 +8351,9 @@ popup_targets_received (GtkClipboard *clipboard,
|
||||
|
||||
static void
|
||||
gtk_text_view_do_popup (GtkTextView *text_view,
|
||||
GdkEventButton *event)
|
||||
GdkDevice *device,
|
||||
guint32 _time,
|
||||
guint button)
|
||||
{
|
||||
PopupInfo *info = g_new (PopupInfo, 1);
|
||||
|
||||
@@ -8344,19 +8362,9 @@ gtk_text_view_do_popup (GtkTextView *text_view,
|
||||
* we get them, then we actually pop up the menu.
|
||||
*/
|
||||
info->text_view = g_object_ref (text_view);
|
||||
|
||||
if (event)
|
||||
{
|
||||
info->button = event->button;
|
||||
info->time = event->time;
|
||||
info->device = event->device;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->button = 0;
|
||||
info->time = gtk_get_current_event_time ();
|
||||
info->device = NULL;
|
||||
}
|
||||
info->button = button;
|
||||
info->time = _time;
|
||||
info->device = device;
|
||||
|
||||
gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (text_view),
|
||||
GDK_SELECTION_CLIPBOARD),
|
||||
@@ -8368,7 +8376,35 @@ gtk_text_view_do_popup (GtkTextView *text_view,
|
||||
static gboolean
|
||||
gtk_text_view_popup_menu (GtkWidget *widget)
|
||||
{
|
||||
gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);
|
||||
gtk_text_view_do_popup (GTK_TEXT_VIEW (widget),
|
||||
gtk_get_current_event_device (),
|
||||
gtk_get_current_event_time (),
|
||||
0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_text_view_press_and_hold (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
GtkPressAndHoldAction action,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
if (action == GTK_PRESS_AND_HOLD_TRIGGER)
|
||||
gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), device,
|
||||
GDK_CURRENT_TIME, 1);
|
||||
else if (action == GTK_PRESS_AND_HOLD_QUERY)
|
||||
{
|
||||
GdkDevice *source_device;
|
||||
GdkEvent *event;
|
||||
|
||||
event = gtk_get_current_event ();
|
||||
source_device = gdk_event_get_source_device (event);
|
||||
|
||||
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -2792,6 +2792,63 @@ render_spinner (GtkThemingEngine *engine,
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
render_press_and_hold (GtkThemingEngine *engine,
|
||||
cairo_t *cr,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
gdouble width,
|
||||
gdouble height)
|
||||
{
|
||||
gdouble progress, radius, border_width;
|
||||
GdkRGBA color, bg_color;
|
||||
GtkStateFlags flags;
|
||||
GtkBorder border;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
if (!gtk_theming_engine_state_is_running (engine,
|
||||
GTK_STATE_FLAG_ACTIVE,
|
||||
&progress))
|
||||
progress = 0;
|
||||
|
||||
flags = gtk_theming_engine_get_state (engine);
|
||||
gtk_theming_engine_get_background_color (engine, flags, &bg_color);
|
||||
gtk_theming_engine_get_color (engine, flags, &color);
|
||||
gtk_theming_engine_get_border (engine, flags, &border);
|
||||
|
||||
border_width = (gdouble) MAX (MAX (border.top, border.bottom),
|
||||
MAX (border.left, border.right));
|
||||
|
||||
radius = MIN (width, height) / 2;
|
||||
|
||||
if (border_width == 0 ||
|
||||
border_width >= radius - border_width)
|
||||
border_width = MAX (1, radius / 4);
|
||||
|
||||
cairo_set_line_width (cr, border_width);
|
||||
radius -= border_width;
|
||||
|
||||
/* Arcs start from the negative Y axis */
|
||||
cairo_arc (cr,
|
||||
width / 2, height / 2,
|
||||
radius,
|
||||
- G_PI_2, - G_PI_2 + (2 * G_PI));
|
||||
|
||||
gdk_cairo_set_source_rgba (cr, &bg_color);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_arc (cr,
|
||||
width / 2, height / 2,
|
||||
radius,
|
||||
- G_PI_2,
|
||||
- G_PI_2 + (2 * G_PI * progress));
|
||||
gdk_cairo_set_source_rgba (cr, &color);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_theming_engine_render_activity (GtkThemingEngine *engine,
|
||||
cairo_t *cr,
|
||||
@@ -2804,6 +2861,10 @@ gtk_theming_engine_render_activity (GtkThemingEngine *engine,
|
||||
{
|
||||
render_spinner (engine, cr, x, y, width, height);
|
||||
}
|
||||
else if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_PRESS_AND_HOLD))
|
||||
{
|
||||
render_press_and_hold (engine, cr, x, y, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_theming_engine_render_background (engine, cr, x, y, width, height);
|
||||
|
||||
+22
-3
@@ -35,6 +35,7 @@ struct GtkTimelinePriv
|
||||
guint source_id;
|
||||
|
||||
GTimer *timer;
|
||||
gdouble elapsed_time;
|
||||
|
||||
gdouble progress;
|
||||
gdouble last_progress;
|
||||
@@ -309,19 +310,18 @@ gtk_timeline_run_frame (GtkTimeline *timeline)
|
||||
{
|
||||
GtkTimelinePriv *priv;
|
||||
gdouble delta_progress, progress;
|
||||
guint elapsed_time;
|
||||
|
||||
/* the user may unref us during the signals, so save ourselves */
|
||||
g_object_ref (timeline);
|
||||
|
||||
priv = timeline->priv;
|
||||
|
||||
elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
|
||||
priv->elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
|
||||
g_timer_start (priv->timer);
|
||||
|
||||
if (priv->animations_enabled)
|
||||
{
|
||||
delta_progress = (gdouble) elapsed_time / priv->duration;
|
||||
delta_progress = (gdouble) priv->elapsed_time / priv->duration;
|
||||
progress = priv->last_progress;
|
||||
|
||||
if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
|
||||
@@ -515,6 +515,25 @@ _gtk_timeline_is_running (GtkTimeline *timeline)
|
||||
return (priv->source_id != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_timeline_get_elapsed_time:
|
||||
* @timeline: A #GtkTimeline
|
||||
*
|
||||
* Returns the elapsed time since the last GtkTimeline::frame signal
|
||||
*
|
||||
* Return Value: elapsed time in milliseconds since the last frame
|
||||
**/
|
||||
guint
|
||||
_gtk_timeline_get_elapsed_time (GtkTimeline *timeline)
|
||||
{
|
||||
GtkTimelinePriv *priv;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
|
||||
|
||||
priv = timeline->priv;
|
||||
return priv->elapsed_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_timeline_get_fps:
|
||||
* @timeline: A #GtkTimeline
|
||||
|
||||
@@ -84,6 +84,7 @@ void _gtk_timeline_pause (GtkTimeline
|
||||
void _gtk_timeline_rewind (GtkTimeline *timeline);
|
||||
|
||||
gboolean _gtk_timeline_is_running (GtkTimeline *timeline);
|
||||
guint _gtk_timeline_get_elapsed_time (GtkTimeline *timeline);
|
||||
|
||||
guint _gtk_timeline_get_fps (GtkTimeline *timeline);
|
||||
void _gtk_timeline_set_fps (GtkTimeline *timeline,
|
||||
|
||||
@@ -658,13 +658,9 @@ gtk_toggle_button_update_state (GtkButton *button)
|
||||
{
|
||||
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
|
||||
GtkToggleButtonPrivate *priv = toggle_button->priv;
|
||||
gboolean depressed, touchscreen;
|
||||
gboolean depressed;
|
||||
GtkStateFlags new_state = 0;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (button)),
|
||||
"gtk-touchscreen-mode", &touchscreen,
|
||||
NULL);
|
||||
|
||||
new_state = gtk_widget_get_state_flags (GTK_WIDGET (button)) &
|
||||
~(GTK_STATE_FLAG_INCONSISTENT |
|
||||
GTK_STATE_FLAG_PRELIGHT |
|
||||
@@ -680,7 +676,7 @@ gtk_toggle_button_update_state (GtkButton *button)
|
||||
else
|
||||
depressed = priv->active;
|
||||
|
||||
if (!touchscreen && button->priv->in_button && (!button->priv->button_down || priv->draw_indicator))
|
||||
if (button->priv->in_button && (!button->priv->button_down || priv->draw_indicator))
|
||||
new_state |= GTK_STATE_FLAG_PRELIGHT;
|
||||
|
||||
if (depressed)
|
||||
|
||||
+17
-5
@@ -1537,22 +1537,34 @@ _gtk_tooltip_hide (GtkWidget *widget)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tooltips_enabled (GdkWindow *window)
|
||||
tooltips_enabled (GdkEvent *event)
|
||||
{
|
||||
GdkDevice *source_device;
|
||||
GdkInputSource source;
|
||||
GdkWindow *window;
|
||||
gboolean enabled;
|
||||
gboolean touchscreen;
|
||||
GdkScreen *screen;
|
||||
GtkSettings *settings;
|
||||
|
||||
window = event->any.window;
|
||||
source_device = gdk_event_get_source_device (event);
|
||||
|
||||
if (!source_device)
|
||||
return FALSE;
|
||||
|
||||
source = gdk_device_get_source (source_device);
|
||||
screen = gdk_window_get_screen (window);
|
||||
settings = gtk_settings_get_for_screen (screen);
|
||||
|
||||
g_object_get (settings,
|
||||
"gtk-touchscreen-mode", &touchscreen,
|
||||
"gtk-enable-tooltips", &enabled,
|
||||
NULL);
|
||||
|
||||
return (!touchscreen && enabled);
|
||||
if (enabled &&
|
||||
source != GDK_SOURCE_TOUCH)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1564,7 +1576,7 @@ _gtk_tooltip_handle_event (GdkEvent *event)
|
||||
GdkDisplay *display;
|
||||
GtkTooltip *current_tooltip;
|
||||
|
||||
if (!tooltips_enabled (event->any.window))
|
||||
if (!tooltips_enabled (event))
|
||||
return;
|
||||
|
||||
/* Returns coordinates relative to has_tooltip_widget's allocation. */
|
||||
|
||||
@@ -2193,6 +2193,7 @@ gtk_tree_view_ensure_background (GtkTreeView *tree_view)
|
||||
gtk_style_context_save (context);
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
|
||||
gtk_style_context_set_background (context, tree_view->priv->bin_window);
|
||||
gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
|
||||
gtk_style_context_restore (context);
|
||||
|
||||
gtk_style_context_set_background (context, tree_view->priv->header_window);
|
||||
|
||||
+831
@@ -60,6 +60,7 @@
|
||||
#include "gtkcssprovider.h"
|
||||
#include "gtkanimationdescription.h"
|
||||
#include "gtkmodifierstyle.h"
|
||||
#include "gtkgesturesinterpreter.h"
|
||||
#include "gtkversion.h"
|
||||
#include "gtkdebug.h"
|
||||
#include "gtkplug.h"
|
||||
@@ -403,6 +404,9 @@ struct _GtkWidgetPrivate
|
||||
/* The widget's parent */
|
||||
GtkWidget *parent;
|
||||
|
||||
GSList *captured_events;
|
||||
GArray *gestures;
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
/* Number of gtk_widget_push_verify_invariants () */
|
||||
guint verifying_invariants_count;
|
||||
@@ -484,6 +488,10 @@ enum {
|
||||
QUERY_TOOLTIP,
|
||||
DRAG_FAILED,
|
||||
STYLE_UPDATED,
|
||||
CAPTURED_EVENT,
|
||||
PRESS_AND_HOLD,
|
||||
MULTITOUCH_EVENT,
|
||||
GESTURE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
@@ -539,6 +547,25 @@ struct _GtkStateData
|
||||
guint operation : 2;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* The widget */
|
||||
GtkWidget *widget;
|
||||
|
||||
/* animation */
|
||||
GtkWidget *popup;
|
||||
guint delay_animation_id;
|
||||
guint trigger_id;
|
||||
guint size;
|
||||
|
||||
gint start_x;
|
||||
gint start_y;
|
||||
gint current_x;
|
||||
gint current_y;
|
||||
|
||||
GdkDevice *device;
|
||||
} PressAndHoldData;
|
||||
|
||||
/* --- prototypes --- */
|
||||
static void gtk_widget_base_class_init (gpointer g_class);
|
||||
static void gtk_widget_class_init (GtkWidgetClass *klass);
|
||||
@@ -705,6 +732,10 @@ static void gtk_widget_set_device_enabled_internal (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
gboolean recurse,
|
||||
gboolean enabled);
|
||||
static gboolean event_window_is_still_viewable (GdkEvent *event);
|
||||
|
||||
static gboolean gtk_widget_press_and_hold_cancel (GtkWidget *widget);
|
||||
|
||||
|
||||
/* --- variables --- */
|
||||
static gpointer gtk_widget_parent_class = NULL;
|
||||
@@ -733,6 +764,8 @@ static GQuark quark_visual = 0;
|
||||
static GQuark quark_modifier_style = 0;
|
||||
static GQuark quark_enabled_devices = 0;
|
||||
static GQuark quark_size_groups = 0;
|
||||
static GQuark quark_press_and_hold = 0;
|
||||
static GQuark quark_gestures_interpreter = 0;
|
||||
GParamSpecPool *_gtk_widget_child_property_pool = NULL;
|
||||
GObjectNotifyContext *_gtk_widget_child_property_notify_context = NULL;
|
||||
|
||||
@@ -857,6 +890,8 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
quark_modifier_style = g_quark_from_static_string ("gtk-widget-modifier-style");
|
||||
quark_enabled_devices = g_quark_from_static_string ("gtk-widget-enabled-devices");
|
||||
quark_size_groups = g_quark_from_static_string ("gtk-widget-size-groups");
|
||||
quark_press_and_hold = g_quark_from_static_string ("gtk-widget-press-and-hold");
|
||||
quark_gestures_interpreter = g_quark_from_static_string ("gtk-widget-gestures-interpreter");
|
||||
|
||||
style_property_spec_pool = g_param_spec_pool_new (FALSE);
|
||||
_gtk_widget_child_property_pool = g_param_spec_pool_new (TRUE);
|
||||
@@ -935,6 +970,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
klass->grab_broken_event = NULL;
|
||||
klass->query_tooltip = gtk_widget_real_query_tooltip;
|
||||
klass->style_updated = gtk_widget_real_style_updated;
|
||||
klass->press_and_hold = NULL;
|
||||
|
||||
klass->show_help = gtk_widget_real_show_help;
|
||||
|
||||
@@ -1798,6 +1834,9 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
* #GtkWidget::key-press-event) and finally a generic
|
||||
* #GtkWidget::event-after signal.
|
||||
*
|
||||
* An event can be captured before ::event signal is emitted by connecting to
|
||||
* ::captured-event event signal.
|
||||
*
|
||||
* Returns: %TRUE to stop other handlers from being invoked for the event
|
||||
* and to cancel the emission of the second specific ::event signal.
|
||||
* %FALSE to propagate the event further and to allow the emission of
|
||||
@@ -1834,6 +1873,54 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
G_TYPE_NONE, 1,
|
||||
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
||||
|
||||
/**
|
||||
* GtkWidget::captured-event:
|
||||
* @widget: the object which received the signal.
|
||||
* @event: the #GdkEvent which triggered this signal
|
||||
*
|
||||
* The #GtkWidget::captured-event signal is emitted before the
|
||||
* #GtkWidget::event signal to allow capturing an event before the
|
||||
* specialized events are emitted. The event is propagated starting
|
||||
* from the top-level container to the widget that received the event
|
||||
* going down the hierarchy.
|
||||
*
|
||||
* This signal returns a #GtkCapturedEventFlags with the handling
|
||||
* status of the event, if %GTK_CAPTURED_EVENT_HANDLED is enabled,
|
||||
* the event will be considered to be handled, and thus not propagated
|
||||
* further.
|
||||
*
|
||||
* If, additionally, %GTK_CAPTURED_EVENT_STORE is enabled, the event will
|
||||
* be stored so it can possibly be re-sent at a later stage. See
|
||||
* gtk_widget_release_captured_events().
|
||||
*
|
||||
* If no flags are enabled, the captured event will be propagated even
|
||||
* further, eventually triggering the emission of the #GtkWidget::event
|
||||
* signal on the widget that received the event if no parent widgets
|
||||
* captured it.
|
||||
*
|
||||
* <note><para>Enabling %GTK_CAPTURED_EVENT_STORE without
|
||||
* %GTK_CAPTURED_EVENT_HANDLED is not allowed to avoid doubly
|
||||
* event emission.</para></note>
|
||||
*
|
||||
* <warning><para>%GTK_CAPTURED_EVENT_STORE will not keep any track of
|
||||
* event parity (eg. ensuring that button/key presses and releases
|
||||
* are paired, or focus/crossing events) nor consistency, so use with
|
||||
* discretion.</para></warning>
|
||||
*
|
||||
* Returns: a #GtkCapturedEventFlags specifying what to do with the event.
|
||||
*
|
||||
* Since: 3.4
|
||||
*/
|
||||
widget_signals[CAPTURED_EVENT] =
|
||||
g_signal_new (I_("captured-event"),
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GtkWidgetClass, captured_event),
|
||||
_gtk_captured_enum_accumulator, NULL,
|
||||
_gtk_marshal_FLAGS__BOXED,
|
||||
GTK_TYPE_CAPTURED_EVENT_FLAGS, 1,
|
||||
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
||||
|
||||
/**
|
||||
* GtkWidget::button-press-event:
|
||||
* @widget: the object which received the signal.
|
||||
@@ -2874,6 +2961,15 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
G_TYPE_BOOLEAN, 1,
|
||||
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
||||
|
||||
widget_signals[MULTITOUCH_EVENT] =
|
||||
g_signal_new (I_("multitouch-event"),
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GtkWidgetClass, multitouch_event),
|
||||
_gtk_boolean_handled_accumulator, NULL,
|
||||
_gtk_marshal_BOOLEAN__BOXED,
|
||||
G_TYPE_BOOLEAN, 1,
|
||||
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
||||
/**
|
||||
* GtkWidget::query-tooltip:
|
||||
* @widget: the object which received the signal
|
||||
@@ -3005,6 +3101,86 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
_gtk_marshal_BOOLEAN__UINT,
|
||||
G_TYPE_BOOLEAN, 1, G_TYPE_UINT);
|
||||
|
||||
/**
|
||||
* GtkWidget::press-and-hold:
|
||||
* @widget: the object which received the signal
|
||||
* @action: a #GtkPressAndHoldAction specifying the action
|
||||
* @x: if the action is not %GTK_PRESS_AND_HOLD_CANCEL, the x coordinate
|
||||
* of the cursor position where the request has been emitted, relative
|
||||
* to widget->window, otherwise undefined
|
||||
* @y: if the action is not %GTK_PRESS_AND_HOLD_CANCEL, the y coordinate
|
||||
* of the cursor position where the request has been emitted, relative
|
||||
* to widget->window, otherwise undefined
|
||||
*
|
||||
* Connect to this signal and correctly handle all of its actions if you
|
||||
* want your widget to support the press-n-hold operation. The
|
||||
* press-and-hold operation is defined as keeping a mouse button pressed
|
||||
* for a given amount of time (specified in the "press-and-hold-timeout"
|
||||
* GtkSetting); during this time the mouse is only allowed to move a little
|
||||
* bit (not past the drag threshold), else the press-and-hold operation will
|
||||
* be terminated.
|
||||
*
|
||||
* From the above passage we can distill three actions for which this
|
||||
* signal will be emitted: query, emitted when the mouse button goes
|
||||
* down; trigger, emitted if the mouse button has been kept down for the
|
||||
* specified amount of time and movements did not pass the drag threshold;
|
||||
* and cancel, emitted when the press-and-hold operation has been terminated
|
||||
* before the trigger action has been emitted.
|
||||
*
|
||||
* For query, @action will be set to %GTK_PRESS_AND_HOLD_QUERY, @x and @y
|
||||
* will be set to the cursor position.
|
||||
* A return value of %FALSE means no press-and-hold action should occur
|
||||
* for these coordinates on the given widget, when %TRUE is returned
|
||||
* a trigger action may be emitted later on.
|
||||
*
|
||||
* The trigger action is emitted by setting @action to be
|
||||
* %GTK_PRESS_AND_HOLD_TRIGGER, the @x and @y coordinates are set to the
|
||||
* cursor's current location (this includes any movements made between
|
||||
* the original query and this trigger). In this case the return value
|
||||
* is ignored.
|
||||
*
|
||||
* When @action is %GTK_WIDGET_PRESS_AND_HOLD_CANCEL, @x and @y are both
|
||||
* undefined. The return value is ignored too as
|
||||
* this action is only there for informational purposes.
|
||||
*
|
||||
* Returns: a boolean indicating how to proceed based on the value of
|
||||
* @action, as described above.
|
||||
*
|
||||
* Since: 3.2
|
||||
*/
|
||||
widget_signals[PRESS_AND_HOLD] =
|
||||
g_signal_new (I_("press-and-hold"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GtkWidgetClass, press_and_hold),
|
||||
_gtk_boolean_handled_accumulator, NULL,
|
||||
_gtk_marshal_BOOLEAN__OBJECT_ENUM_INT_INT,
|
||||
G_TYPE_BOOLEAN, 4,
|
||||
GDK_TYPE_DEVICE,
|
||||
GTK_TYPE_PRESS_AND_HOLD_ACTION,
|
||||
G_TYPE_INT,
|
||||
G_TYPE_INT);
|
||||
|
||||
/**
|
||||
* GtkWidget::gesture:
|
||||
* @widget: the object which received the signal
|
||||
* @gesture_id: gesture recognized
|
||||
*
|
||||
* This signal is emitted whenever a device with source
|
||||
* #GDK_SOURCE_TOUCH finishes a gesture that resembles
|
||||
* one of those added through gtk_widget_enable_gesture().
|
||||
*
|
||||
* Since: 3.4
|
||||
*/
|
||||
widget_signals[GESTURE] =
|
||||
g_signal_new (I_("gesture"),
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GtkWidgetClass, gesture),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
|
||||
binding_set = gtk_binding_set_by_class (klass);
|
||||
gtk_binding_entry_add_signal (binding_set, GDK_KEY_F10, GDK_SHIFT_MASK,
|
||||
"popup-menu", 0);
|
||||
@@ -4429,6 +4605,12 @@ gtk_widget_realize (GtkWidget *widget)
|
||||
_gtk_widget_enable_device_events (widget);
|
||||
gtk_widget_update_devices_mask (widget, TRUE);
|
||||
|
||||
/* Enable button motion events for press and hold */
|
||||
if (!gtk_widget_get_has_window (widget))
|
||||
gdk_window_set_events (priv->window,
|
||||
gdk_window_get_events (priv->window) | GDK_BUTTON_MOTION_MASK);
|
||||
gtk_widget_add_events (widget, GDK_BUTTON_MOTION_MASK);
|
||||
|
||||
gtk_widget_pop_verify_invariants (widget);
|
||||
}
|
||||
}
|
||||
@@ -5870,6 +6052,68 @@ gtk_widget_event (GtkWidget *widget,
|
||||
return gtk_widget_event_internal (widget, event);
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_widget_captured_event (GtkWidget *widget,
|
||||
GdkEvent *event)
|
||||
{
|
||||
GtkCapturedEventFlags flags;
|
||||
GtkWidgetPrivate *priv;
|
||||
gboolean return_val;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
|
||||
g_return_val_if_fail (WIDGET_REALIZED_FOR_EVENT (widget, event), TRUE);
|
||||
|
||||
priv = widget->priv;
|
||||
|
||||
if (event->type == GDK_EXPOSE)
|
||||
{
|
||||
g_warning ("Events of type GDK_EXPOSE cannot be synthesized. To get "
|
||||
"the same effect, call gdk_window_invalidate_rect/region(), "
|
||||
"followed by gdk_window_process_updates().");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!event_window_is_still_viewable (event))
|
||||
return TRUE;
|
||||
|
||||
g_object_ref (widget);
|
||||
|
||||
g_signal_emit (widget, widget_signals[CAPTURED_EVENT], 0, event, &flags);
|
||||
|
||||
/* Only store events that have been handled, so we don't end up
|
||||
* sending twice the same event.
|
||||
*/
|
||||
if (flags & GTK_CAPTURED_EVENT_STORE)
|
||||
{
|
||||
if ((flags & GTK_CAPTURED_EVENT_HANDLED) != 0)
|
||||
priv->captured_events = g_slist_prepend (priv->captured_events,
|
||||
gdk_event_copy (event));
|
||||
else
|
||||
g_warning ("Captured events can only be stored if they are claimed "
|
||||
"to be handled by the capturing widget. The use of "
|
||||
"GTK_CAPTURED_EVENT_HANDLED is mandatory if using "
|
||||
"GTK_CAPTURED_EVENT_STORE.");
|
||||
}
|
||||
|
||||
/* The widget that was originally to receive the event
|
||||
* handles motion hints, but the capturing widget might
|
||||
* not, so ensure we get further motion events.
|
||||
*/
|
||||
if ((flags & GTK_CAPTURED_EVENT_HANDLED) != 0 &&
|
||||
event->type == GDK_MOTION_NOTIFY &&
|
||||
event->motion.is_hint &&
|
||||
(gdk_window_get_events (event->any.window) &
|
||||
GDK_POINTER_MOTION_HINT_MASK) != 0)
|
||||
gdk_event_request_motions (&event->motion);
|
||||
|
||||
return_val = (flags & GTK_CAPTURED_EVENT_HANDLED) != 0 ||
|
||||
!WIDGET_REALIZED_FOR_EVENT (widget, event);
|
||||
|
||||
g_object_unref (widget);
|
||||
|
||||
return return_val;
|
||||
}
|
||||
|
||||
/* Returns TRUE if a translation should be done */
|
||||
gboolean
|
||||
_gtk_widget_get_translation_to_window (GtkWidget *widget,
|
||||
@@ -6069,15 +6313,18 @@ gtk_widget_event_internal (GtkWidget *widget,
|
||||
case GDK_BUTTON_PRESS:
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_TOUCH_PRESS:
|
||||
signal_num = BUTTON_PRESS_EVENT;
|
||||
break;
|
||||
case GDK_SCROLL:
|
||||
signal_num = SCROLL_EVENT;
|
||||
break;
|
||||
case GDK_BUTTON_RELEASE:
|
||||
case GDK_TOUCH_RELEASE:
|
||||
signal_num = BUTTON_RELEASE_EVENT;
|
||||
break;
|
||||
case GDK_MOTION_NOTIFY:
|
||||
case GDK_TOUCH_MOTION:
|
||||
signal_num = MOTION_NOTIFY_EVENT;
|
||||
break;
|
||||
case GDK_DELETE:
|
||||
@@ -6145,6 +6392,11 @@ gtk_widget_event_internal (GtkWidget *widget,
|
||||
case GDK_DAMAGE:
|
||||
signal_num = DAMAGE_EVENT;
|
||||
break;
|
||||
case GDK_MULTITOUCH_ADDED:
|
||||
case GDK_MULTITOUCH_REMOVED:
|
||||
case GDK_MULTITOUCH_UPDATED:
|
||||
signal_num = MULTITOUCH_EVENT;
|
||||
break;
|
||||
default:
|
||||
g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
|
||||
signal_num = -1;
|
||||
@@ -6407,6 +6659,14 @@ void
|
||||
_gtk_widget_grab_notify (GtkWidget *widget,
|
||||
gboolean was_grabbed)
|
||||
{
|
||||
PressAndHoldData *data;
|
||||
|
||||
data = g_object_get_qdata (G_OBJECT (widget), quark_press_and_hold);
|
||||
|
||||
if (data && data->device &&
|
||||
gtk_widget_device_is_shadowed (widget, data->device))
|
||||
gtk_widget_press_and_hold_cancel (widget);
|
||||
|
||||
g_signal_emit (widget, widget_signals[GRAB_NOTIFY], 0, was_grabbed);
|
||||
}
|
||||
|
||||
@@ -6721,6 +6981,353 @@ gtk_widget_has_focus (GtkWidget *widget)
|
||||
return widget->priv->has_focus;
|
||||
}
|
||||
|
||||
/* --- Press and hold --- */
|
||||
|
||||
static inline PressAndHoldData *
|
||||
gtk_widget_peek_press_and_hold_data (GtkWidget *widget)
|
||||
{
|
||||
return g_object_get_qdata (G_OBJECT (widget), quark_press_and_hold);
|
||||
}
|
||||
|
||||
static void
|
||||
press_and_hold_data_free (PressAndHoldData *data)
|
||||
{
|
||||
if (data->popup)
|
||||
gtk_widget_destroy (data->popup);
|
||||
|
||||
if (data->delay_animation_id)
|
||||
g_source_remove (data->delay_animation_id);
|
||||
|
||||
if (data->trigger_id)
|
||||
g_source_remove (data->trigger_id);
|
||||
|
||||
g_slice_free (PressAndHoldData, data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gtk_widget_set_press_and_hold_data (GtkWidget *widget,
|
||||
PressAndHoldData *data)
|
||||
{
|
||||
g_object_set_qdata_full (G_OBJECT (widget),
|
||||
quark_press_and_hold,
|
||||
data,
|
||||
(GDestroyNotify) press_and_hold_data_free);
|
||||
}
|
||||
|
||||
static inline PressAndHoldData *
|
||||
gtk_widget_get_press_and_hold_data (GtkWidget *widget)
|
||||
{
|
||||
PressAndHoldData *data;
|
||||
|
||||
data = gtk_widget_peek_press_and_hold_data (widget);
|
||||
if (!data)
|
||||
{
|
||||
data = g_slice_new0 (PressAndHoldData);
|
||||
data->widget = widget;
|
||||
gtk_widget_set_press_and_hold_data (widget, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_widget_press_and_hold_cancel (GtkWidget *widget)
|
||||
{
|
||||
PressAndHoldData *data;
|
||||
gboolean return_value;
|
||||
|
||||
data = gtk_widget_peek_press_and_hold_data (widget);
|
||||
g_assert (data != NULL);
|
||||
|
||||
g_signal_emit (widget, widget_signals[PRESS_AND_HOLD],
|
||||
0,
|
||||
data->device,
|
||||
GTK_PRESS_AND_HOLD_CANCEL,
|
||||
-1, -1,
|
||||
&return_value);
|
||||
|
||||
gtk_widget_set_press_and_hold_data (widget, NULL);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_widget_press_and_hold_check_cancel (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
PressAndHoldData *data;
|
||||
|
||||
if (event->type != GDK_BUTTON_RELEASE &&
|
||||
event->type != GDK_TOUCH_RELEASE)
|
||||
return FALSE;
|
||||
|
||||
data = gtk_widget_peek_press_and_hold_data (widget);
|
||||
|
||||
if (data &&
|
||||
data->trigger_id == 0 &&
|
||||
data->device == gdk_event_get_device ((GdkEvent *) event))
|
||||
{
|
||||
gtk_widget_press_and_hold_cancel (widget);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_widget_press_and_hold_check_threshold (GtkWidget *widget,
|
||||
GdkEventMotion *event)
|
||||
{
|
||||
PressAndHoldData *data;
|
||||
GdkDevice *device;
|
||||
|
||||
if (event->type != GDK_MOTION_NOTIFY &&
|
||||
event->type != GDK_TOUCH_MOTION)
|
||||
return FALSE;
|
||||
|
||||
data = gtk_widget_peek_press_and_hold_data (widget);
|
||||
|
||||
if (!data)
|
||||
return FALSE;
|
||||
|
||||
device = gdk_event_get_device ((GdkEvent *) event);
|
||||
|
||||
if (data->device != device)
|
||||
return FALSE;
|
||||
|
||||
_gtk_widget_find_at_coords (event->window, event->x, event->y,
|
||||
&data->current_x, &data->current_y);
|
||||
|
||||
/* Stop press-and-hold if we dragged too far from the starting point */
|
||||
if (gtk_drag_check_threshold (widget, data->start_x, data->start_y,
|
||||
data->current_x, data->current_y))
|
||||
{
|
||||
gtk_widget_press_and_hold_cancel (widget);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (data->popup)
|
||||
gtk_window_move (GTK_WINDOW (data->popup),
|
||||
event->x_root - data->size / 2,
|
||||
event->y_root - data->size / 2);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_widget_press_and_hold_timeout (gpointer user_data)
|
||||
{
|
||||
gboolean return_value;
|
||||
GtkWidget *widget = GTK_WIDGET (user_data);
|
||||
PressAndHoldData *data;
|
||||
|
||||
data = gtk_widget_peek_press_and_hold_data (widget);
|
||||
g_assert (data != NULL);
|
||||
|
||||
/* Done, clean up and emit the trigger signal */
|
||||
g_signal_emit (widget, widget_signals[PRESS_AND_HOLD],
|
||||
0,
|
||||
data->device,
|
||||
GTK_PRESS_AND_HOLD_TRIGGER,
|
||||
data->current_x, data->current_y,
|
||||
&return_value);
|
||||
|
||||
gtk_widget_set_press_and_hold_data (widget, NULL);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
press_and_hold_animation_draw (GtkWidget *widget,
|
||||
cairo_t *cr,
|
||||
gpointer user_data)
|
||||
{
|
||||
PressAndHoldData *data;
|
||||
GtkStyleContext *context;
|
||||
GtkStateFlags state;
|
||||
gint width, height;
|
||||
|
||||
data = gtk_widget_peek_press_and_hold_data (GTK_WIDGET (user_data));
|
||||
g_assert (data != NULL);
|
||||
|
||||
width = gtk_widget_get_allocated_width (widget);
|
||||
height = gtk_widget_get_allocated_height (widget);
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
state = gtk_widget_get_state_flags (widget);
|
||||
gtk_style_context_set_state (context, state);
|
||||
|
||||
if (!gtk_style_context_state_is_running (context, GTK_STATE_FLAG_ACTIVE, NULL))
|
||||
{
|
||||
/* The animation just finished, so hide the widget
|
||||
* and finish the press and hold operation.
|
||||
*/
|
||||
data->trigger_id =
|
||||
gdk_threads_add_idle (gtk_widget_press_and_hold_timeout,
|
||||
user_data);
|
||||
gtk_widget_hide (widget);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gtk_widget_is_composited (widget))
|
||||
{
|
||||
cairo_t *mask_cr;
|
||||
cairo_region_t *region;
|
||||
cairo_surface_t *mask;
|
||||
|
||||
mask = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
|
||||
|
||||
mask_cr = cairo_create (mask);
|
||||
gtk_render_activity (context, mask_cr, 0, 0, width, height);
|
||||
cairo_destroy (mask_cr);
|
||||
|
||||
region = gdk_cairo_region_create_from_surface (mask);
|
||||
gdk_window_shape_combine_region (gtk_widget_get_window (widget), region, 0, 0);
|
||||
cairo_region_destroy (region);
|
||||
|
||||
cairo_surface_destroy (mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_save (cr);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint (cr);
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
gtk_render_activity (context, cr, 0, 0, width, height);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_widget_press_and_hold_begin_animation_timeout (gpointer user_data)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (user_data);
|
||||
PressAndHoldData *data;
|
||||
gint x, y;
|
||||
|
||||
data = gtk_widget_peek_press_and_hold_data (widget);
|
||||
g_assert (data != NULL);
|
||||
|
||||
gdk_window_get_device_position (gdk_screen_get_root_window (gtk_widget_get_screen (widget)),
|
||||
data->device, &x, &y, NULL);
|
||||
gtk_window_move (GTK_WINDOW (data->popup),
|
||||
x - data->size / 2,
|
||||
y - data->size / 2);
|
||||
gtk_widget_show (data->popup);
|
||||
|
||||
gtk_widget_set_state_flags (GTK_WIDGET (data->popup),
|
||||
GTK_STATE_FLAG_ACTIVE, FALSE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_widget_press_and_hold_query (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
gboolean return_value = FALSE;
|
||||
|
||||
g_signal_emit (widget, widget_signals[PRESS_AND_HOLD],
|
||||
0,
|
||||
device,
|
||||
GTK_PRESS_AND_HOLD_QUERY,
|
||||
x, y,
|
||||
&return_value);
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_widget_press_and_hold_check_start (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
PressAndHoldData *data = gtk_widget_peek_press_and_hold_data (widget);
|
||||
|
||||
if (event->type != GDK_BUTTON_PRESS &&
|
||||
event->type != GDK_TOUCH_PRESS)
|
||||
return FALSE;
|
||||
|
||||
/* Press and hold already in process? */
|
||||
if (data)
|
||||
return FALSE;
|
||||
|
||||
data = gtk_widget_get_press_and_hold_data (widget);
|
||||
data->device = gdk_event_get_device ((GdkEvent *) event);
|
||||
|
||||
if (gtk_widget_press_and_hold_query (widget, data->device,
|
||||
event->x, event->y))
|
||||
{
|
||||
gint timeout, begin_ani_timeout;
|
||||
GdkScreen *screen;
|
||||
GdkVisual *visual;
|
||||
GtkStyleContext *context;
|
||||
cairo_region_t *region;
|
||||
GdkDevice *source_device;
|
||||
GdkInputSource source;
|
||||
|
||||
_gtk_widget_find_at_coords (event->window,
|
||||
event->x, event->y,
|
||||
&data->start_x, &data->start_y);
|
||||
|
||||
data->current_x = data->start_x;
|
||||
data->current_y = data->start_y;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)),
|
||||
"gtk-press-and-hold-timeout", &timeout,
|
||||
"gtk-timeout-initial", &begin_ani_timeout,
|
||||
NULL);
|
||||
|
||||
screen = gtk_widget_get_screen (widget);
|
||||
visual = gdk_screen_get_rgba_visual (screen);
|
||||
|
||||
data->popup = gtk_window_new (GTK_WINDOW_POPUP);
|
||||
gtk_window_set_screen (GTK_WINDOW (data->popup), screen);
|
||||
if (visual)
|
||||
gtk_widget_set_visual (data->popup, visual);
|
||||
gtk_widget_set_app_paintable (data->popup, TRUE);
|
||||
gtk_widget_realize (data->popup);
|
||||
|
||||
context = gtk_widget_get_style_context (data->popup);
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_PRESS_AND_HOLD);
|
||||
|
||||
g_signal_connect (data->popup, "draw",
|
||||
G_CALLBACK (press_and_hold_animation_draw),
|
||||
widget);
|
||||
|
||||
source_device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
source = gdk_device_get_source (source_device);
|
||||
|
||||
if (source == GDK_SOURCE_TOUCH)
|
||||
{
|
||||
/* Have an indicator with 2.5cm of diameter */
|
||||
data->size = (25 * gdk_screen_get_width (screen)) /
|
||||
gdk_screen_get_width_mm (screen);
|
||||
}
|
||||
else
|
||||
data->size = gdk_display_get_default_cursor_size (gtk_widget_get_display (widget));
|
||||
|
||||
gtk_window_resize (GTK_WINDOW (data->popup), data->size, data->size);
|
||||
|
||||
region = cairo_region_create ();
|
||||
gdk_window_input_shape_combine_region (gtk_widget_get_window (data->popup), region, 0, 0);
|
||||
cairo_region_destroy (region);
|
||||
|
||||
/* delay loading the animation by the double click timeout */
|
||||
data->delay_animation_id =
|
||||
gdk_threads_add_timeout (begin_ani_timeout,
|
||||
gtk_widget_press_and_hold_begin_animation_timeout,
|
||||
widget);
|
||||
}
|
||||
else
|
||||
gtk_widget_set_press_and_hold_data (widget, NULL);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_has_visible_focus:
|
||||
* @widget: a #GtkWidget
|
||||
@@ -10180,6 +10787,15 @@ gtk_widget_finalize (GObject *object)
|
||||
|
||||
_gtk_widget_free_cached_sizes (widget);
|
||||
|
||||
if (priv->captured_events)
|
||||
{
|
||||
g_slist_foreach (priv->captured_events, (GFunc) gdk_event_free, NULL);
|
||||
g_slist_free (priv->captured_events);
|
||||
}
|
||||
|
||||
if (priv->gestures)
|
||||
g_array_free (priv->gestures, TRUE);
|
||||
|
||||
if (g_object_is_floating (object))
|
||||
g_warning ("A floating object was finalized. This means that someone\n"
|
||||
"called g_object_unref() on an object that had only a floating\n"
|
||||
@@ -13950,3 +14566,218 @@ _gtk_widget_set_style (GtkWidget *widget,
|
||||
{
|
||||
widget->priv->style = style;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_release_captured_events:
|
||||
* @widget: the #GtkWidget holding the events
|
||||
* @emit: #TRUE if the events must be emitted on the targed widget
|
||||
*
|
||||
* Releases the events that a widget has captured and stored
|
||||
* in the #GtkWidget::captured-event signal. if @emit is #TRUE,
|
||||
* event emission will continue as if uncaptured (i.e. the
|
||||
* capturing phase will continue down the hierarchy to the event
|
||||
* widget, followed by the event being propagated up again).
|
||||
*
|
||||
* Since: 3.4
|
||||
**/
|
||||
void
|
||||
gtk_widget_release_captured_events (GtkWidget *widget,
|
||||
gboolean emit)
|
||||
{
|
||||
GtkWidgetPrivate *priv;
|
||||
GSList *l;
|
||||
|
||||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||||
|
||||
priv = widget->priv;
|
||||
|
||||
if (emit)
|
||||
{
|
||||
priv->captured_events = g_slist_reverse (priv->captured_events);
|
||||
|
||||
for (l = priv->captured_events; l; l = l->next)
|
||||
{
|
||||
GtkWidget *event_widget;
|
||||
GdkEvent *event = l->data;
|
||||
gboolean propagated = FALSE;
|
||||
|
||||
event_widget = gtk_get_event_widget (event);
|
||||
|
||||
/* Find out the next widget handling the captured event,
|
||||
* if event_widget == widget, the capturing phase for
|
||||
* this event has finished, so the next thing is bubbling
|
||||
* it up.
|
||||
*/
|
||||
if (event_widget != widget &&
|
||||
gtk_widget_is_ancestor (event_widget, widget))
|
||||
{
|
||||
GtkWidget *parent, *intermediate;
|
||||
|
||||
intermediate = event_widget;
|
||||
|
||||
while (intermediate)
|
||||
{
|
||||
parent = gtk_widget_get_parent (intermediate);
|
||||
|
||||
/* Found the next intermediate that should handle
|
||||
* the captured event.
|
||||
*/
|
||||
if (parent == widget)
|
||||
break;
|
||||
|
||||
intermediate = parent;
|
||||
}
|
||||
|
||||
if (intermediate)
|
||||
propagated = _gtk_propagate_captured_event (event_widget,
|
||||
event,
|
||||
intermediate);
|
||||
}
|
||||
|
||||
if (propagated)
|
||||
continue;
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_PROPERTY_NOTIFY:
|
||||
case GDK_FOCUS_CHANGE:
|
||||
case GDK_CONFIGURE:
|
||||
case GDK_MAP:
|
||||
case GDK_UNMAP:
|
||||
case GDK_SELECTION_CLEAR:
|
||||
case GDK_SELECTION_REQUEST:
|
||||
case GDK_SELECTION_NOTIFY:
|
||||
case GDK_CLIENT_EVENT:
|
||||
case GDK_VISIBILITY_NOTIFY:
|
||||
case GDK_WINDOW_STATE:
|
||||
case GDK_GRAB_BROKEN:
|
||||
case GDK_DAMAGE:
|
||||
/* These events are capturable, but don't bubble up */
|
||||
gtk_widget_event (event_widget, event);
|
||||
break;
|
||||
default:
|
||||
/* All other capturable events do bubble up */
|
||||
gtk_propagate_event (event_widget, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_slist_foreach (priv->captured_events, (GFunc) gdk_event_free, NULL);
|
||||
g_slist_free (priv->captured_events);
|
||||
priv->captured_events = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_widget_gesture_stroke (GtkWidget *widget,
|
||||
GdkEvent *event)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
GtkWidgetPrivate *priv;
|
||||
|
||||
priv = widget->priv;
|
||||
|
||||
if (!priv->gestures ||
|
||||
priv->gestures->len == 0)
|
||||
return;
|
||||
|
||||
interpreter = g_object_get_qdata (G_OBJECT (widget), quark_gestures_interpreter);
|
||||
g_assert (interpreter != NULL);
|
||||
|
||||
gtk_gestures_interpreter_feed_event (interpreter, event);
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_widget_gesture_finish (GtkWidget *widget)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
GtkWidgetPrivate *priv;
|
||||
guint gesture;
|
||||
|
||||
priv = widget->priv;
|
||||
|
||||
if (!priv->gestures ||
|
||||
priv->gestures->len == 0)
|
||||
return;
|
||||
|
||||
interpreter = g_object_get_qdata (G_OBJECT (widget), quark_gestures_interpreter);
|
||||
g_assert (interpreter != NULL);
|
||||
|
||||
if (gtk_gestures_interpreter_get_n_active_strokes (interpreter) != 0)
|
||||
return;
|
||||
|
||||
if (gtk_gestures_interpreter_finish (interpreter, &gesture))
|
||||
g_signal_emit (widget, widget_signals[GESTURE], 0, gesture);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_enable_gesture:
|
||||
* @widget: a #GtkWidget
|
||||
* @gesture_id: gesture ID to handle
|
||||
*
|
||||
* Enables @gesture_id to be recognized in @widget.
|
||||
**/
|
||||
void
|
||||
gtk_widget_enable_gesture (GtkWidget *widget,
|
||||
guint gesture_id)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
GtkWidgetPrivate *priv;
|
||||
|
||||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||||
|
||||
priv = widget->priv;
|
||||
interpreter = g_object_get_qdata (G_OBJECT (widget), quark_gestures_interpreter);
|
||||
|
||||
if (!interpreter)
|
||||
{
|
||||
interpreter = gtk_gestures_interpreter_new ();
|
||||
g_object_set_qdata_full (G_OBJECT (widget), quark_gestures_interpreter,
|
||||
interpreter, (GDestroyNotify) g_object_unref);
|
||||
}
|
||||
|
||||
if (!gtk_gestures_interpreter_add_gesture (interpreter, gesture_id))
|
||||
return;
|
||||
|
||||
if (!priv->gestures)
|
||||
priv->gestures = g_array_new (FALSE, FALSE, sizeof (guint));
|
||||
|
||||
g_array_append_val (priv->gestures, gesture_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_disable_gesture:
|
||||
* @widget: a #GtkWidget
|
||||
* @gesture_id: Gesture ID to stop handling
|
||||
*
|
||||
* Disables a gesture previously added through gtk_widget_enable_gesture()
|
||||
**/
|
||||
void
|
||||
gtk_widget_disable_gesture (GtkWidget *widget,
|
||||
guint gesture_id)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
GtkWidgetPrivate *priv;
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||||
|
||||
priv = widget->priv;
|
||||
|
||||
if (!priv->gestures ||
|
||||
priv->gestures->len == 0)
|
||||
return;
|
||||
|
||||
interpreter = g_object_get_qdata (G_OBJECT (widget), quark_gestures_interpreter);
|
||||
g_assert (interpreter != NULL);
|
||||
|
||||
for (i = 0; i < priv->gestures->len; i++)
|
||||
{
|
||||
if (gesture_id == g_array_index (priv->gestures, guint, i))
|
||||
{
|
||||
g_array_remove_index (priv->gestures, i);
|
||||
gtk_gestures_interpreter_remove_gesture (interpreter, gesture_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-4
@@ -49,6 +49,13 @@ typedef enum
|
||||
GTK_WIDGET_HELP_WHATS_THIS
|
||||
} GtkWidgetHelpType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_PRESS_AND_HOLD_QUERY,
|
||||
GTK_PRESS_AND_HOLD_TRIGGER,
|
||||
GTK_PRESS_AND_HOLD_CANCEL
|
||||
} GtkPressAndHoldAction;
|
||||
|
||||
/* Macro for casting a pointer to a GtkWidget or GtkWidgetClass pointer.
|
||||
* Macros for testing whether `widget' or `klass' are of type GTK_TYPE_WIDGET.
|
||||
*/
|
||||
@@ -425,6 +432,21 @@ struct _GtkWidgetClass
|
||||
|
||||
void (* style_updated) (GtkWidget *widget);
|
||||
|
||||
GtkCapturedEventFlags (* captured_event) (GtkWidget *widget,
|
||||
GdkEvent *event);
|
||||
|
||||
gboolean (* press_and_hold) (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
GtkPressAndHoldAction action,
|
||||
gint x,
|
||||
gint y);
|
||||
|
||||
gboolean (* multitouch_event) (GtkWidget *widget,
|
||||
GdkEventMultiTouch *event);
|
||||
|
||||
void (* gesture) (GtkWidget *widget,
|
||||
guint gesture_id);
|
||||
|
||||
/*< private >*/
|
||||
|
||||
GtkWidgetClassPrivate *priv;
|
||||
@@ -433,10 +455,6 @@ struct _GtkWidgetClass
|
||||
void (*_gtk_reserved2) (void);
|
||||
void (*_gtk_reserved3) (void);
|
||||
void (*_gtk_reserved4) (void);
|
||||
void (*_gtk_reserved5) (void);
|
||||
void (*_gtk_reserved6) (void);
|
||||
void (*_gtk_reserved7) (void);
|
||||
void (*_gtk_reserved8) (void);
|
||||
};
|
||||
|
||||
struct _GtkWidgetAuxInfo
|
||||
@@ -892,6 +910,14 @@ GtkWidgetPath * gtk_widget_get_path (GtkWidget *widget);
|
||||
GdkModifierType gtk_widget_get_modifier_mask (GtkWidget *widget,
|
||||
GdkModifierIntent intent);
|
||||
|
||||
void gtk_widget_release_captured_events (GtkWidget *widget,
|
||||
gboolean emit);
|
||||
|
||||
/* Gestures */
|
||||
void gtk_widget_enable_gesture (GtkWidget *widget,
|
||||
guint gesture_id);
|
||||
void gtk_widget_disable_gesture (GtkWidget *widget,
|
||||
guint gesture_id);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
@@ -168,6 +168,20 @@ void _gtk_widget_set_style (GtkWidget *widget,
|
||||
GtkStyle *style);
|
||||
|
||||
|
||||
gboolean _gtk_widget_captured_event (GtkWidget *widget,
|
||||
GdkEvent *event);
|
||||
|
||||
gboolean _gtk_widget_press_and_hold_check_start (GtkWidget *widget,
|
||||
GdkEventButton *event);
|
||||
gboolean _gtk_widget_press_and_hold_check_cancel (GtkWidget *widget,
|
||||
GdkEventButton *event);
|
||||
gboolean _gtk_widget_press_and_hold_check_threshold (GtkWidget *widget,
|
||||
GdkEventMotion *event);
|
||||
|
||||
void _gtk_widget_gesture_stroke (GtkWidget *widget,
|
||||
GdkEvent *event);
|
||||
void _gtk_widget_gesture_finish (GtkWidget *widget);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_WIDGET_PRIVATE_H__ */
|
||||
|
||||
@@ -143,6 +143,10 @@ TEST_PROGS += regression-tests
|
||||
regression_tests_SOURCES = regression-tests.c
|
||||
regression_tests_LDADD = $(progs_ldadd)
|
||||
|
||||
TEST_PROGS += gestures
|
||||
gestures_SOURCES = gestures.c
|
||||
gestures_LDADD = $(progs_ldadd)
|
||||
|
||||
EXTRA_DIST += \
|
||||
file-chooser-test-dir/empty \
|
||||
file-chooser-test-dir/text.txt
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/* gestures tests.
|
||||
*
|
||||
* Copyright (C) 2011 Carlos Garnacho <carlos@lanedo.com>
|
||||
* Authors: Carlos Garnacho <carlos@lanedo.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
gesture_detected_cb (GtkGesturesInterpreter *interpreter,
|
||||
guint gesture_id,
|
||||
gdouble confidence,
|
||||
gpointer user_data)
|
||||
{
|
||||
gdouble *confidence_ret = user_data;
|
||||
|
||||
*confidence_ret = confidence;
|
||||
}
|
||||
|
||||
static void
|
||||
append_event_to_interpreter (GtkGesturesInterpreter *interpreter,
|
||||
gdouble x,
|
||||
gdouble y)
|
||||
{
|
||||
GdkDeviceManager *device_manager;
|
||||
GdkDevice *client_pointer;
|
||||
GdkDisplay *display;
|
||||
GdkEvent *event;
|
||||
|
||||
display = gdk_display_get_default ();
|
||||
device_manager = gdk_display_get_device_manager (display);
|
||||
client_pointer = gdk_device_manager_get_client_pointer (device_manager);
|
||||
|
||||
event = gdk_event_new (GDK_MOTION_NOTIFY);
|
||||
event->motion.x = x;
|
||||
event->motion.y = y;
|
||||
event->motion.x_root = x;
|
||||
event->motion.y_root = y;
|
||||
gdk_event_set_device (event, client_pointer);
|
||||
gdk_event_set_source_device (event, client_pointer);
|
||||
|
||||
gtk_gestures_interpreter_feed_event (interpreter, event);
|
||||
gdk_event_free (event);
|
||||
}
|
||||
|
||||
static void
|
||||
test_empty_interpreter (void)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
gboolean gesture_detected;
|
||||
gdouble confidence = 0;
|
||||
|
||||
interpreter = gtk_gestures_interpreter_new ();
|
||||
g_signal_connect (interpreter, "gesture-detected",
|
||||
G_CALLBACK (gesture_detected_cb),
|
||||
&confidence);
|
||||
|
||||
/* Feed some events */
|
||||
append_event_to_interpreter (interpreter, 0, 0);
|
||||
append_event_to_interpreter (interpreter, 100, 0);
|
||||
append_event_to_interpreter (interpreter, 100, 100);
|
||||
append_event_to_interpreter (interpreter, 0, 100);
|
||||
|
||||
gesture_detected = gtk_gestures_interpreter_finish (interpreter, NULL);
|
||||
|
||||
g_assert (gesture_detected == FALSE);
|
||||
g_assert_cmpfloat (confidence, ==, 0);
|
||||
|
||||
g_object_unref (interpreter);
|
||||
}
|
||||
|
||||
static void
|
||||
test_equality (void)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
gboolean gesture_detected;
|
||||
gdouble confidence = 0;
|
||||
guint gesture_id;
|
||||
|
||||
interpreter = gtk_gestures_interpreter_new ();
|
||||
g_signal_connect (interpreter, "gesture-detected",
|
||||
G_CALLBACK (gesture_detected_cb),
|
||||
&confidence);
|
||||
|
||||
gtk_gestures_interpreter_add_gesture (interpreter,
|
||||
GTK_GESTURE_SWIPE_RIGHT);
|
||||
|
||||
/* Feed the events */
|
||||
append_event_to_interpreter (interpreter, 0, 0);
|
||||
append_event_to_interpreter (interpreter, 100, 0);
|
||||
|
||||
gesture_detected = gtk_gestures_interpreter_finish (interpreter, &gesture_id);
|
||||
|
||||
g_assert (gesture_detected != FALSE);
|
||||
g_assert_cmpuint (gesture_id, ==, GTK_GESTURE_SWIPE_RIGHT);
|
||||
g_assert_cmpfloat (confidence, ==, 1);
|
||||
|
||||
g_object_unref (interpreter);
|
||||
}
|
||||
|
||||
static void
|
||||
test_events_ubiquity (void)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
gboolean gesture_detected;
|
||||
gdouble confidence = 0;
|
||||
guint gesture_id;
|
||||
|
||||
interpreter = gtk_gestures_interpreter_new ();
|
||||
g_signal_connect (interpreter, "gesture-detected",
|
||||
G_CALLBACK (gesture_detected_cb),
|
||||
&confidence);
|
||||
|
||||
gtk_gestures_interpreter_add_gesture (interpreter,
|
||||
GTK_GESTURE_SWIPE_RIGHT);
|
||||
|
||||
/* Feed the events, huge scale */
|
||||
append_event_to_interpreter (interpreter, 0, 0);
|
||||
append_event_to_interpreter (interpreter, 1000, 0);
|
||||
|
||||
gesture_detected = gtk_gestures_interpreter_finish (interpreter, &gesture_id);
|
||||
|
||||
g_assert (gesture_detected != FALSE);
|
||||
g_assert_cmpuint (gesture_id, ==, GTK_GESTURE_SWIPE_RIGHT);
|
||||
g_assert_cmpfloat (confidence, ==, 1);
|
||||
|
||||
/* Feed other events, displaced somewhere, and
|
||||
* at a different scale, it's the X displacement
|
||||
* to the right what counts.
|
||||
*/
|
||||
append_event_to_interpreter (interpreter, 500, 120);
|
||||
append_event_to_interpreter (interpreter, 600, 120);
|
||||
|
||||
gesture_detected = gtk_gestures_interpreter_finish (interpreter, &gesture_id);
|
||||
|
||||
g_assert (gesture_detected != FALSE);
|
||||
g_assert_cmpuint (gesture_id, ==, GTK_GESTURE_SWIPE_RIGHT);
|
||||
g_assert_cmpfloat (confidence, ==, 1);
|
||||
|
||||
g_object_unref (interpreter);
|
||||
}
|
||||
|
||||
static void
|
||||
test_opposite_gesture (void)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
gboolean gesture_detected;
|
||||
gdouble confidence = 1;
|
||||
|
||||
interpreter = gtk_gestures_interpreter_new ();
|
||||
g_signal_connect (interpreter, "gesture-detected",
|
||||
G_CALLBACK (gesture_detected_cb),
|
||||
&confidence);
|
||||
|
||||
gtk_gestures_interpreter_add_gesture (interpreter,
|
||||
GTK_GESTURE_SWIPE_RIGHT);
|
||||
|
||||
/* Feed the events, swipe to left */
|
||||
append_event_to_interpreter (interpreter, 100, 0);
|
||||
append_event_to_interpreter (interpreter, 0, 0);
|
||||
|
||||
gesture_detected = gtk_gestures_interpreter_finish (interpreter, NULL);
|
||||
|
||||
g_assert (gesture_detected == FALSE);
|
||||
g_assert_cmpfloat (confidence, ==, 0);
|
||||
|
||||
g_object_unref (interpreter);
|
||||
}
|
||||
|
||||
static void
|
||||
test_ignore_initial_orientation (void)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
gdouble initial_confidence, conf;
|
||||
gdouble confidence = 1;
|
||||
|
||||
interpreter = gtk_gestures_interpreter_new ();
|
||||
g_signal_connect (interpreter, "gesture-detected",
|
||||
G_CALLBACK (gesture_detected_cb),
|
||||
&confidence);
|
||||
|
||||
/* Use a circular gesture, which ignores initial orientation.
|
||||
* Stroke square rectangles at different orientations, which should
|
||||
* yield the same (lack of) confidence about the stroke.
|
||||
*/
|
||||
gtk_gestures_interpreter_add_gesture (interpreter,
|
||||
GTK_GESTURE_CIRCULAR_CLOCKWISE);
|
||||
|
||||
/* First rectangle */
|
||||
append_event_to_interpreter (interpreter, 0, 0);
|
||||
append_event_to_interpreter (interpreter, 100, 0);
|
||||
append_event_to_interpreter (interpreter, 100, 100);
|
||||
append_event_to_interpreter (interpreter, 0, 100);
|
||||
append_event_to_interpreter (interpreter, 0, 0);
|
||||
|
||||
gtk_gestures_interpreter_finish (interpreter, NULL);
|
||||
initial_confidence = confidence;
|
||||
|
||||
/* Second rectangle, completely flipped over */
|
||||
append_event_to_interpreter (interpreter, 100, 100);
|
||||
append_event_to_interpreter (interpreter, 0, 100);
|
||||
append_event_to_interpreter (interpreter, 0, 0);
|
||||
append_event_to_interpreter (interpreter, 100, 0);
|
||||
append_event_to_interpreter (interpreter, 100, 100);
|
||||
|
||||
gtk_gestures_interpreter_finish (interpreter, NULL);
|
||||
conf = confidence;
|
||||
|
||||
g_assert_cmpfloat (initial_confidence, ==, conf);
|
||||
|
||||
/* Third rectangle, 45º degrees rotation, different scale */
|
||||
append_event_to_interpreter (interpreter, 50, 0);
|
||||
append_event_to_interpreter (interpreter, 100, 50);
|
||||
append_event_to_interpreter (interpreter, 50, 100);
|
||||
append_event_to_interpreter (interpreter, 0, 50);
|
||||
append_event_to_interpreter (interpreter, 50, 0);
|
||||
|
||||
gtk_gestures_interpreter_finish (interpreter, NULL);
|
||||
conf = confidence;
|
||||
|
||||
g_assert_cmpfloat (initial_confidence, ==, conf);
|
||||
|
||||
g_object_unref (interpreter);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/gestures/empty-interpreter", test_empty_interpreter);
|
||||
g_test_add_func ("/gestures/equality", test_equality);
|
||||
g_test_add_func ("/gestures/events-ubiquity", test_events_ubiquity);
|
||||
g_test_add_func ("/gestures/opposite-gesture", test_opposite_gesture);
|
||||
g_test_add_func ("/gestures/ignore-initial-orientation", test_ignore_initial_orientation);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -55,6 +55,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
|
||||
testfontchooserdialog \
|
||||
testframe \
|
||||
testgeometry \
|
||||
testgestures \
|
||||
testgiconpixbuf \
|
||||
testgrid \
|
||||
testgtk \
|
||||
@@ -64,6 +65,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
|
||||
testicontheme \
|
||||
testimage \
|
||||
testinput \
|
||||
testkineticscrolling \
|
||||
testlockbutton \
|
||||
testmenubars \
|
||||
testmountoperation \
|
||||
@@ -77,6 +79,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
|
||||
testorientable \
|
||||
testoverlay \
|
||||
testprint \
|
||||
testpressandhold \
|
||||
testrecentchooser \
|
||||
testrecentchoosermenu \
|
||||
testrichtext \
|
||||
@@ -176,11 +179,13 @@ testfontchooser_DEPENDENCIES = $(TEST_DEPS)
|
||||
testfontchooserdialog_DEPENDENCIES = $(TEST_DEPS)
|
||||
testframe_DEPENDENCIES = $(TEST_DEPS)
|
||||
testgeometry_DEPENDENCIES = $(TEST_DEPS)
|
||||
testgestures_DEPENDENCIES = $(TEST_DEPS)
|
||||
testgiconpixbuf = $(TEST_DEPS)
|
||||
testgrid_DEPENDENCIES = $(TEST_DEPS)
|
||||
testgtk_DEPENDENCIES = $(TEST_DEPS)
|
||||
testinput_DEPENDENCIES = $(TEST_DEPS)
|
||||
testimage_DEPENDENCIES = $(TEST_DEPS)
|
||||
testkineticscrolling_DEPENDENCIES = $(TEST_DEPS)
|
||||
testlockbutton_DEPENDENCIES = $(TEST_DEPS)
|
||||
testmenubars_DEPENDENCIES = $(TEST_DEPS)
|
||||
testmountoperation_DEPENDENCIES = $(TEST_DEPS)
|
||||
@@ -196,6 +201,7 @@ testappchooserbutton_DEPENDENCIES = $(TEST_DEPS)
|
||||
testorientable_DEPENDENCIES = $(TEST_DEPS)
|
||||
testoverlay_DEPENDENCIES = $(TEST_DEPS)
|
||||
testprint_DEPENDENCIES = $(TEST_DEPS)
|
||||
testpressandhold_DEPENDENCIES = $(TEST_DEPS)
|
||||
testrecentchooser_DEPENDENCIES = $(TEST_DEPS)
|
||||
testrecentchoosermenu_DEPENDENCIES = $(TEST_DEPS)
|
||||
testrichtext_DEPENDENCIES = $(TEST_DEPS)
|
||||
@@ -271,6 +277,7 @@ testfontchooser_LDADD = $(LDADDS)
|
||||
testfontchooserdialog_LDADD = $(LDADDS)
|
||||
testframe_LDADD = $(LDADDS)
|
||||
testgeometry_LDADD = $(LDADDS)
|
||||
testgestures_LDADD = $(LDADDS)
|
||||
testgiconpixbuf_LDADD = $(LDADDS)
|
||||
testgrid_LDADD = $(LDADDS)
|
||||
testgtk_LDADD = $(LDADDS)
|
||||
@@ -280,6 +287,7 @@ testiconview_LDADD = $(LDADDS)
|
||||
testiconview_keynav_LDADD = $(LDADDS)
|
||||
testinput_LDADD = $(LDADDS)
|
||||
testimage_LDADD = $(LDADDS)
|
||||
testkineticscrolling_LDADD = $(LDADDS)
|
||||
testlockbutton_LDADD = $(LDADDS)
|
||||
testmenubars_LDADD = $(LDADDS)
|
||||
testmountoperation_LDADD = $(LDADDS)
|
||||
@@ -295,6 +303,7 @@ testappchooserbutton_LDADD = $(LDADDS)
|
||||
testorientable_LDADD = $(LDADDS)
|
||||
testoverlay_LDADD = $(LDADDS)
|
||||
testprint_LDADD = $(LDADDS)
|
||||
testpressandhold_LDADD = $(LDADDS)
|
||||
testrecentchooser_LDADD = $(LDADDS)
|
||||
testrecentchoosermenu_LDADD = $(LDADDS)
|
||||
testrichtext_LDADD = $(LDADDS)
|
||||
@@ -408,6 +417,9 @@ testprint_SOURCES = \
|
||||
testprintfileoperation.h \
|
||||
testprintfileoperation.c
|
||||
|
||||
testpressandhold_SOURCES = \
|
||||
testpressandhold.c
|
||||
|
||||
testsocket_SOURCES = \
|
||||
testsocket.c \
|
||||
testsocket_common.c
|
||||
@@ -441,6 +453,9 @@ testframe_SOURCES = \
|
||||
testgeometry_SOURCES = \
|
||||
testgeometry.c
|
||||
|
||||
testgestures_SOURCES = \
|
||||
testgestures.c
|
||||
|
||||
testgiconpixbuf_SOURCES = \
|
||||
testgiconpixbuf.c
|
||||
|
||||
@@ -524,6 +539,8 @@ testpixbuf_save_SOURCES = testpixbuf-save.c
|
||||
|
||||
testcolorchooser_SOURCES = testcolorchooser.c
|
||||
|
||||
testkineticscrolling_SOURCES = testkineticscrolling.c
|
||||
|
||||
EXTRA_DIST += \
|
||||
gradient1.png \
|
||||
prop-editor.h \
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_COMPILATION
|
||||
#include <gtk/gtkgesturesinterpreter.h>
|
||||
#undef GTK_COMPILATION
|
||||
|
||||
static GHashTable *strokes;
|
||||
static guint shown_gesture = 0;
|
||||
static guint timeout_id = 0;
|
||||
static guint m_shaped_gesture_id = 0;
|
||||
static guint two_touches_upwards_gesture_id = 0;
|
||||
|
||||
static void
|
||||
free_stroke (GArray *array)
|
||||
{
|
||||
g_array_free (array, TRUE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
hide_gesture_cb (gpointer user_data)
|
||||
{
|
||||
timeout_id = 0;
|
||||
shown_gesture = 0;
|
||||
|
||||
g_hash_table_remove_all (strokes);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (user_data));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
draw_cb (GtkWidget *widget,
|
||||
cairo_t *cr,
|
||||
gpointer user_data)
|
||||
{
|
||||
GdkPoint *point;
|
||||
GHashTableIter iter;
|
||||
gpointer value;
|
||||
gint i;
|
||||
|
||||
if (shown_gesture != 0)
|
||||
{
|
||||
gint width, height, size;
|
||||
|
||||
cairo_save (cr);
|
||||
width = gdk_window_get_width (gtk_widget_get_window (widget));
|
||||
height = gdk_window_get_height (gtk_widget_get_window (widget));
|
||||
|
||||
size = MIN (width, height);
|
||||
size -= size / 4;
|
||||
|
||||
cairo_translate (cr, width / 2, height / 2);
|
||||
|
||||
cairo_set_line_width (cr, 10);
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
|
||||
cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
|
||||
|
||||
switch (shown_gesture)
|
||||
{
|
||||
case GTK_GESTURE_SWIPE_LEFT:
|
||||
case GTK_GESTURE_SWIPE_RIGHT:
|
||||
case GTK_GESTURE_SWIPE_UP:
|
||||
case GTK_GESTURE_SWIPE_DOWN:
|
||||
if (shown_gesture == GTK_GESTURE_SWIPE_RIGHT)
|
||||
cairo_rotate (cr, (G_PI * 3) / 2);
|
||||
else if (shown_gesture == GTK_GESTURE_SWIPE_LEFT)
|
||||
cairo_rotate (cr, G_PI / 2);
|
||||
else if (shown_gesture == GTK_GESTURE_SWIPE_UP)
|
||||
cairo_rotate (cr, G_PI);
|
||||
|
||||
cairo_move_to (cr, 0, - size / 2);
|
||||
cairo_line_to (cr, 0, size / 2);
|
||||
|
||||
cairo_move_to (cr, 0, size / 2);
|
||||
cairo_rel_line_to (cr, -size / 4, -size / 4);
|
||||
|
||||
cairo_move_to (cr, 0, size / 2);
|
||||
cairo_rel_line_to (cr, size / 4, -size / 4);
|
||||
|
||||
cairo_stroke (cr);
|
||||
break;
|
||||
case GTK_GESTURE_CIRCULAR_COUNTERCLOCKWISE:
|
||||
cairo_move_to (cr, 0, - size / 2);
|
||||
cairo_rel_line_to (cr, size / 8, - size / 8);
|
||||
|
||||
cairo_move_to (cr, 0, - size / 2);
|
||||
cairo_rel_line_to (cr, size / 8, size / 8);
|
||||
|
||||
cairo_new_sub_path (cr);
|
||||
cairo_arc (cr, 0, 0, size / 2, (3 * G_PI) / 2, (5 * G_PI) / 4);
|
||||
|
||||
cairo_stroke (cr);
|
||||
break;
|
||||
case GTK_GESTURE_CIRCULAR_CLOCKWISE:
|
||||
cairo_move_to (cr, 0, - size / 2);
|
||||
cairo_rel_line_to (cr, - size / 8, - size / 8);
|
||||
|
||||
cairo_move_to (cr, 0, - size / 2);
|
||||
cairo_rel_line_to (cr, - size / 8, size / 8);
|
||||
|
||||
cairo_new_sub_path (cr);
|
||||
cairo_arc_negative (cr, 0, 0, size / 2, (3 * G_PI) / 2, (7* G_PI) / 4);
|
||||
|
||||
cairo_stroke (cr);
|
||||
break;
|
||||
default:
|
||||
if (shown_gesture == m_shaped_gesture_id ||
|
||||
shown_gesture == two_touches_upwards_gesture_id)
|
||||
{
|
||||
PangoAttrList *attr_list;
|
||||
PangoLayout *layout;
|
||||
|
||||
if (shown_gesture == two_touches_upwards_gesture_id)
|
||||
layout = gtk_widget_create_pango_layout (widget, "II");
|
||||
else
|
||||
layout = gtk_widget_create_pango_layout (widget, "M");
|
||||
|
||||
attr_list = pango_layout_get_attributes (layout);
|
||||
|
||||
if (!attr_list)
|
||||
attr_list = pango_attr_list_new ();
|
||||
|
||||
pango_attr_list_insert (attr_list,
|
||||
pango_attr_size_new_absolute (200 * PANGO_SCALE));
|
||||
pango_layout_set_attributes (layout, attr_list);
|
||||
|
||||
cairo_move_to (cr, -100, -100);
|
||||
pango_cairo_show_layout (cr, layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
g_hash_table_iter_init (&iter, strokes);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, NULL, &value))
|
||||
{
|
||||
GArray *coords = value;
|
||||
|
||||
if (coords->len > 0)
|
||||
{
|
||||
cairo_save (cr);
|
||||
cairo_set_line_width (cr, 2);
|
||||
|
||||
point = &g_array_index (coords, GdkPoint, 0);
|
||||
cairo_move_to (cr, point->x, point->y);
|
||||
|
||||
for (i = 0; i < coords->len; i++)
|
||||
{
|
||||
point = &g_array_index (coords, GdkPoint, i);
|
||||
cairo_line_to (cr, point->x, point->y);
|
||||
}
|
||||
|
||||
cairo_stroke (cr);
|
||||
cairo_restore (cr);
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
append_event_coordinates (GdkEvent *event)
|
||||
{
|
||||
GdkPoint point;
|
||||
gdouble x, y;
|
||||
guint touch_id;
|
||||
GArray *coords;
|
||||
|
||||
if (!gdk_event_get_coords (event, &x, &y))
|
||||
return FALSE;
|
||||
|
||||
if (!gdk_event_get_touch_id (event, &touch_id))
|
||||
touch_id = 0;
|
||||
|
||||
coords = g_hash_table_lookup (strokes,
|
||||
GUINT_TO_POINTER (touch_id));
|
||||
|
||||
if (!coords)
|
||||
return FALSE;
|
||||
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
g_array_append_val (coords, point);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
motion_notify_cb (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
GdkModifierType state;
|
||||
|
||||
interpreter = user_data;
|
||||
|
||||
if (!gdk_event_get_state (event, &state))
|
||||
return FALSE;
|
||||
|
||||
if ((state & GDK_BUTTON1_MASK) == 0)
|
||||
return FALSE;
|
||||
|
||||
append_event_coordinates (event);
|
||||
gtk_gestures_interpreter_feed_event (interpreter, event);
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
button_release_cb (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
guint gesture_id;
|
||||
|
||||
interpreter = user_data;
|
||||
|
||||
if (timeout_id != 0)
|
||||
g_source_remove (timeout_id);
|
||||
|
||||
timeout_id = g_timeout_add (500, hide_gesture_cb, widget);
|
||||
|
||||
append_event_coordinates (event);
|
||||
gtk_gestures_interpreter_feed_event (interpreter, event);
|
||||
|
||||
if (gtk_gestures_interpreter_get_n_active_strokes (interpreter) == 0)
|
||||
{
|
||||
if (gtk_gestures_interpreter_finish (interpreter, &gesture_id))
|
||||
shown_gesture = gesture_id;
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
button_press_cb (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
GArray *coords;
|
||||
guint touch_id;
|
||||
|
||||
interpreter = user_data;
|
||||
|
||||
if (timeout_id != 0)
|
||||
{
|
||||
g_hash_table_remove_all (strokes);
|
||||
g_source_remove (timeout_id);
|
||||
timeout_id = 0;
|
||||
}
|
||||
|
||||
shown_gesture = 0;
|
||||
|
||||
if (!gdk_event_get_touch_id (event, &touch_id))
|
||||
touch_id = 0;
|
||||
|
||||
coords = g_array_new (FALSE, FALSE, sizeof (GdkPoint));
|
||||
g_hash_table_insert (strokes,
|
||||
GUINT_TO_POINTER (touch_id),
|
||||
coords);
|
||||
|
||||
append_event_coordinates (event);
|
||||
gtk_gestures_interpreter_feed_event (interpreter, event);
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_window (GtkGesturesInterpreter *interpreter)
|
||||
{
|
||||
GtkWidget *window;
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_widget_set_app_paintable (window, TRUE);
|
||||
gtk_widget_set_size_request (window, 400, 400);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Gestures demo");
|
||||
|
||||
gtk_widget_add_events (window,
|
||||
GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK |
|
||||
GDK_POINTER_MOTION_MASK |
|
||||
GDK_TOUCH_MASK);
|
||||
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_main_quit), NULL);
|
||||
g_signal_connect (window, "motion-notify-event",
|
||||
G_CALLBACK (motion_notify_cb), interpreter);
|
||||
g_signal_connect (window, "button-release-event",
|
||||
G_CALLBACK (button_release_cb), interpreter);
|
||||
g_signal_connect (window, "button-press-event",
|
||||
G_CALLBACK (button_press_cb), interpreter);
|
||||
g_signal_connect (window, "draw",
|
||||
G_CALLBACK (draw_cb), NULL);
|
||||
|
||||
gtk_widget_show (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
static GtkGesture *
|
||||
create_m_shaped_gesture (void)
|
||||
{
|
||||
GtkGestureStroke *stroke;
|
||||
GtkGesture *gesture;
|
||||
|
||||
stroke = gtk_gesture_stroke_new ();
|
||||
|
||||
gtk_gesture_stroke_append_vector (stroke, 0, 100);
|
||||
gtk_gesture_stroke_append_vector (stroke, 3 * G_PI_4, 50);
|
||||
gtk_gesture_stroke_append_vector (stroke, G_PI_4, 50);
|
||||
gtk_gesture_stroke_append_vector (stroke, G_PI, 100);
|
||||
|
||||
gesture = gtk_gesture_new (stroke, 0);
|
||||
gtk_gesture_stroke_free (stroke);
|
||||
|
||||
return gesture;
|
||||
}
|
||||
|
||||
static GtkGesture *
|
||||
create_two_touches_upwards_gesture (void)
|
||||
{
|
||||
GtkGestureStroke *stroke;
|
||||
GtkGesture *gesture;
|
||||
|
||||
stroke = gtk_gesture_stroke_new ();
|
||||
gtk_gesture_stroke_append_vector (stroke, 0, 100);
|
||||
gesture = gtk_gesture_new (stroke, 0); //GTK_GESTURE_FLAG_IGNORE_INITIAL_ORIENTATION);
|
||||
|
||||
/* Add again the same stroke, with an offset */
|
||||
gtk_gesture_add_stroke (gesture, stroke, 50, 0);
|
||||
gtk_gesture_stroke_free (stroke);
|
||||
|
||||
return gesture;
|
||||
}
|
||||
|
||||
static GtkGesturesInterpreter *
|
||||
create_interpreter (void)
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
GtkGesture *gesture;
|
||||
|
||||
interpreter = gtk_gestures_interpreter_new ();
|
||||
gtk_gestures_interpreter_add_gesture (interpreter, GTK_GESTURE_SWIPE_RIGHT);
|
||||
gtk_gestures_interpreter_add_gesture (interpreter, GTK_GESTURE_SWIPE_LEFT);
|
||||
gtk_gestures_interpreter_add_gesture (interpreter, GTK_GESTURE_SWIPE_UP);
|
||||
gtk_gestures_interpreter_add_gesture (interpreter, GTK_GESTURE_SWIPE_DOWN);
|
||||
gtk_gestures_interpreter_add_gesture (interpreter, GTK_GESTURE_CIRCULAR_CLOCKWISE);
|
||||
gtk_gestures_interpreter_add_gesture (interpreter, GTK_GESTURE_CIRCULAR_COUNTERCLOCKWISE);
|
||||
|
||||
gesture = create_m_shaped_gesture ();
|
||||
m_shaped_gesture_id = gtk_gesture_register_static (gesture);
|
||||
gtk_gestures_interpreter_add_gesture (interpreter, m_shaped_gesture_id);
|
||||
|
||||
gesture = create_two_touches_upwards_gesture ();
|
||||
two_touches_upwards_gesture_id = gtk_gesture_register_static (gesture);
|
||||
gtk_gestures_interpreter_add_gesture (interpreter,
|
||||
two_touches_upwards_gesture_id);
|
||||
return interpreter;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkGesturesInterpreter *interpreter;
|
||||
GtkWidget *window;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
strokes = g_hash_table_new_full (NULL, NULL, NULL,
|
||||
(GDestroyNotify) free_stroke);
|
||||
|
||||
interpreter = create_interpreter ();
|
||||
window = create_window (interpreter);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
gtk_widget_destroy (window);
|
||||
g_object_unref (interpreter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
enum
|
||||
{
|
||||
TARGET_GTK_TREE_MODEL_ROW
|
||||
};
|
||||
|
||||
static GtkTargetEntry row_targets[] =
|
||||
{
|
||||
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_GTK_TREE_MODEL_ROW }
|
||||
};
|
||||
|
||||
static void
|
||||
on_button_clicked (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
g_print ("Button %d clicked\n", GPOINTER_TO_INT (data));
|
||||
}
|
||||
|
||||
static void
|
||||
kinetic_scrolling (void)
|
||||
{
|
||||
GtkWidget *window, *swindow, *grid;
|
||||
GtkWidget *label;
|
||||
GtkWidget *button_grid, *button;
|
||||
GtkWidget *treeview;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkListStore *store;
|
||||
GtkWidget *textview;
|
||||
gint i;
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (window), 5);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
|
||||
g_signal_connect (window, "delete_event",
|
||||
G_CALLBACK (gtk_main_quit), NULL);
|
||||
|
||||
grid = gtk_grid_new ();
|
||||
|
||||
label = gtk_label_new ("Non scrollable widget using viewport");
|
||||
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
|
||||
gtk_widget_set_hexpand (label, TRUE);
|
||||
gtk_widget_show (label);
|
||||
|
||||
label = gtk_label_new ("Scrollable widget: TreeView");
|
||||
gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1);
|
||||
gtk_widget_set_hexpand (label, TRUE);
|
||||
gtk_widget_show (label);
|
||||
|
||||
label = gtk_label_new ("Scrollable widget: TextView");
|
||||
gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
|
||||
gtk_widget_set_hexpand (label, TRUE);
|
||||
gtk_widget_show (label);
|
||||
|
||||
button_grid = gtk_grid_new ();
|
||||
for (i = 0; i < 80; i++)
|
||||
{
|
||||
gchar *label = g_strdup_printf ("Button number %d", i);
|
||||
|
||||
button = gtk_button_new_with_label (label);
|
||||
gtk_grid_attach (GTK_GRID (button_grid), button, 0, i, 1, 1);
|
||||
gtk_widget_set_hexpand (button, TRUE);
|
||||
gtk_widget_show (button);
|
||||
g_signal_connect (button, "clicked",
|
||||
G_CALLBACK (on_button_clicked),
|
||||
GINT_TO_POINTER (i));
|
||||
g_free (label);
|
||||
}
|
||||
|
||||
swindow = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow),
|
||||
GTK_KINETIC_SCROLLING_ENABLED);
|
||||
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (swindow), button_grid);
|
||||
gtk_widget_show (button_grid);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), swindow, 0, 1, 1, 1);
|
||||
gtk_widget_show (swindow);
|
||||
|
||||
treeview = gtk_tree_view_new ();
|
||||
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview),
|
||||
GDK_BUTTON1_MASK,
|
||||
row_targets,
|
||||
G_N_ELEMENTS (row_targets),
|
||||
GDK_ACTION_MOVE | GDK_ACTION_COPY);
|
||||
gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (treeview),
|
||||
row_targets,
|
||||
G_N_ELEMENTS (row_targets),
|
||||
GDK_ACTION_MOVE | GDK_ACTION_COPY);
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
g_object_set (renderer, "editable", TRUE, NULL);
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
0, "Title",
|
||||
renderer,
|
||||
"text", 0,
|
||||
NULL);
|
||||
store = gtk_list_store_new (1, G_TYPE_STRING);
|
||||
for (i = 0; i < 80; i++)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
gchar *label = g_strdup_printf ("Row number %d", i);
|
||||
|
||||
gtk_list_store_append (store, &iter);
|
||||
gtk_list_store_set (store, &iter, 0, label, -1);
|
||||
g_free (label);
|
||||
}
|
||||
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
|
||||
g_object_unref (store);
|
||||
|
||||
swindow = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow),
|
||||
GTK_KINETIC_SCROLLING_ENABLED |
|
||||
GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS);
|
||||
gtk_container_add (GTK_CONTAINER (swindow), treeview);
|
||||
gtk_widget_show (treeview);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), swindow, 1, 1, 1, 1);
|
||||
gtk_widget_set_hexpand (swindow, TRUE);
|
||||
gtk_widget_set_vexpand (swindow, TRUE);
|
||||
gtk_widget_show (swindow);
|
||||
|
||||
textview = gtk_text_view_new ();
|
||||
swindow = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow),
|
||||
GTK_KINETIC_SCROLLING_ENABLED |
|
||||
GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS);
|
||||
gtk_container_add (GTK_CONTAINER (swindow), textview);
|
||||
gtk_widget_show (textview);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), swindow, 2, 1, 1, 1);
|
||||
gtk_widget_set_hexpand (swindow, TRUE);
|
||||
gtk_widget_set_vexpand (swindow, TRUE);
|
||||
gtk_widget_show (swindow);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (window), grid);
|
||||
gtk_widget_show (grid);
|
||||
|
||||
gtk_widget_show (window);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
gtk_init (NULL, NULL);
|
||||
|
||||
kinetic_scrolling ();
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/* testpressandhold.c: Test application for GTK+ >= 3.2 press-n-hold code
|
||||
*
|
||||
* Copyright (C) 2007,2008 Imendio AB
|
||||
* Contact: Kristian Rietveld <kris@imendio.com>
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
press_and_hold_show_menu (GtkWidget *widget,
|
||||
GdkDevice *device)
|
||||
{
|
||||
GtkWidget *menu;
|
||||
GtkWidget *item;
|
||||
|
||||
menu = gtk_menu_new ();
|
||||
|
||||
item = gtk_menu_item_new_with_label ("Test 1");
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
||||
gtk_widget_show (item);
|
||||
|
||||
item = gtk_menu_item_new_with_label ("Test 2");
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
||||
gtk_widget_show (item);
|
||||
|
||||
item = gtk_menu_item_new_with_label ("Test 3");
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
||||
gtk_widget_show (item);
|
||||
|
||||
gtk_menu_popup_for_device (GTK_MENU (menu), device,
|
||||
NULL, NULL, NULL, NULL, NULL,
|
||||
1,
|
||||
GDK_CURRENT_TIME);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
press_and_hold (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
GtkPressAndHoldAction action,
|
||||
gint x,
|
||||
gint y,
|
||||
gpointer user_data)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case GTK_PRESS_AND_HOLD_QUERY:
|
||||
g_print ("press-and-hold-query on %s\n", gtk_widget_get_name (widget));
|
||||
break;
|
||||
|
||||
case GTK_PRESS_AND_HOLD_TRIGGER:
|
||||
g_print ("press-and-hold-trigger on %s\n", gtk_widget_get_name (widget));
|
||||
press_and_hold_show_menu (widget, device);
|
||||
break;
|
||||
|
||||
case GTK_PRESS_AND_HOLD_CANCEL:
|
||||
g_print ("press-and-hold-cancel on %s\n", gtk_widget_get_name (widget));
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GtkTreeModel *
|
||||
create_model (void)
|
||||
{
|
||||
GtkTreeStore *store;
|
||||
GtkTreeIter iter;
|
||||
|
||||
store = gtk_tree_store_new (1, G_TYPE_STRING);
|
||||
|
||||
/* A tree store with some random words ... */
|
||||
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
|
||||
0, "File Manager", -1);
|
||||
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
|
||||
0, "Gossip", -1);
|
||||
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
|
||||
0, "System Settings", -1);
|
||||
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
|
||||
0, "The GIMP", -1);
|
||||
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
|
||||
0, "Terminal", -1);
|
||||
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
|
||||
0, "Word Processor", -1);
|
||||
|
||||
return GTK_TREE_MODEL (store);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *box;
|
||||
GtkWidget *label, *checkbutton, *tree_view, *entry;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Press and Hold test");
|
||||
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
|
||||
g_signal_connect (window, "delete_event",
|
||||
G_CALLBACK (gtk_main_quit), NULL);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
|
||||
gtk_container_add (GTK_CONTAINER (window), box);
|
||||
|
||||
label = gtk_button_new_with_label ("Press-n-hold me!");
|
||||
g_signal_connect (label, "press-and-hold",
|
||||
G_CALLBACK (press_and_hold), NULL);
|
||||
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
|
||||
|
||||
label = gtk_button_new_with_label ("No press and hold");
|
||||
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
|
||||
|
||||
checkbutton = gtk_check_button_new_with_label ("Checkable check button");
|
||||
g_signal_connect (checkbutton, "press-and-hold",
|
||||
G_CALLBACK (press_and_hold), NULL);
|
||||
gtk_box_pack_start (GTK_BOX (box), checkbutton, FALSE, FALSE, 0);
|
||||
|
||||
|
||||
tree_view = gtk_tree_view_new_with_model (create_model ());
|
||||
gtk_widget_set_size_request (tree_view, 200, 240);
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view),
|
||||
0, "Test",
|
||||
gtk_cell_renderer_text_new (),
|
||||
"text", 0,
|
||||
NULL);
|
||||
|
||||
g_signal_connect (tree_view, "press-and-hold",
|
||||
G_CALLBACK (press_and_hold), NULL);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (box), tree_view, FALSE, FALSE, 0);
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
gtk_entry_set_text (GTK_ENTRY (entry), "Press and hold me");
|
||||
g_signal_connect (entry, "press-and-hold",
|
||||
G_CALLBACK (press_and_hold), NULL);
|
||||
gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 0);
|
||||
|
||||
gtk_widget_show_all (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -53,6 +53,16 @@ content_height_changed (GtkSpinButton *spin_button,
|
||||
gtk_scrolled_window_set_min_content_height (swindow, (gint)value);
|
||||
}
|
||||
|
||||
static void
|
||||
kinetic_scrolling_changed (GtkToggleButton *toggle_button,
|
||||
gpointer data)
|
||||
{
|
||||
GtkScrolledWindow *swindow = data;
|
||||
gboolean enabled = gtk_toggle_button_get_active (toggle_button);
|
||||
|
||||
gtk_scrolled_window_set_kinetic_scrolling (swindow, enabled);
|
||||
}
|
||||
|
||||
static void
|
||||
scrollable_policy (void)
|
||||
{
|
||||
@@ -199,6 +209,13 @@ scrollable_policy (void)
|
||||
g_signal_connect (G_OBJECT (widget), "changed",
|
||||
G_CALLBACK (label_flip_changed), label);
|
||||
|
||||
/* Add Kinetic scrolling control here */
|
||||
widget = gtk_check_button_new_with_label ("Kinetic scrolling");
|
||||
gtk_widget_show (widget);
|
||||
gtk_box_pack_start (GTK_BOX (cntl), widget, TRUE, TRUE, 0);
|
||||
g_signal_connect (G_OBJECT (widget), "toggled",
|
||||
G_CALLBACK (kinetic_scrolling_changed), swindow);
|
||||
|
||||
gtk_widget_show (window);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user