[gtk+/multitouch: 58/121] gtk, menu: Implement scrolling through ::captured-event for touch devices
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/multitouch: 58/121] gtk, menu: Implement scrolling through ::captured-event for touch devices
- Date: Thu, 12 Jan 2012 14:21:59 +0000 (UTC)
commit 41a065d463259a784a4c6c470e86ffaf534c2eda
Author: Carlos Garnacho <carlosg gnome org>
Date: Mon Dec 12 18:11:57 2011 +0100
gtk,menu: Implement scrolling through ::captured-event for touch devices
This makes overflown menus scrollable via direct manipulation. Once past
the threshold, the item below the pointer is unselected and scrolling
starts.
gtk/gtkmenu.c | 199 +++++++++++++++++++++++++++++++++++++++-----------
gtk/gtkmenuprivate.h | 5 +
2 files changed, 161 insertions(+), 43 deletions(-)
---
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index 96f42a6..995eda3 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -110,6 +110,7 @@
#include "gtksettings.h"
#include "gtkprivate.h"
#include "gtkwidgetprivate.h"
+#include "gtkdnd.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
@@ -227,6 +228,10 @@ 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);
@@ -510,6 +515,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 +1063,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);
@@ -3313,34 +3320,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,11 +3359,6 @@ gtk_menu_button_press (GtkWidget *widget,
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;
-
/* 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
* works because we have the pointer grabbed on menu_shell->window
@@ -3414,11 +3388,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()).
*/
@@ -3662,10 +3631,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;
@@ -4283,10 +4256,11 @@ gtk_menu_enter_notify (GtkWidget *widget,
event->mode == GDK_CROSSING_STATE_CHANGED)
return TRUE;
- source_device = gdk_event_get_source_device (event);
+ 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);
@@ -4353,6 +4327,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 ||
@@ -4365,7 +4340,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);
@@ -4400,6 +4378,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)
{
@@ -5660,7 +5770,6 @@ gtk_menu_real_move_scroll (GtkMenu *menu,
}
}
-
/**
* gtk_menu_set_monitor:
* @menu: a #GtkMenu
@@ -5737,11 +5846,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 ||
@@ -5758,6 +5869,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;
}
/**
diff --git a/gtk/gtkmenuprivate.h b/gtk/gtkmenuprivate.h
index cbac9fd..9c88dfb 100644
--- a/gtk/gtkmenuprivate.h
+++ b/gtk/gtkmenuprivate.h
@@ -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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]