diff options
author | Michael Natterer <mitch@imendio.com> | 2006-02-22 10:10:23 +0000 |
---|---|---|
committer | Michael Natterer <mitch@src.gnome.org> | 2006-02-22 10:10:23 +0000 |
commit | 3b3b722ccffb4a3517cbbf80c2718f99f7393e97 (patch) | |
tree | 07999b126d14d21f62aeb95481771215eb4f6dd2 /gtk | |
parent | 9c359af5d8422b90605af964f2a073901a34cbfe (diff) | |
download | gtk+-3b3b722ccffb4a3517cbbf80c2718f99f7393e97.tar.gz |
added boolean property gtk-touchscreen-mode, which essentially means
2006-02-22 Michael Natterer <mitch@imendio.com>
* gtk/gtksettings.c: added boolean property gtk-touchscreen-mode,
which essentially means "there are no motion notify events", so
widgets can't use the pointer hovering them for anything.
* gtk/gtkmenu.c: if gtk-touchscreen-mode is TRUE, scroll menus
when clicking the scroll arrows, since hovering goes undetected.
Fixes bug #323028.
Added boolean style property "double-arrows" which always makes
both scroll arrows visible when the menu is too long.
For pushed-in popup menus, both arrows are always shown
(regardless of double-arrows), in order to fix user confusion
about the blank area. Fixes bug #129463.
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/gtkmenu.c | 672 | ||||
-rw-r--r-- | gtk/gtksettings.c | 20 |
2 files changed, 546 insertions, 146 deletions
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 06ac3cef96..cdba77ba7b 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -95,6 +95,13 @@ struct _GtkMenuPrivate gint n_columns; gchar *title; + + /* Arrow states */ + GtkStateType lower_arrow_state; + GtkStateType upper_arrow_state; + + gboolean ignore_button_release; + gboolean initially_pushed_in; }; typedef struct @@ -180,9 +187,11 @@ static void gtk_menu_scroll_to (GtkMenu *menu, static void gtk_menu_grab_notify (GtkWidget *widget, gboolean was_grabbed); -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 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); @@ -196,7 +205,8 @@ static void gtk_menu_scrollbar_changed (GtkAdjustment *adjustment, static void gtk_menu_handle_scrolling (GtkMenu *menu, gint event_x, gint event_y, - gboolean enter); + gboolean enter, + gboolean motion); static void gtk_menu_set_tearoff_hints (GtkMenu *menu, gint width); static void gtk_menu_style_set (GtkWidget *widget, @@ -559,6 +569,13 @@ gtk_menu_class_init (GtkMenuClass *class) -2, GTK_PARAM_READABLE)); + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boolean ("double-arrows", + P_("Double Arrows"), + P_("When scrolling, always show both arrows."), + TRUE, + GTK_PARAM_READABLE)); + gtk_container_class_install_child_property (container_class, CHILD_PROP_LEFT_ATTACH, @@ -908,6 +925,9 @@ gtk_menu_init (GtkMenu *menu) menu->upper_arrow_prelight = FALSE; menu->lower_arrow_prelight = FALSE; + priv->upper_arrow_state = GTK_STATE_NORMAL; + priv->lower_arrow_state = GTK_STATE_NORMAL; + priv->have_layout = FALSE; } @@ -2052,6 +2072,7 @@ gtk_menu_realize (GtkWidget *widget) attributes.y += MENU_SCROLL_ARROW_HEIGHT; attributes.height -= MENU_SCROLL_ARROW_HEIGHT; } + if (menu->lower_arrow_visible) attributes.height -= MENU_SCROLL_ARROW_HEIGHT; @@ -2301,16 +2322,16 @@ gtk_menu_size_allocate (GtkWidget *widget, if (menu_shell->active) gtk_menu_scroll_to (menu, menu->scroll_offset); - + if (menu->upper_arrow_visible && !menu->tearoff_active) { y += MENU_SCROLL_ARROW_HEIGHT; height -= MENU_SCROLL_ARROW_HEIGHT; } - + if (menu->lower_arrow_visible && !menu->tearoff_active) height -= MENU_SCROLL_ARROW_HEIGHT; - + if (GTK_WIDGET_REALIZED (widget)) { gdk_window_move_resize (widget->window, @@ -2428,6 +2449,7 @@ gtk_menu_paint (GtkWidget *widget, GdkEventExpose *event) { GtkMenu *menu; + GtkMenuPrivate *priv; gint width, height; gint border_x, border_y; guint vertical_padding; @@ -2436,6 +2458,7 @@ gtk_menu_paint (GtkWidget *widget, g_return_if_fail (GTK_IS_MENU (widget)); menu = GTK_MENU (widget); + priv = gtk_menu_get_private (menu); gtk_widget_style_get (GTK_WIDGET (menu), "vertical-padding", &vertical_padding, @@ -2450,30 +2473,29 @@ gtk_menu_paint (GtkWidget *widget, { gint arrow_space = MENU_SCROLL_ARROW_HEIGHT - 2 * widget->style->ythickness; gint arrow_size = 0.7 * arrow_space; - + gtk_paint_box (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, widget, "menu", 0, 0, -1, -1); + if (menu->upper_arrow_visible && !menu->tearoff_active) { gtk_paint_box (widget->style, widget->window, - menu->upper_arrow_prelight ? - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL, + priv->upper_arrow_state, GTK_SHADOW_OUT, NULL, widget, "menu", border_x, border_y, width - 2 * border_x, MENU_SCROLL_ARROW_HEIGHT); - + gtk_paint_arrow (widget->style, widget->window, - menu->upper_arrow_prelight ? - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL, + priv->upper_arrow_state, GTK_SHADOW_OUT, NULL, widget, "menu_scroll_arrow_up", GTK_ARROW_UP, @@ -2482,24 +2504,22 @@ gtk_menu_paint (GtkWidget *widget, border_y + widget->style->ythickness + (arrow_space - arrow_size)/2, arrow_size, arrow_size); } - + if (menu->lower_arrow_visible && !menu->tearoff_active) { gtk_paint_box (widget->style, widget->window, - menu->lower_arrow_prelight ? - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL, + priv->lower_arrow_state, GTK_SHADOW_OUT, NULL, widget, "menu", border_x, height - border_y - MENU_SCROLL_ARROW_HEIGHT, width - 2*border_x, MENU_SCROLL_ARROW_HEIGHT); - + gtk_paint_arrow (widget->style, widget->window, - menu->lower_arrow_prelight ? - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL, + priv->lower_arrow_state, GTK_SHADOW_OUT, NULL, widget, "menu_scroll_arrow_down", GTK_ARROW_DOWN, @@ -2550,19 +2570,47 @@ gtk_menu_show (GtkWidget *widget) } static gboolean -gtk_menu_button_press (GtkWidget *widget, - GdkEventButton *event) +gtk_menu_button_scroll (GtkWidget *widget, + GdkEventButton *event) { - /* Don't pop down the menu for releases over scroll arrows - */ if (GTK_IS_MENU (widget)) { GtkMenu *menu = GTK_MENU (widget); - if (menu->upper_arrow_prelight || menu->lower_arrow_prelight) - return TRUE; + if (menu->upper_arrow_prelight || menu->lower_arrow_prelight) + { + GtkSettings *settings = gtk_widget_get_settings (widget); + gboolean touchscreen_mode; + + g_object_get (G_OBJECT (settings), + "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 +gtk_menu_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + if (event->type != GDK_BUTTON_PRESS) + return FALSE; + + /* Don't pop down the menu for presses over scroll arrows + */ + if (gtk_menu_button_scroll (widget, event)) + return TRUE; + return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event); } @@ -2570,16 +2618,25 @@ static gboolean gtk_menu_button_release (GtkWidget *widget, GdkEventButton *event) { - /* Don't pop down the menu for releases over scroll arrows - */ if (GTK_IS_MENU (widget)) { - GtkMenu *menu = GTK_MENU (widget); + GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (widget)); - if (menu->upper_arrow_prelight || menu->lower_arrow_prelight) - return TRUE; + if (priv->ignore_button_release) + { + priv->ignore_button_release = FALSE; + return FALSE; + } } + if (event->type != GDK_BUTTON_RELEASE) + return FALSE; + + /* Don't pop down the menu for releases over scroll arrows + */ + if (gtk_menu_button_scroll (widget, event)) + return TRUE; + return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event); } @@ -2797,7 +2854,15 @@ gtk_menu_motion_notify (GtkWidget *widget, gboolean need_enter; if (GTK_IS_MENU (widget)) - gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE); + { + GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (widget)); + + if (priv->ignore_button_release) + priv->ignore_button_release = FALSE; + + gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, + TRUE, TRUE); + } /* We received the event for one of two reasons: * @@ -2809,7 +2874,7 @@ gtk_menu_motion_notify (GtkWidget *widget, * which may be different from 'widget'. */ menu_item = gtk_get_event_widget ((GdkEvent*) event); - if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) || + if (!GTK_IS_MENU_ITEM (menu_item) || !GTK_IS_MENU (menu_item->parent)) return FALSE; @@ -2875,6 +2940,20 @@ gtk_menu_motion_notify (GtkWidget *widget, return FALSE; } +static gboolean +get_double_arrows (GtkMenu *menu) +{ + GtkMenuPrivate *priv = gtk_menu_get_private (menu); + gboolean double_arrows; + + gtk_widget_style_get (GTK_WIDGET (menu), + "double-arrows", &double_arrows, + NULL); + + return double_arrows || (priv->initially_pushed_in && + menu->scroll_offset != 0); +} + static void gtk_menu_scroll_by (GtkMenu *menu, gint step) @@ -2882,17 +2961,21 @@ gtk_menu_scroll_by (GtkMenu *menu, GtkWidget *widget; gint offset; gint view_width, view_height; + gboolean double_arrows; widget = GTK_WIDGET (menu); offset = menu->scroll_offset + step; + double_arrows = get_double_arrows (menu); + /* If we scroll upward and the non-visible top part * is smaller than the scroll arrow it would be * pretty stupid to show the arrow and taking more * screen space than just scrolling to the top. */ - if ((step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT)) - offset = 0; + if (!double_arrows) + if ((step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT)) + offset = 0; /* Don't scroll over the top if we weren't before: */ if ((menu->scroll_offset >= 0) && (offset < 0)) @@ -2903,7 +2986,13 @@ gtk_menu_scroll_by (GtkMenu *menu, /* Don't scroll past the bottom if we weren't before: */ if (menu->scroll_offset > 0) view_height -= MENU_SCROLL_ARROW_HEIGHT; - + + /* When both arrows are always shown, reduce + * view height even more. + */ + if (double_arrows) + view_height -= MENU_SCROLL_ARROW_HEIGHT; + if ((menu->scroll_offset + view_height <= widget->requisition.height) && (offset + view_height > widget->requisition.height)) offset = widget->requisition.height - view_height; @@ -2912,15 +3001,48 @@ gtk_menu_scroll_by (GtkMenu *menu, gtk_menu_scroll_to (menu, offset); } +static void +gtk_menu_do_timeout_scroll (GtkMenu *menu, + gboolean touchscreen_mode) +{ + gboolean upper_visible; + gboolean lower_visible; + + upper_visible = menu->upper_arrow_visible; + lower_visible = menu->lower_arrow_visible; + + gtk_menu_scroll_by (menu, menu->scroll_step); + + if (touchscreen_mode && + (upper_visible != menu->upper_arrow_visible || + lower_visible != menu->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)->ignore_enter = TRUE; + gtk_menu_get_private (menu)->ignore_button_release = TRUE; + } +} + static gboolean gtk_menu_scroll_timeout (gpointer data) { - GtkMenu *menu; + GtkMenu *menu; + GtkSettings *settings; + gboolean touchscreen_mode; GDK_THREADS_ENTER (); menu = GTK_MENU (data); - gtk_menu_scroll_by (menu, menu->scroll_step); + + settings = gtk_widget_get_settings (GTK_WIDGET (menu)); + g_object_get (settings, + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); + + gtk_menu_do_timeout_scroll (menu, touchscreen_mode); GDK_THREADS_LEAVE (); @@ -2928,6 +3050,54 @@ gtk_menu_scroll_timeout (gpointer data) } static gboolean +gtk_menu_scroll_timeout_initial (gpointer data) +{ + GtkMenu *menu; + GtkSettings *settings; + guint timeout; + gboolean touchscreen_mode; + + GDK_THREADS_ENTER (); + + menu = GTK_MENU (data); + + settings = gtk_widget_get_settings (GTK_WIDGET (menu)); + g_object_get (settings, + "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->timeout_id = g_timeout_add (timeout, gtk_menu_scroll_timeout, menu); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static void +gtk_menu_start_scrolling (GtkMenu *menu) +{ + GtkSettings *settings; + guint timeout; + gboolean touchscreen_mode; + + settings = gtk_widget_get_settings (GTK_WIDGET (menu)); + g_object_get (settings, + "gtk-timeout-repeat", &timeout, + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); + + gtk_menu_do_timeout_scroll (menu, touchscreen_mode); + + menu->timeout_id = g_timeout_add (timeout, gtk_menu_scroll_timeout_initial, + menu); +} + +static gboolean gtk_menu_scroll (GtkWidget *widget, GdkEventScroll *event) { @@ -2950,11 +3120,13 @@ gtk_menu_scroll (GtkWidget *widget, static void gtk_menu_handle_scrolling (GtkMenu *menu, - gint x, - gint y, - gboolean enter) + gint x, + gint y, + gboolean enter, + gboolean motion) { GtkMenuShell *menu_shell; + GtkMenuPrivate *priv; gint width, height; gint border; GdkRectangle rect; @@ -2963,15 +3135,23 @@ gtk_menu_handle_scrolling (GtkMenu *menu, guint vertical_padding; gint top_x, top_y; gint win_x, win_y; + GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (menu)); + gboolean touchscreen_mode; + + priv = gtk_menu_get_private (menu); menu_shell = GTK_MENU_SHELL (menu); gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height); + g_object_get (G_OBJECT (settings), + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); + gtk_widget_style_get (GTK_WIDGET (menu), "vertical-padding", &vertical_padding, NULL); - + border = GTK_CONTAINER (menu)->border_width + GTK_WIDGET (menu)->style->ythickness + vertical_padding; @@ -2980,83 +3160,193 @@ gtk_menu_handle_scrolling (GtkMenu *menu, y -= top_y; gdk_window_get_position (GTK_WIDGET (menu)->window, &win_x, &win_y); - - if (menu->upper_arrow_visible && !menu->tearoff_active) + + /* upper arrow handling */ + + rect.x = win_x; + rect.y = win_y; + rect.width = width; + rect.height = MENU_SCROLL_ARROW_HEIGHT + border; + + in_arrow = FALSE; + if (menu->upper_arrow_visible && !menu->tearoff_active && + (x >= rect.x) && (x < rect.x + rect.width) && + (y >= rect.y) && (y < rect.y + rect.height)) { - rect.x = win_x; - rect.y = win_y; - rect.width = width; - rect.height = MENU_SCROLL_ARROW_HEIGHT + border; - - in_arrow = FALSE; - if ((x >= rect.x) && (x < rect.x + rect.width) && - (y >= rect.y) && (y < rect.y + rect.height)) - { - in_arrow = TRUE; - scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE); - } - - if (enter && in_arrow && - (!menu->upper_arrow_prelight || menu->scroll_fast != scroll_fast)) - { - menu->upper_arrow_prelight = TRUE; - menu->scroll_fast = scroll_fast; - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); - - /* Deselect the active item so that any submenus are poped down */ - gtk_menu_shell_deselect (menu_shell); + in_arrow = TRUE; + } - gtk_menu_remove_scroll_timeout (menu); - menu->scroll_step = (scroll_fast) ? -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1; - menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1, - gtk_menu_scroll_timeout, - menu); - } - else if (!enter && !in_arrow && menu->upper_arrow_prelight) - { - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); - - gtk_menu_stop_scrolling (menu); - } + if (touchscreen_mode) + menu->upper_arrow_prelight = in_arrow; + + if (priv->upper_arrow_state != GTK_STATE_INSENSITIVE) + { + if (menu->upper_arrow_visible && !menu->tearoff_active) + { + if (touchscreen_mode) + { + if (enter && menu->upper_arrow_prelight && + menu->timeout_id == 0) + { + /* Deselect the active item so that + * any submenus are popped down + */ + gtk_menu_shell_deselect (menu_shell); + + gtk_menu_remove_scroll_timeout (menu); + menu->scroll_step = -MENU_SCROLL_STEP2; /* always fast */ + + if (!motion) + { + /* Only do stuff on click. */ + gtk_menu_start_scrolling (menu); + priv->upper_arrow_state = GTK_STATE_ACTIVE; + } + + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + &rect, FALSE); + } + else if (!enter) + { + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + &rect, FALSE); + + gtk_menu_stop_scrolling (menu); + } + } + else /* !touchscreen_mode */ + { + scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE); + + if (enter && in_arrow && + (!menu->upper_arrow_prelight || + menu->scroll_fast != scroll_fast)) + { + menu->upper_arrow_prelight = TRUE; + menu->scroll_fast = scroll_fast; + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + &rect, FALSE); + + /* Deselect the active item so that + * any submenus are popped down + */ + gtk_menu_shell_deselect (menu_shell); + + gtk_menu_remove_scroll_timeout (menu); + menu->scroll_step = scroll_fast ? + -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1; + + menu->timeout_id = + g_timeout_add (scroll_fast ? + MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1, + gtk_menu_scroll_timeout, menu); + } + else if (!enter && !in_arrow && menu->upper_arrow_prelight) + { + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + &rect, FALSE); + + gtk_menu_stop_scrolling (menu); + } + } + } + + priv->upper_arrow_state = menu->upper_arrow_prelight ? + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; } - - if (menu->lower_arrow_visible && !menu->tearoff_active) + + /* lower arrow handling */ + + rect.x = win_x; + rect.y = win_y + height - border - MENU_SCROLL_ARROW_HEIGHT; + rect.width = width; + rect.height = MENU_SCROLL_ARROW_HEIGHT + border; + + in_arrow = FALSE; + if (menu->lower_arrow_visible && !menu->tearoff_active && + (x >= rect.x) && (x < rect.x + rect.width) && + (y >= rect.y) && (y < rect.y + rect.height)) { - rect.x = win_x; - rect.y = win_y + height - border - MENU_SCROLL_ARROW_HEIGHT; - rect.width = width; - rect.height = MENU_SCROLL_ARROW_HEIGHT + border; - - in_arrow = FALSE; - if ((x >= rect.x) && (x < rect.x + rect.width) && - (y >= rect.y) && (y < rect.y + rect.height)) - { - in_arrow = TRUE; - scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE); - } + in_arrow = TRUE; + } - if (enter && in_arrow && - (!menu->lower_arrow_prelight || menu->scroll_fast != scroll_fast)) - { - menu->lower_arrow_prelight = TRUE; - menu->scroll_fast = scroll_fast; - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); + if (touchscreen_mode) + menu->lower_arrow_prelight = in_arrow; - /* Deselect the active item so that any submenus are poped down */ - gtk_menu_shell_deselect (menu_shell); + if (priv->lower_arrow_state != GTK_STATE_INSENSITIVE) + { + if (menu->lower_arrow_visible && !menu->tearoff_active) + { + if (touchscreen_mode) + { + if (enter && menu->lower_arrow_prelight && + menu->timeout_id == 0) + { + /* Deselect the active item so that + * any submenus are popped down + */ + gtk_menu_shell_deselect (menu_shell); + + gtk_menu_remove_scroll_timeout (menu); + menu->scroll_step = MENU_SCROLL_STEP2; /* always fast */ + + if (!motion) + { + /* Only do stuff on click. */ + gtk_menu_start_scrolling (menu); + priv->lower_arrow_state = GTK_STATE_ACTIVE; + } + + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + &rect, FALSE); + } + else if (!enter) + { + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + &rect, FALSE); - gtk_menu_remove_scroll_timeout (menu); - menu->scroll_step = (scroll_fast) ? MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1; - menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1, - gtk_menu_scroll_timeout, - menu); - } - else if (!enter && !in_arrow && menu->lower_arrow_prelight) - { - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); - - gtk_menu_stop_scrolling (menu); - } + gtk_menu_stop_scrolling (menu); + } + } + else /* !touchscreen_mode */ + { + scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE); + + if (enter && in_arrow && + (!menu->lower_arrow_prelight || + menu->scroll_fast != scroll_fast)) + { + menu->lower_arrow_prelight = TRUE; + menu->scroll_fast = scroll_fast; + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + &rect, FALSE); + + /* Deselect the active item so that + * any submenus are popped down + */ + gtk_menu_shell_deselect (menu_shell); + + gtk_menu_remove_scroll_timeout (menu); + menu->scroll_step = scroll_fast ? + MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1; + + menu->timeout_id = + g_timeout_add (scroll_fast ? + MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1, + gtk_menu_scroll_timeout, menu); + } + else if (!enter && !in_arrow && menu->lower_arrow_prelight) + { + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + &rect, FALSE); + + gtk_menu_stop_scrolling (menu); + } + } + } + + priv->lower_arrow_state = menu->lower_arrow_prelight ? + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; } } @@ -3064,22 +3354,29 @@ static gboolean gtk_menu_enter_notify (GtkWidget *widget, GdkEventCrossing *event) { + GtkSettings *settings = gtk_widget_get_settings (widget); GtkWidget *menu_item; + gboolean touchscreen_mode; + + g_object_get (G_OBJECT (settings), + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); menu_item = gtk_get_event_widget ((GdkEvent*) event); - if (widget && GTK_IS_MENU (widget)) + if (GTK_IS_MENU (widget)) { GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget); if (!menu_shell->ignore_enter) - gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE); + gtk_menu_handle_scrolling (GTK_MENU (widget), + event->x_root, event->y_root, TRUE, TRUE); } - if (menu_item && GTK_IS_MENU_ITEM (menu_item)) + if (!touchscreen_mode && GTK_IS_MENU_ITEM (menu_item)) { GtkWidget *menu = menu_item->parent; - if (menu && GTK_IS_MENU (menu)) + if (GTK_IS_MENU (menu)) { GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (menu)); GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu); @@ -3115,8 +3412,9 @@ gtk_menu_enter_notify (GtkWidget *widget, * will not correspond to the event widget's parent. Check to see * if we are in the parent's navigation region. */ - if (menu_item && GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) && - gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent), event->x_root, event->y_root)) + if (GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) && + gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent), + event->x_root, event->y_root)) return TRUE; return GTK_WIDGET_CLASS (parent_class)->enter_notify_event (widget, event); @@ -3137,11 +3435,11 @@ 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); - + gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE); + event_widget = gtk_get_event_widget ((GdkEvent*) event); - if (!event_widget || !GTK_IS_MENU_ITEM (event_widget)) + if (!GTK_IS_MENU_ITEM (event_widget)) return TRUE; menu_item = GTK_MENU_ITEM (event_widget); @@ -3429,7 +3727,6 @@ gtk_menu_position (GtkMenu *menu) gint x, y; gint scroll_offset; gint menu_height; - gboolean push_in; GdkScreen *screen; GdkScreen *pointer_screen; GdkRectangle monitor; @@ -3463,11 +3760,12 @@ gtk_menu_position (GtkMenu *menu) private = gtk_menu_get_private (menu); private->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y); - push_in = FALSE; + private->initially_pushed_in = FALSE; if (menu->position_func) { - (* menu->position_func) (menu, &x, &y, &push_in, menu->position_func_data); + (* menu->position_func) (menu, &x, &y, &private->initially_pushed_in, + menu->position_func_data); if (private->monitor_num < 0) private->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y); @@ -3593,7 +3891,7 @@ gtk_menu_position (GtkMenu *menu) scroll_offset = 0; - if (push_in) + if (private->initially_pushed_in) { menu_height = GTK_WIDGET (menu)->requisition.height; @@ -3658,10 +3956,20 @@ gtk_menu_remove_scroll_timeout (GtkMenu *menu) static void gtk_menu_stop_scrolling (GtkMenu *menu) { + GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (menu)); + gboolean touchscreen_mode; + gtk_menu_remove_scroll_timeout (menu); - menu->upper_arrow_prelight = FALSE; - menu->lower_arrow_prelight = FALSE; + g_object_get (G_OBJECT (settings), + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); + + if (!touchscreen_mode) + { + menu->upper_arrow_prelight = FALSE; + menu->lower_arrow_prelight = FALSE; + } } static void @@ -3672,10 +3980,10 @@ gtk_menu_scroll_to (GtkMenu *menu, gint x, y; gint view_width, view_height; gint border_width; - gboolean last_visible; gint menu_height; guint vertical_padding; guint horizontal_padding; + gboolean double_arrows; widget = GTK_WIDGET (menu); @@ -3694,32 +4002,103 @@ gtk_menu_scroll_to (GtkMenu *menu, view_height = widget->allocation.height; gtk_widget_style_get (GTK_WIDGET (menu), - "vertical-padding", &vertical_padding, + "vertical-padding", &vertical_padding, "horizontal-padding", &horizontal_padding, - NULL); - + NULL); + + double_arrows = get_double_arrows (menu); + border_width = GTK_CONTAINER (menu)->border_width; view_width -= (border_width + widget->style->xthickness + horizontal_padding) * 2; view_height -= (border_width + widget->style->ythickness + vertical_padding) * 2; menu_height = widget->requisition.height - - (border_width + widget->style->ythickness + vertical_padding) * 2; + (border_width + widget->style->ythickness + vertical_padding) * 2; x = border_width + widget->style->xthickness + horizontal_padding; y = border_width + widget->style->ythickness + vertical_padding; - if (!menu->tearoff_active) + if (double_arrows && !menu->tearoff_active) { + if (view_height < menu_height || + (offset > 0 && menu->scroll_offset > 0) || + (offset < 0 && menu->scroll_offset < 0)) + { + GtkMenuPrivate *priv = gtk_menu_get_private (menu); + GtkStateType upper_arrow_previous_state = priv->upper_arrow_state; + GtkStateType lower_arrow_previous_state = priv->lower_arrow_state; + + if (!menu->upper_arrow_visible || !menu->lower_arrow_visible) + gtk_widget_queue_draw (GTK_WIDGET (menu)); + + view_height -= 2 * MENU_SCROLL_ARROW_HEIGHT; + y += MENU_SCROLL_ARROW_HEIGHT; + + menu->upper_arrow_visible = menu->lower_arrow_visible = TRUE; + + if (offset <= 0) + priv->upper_arrow_state = GTK_STATE_INSENSITIVE; + else + priv->upper_arrow_state = menu->upper_arrow_prelight ? + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; + + if (offset >= menu_height - view_height) + priv->lower_arrow_state = GTK_STATE_INSENSITIVE; + else + priv->lower_arrow_state = menu->lower_arrow_prelight ? + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; + + if ((priv->upper_arrow_state != upper_arrow_previous_state) || + (priv->lower_arrow_state != lower_arrow_previous_state)) + gtk_widget_queue_draw (GTK_WIDGET (menu)); + + if (upper_arrow_previous_state != GTK_STATE_INSENSITIVE && + priv->upper_arrow_state == GTK_STATE_INSENSITIVE) + { + /* At the upper border, possibly remove timeout */ + if (menu->scroll_step < 0) + { + gtk_menu_stop_scrolling (menu); + gtk_widget_queue_draw (GTK_WIDGET (menu)); + } + } + + if (lower_arrow_previous_state != GTK_STATE_INSENSITIVE && + priv->lower_arrow_state == GTK_STATE_INSENSITIVE) + { + /* At the lower border, possibly remove timeout */ + if (menu->scroll_step > 0) + { + gtk_menu_stop_scrolling (menu); + gtk_widget_queue_draw (GTK_WIDGET (menu)); + } + } + } + else if (menu->upper_arrow_visible || menu->lower_arrow_visible) + { + offset = 0; + + menu->upper_arrow_visible = menu->lower_arrow_visible = FALSE; + menu->upper_arrow_prelight = menu->lower_arrow_prelight = FALSE; + + gtk_menu_stop_scrolling (menu); + gtk_widget_queue_draw (GTK_WIDGET (menu)); + } + } + else if (!menu->tearoff_active) + { + gboolean last_visible; + last_visible = menu->upper_arrow_visible; menu->upper_arrow_visible = offset > 0; if (menu->upper_arrow_visible) view_height -= MENU_SCROLL_ARROW_HEIGHT; - if ( (last_visible != menu->upper_arrow_visible) && - !menu->upper_arrow_visible) + if ((last_visible != menu->upper_arrow_visible) && + !menu->upper_arrow_visible) { - menu->upper_arrow_prelight = FALSE; - + menu->upper_arrow_prelight = FALSE; + /* If we hid the upper arrow, possibly remove timeout */ if (menu->scroll_step < 0) { @@ -3734,11 +4113,11 @@ gtk_menu_scroll_to (GtkMenu *menu, if (menu->lower_arrow_visible) view_height -= MENU_SCROLL_ARROW_HEIGHT; - if ( (last_visible != menu->lower_arrow_visible) && + if ((last_visible != menu->lower_arrow_visible) && !menu->lower_arrow_visible) { - menu->lower_arrow_prelight = FALSE; - + menu->lower_arrow_prelight = FALSE; + /* If we hid the lower arrow, possibly remove timeout */ if (menu->scroll_step > 0) { @@ -3825,14 +4204,17 @@ gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell, &child_offset, &child_height, &last_child)) { guint vertical_padding; - + gboolean double_arrows; + y = menu->scroll_offset; gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height); gtk_widget_style_get (GTK_WIDGET (menu), "vertical-padding", &vertical_padding, - NULL); - + NULL); + + double_arrows = get_double_arrows (menu); + height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness + 2*vertical_padding; if (child_offset < y) @@ -3853,11 +4235,11 @@ gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell, if (child_offset + child_height > y + height - arrow_height) { arrow_height = 0; - if (!last_child && !menu->tearoff_active) + if ((!last_child && !menu->tearoff_active) || double_arrows) arrow_height += MENU_SCROLL_ARROW_HEIGHT; - + y = child_offset + child_height - height + arrow_height; - if ((y > 0) && !menu->tearoff_active) + if (((y > 0) && !menu->tearoff_active) || double_arrows) { /* Need upper arrow */ arrow_height += MENU_SCROLL_ARROW_HEIGHT; diff --git a/gtk/gtksettings.c b/gtk/gtksettings.c index 5c15391521..ea2f375ec0 100644 --- a/gtk/gtksettings.c +++ b/gtk/gtksettings.c @@ -89,6 +89,7 @@ enum { PROP_TIMEOUT_REPEAT, PROP_COLOR_SCHEME, PROP_ENABLE_ANIMATIONS, + PROP_TOUCHSCREEN_MODE, PROP_COLOR_HASH }; @@ -491,6 +492,24 @@ gtk_settings_class_init (GtkSettingsClass *class) g_assert (result == PROP_ENABLE_ANIMATIONS); + /** + * GtkSettings:gtk-touchscreen-mode: + * + * When TRUE, there are no motion notify events delivered on this screen, + * and widgets can't use the pointer hovering them for any essential + * functionality. + * + * Since: 2.10 + */ + 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), + NULL); + + g_assert (result == PROP_TOUCHSCREEN_MODE); /** * GtkSettings:color-hash: @@ -509,7 +528,6 @@ gtk_settings_class_init (GtkSettingsClass *class) GTK_PARAM_READABLE)); class_n_properties++; - } static void |