diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2011-12-12 18:11:57 +0100 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2012-03-01 16:25:23 -0500 |
commit | 47f9435e995b1d5cb4d3c67841e4e809bafaf730 (patch) | |
tree | 2e1e5db3c6c15f324867f9a9c475f5bda78da554 /gtk/gtkmenu.c | |
parent | 5139617b91004c0aaa69da0ffa56dfc832bddacb (diff) | |
download | gtk+-47f9435e995b1d5cb4d3c67841e4e809bafaf730.tar.gz |
menus: Implement scrolling through event capture 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.
Diffstat (limited to 'gtk/gtkmenu.c')
-rw-r--r-- | gtk/gtkmenu.c | 204 |
1 files changed, 161 insertions, 43 deletions
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index faa6896160..f1571d972b 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -108,8 +108,10 @@ #include "gtksettings.h" #include "gtkprivate.h" #include "gtkwidgetprivate.h" +#include "gtkdnd.h" #include "gtkintl.h" #include "gtktypebuiltins.h" +#include "gtkwidgetprivate.h" #include "deprecated/gtktearoffmenuitem.h" @@ -225,6 +227,9 @@ static void gtk_menu_scroll_to (GtkMenu *menu, gint offset); static void gtk_menu_grab_notify (GtkWidget *widget, gboolean was_grabbed); +static gboolean 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); @@ -1064,9 +1069,12 @@ 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); + + _gtk_widget_set_captured_event_handler (GTK_WIDGET (menu), gtk_menu_captured_event); } static void @@ -3323,34 +3331,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, @@ -3390,11 +3370,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 @@ -3424,11 +3399,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()). */ @@ -3672,10 +3642,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_TOUCHSCREEN) { GtkMenuPrivate *priv = GTK_MENU(widget)->priv; @@ -4293,10 +4267,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_TOUCHSCREEN) { GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget); @@ -4363,6 +4338,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 || @@ -4375,7 +4351,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_TOUCHSCREEN) + gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE); event_widget = gtk_get_event_widget ((GdkEvent*) event); @@ -4410,6 +4389,142 @@ 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 gboolean +gtk_menu_captured_event (GtkWidget *widget, + GdkEvent *event) +{ + GdkDevice *source_device; + gboolean retval = FALSE; + GtkMenuPrivate *priv; + GtkMenu *menu; + gdouble x_root, y_root; + guint button; + GdkModifierType state; + + menu = GTK_MENU (widget); + priv = menu->priv; + + if (!priv->upper_arrow_visible && !priv->lower_arrow_visible) + return retval; + + source_device = gdk_event_get_source_device (event); + gdk_event_get_root_coords (event, &x_root, &y_root); + + switch (event->type) + { + case GDK_TOUCH_BEGIN: + case GDK_BUTTON_PRESS: + if ((!gdk_event_get_button (event, &button) || button == 1) && + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN && + pointer_on_menu_widget (menu, x_root, 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_TOUCH_END: + case GDK_BUTTON_RELEASE: + if (priv->drag_scroll_started) + { + priv->drag_scroll_started = FALSE; + priv->drag_start_y = -1; + priv->drag_already_pressed = FALSE; + retval = TRUE; + } + break; + case GDK_TOUCH_UPDATE: + case GDK_MOTION_NOTIFY: + if ((!gdk_event_get_state (event, &state) || (state & GDK_BUTTON1_MASK) +!= 0) && + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) + { + if (!priv->drag_already_pressed) + { + if (pointer_on_menu_widget (menu, x_root, y_root)) + { + priv->drag_start_y = 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 = 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); + + retval = TRUE; + } + else if (gtk_drag_check_threshold (widget, + 0, priv->drag_start_y, + 0, y_root)) + { + priv->drag_scroll_started = TRUE; + gtk_menu_shell_deselect (GTK_MENU_SHELL (menu)); + retval = TRUE; + } + } + break; + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + if (priv->drag_scroll_started) + retval = TRUE; + break; + default: + break; + } + + return retval; +} + static void gtk_menu_stop_navigating_submenu (GtkMenu *menu) { @@ -5670,7 +5785,6 @@ gtk_menu_real_move_scroll (GtkMenu *menu, } } - /** * gtk_menu_set_monitor: * @menu: a #GtkMenu @@ -5747,11 +5861,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 || @@ -5768,6 +5884,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; } /** |