Scrolling menus take 3
- From: Alexander Larsson <alla lysator liu se>
- To: gtk-devel-list gnome org
- Subject: Scrolling menus take 3
- Date: Mon, 23 Oct 2000 18:48:42 +0200 (CEST)
Ok, here is my latest try at scrolling menus. According to previous
discussions this one implements a more Mac-like handling of menu size and
positioning for srolling menus. It also includes a patch by Jonathan that
gives a new system for allocating size of toggles in menu items.
About the behaviour: When a menu is popped up from a menubar and the space
above the menubar is larger than below the menu is (still) placed above
the menu bar. This is a bit contrary to the stuff i've read about
menus. You're supposed to always use the same motion to active some menu
item so that you leverage the muscle memory. On the other hand this makes
the menus scroll when they might not need to, so I left the old behaviour.
There is still a bug, and I really need some help fixing it. When you pop
up a menu from a GtkOptionMenu the first time after having changed the
option everything works, but each time after that (until selecting
another menu item) the GtkMenuItem placed over the GtkOptionMenu doesn't
get any enter_notify. This means the current menu item isn't selected
until you mouse out of the item and in again.
I've tried pretty hard to understand why this happens, but I just can't
figure out it, and I don't know how to continue debugging it.
Comments on the behaviour and/or the code?
/ Alex
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.1439
diff -u -p -r1.1439 ChangeLog
--- ChangeLog 2000/10/22 17:19:51 1.1439
+++ ChangeLog 2000/10/23 16:21:29
@@ -1,3 +1,39 @@
+2000-10-23 Alexander Larsson <alexl redhat com>
+
+ * gtk/gtkmenu.c: Add support for scrolling menus.
+
+ * gtk/gtkmenu.h: Add data needed for scrolling menus.
+
+ * gtk/gtkmenuitem.c (gtk_menu_item_popup_submenu): Pass the
+ current event time (if any) to gtk_menu_popup() so that single
+ click / button hold down mode can be detected by GtkMenu if
+ the menu appears over the menuitem.
+
+ * gtk/gtkmenuitem.c (gtk_menu_item_position_menu): Change menu
+ positioning behaviour to fit to scrolling menus.
+
+ * gtk/gtkmenushell.[ch]: Virtualize gtk_menu_shell_insert() and
+ gtk_menu_shell_select_item() since these need to be overridden in
+ GtkMenu.
+
+ * gtk/gtkoptionmenu.c (gtk_opttion_menu_position): Change menu
+ positioning behaviour to fit to scrolling menus.
+
+ Patch from Jonathan Blandford <jrb redhat com>
+
+ * gtk/gtkmenuitem.[ch] (gtk_menu_item_toggle_size_request): new
+ system to handle size requests. First, we ask what the size of
+ the toggle is. Then, when allocating the size, we allocate the
+ toggle_size first. This way we can have multiple menu-item
+ classes w/o needing a seperate class for each.
+
+ * gtk/gtkmenu.c (gtk_menu_size_request): Actually use the new system.
+ * gtk/gtkmenu.c (gtk_menu_size_allocate): Use the new system.
+
+ * gtk/gtkcheckmenuitem.c
+ (gtk_check_menu_item_toggle_size_request): New function to handle
+ the toggle size-request.
+
2000-10-22 Tor Lillqvist <tml iki fi>
* gdk/win32/gdkgc-win32.c
Index: gtk/gtkcheckmenuitem.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkcheckmenuitem.c,v
retrieving revision 1.22
diff -u -p -r1.22 gtkcheckmenuitem.c
--- gtk/gtkcheckmenuitem.c 2000/07/26 11:32:42 1.22
+++ gtk/gtkcheckmenuitem.c 2000/10/23 16:21:30
@@ -28,25 +28,27 @@
#include "gtkaccellabel.h"
#include "gtksignal.h"
+#define CHECK_TOGGLE_SIZE 12
-
enum {
TOGGLED,
LAST_SIGNAL
};
-static void gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass);
-static void gtk_check_menu_item_init (GtkCheckMenuItem *check_menu_item);
-static void gtk_check_menu_item_draw (GtkWidget *widget,
- GdkRectangle *area);
-static gint gtk_check_menu_item_expose (GtkWidget *widget,
- GdkEventExpose *event);
-static void gtk_check_menu_item_activate (GtkMenuItem *menu_item);
-static void gtk_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item,
- GdkRectangle *area);
-static void gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item,
- GdkRectangle *area);
+static void gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass);
+static void gtk_check_menu_item_init (GtkCheckMenuItem *check_menu_item);
+static void gtk_check_menu_item_draw (GtkWidget *widget,
+ GdkRectangle *area);
+static gint gtk_check_menu_item_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static void gtk_check_menu_item_activate (GtkMenuItem *menu_item);
+static void gtk_check_menu_item_toggle_size_request (GtkMenuItem *menu_item,
+ guint16 *requisition);
+static void gtk_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item,
+ GdkRectangle *area);
+static void gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item,
+ GdkRectangle *area);
static GtkMenuItemClass *parent_class = NULL;
@@ -95,8 +97,8 @@ gtk_check_menu_item_class_init (GtkCheck
widget_class->expose_event = gtk_check_menu_item_expose;
menu_item_class->activate = gtk_check_menu_item_activate;
- menu_item_class->toggle_size = 12;
menu_item_class->hide_on_activate = FALSE;
+ menu_item_class->toggle_size_request = gtk_check_menu_item_toggle_size_request;
klass->toggled = NULL;
klass->draw_indicator = gtk_real_check_menu_item_draw_indicator;
@@ -145,6 +147,16 @@ gtk_check_menu_item_set_active (GtkCheck
if (check_menu_item->active != is_active)
gtk_menu_item_activate (GTK_MENU_ITEM (check_menu_item));
+}
+
+static void
+gtk_check_menu_item_toggle_size_request (GtkMenuItem *menu_item,
+ guint16 *requisition)
+{
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (menu_item));
+
+ *requisition = CHECK_TOGGLE_SIZE;
}
void
Index: gtk/gtkmenu.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenu.c,v
retrieving revision 1.49
diff -u -p -r1.49 gtkmenu.c
--- gtk/gtkmenu.c 2000/09/08 01:53:46 1.49
+++ gtk/gtkmenu.c 2000/10/23 16:21:30
@@ -33,6 +33,8 @@
#include "gtkmenuitem.h"
#include "gtksignal.h"
#include "gtkwindow.h"
+#include "gtkhbox.h"
+#include "gtkvscrollbar.h"
#define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w)
@@ -41,6 +43,12 @@
#define SUBMENU_NAV_REGION_PADDING 2
#define SUBMENU_NAV_HYSTERESIS_TIMEOUT 333
+#define MENU_SCROLL_STEP 10
+#define MENU_SCROLL_ARROW_HEIGHT 16
+#define MENU_SCROLL_FAST_ZONE 4
+#define MENU_SCROLL_TIMEOUT1 150
+#define MENU_SCROLL_TIMEOUT2 50
+
typedef struct _GtkMenuAttachData GtkMenuAttachData;
struct _GtkMenuAttachData
@@ -50,27 +58,40 @@ struct _GtkMenuAttachData
};
-static void gtk_menu_class_init (GtkMenuClass *klass);
-static void gtk_menu_init (GtkMenu *menu);
-static void gtk_menu_destroy (GtkObject *object);
-static void gtk_menu_realize (GtkWidget *widget);
-static void gtk_menu_size_request (GtkWidget *widget,
- GtkRequisition *requisition);
-static void gtk_menu_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation);
-static void gtk_menu_paint (GtkWidget *widget);
-static void gtk_menu_draw (GtkWidget *widget,
- GdkRectangle *area);
-static gboolean gtk_menu_expose (GtkWidget *widget,
- GdkEventExpose *event);
-static gboolean gtk_menu_key_press (GtkWidget *widget,
- GdkEventKey *event);
-static gboolean gtk_menu_motion_notify (GtkWidget *widget,
- GdkEventMotion *event);
-static gboolean gtk_menu_enter_notify (GtkWidget *widget,
- GdkEventCrossing *event);
-static gboolean gtk_menu_leave_notify (GtkWidget *widget,
- GdkEventCrossing *event);
+static void gtk_menu_class_init (GtkMenuClass *klass);
+static void gtk_menu_init (GtkMenu *menu);
+static void gtk_menu_destroy (GtkObject *object);
+static void gtk_menu_realize (GtkWidget *widget);
+static void gtk_menu_unrealize (GtkWidget *widget);
+static void gtk_menu_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gtk_menu_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gtk_menu_paint (GtkWidget *widget);
+static void gtk_menu_draw (GtkWidget *widget,
+ GdkRectangle *area);
+static gboolean gtk_menu_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean gtk_menu_key_press (GtkWidget *widget,
+ GdkEventKey *event);
+static gboolean gtk_menu_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean gtk_menu_enter_notify (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gboolean gtk_menu_leave_notify (GtkWidget *widget,
+ GdkEventCrossing *event);
+static void gtk_menu_scroll_to (GtkMenu *menu,
+ gint offset);
+static gboolean gtk_menu_scroll_timeout (gpointer data);
+static void gtk_menu_select_item (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item);
+static void gtk_menu_real_insert (GtkMenuShell *menu_shell,
+ GtkWidget *child,
+ gint position);
+static void gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
+ GtkMenu *menu);
+static void gtk_menu_handle_scrolling (GtkMenu *menu,
+ gboolean enter);
static void gtk_menu_stop_navigating_submenu (GtkMenu *menu);
static gboolean gtk_menu_stop_navigating_submenu_cb (gpointer user_data);
@@ -137,6 +158,7 @@ gtk_menu_class_init (GtkMenuClass *class
object_class->destroy = gtk_menu_destroy;
widget_class->realize = gtk_menu_realize;
+ widget_class->unrealize = gtk_menu_unrealize;
widget_class->draw = gtk_menu_draw;
widget_class->size_request = gtk_menu_size_request;
widget_class->size_allocate = gtk_menu_size_allocate;
@@ -150,6 +172,8 @@ gtk_menu_class_init (GtkMenuClass *class
menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
menu_shell_class->deactivate = gtk_menu_deactivate;
+ menu_shell_class->select_item = gtk_menu_select_item;
+ menu_shell_class->insert = gtk_menu_real_insert;
binding_set = gtk_binding_set_by_class (class);
gtk_binding_entry_add_signal (binding_set,
@@ -188,8 +212,7 @@ gtk_menu_window_event (GtkWidget *window
{
case GDK_KEY_PRESS:
case GDK_KEY_RELEASE:
- gtk_widget_event (menu, event);
- handled = TRUE;
+ handled = gtk_widget_event (menu, event);
break;
default:
break;
@@ -209,6 +232,7 @@ gtk_menu_init (GtkMenu *menu)
menu->accel_group = NULL;
menu->position_func = NULL;
menu->position_func_data = NULL;
+ menu->toggle_size = 0;
menu->toplevel = gtk_widget_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_POPUP,
@@ -225,9 +249,24 @@ gtk_menu_init (GtkMenu *menu)
GTK_WIDGET_SET_FLAGS (menu, GTK_FLOATING);
menu->needs_destruction_ref_count = TRUE;
+ menu->view_window = NULL;
+ menu->bin_window = NULL;
+
+ menu->scroll_offset = 0;
+ menu->scroll_step = 0;
+ menu->timeout_id = 0;
+ menu->scroll_fast = FALSE;
+
menu->tearoff_window = NULL;
+ menu->tearoff_hbox = NULL;
menu->torn_off = FALSE;
+ menu->tearoff_active = FALSE;
+ menu->upper_arrow_visible = FALSE;
+ menu->lower_arrow_visible = FALSE;
+ menu->upper_arrow_prelight = FALSE;
+ menu->lower_arrow_prelight = FALSE;
+
MENU_NEEDS_RESIZE (menu) = TRUE;
}
@@ -241,6 +280,12 @@ gtk_menu_destroy (GtkObject *object)
menu = GTK_MENU (object);
+ if (menu->timeout_id)
+ {
+ g_source_remove (menu->timeout_id);
+ menu->timeout_id = 0;
+ }
+
data = gtk_object_get_data (object, attach_data_key);
if (data)
gtk_menu_detach (menu);
@@ -382,9 +427,25 @@ gtk_menu_insert (GtkMenu *menu,
}
static void
+gtk_menu_real_insert (GtkMenuShell *menu_shell,
+ GtkWidget *child,
+ gint position)
+{
+ g_return_if_fail (menu_shell != NULL);
+ g_return_if_fail (GTK_IS_MENU (menu_shell));
+ g_return_if_fail (child != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (child));
+
+ GTK_MENU_SHELL_CLASS (parent_class)->insert (menu_shell, child, position);
+
+ gtk_widget_set_parent_window (child, GTK_MENU (menu_shell)->bin_window);
+}
+
+static void
gtk_menu_tearoff_bg_copy (GtkMenu *menu)
{
GtkWidget *widget;
+ gint width, height;
widget = GTK_WIDGET (menu);
@@ -393,25 +454,30 @@ gtk_menu_tearoff_bg_copy (GtkMenu *menu)
GdkPixmap *pixmap;
GdkGC *gc;
GdkGCValues gc_values;
+
+ menu->tearoff_active = FALSE;
+ menu->saved_scroll_offset = menu->scroll_offset;
gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
gc = gdk_gc_new_with_values (widget->window,
&gc_values, GDK_GC_SUBWINDOW);
- pixmap = gdk_pixmap_new (widget->window,
- widget->requisition.width,
- widget->requisition.height,
+ gdk_window_get_size (menu->tearoff_window->window, &width, &height);
+
+ pixmap = gdk_pixmap_new (menu->tearoff_window->window,
+ width,
+ height,
-1);
gdk_draw_pixmap (pixmap, gc,
- widget->window,
+ menu->tearoff_window->window,
0, 0, 0, 0, -1, -1);
gdk_gc_unref (gc);
gtk_widget_set_usize (menu->tearoff_window,
- widget->requisition.width,
- widget->requisition.height);
-
+ width,
+ height);
+
gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
gdk_pixmap_unref (pixmap);
}
@@ -431,7 +497,7 @@ gtk_menu_popup (GtkMenu *menu,
GtkWidget *parent;
GdkEvent *current_event;
GtkMenuShell *menu_shell;
-
+
g_return_if_fail (menu != NULL);
g_return_if_fail (GTK_IS_MENU (menu));
@@ -479,7 +545,7 @@ gtk_menu_popup (GtkMenu *menu,
*/
gtk_widget_show (GTK_WIDGET (menu));
gtk_widget_show (menu->toplevel);
-
+
/* Find the last viewable ancestor, and make an X grab on it
*/
parent = GTK_WIDGET (menu);
@@ -526,7 +592,9 @@ gtk_menu_popup (GtkMenu *menu,
gdk_cursor_destroy (cursor);
}
-
+
+ gtk_menu_scroll_to (menu, menu->scroll_offset);
+
gtk_grab_add (GTK_WIDGET (menu));
}
@@ -534,7 +602,7 @@ void
gtk_menu_popdown (GtkMenu *menu)
{
GtkMenuShell *menu_shell;
-
+
g_return_if_fail (menu != NULL);
g_return_if_fail (GTK_IS_MENU (menu));
@@ -543,6 +611,12 @@ gtk_menu_popdown (GtkMenu *menu)
menu_shell->parent_menu_shell = NULL;
menu_shell->active = FALSE;
menu_shell->ignore_enter = FALSE;
+
+ if (menu->timeout_id)
+ {
+ g_source_remove (menu->timeout_id);
+ menu->timeout_id = 0;
+ }
gtk_menu_stop_navigating_submenu (menu);
@@ -562,9 +636,15 @@ gtk_menu_popdown (GtkMenu *menu)
if (menu->torn_off)
{
+ gint width, height;
+ gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
+ gtk_widget_set_usize (menu->tearoff_window,
+ -1,
+ height);
+
if (GTK_BIN (menu->toplevel)->child)
{
- gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
+ gtk_menu_reparent (menu, menu->tearoff_hbox, FALSE);
}
else
{
@@ -577,6 +657,12 @@ gtk_menu_popdown (GtkMenu *menu)
gdk_keyboard_ungrab (GDK_CURRENT_TIME);
}
}
+ menu->tearoff_active = TRUE;
+ menu->tearoff_adjustment->upper = GTK_WIDGET (menu)->requisition.height;
+ gtk_adjustment_changed (menu->tearoff_adjustment);
+
+ gtk_menu_scroll_to (menu, menu->saved_scroll_offset);
+
}
else
gtk_widget_hide (GTK_WIDGET (menu));
@@ -707,17 +793,29 @@ gtk_menu_reposition (GtkMenu *menu)
gtk_menu_position (menu);
}
+static void
+gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
+ GtkMenu *menu)
+{
+ g_return_if_fail (menu != NULL);
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ gtk_menu_scroll_to (menu, adjustment->value);
+}
void
gtk_menu_set_tearoff_state (GtkMenu *menu,
gboolean torn_off)
{
+ gint width, height;
+
g_return_if_fail (menu != NULL);
g_return_if_fail (GTK_IS_MENU (menu));
if (menu->torn_off != torn_off)
{
menu->torn_off = torn_off;
+ menu->tearoff_active = torn_off;
if (menu->torn_off)
{
@@ -762,13 +860,44 @@ gtk_menu_set_tearoff_state (GtkMenu *me
GDK_DECOR_MAXIMIZE);
gtk_window_set_policy (GTK_WINDOW (menu->tearoff_window),
FALSE, FALSE, TRUE);
+
+ menu->tearoff_hbox = gtk_hbox_new (FALSE, FALSE);
+ gtk_container_add (GTK_CONTAINER (menu->tearoff_window), menu->tearoff_hbox);
+
+ gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
+ menu->tearoff_adjustment =
+ GTK_ADJUSTMENT (gtk_adjustment_new (0,
+ 0,
+ GTK_WIDGET (menu)->requisition.height,
+ MENU_SCROLL_STEP,
+ height/2,
+ height));
+ gtk_signal_connect (GTK_OBJECT (menu->tearoff_adjustment), "value_changed",
+ gtk_menu_scrollbar_changed,
+ menu);
+ menu->tearoff_scrollbar = gtk_vscrollbar_new (menu->tearoff_adjustment);
+
+ gtk_box_pack_end (GTK_BOX (menu->tearoff_hbox),
+ menu->tearoff_scrollbar,
+ FALSE, FALSE, 0);
+
+ if (menu->tearoff_adjustment->upper > height)
+ gtk_widget_show (menu->tearoff_scrollbar);
+ gtk_widget_show (menu->tearoff_hbox);
}
- gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
+ gtk_menu_reparent (menu, menu->tearoff_hbox, FALSE);
gtk_menu_position (menu);
gtk_widget_show (GTK_WIDGET (menu));
gtk_widget_show (menu->tearoff_window);
+
+ menu->tearoff_adjustment->value = 0;
+ menu->tearoff_adjustment->upper = GTK_WIDGET (menu)->requisition.height;
+ gtk_adjustment_changed (menu->tearoff_adjustment);
+
+ gtk_menu_scroll_to (menu, 0);
+
}
else
{
@@ -812,9 +941,15 @@ gtk_menu_realize (GtkWidget *widget)
{
GdkWindowAttr attributes;
gint attributes_mask;
-
+ gint border_width;
+ GtkMenu *menu;
+ GtkWidget *child;
+ GList *children;
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_MENU (widget));
+
+ menu = GTK_MENU (widget);
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
@@ -826,19 +961,82 @@ gtk_menu_realize (GtkWidget *widget)
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
+
attributes.event_mask = gtk_widget_get_events (widget);
- attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK);
+ attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
+ GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
gdk_window_set_user_data (widget->window, widget);
+ border_width = GTK_CONTAINER (widget)->border_width;
+
+ attributes.x = border_width + widget->style->xthickness;
+ attributes.y = border_width + widget->style->ythickness;
+ attributes.width = MAX (1, (gint)widget->allocation.width - attributes.x * 2);
+ attributes.height = MAX (1, (gint)widget->allocation.height - attributes.y * 2);
+
+ if (menu->upper_arrow_visible)
+ {
+ attributes.y += MENU_SCROLL_ARROW_HEIGHT;
+ attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
+ }
+ if (menu->lower_arrow_visible)
+ attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
+
+ menu->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
+ gdk_window_set_user_data (menu->view_window, menu);
+
+ attributes.x = 0;
+ attributes.y = 0;
+ attributes.height = widget->requisition.height;
+
+ menu->bin_window = gdk_window_new (menu->view_window, &attributes, attributes_mask);
+ gdk_window_set_user_data (menu->bin_window, menu);
+
+ children = GTK_MENU_SHELL (menu)->children;
+ while (children)
+ {
+ child = children->data;
+ children = children->next;
+
+ gtk_widget_set_parent_window (child, menu->bin_window);
+ }
+
widget->style = gtk_style_attach (widget->style, widget->window);
+ gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
+ gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
- gtk_menu_paint(widget);
+
+ gtk_menu_paint (widget);
+
+ gdk_window_show (menu->bin_window);
+ gdk_window_show (menu->view_window);
}
static void
+gtk_menu_unrealize (GtkWidget *widget)
+{
+ GtkMenu *menu;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_MENU (widget));
+
+ menu = GTK_MENU (widget);
+
+ gdk_window_set_user_data (menu->view_window, NULL);
+ gdk_window_destroy (menu->view_window);
+ menu->view_window = NULL;
+
+ gdk_window_set_user_data (menu->bin_window, NULL);
+ gdk_window_destroy (menu->bin_window);
+ menu->bin_window = NULL;
+
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
gtk_menu_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
@@ -871,13 +1069,16 @@ gtk_menu_size_request (GtkWidget *w
if (GTK_WIDGET_VISIBLE (child))
{
+ guint16 toggle_size;
+
GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
gtk_widget_size_request (child, &child_requisition);
requisition->width = MAX (requisition->width, child_requisition.width);
requisition->height += child_requisition.height;
-
- max_toggle_size = MAX (max_toggle_size, MENU_ITEM_CLASS (child)->toggle_size);
+
+ gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
+ max_toggle_size = MAX (max_toggle_size, toggle_size);
max_accel_width = MAX (max_accel_width, GTK_MENU_ITEM (child)->accelerator_width);
}
}
@@ -888,14 +1089,7 @@ gtk_menu_size_request (GtkWidget *w
requisition->height += (GTK_CONTAINER (menu)->border_width +
widget->style->ythickness) * 2;
- children = menu_shell->children;
- while (children)
- {
- child = children->data;
- children = children->next;
-
- GTK_MENU_ITEM (child)->toggle_size = max_toggle_size;
- }
+ menu->toggle_size = max_toggle_size;
}
static void
@@ -907,28 +1101,51 @@ gtk_menu_size_allocate (GtkWidget *w
GtkWidget *child;
GtkAllocation child_allocation;
GList *children;
-
+ gint x, y;
+ gint width, height;
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_MENU (widget));
g_return_if_fail (allocation != NULL);
menu = GTK_MENU (widget);
menu_shell = GTK_MENU_SHELL (widget);
-
+
widget->allocation = *allocation;
- if (GTK_WIDGET_REALIZED (widget))
- gdk_window_move_resize (widget->window,
- allocation->x, allocation->y,
- allocation->width, allocation->height);
+ x = (GTK_CONTAINER (menu)->border_width + widget->style->xthickness);
+ y = (GTK_CONTAINER (menu)->border_width + widget->style->ythickness);
+
+ width = MAX (1, (gint)allocation->width - x * 2);
+ height = MAX (1, (gint)allocation->height - y * 2);
+
+ 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,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ gdk_window_move_resize (menu->view_window,
+ x,
+ y,
+ width,
+ height);
+ }
if (menu_shell->children)
{
- child_allocation.x = (GTK_CONTAINER (menu)->border_width +
- widget->style->xthickness);
- child_allocation.y = (GTK_CONTAINER (menu)->border_width +
- widget->style->ythickness);
- child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
+ child_allocation.x = 0;
+ child_allocation.y = 0;
+ child_allocation.width = width;
children = menu_shell->children;
while (children)
@@ -942,21 +1159,39 @@ gtk_menu_size_allocate (GtkWidget *w
gtk_widget_get_child_requisition (child, &child_requisition);
child_allocation.height = child_requisition.height;
-
+
+ gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
+ menu->toggle_size);
gtk_widget_size_allocate (child, &child_allocation);
gtk_widget_queue_draw (child);
child_allocation.y += child_allocation.height;
}
}
+
+ /* Resize the item window */
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ gdk_window_resize (menu->bin_window,
+ child_allocation.width,
+ child_allocation.y + allocation->height);
+ }
+
}
}
static void
gtk_menu_paint (GtkWidget *widget)
{
+ guint border_x;
+ guint border_y;
+ guint width, height;
+ GtkMenu *menu;
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_MENU (widget));
+
+ menu = GTK_MENU(widget);
if (GTK_WIDGET_DRAWABLE (widget))
{
@@ -966,7 +1201,66 @@ gtk_menu_paint (GtkWidget *widget)
GTK_SHADOW_OUT,
NULL, widget, "menu",
0, 0, -1, -1);
+
+ border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
+ border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness;
+ gdk_window_get_size (widget->window, &width, &height);
+
+ 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,
+ 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,
+ GTK_SHADOW_OUT,
+ NULL, widget, "menu",
+ GTK_ARROW_UP,
+ TRUE,
+ width / 2 - MENU_SCROLL_ARROW_HEIGHT / 2 + 1,
+ 2 * border_y + 1,
+ MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2,
+ MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2);
+ }
+
+ 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,
+ GTK_SHADOW_OUT,
+ NULL, widget, "menu",
+ border_x,
+ height - border_y - MENU_SCROLL_ARROW_HEIGHT + 1,
+ 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,
+ GTK_SHADOW_OUT,
+ NULL, widget, "menu",
+ GTK_ARROW_DOWN,
+ TRUE,
+ width / 2 - MENU_SCROLL_ARROW_HEIGHT / 2 + 1,
+ height - MENU_SCROLL_ARROW_HEIGHT + 1,
+ MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2,
+ MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2);
+ }
}
+
}
static void
@@ -1044,7 +1338,7 @@ gtk_menu_key_press (GtkWidget *widget,
{
GtkMenuShell *menu_shell;
gboolean delete = FALSE;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
@@ -1136,6 +1430,10 @@ gtk_menu_motion_notify (GtkWidget *w
gboolean need_enter;
+
+ if (GTK_IS_MENU (widget))
+ gtk_menu_handle_scrolling (GTK_MENU (widget), TRUE);
+
/* We received the event for one of two reasons:
*
* a) We are the active menu, and did gtk_grab_add()
@@ -1145,7 +1443,6 @@ gtk_menu_motion_notify (GtkWidget *w
* is the parent of the menu item, for a), we need to find that menu,
* which may be different from 'widget'.
*/
-
menu_item = gtk_get_event_widget ((GdkEvent*) event);
if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) || !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
!GTK_IS_MENU (menu_item->parent))
@@ -1199,11 +1496,164 @@ gtk_menu_motion_notify (GtkWidget *w
}
static gboolean
+gtk_menu_scroll_timeout (gpointer data)
+{
+ GtkMenu *menu;
+ GtkWidget *widget;
+ gint offset;
+ guint view_width, view_height;
+
+ menu = GTK_MENU (data);
+ widget = GTK_WIDGET (menu);
+
+ offset = menu->scroll_offset + menu->scroll_step;
+
+ if ((menu->scroll_offset >= 0) && (offset < 0))
+ offset = 0;
+
+ /* 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 ((menu->scroll_step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT))
+ offset = 0;
+
+ /* Move/resize the viewport according to arrows: */
+ gdk_window_get_size (widget->window, &view_width, &view_height);
+
+ if (offset > 0)
+ 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;
+
+ gtk_menu_scroll_to (menu, offset);
+
+ return TRUE;
+}
+
+static void
+gtk_menu_handle_scrolling (GtkMenu *menu, gboolean enter)
+{
+ GtkMenuShell *menu_shell;
+ gint width, height;
+ gint x, y;
+ guint border;
+ GdkRectangle rect;
+ gboolean in_arrow;
+ gboolean scroll_fast = FALSE;
+
+ menu_shell = GTK_MENU_SHELL (menu);
+
+ gdk_window_get_pointer (GTK_WIDGET (menu)->window, &x, &y, NULL);
+ gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
+
+ border = GTK_CONTAINER (menu)->border_width + GTK_WIDGET (menu)->style->ythickness;
+
+ if (menu->upper_arrow_visible && !menu->tearoff_active)
+ {
+ rect.x = 0;
+ rect.y = 0;
+ 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);
+
+ if (menu->timeout_id)
+ g_source_remove (menu->timeout_id);
+ menu->scroll_step = -MENU_SCROLL_STEP;
+ 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)
+ {
+ menu->upper_arrow_prelight = FALSE;
+ gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
+
+ if (menu->timeout_id)
+ {
+ g_source_remove (menu->timeout_id);
+ menu->timeout_id = 0;
+ menu->scroll_step = 0;
+ }
+ }
+ }
+
+ if (menu->lower_arrow_visible && !menu->tearoff_active)
+ {
+ rect.x = 0;
+ rect.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);
+ }
+
+ 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 poped down */
+ gtk_menu_shell_deselect (menu_shell);
+
+ if (menu->timeout_id)
+ g_source_remove (menu->timeout_id);
+ menu->scroll_step = MENU_SCROLL_STEP;
+ 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)
+ {
+ menu->lower_arrow_prelight = FALSE;
+ gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
+
+ if (menu->timeout_id)
+ {
+ g_source_remove (menu->timeout_id);
+ menu->timeout_id = 0;
+ menu->scroll_step = 0;
+ }
+ }
+ }
+}
+
+static gboolean
gtk_menu_enter_notify (GtkWidget *widget,
GdkEventCrossing *event)
{
GtkWidget *menu_item;
+ if (widget && GTK_IS_MENU(widget))
+ gtk_menu_handle_scrolling (GTK_MENU (widget), TRUE);
+
/* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
* will not correspond to the event widget's parent. Check to see
* if we are in the parent's navigation region.
@@ -1211,7 +1661,7 @@ gtk_menu_enter_notify (GtkWidget
menu_item = gtk_get_event_widget ((GdkEvent*) event);
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))
- return TRUE;
+ return TRUE;
return GTK_WIDGET_CLASS (parent_class)->enter_notify_event (widget, event);
}
@@ -1223,13 +1673,15 @@ gtk_menu_leave_notify (GtkWidget
GtkMenuShell *menu_shell;
GtkMenu *menu;
GtkMenuItem *menu_item;
- GtkWidget *event_widget;
+ GtkWidget *event_widget;
menu = GTK_MENU (widget);
menu_shell = GTK_MENU_SHELL (widget);
if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
return TRUE;
+
+ gtk_menu_handle_scrolling (menu, FALSE);
event_widget = gtk_get_event_widget ((GdkEvent*) event);
@@ -1416,12 +1868,18 @@ gtk_menu_position (GtkMenu *menu)
GtkWidget *widget;
GtkRequisition requisition;
gint x, y;
-
+ gint screen_width;
+ gint screen_height;
+ gint scroll_offset;
+
g_return_if_fail (menu != NULL);
g_return_if_fail (GTK_IS_MENU (menu));
widget = GTK_WIDGET (menu);
+ screen_width = gdk_screen_width ();
+ screen_height = gdk_screen_height ();
+
gdk_window_get_pointer (NULL, &x, &y, NULL);
/* We need the requisition to figure out the right place to
@@ -1430,30 +1888,232 @@ gtk_menu_position (GtkMenu *menu)
* the requisition won't have been recomputed yet.
*/
gtk_widget_size_request (widget, &requisition);
-
+
if (menu->position_func)
(* menu->position_func) (menu, &x, &y, menu->position_func_data);
else
{
- gint screen_width;
- gint screen_height;
-
- screen_width = gdk_screen_width ();
- screen_height = gdk_screen_height ();
-
x = CLAMP (x - 2, 0, MAX (0, screen_width - requisition.width));
y = CLAMP (y - 2, 0, MAX (0, screen_height - requisition.height));
}
+ scroll_offset = 0;
+
+ if (y + requisition.height > screen_height)
+ {
+ scroll_offset -= y + requisition.height - screen_height;
+ y = screen_height - requisition.height;
+ }
+
+ if (y < 0)
+ {
+ scroll_offset -= y;
+ y = 0;
+ }
+
+ if (scroll_offset > 0)
+ scroll_offset += MENU_SCROLL_ARROW_HEIGHT;
+
+ if (requisition.height > screen_height)
+ requisition.height = screen_height;
+
/* FIXME: The MAX() here is because gtk_widget_set_uposition
* is broken. Once we provide an alternate interface that
* allows negative values, then we can remove them.
*/
gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
- menu->toplevel : menu->tearoff_window,
- MAX (x, 0), MAX (y, 0));
+ menu->toplevel : menu->tearoff_window,
+ MAX (x, 0), y);
+ gtk_widget_set_usize (GTK_MENU_SHELL (menu)->active ?
+ menu->toplevel : menu->tearoff_hbox,
+ -1, requisition.height);
+
+ menu->scroll_offset = scroll_offset;
}
+static void
+gtk_menu_scroll_to (GtkMenu *menu,
+ gint offset)
+{
+ GtkWidget *widget;
+ gint x, y;
+ guint view_width, view_height;
+ gint border_width;
+ gboolean last_visible;
+ guint menu_height;
+
+ widget = GTK_WIDGET (menu);
+
+ /* Scroll the menu: */
+ gdk_window_move (menu->bin_window, 0, -offset);
+
+ /* Move/resize the viewport according to arrows: */
+ gdk_window_get_size (widget->window, &view_width, &view_height);
+
+ border_width = GTK_CONTAINER (menu)->border_width;
+ view_width -= (border_width + widget->style->xthickness) * 2;
+ view_height -= (border_width + widget->style->ythickness) * 2;
+ menu_height = widget->requisition.height - (border_width + widget->style->ythickness) * 2;
+
+ x = border_width + widget->style->xthickness;
+ y = border_width + widget->style->ythickness;
+
+ if (!menu->tearoff_active)
+ {
+ 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)
+ {
+ menu->upper_arrow_prelight = FALSE;
+
+ /* If we hid the upper arrow, possibly remove timeout */
+ if ((menu->scroll_step < 0) && menu->timeout_id)
+ {
+ g_source_remove (menu->timeout_id);
+ menu->timeout_id = 0;
+ menu->scroll_step = 0;
+ }
+ }
+
+ last_visible = menu->lower_arrow_visible;
+ menu->lower_arrow_visible = (view_height + offset < menu_height);
+
+ if (menu->lower_arrow_visible)
+ view_height -= MENU_SCROLL_ARROW_HEIGHT;
+
+ if ( (last_visible != menu->lower_arrow_visible) &&
+ !menu->lower_arrow_visible)
+ {
+ menu->lower_arrow_prelight = FALSE;
+
+ /* If we hid the lower arrow, possibly remove timeout */
+ if ((menu->scroll_step > 0) && menu->timeout_id)
+ {
+ g_source_remove (menu->timeout_id);
+ menu->timeout_id = 0;
+ menu->scroll_step = 0;
+ }
+ }
+
+ if (menu->upper_arrow_visible)
+ y += MENU_SCROLL_ARROW_HEIGHT;
+ }
+
+
+ gdk_window_move_resize (menu->view_window,
+ x,
+ y,
+ view_width,
+ view_height);
+
+ menu->scroll_offset = offset;
+}
+
+static void
+gtk_menu_select_item (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item)
+{
+ GtkMenu *menu;
+ GtkWidget *child;
+ GList *children;
+ GtkRequisition child_requisition;
+ gint child_offset, child_height;
+ gint width, height;
+ gint y;
+ gint arrow_height;
+
+ g_return_if_fail (menu_shell != NULL);
+ g_return_if_fail (GTK_IS_MENU (menu_shell));
+
+ menu = GTK_MENU (menu_shell);
+
+ /* We need to check if the selected item fully visible.
+ * If not we need to scroll the menu so that it becomes fully
+ * visible.
+ */
+
+ child = NULL;
+ child_offset = 0;
+ child_height = 0;
+ children = menu_shell->children;
+ while (children)
+ {
+ child = children->data;
+ children = children->next;
+
+ if (GTK_WIDGET_VISIBLE (child))
+ {
+ gtk_widget_size_request (child, &child_requisition);
+ child_offset += child_height;
+ child_height = child_requisition.height;
+ }
+
+ if (child == menu_item)
+ break;
+ }
+
+ if (child == menu_item)
+ {
+ y = menu->scroll_offset;
+ gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
+
+ height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness;
+
+ if (child_offset + child_height <= y)
+ {
+ /* Ignore the enter event we might get if the pointer is on the menu
+ */
+ menu_shell->ignore_enter = TRUE;
+ gtk_menu_scroll_to (menu, child_offset);
+ }
+ else
+ {
+ arrow_height = 0;
+ if (menu->upper_arrow_visible && !menu->tearoff_active)
+ arrow_height += MENU_SCROLL_ARROW_HEIGHT;
+ if (menu->lower_arrow_visible && !menu->tearoff_active)
+ arrow_height += MENU_SCROLL_ARROW_HEIGHT;
+ if ( child_offset >= y + height - arrow_height)
+ {
+ y = 0;
+ children = menu_shell->children;
+ while (children)
+ {
+ child = children->data;
+ children = children->next;
+
+ if (GTK_WIDGET_VISIBLE (child))
+ {
+ arrow_height = 0;
+ if (y > 0 && !menu->tearoff_active)
+ arrow_height += MENU_SCROLL_ARROW_HEIGHT;
+ if (children && !menu->tearoff_active)
+ arrow_height += MENU_SCROLL_ARROW_HEIGHT;
+ if (child_offset < y + height - arrow_height)
+ break;
+ gtk_widget_size_request (child, &child_requisition);
+ y += child_requisition.height;
+ }
+ }
+
+ /* Ignore the enter event we might get if the pointer is on the menu
+ */
+ menu_shell->ignore_enter = TRUE;
+ gtk_menu_scroll_to (menu, y);
+ }
+ }
+
+ }
+
+ GTK_MENU_SHELL_CLASS (parent_class)->select_item (menu_shell, menu_item);
+}
+
+
/* Reparent the menu, taking care of the refcounting
*/
static void
@@ -1477,7 +2137,6 @@ gtk_menu_reparent (GtkMenu *menu,
}
else
gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
- gtk_widget_set_usize (new_parent, -1, -1);
if (was_floating)
GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
@@ -1505,4 +2164,5 @@ gtk_menu_hide_all (GtkWidget *widget)
/* Hide children, but not self. */
gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
}
+
Index: gtk/gtkmenu.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenu.h,v
retrieving revision 1.22
diff -u -p -r1.22 gtkmenu.h
--- gtk/gtkmenu.h 2000/09/02 02:43:50 1.22
+++ gtk/gtkmenu.h 2000/10/23 16:21:30
@@ -37,7 +37,6 @@
extern "C" {
#endif /* __cplusplus */
-
#define GTK_TYPE_MENU (gtk_menu_get_type ())
#define GTK_MENU(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_MENU, GtkMenu))
#define GTK_MENU_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_MENU, GtkMenuClass))
@@ -62,18 +61,31 @@ struct _GtkMenu
GtkWidget *parent_menu_item;
GtkWidget *old_active_menu_item;
-
+
GtkAccelGroup *accel_group;
GtkMenuPositionFunc position_func;
gpointer position_func_data;
+ guint toggle_size;
/* Do _not_ touch these widgets directly. We hide the reference
* count from the toplevel to the menu, so it must be restored
* before operating on these widgets
*/
GtkWidget *toplevel;
+
GtkWidget *tearoff_window;
-
+ GtkWidget *tearoff_hbox;
+ GtkWidget *tearoff_scrollbar;
+ GtkAdjustment *tearoff_adjustment;
+
+ GdkWindow *view_window;
+ GdkWindow *bin_window;
+
+ gint scroll_offset;
+ gint saved_scroll_offset;
+ gint scroll_step;
+ guint timeout_id;
+
/* When a submenu of this menu is popped up, motion in this
* region is ignored
*/
@@ -82,6 +94,17 @@ struct _GtkMenu
guint needs_destruction_ref_count : 1;
guint torn_off : 1;
+ /* The tearoff is active when it is torn off and the not-torn-off
+ * menu is not popped up.
+ */
+ guint tearoff_active : 1;
+
+ guint scroll_fast : 1;
+
+ guint upper_arrow_visible : 1;
+ guint lower_arrow_visible : 1;
+ guint upper_arrow_prelight : 1;
+ guint lower_arrow_prelight : 1;
};
struct _GtkMenuClass
Index: gtk/gtkmenuitem.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenuitem.c,v
retrieving revision 1.41
diff -u -p -r1.41 gtkmenuitem.c
--- gtk/gtkmenuitem.c 2000/07/26 11:32:45 1.41
+++ gtk/gtkmenuitem.c 2000/10/23 16:21:30
@@ -42,6 +42,8 @@
enum {
ACTIVATE,
ACTIVATE_ITEM,
+ TOGGLE_SIZE_REQUEST,
+ TOGGLE_SIZE_ALLOCATE,
LAST_SIGNAL
};
@@ -59,9 +61,15 @@ static void gtk_menu_item_draw
GdkRectangle *area);
static gint gtk_menu_item_expose (GtkWidget *widget,
GdkEventExpose *event);
-static void gtk_real_menu_item_select (GtkItem *item);
-static void gtk_real_menu_item_deselect (GtkItem *item);
-static void gtk_real_menu_item_activate_item (GtkMenuItem *item);
+
+static void gtk_real_menu_item_select (GtkItem *item);
+static void gtk_real_menu_item_deselect (GtkItem *item);
+static void gtk_real_menu_item_activate_item (GtkMenuItem *item);
+static void gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
+ guint16 *requisition);
+static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
+ guint16 allocation);
+
static gint gtk_menu_item_select_timeout (gpointer data);
static void gtk_menu_item_popup_submenu (gpointer data);
static void gtk_menu_item_position_menu (GtkMenu *menu,
@@ -138,6 +146,24 @@ gtk_menu_item_class_init (GtkMenuItemCla
gtk_signal_default_marshaller,
GTK_TYPE_NONE, 0);
+ menu_item_signals[TOGGLE_SIZE_REQUEST] =
+ gtk_signal_new ("toggle_size_request",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_request),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_POINTER);
+
+ menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
+ gtk_signal_new ("toggle_size_allocate",
+ GTK_RUN_FIRST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_allocate),
+ gtk_marshal_NONE__UINT,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_INT);
+
gtk_object_class_add_signals (object_class, menu_item_signals, LAST_SIGNAL);
object_class->destroy = gtk_menu_item_destroy;
@@ -157,8 +183,9 @@ gtk_menu_item_class_init (GtkMenuItemCla
klass->activate = NULL;
klass->activate_item = gtk_real_menu_item_activate_item;
+ klass->toggle_size_request = gtk_real_menu_item_toggle_size_request;
+ klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
- klass->toggle_size = 0;
klass->hide_on_activate = TRUE;
}
@@ -312,7 +339,26 @@ gtk_menu_item_activate (GtkMenuItem *men
gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[ACTIVATE]);
}
+void
+gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
+ guint16 *requisition)
+{
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_REQUEST], requisition);
+}
+void
+gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
+ guint16 allocation)
+{
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_ALLOCATE], allocation);
+}
+
static void
gtk_menu_item_accel_width_foreach (GtkWidget *widget,
gpointer data)
@@ -631,7 +677,26 @@ gtk_real_menu_item_activate_item (GtkMen
}
}
}
+static void
+gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
+ guint16 *requisition)
+{
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+ *requisition = 0;
+}
+
+static void
+gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
+ guint16 allocation)
+{
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ menu_item->toggle_size = allocation;
+}
+
static gint
gtk_menu_item_select_timeout (gpointer data)
{
@@ -654,13 +719,18 @@ gtk_menu_item_popup_submenu (gpointer da
if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu))
{
+ guint32 etime;
+ GdkEvent *event = gtk_get_current_event ();
+
+ etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
+
gtk_menu_popup (GTK_MENU (menu_item->submenu),
GTK_WIDGET (menu_item)->parent,
GTK_WIDGET (menu_item),
gtk_menu_item_position_menu,
menu_item,
GTK_MENU_SHELL (GTK_WIDGET (menu_item)->parent)->button,
- 0);
+ etime);
}
}
@@ -702,9 +772,10 @@ gtk_menu_item_position_menu (GtkMenu *m
ty += GTK_WIDGET (menu_item)->allocation.height;
else if ((ty - theight) >= 0)
ty -= theight;
- else
+ else if (screen_height - (ty + GTK_WIDGET (menu_item)->allocation.height) > ty)
ty += GTK_WIDGET (menu_item)->allocation.height;
-
+ else
+ ty -= theight;
break;
case GTK_LEFT_RIGHT:
@@ -738,14 +809,16 @@ gtk_menu_item_position_menu (GtkMenu *m
ty += GTK_WIDGET (menu_item)->allocation.height / 4;
+ /* If the height of the menu doesn't fit we move it upward. */
+ ty = CLAMP (ty, 0, MAX (0, screen_height - theight));
break;
}
- /* If we have negative, tx, ty here it is because we can't get
- * the menu all the way on screen. Favor the upper-left portion.
+ /* If we have negative, tx, here it is because we can't get
+ * the menu all the way on screen. Favor the left portion.
*/
*x = CLAMP (tx, 0, MAX (0, screen_width - twidth));
- *y = CLAMP (ty, 0, MAX (0, screen_height - theight));
+ *y = ty;
}
void
Index: gtk/gtkmenuitem.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenuitem.h,v
retrieving revision 1.12
diff -u -p -r1.12 gtkmenuitem.h
--- gtk/gtkmenuitem.h 2000/08/30 00:33:37 1.12
+++ gtk/gtkmenuitem.h 2000/10/23 16:21:30
@@ -70,7 +70,6 @@ struct _GtkMenuItemClass
{
GtkItemClass parent_class;
- guint toggle_size;
/* If the following flag is true, then we should always hide
* the menu when the MenuItem is activated. Otherwise, the
* it is up to the caller. For instance, when navigating
@@ -79,26 +78,35 @@ struct _GtkMenuItemClass
*/
guint hide_on_activate : 1;
- void (* activate) (GtkMenuItem *menu_item);
- void (* activate_item) (GtkMenuItem *menu_item);
+ void (* activate) (GtkMenuItem *menu_item);
+ void (* activate_item) (GtkMenuItem *menu_item);
+ void (* toggle_size_request) (GtkMenuItem *menu_item,
+ guint16 *requisition);
+ void (* toggle_size_allocate) (GtkMenuItem *menu_item,
+ guint16 allocation);
};
-GtkType gtk_menu_item_get_type (void) G_GNUC_CONST;
-GtkWidget* gtk_menu_item_new (void);
-GtkWidget* gtk_menu_item_new_with_label (const gchar *label);
-void gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
- GtkWidget *submenu);
-void gtk_menu_item_remove_submenu (GtkMenuItem *menu_item);
-void gtk_menu_item_set_placement (GtkMenuItem *menu_item,
- GtkSubmenuPlacement placement);
-void gtk_menu_item_configure (GtkMenuItem *menu_item,
- gint show_toggle_indicator,
- gint show_submenu_indicator);
-void gtk_menu_item_select (GtkMenuItem *menu_item);
-void gtk_menu_item_deselect (GtkMenuItem *menu_item);
-void gtk_menu_item_activate (GtkMenuItem *menu_item);
-void gtk_menu_item_right_justify (GtkMenuItem *menu_item);
+GtkType gtk_menu_item_get_type (void) G_GNUC_CONST;
+GtkWidget* gtk_menu_item_new (void);
+GtkWidget* gtk_menu_item_new_with_label (const gchar *label);
+void gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
+ GtkWidget *submenu);
+void gtk_menu_item_remove_submenu (GtkMenuItem *menu_item);
+void gtk_menu_item_set_placement (GtkMenuItem *menu_item,
+ GtkSubmenuPlacement placement);
+void gtk_menu_item_configure (GtkMenuItem *menu_item,
+ gint show_toggle_indicator,
+ gint show_submenu_indicator);
+void gtk_menu_item_select (GtkMenuItem *menu_item);
+void gtk_menu_item_deselect (GtkMenuItem *menu_item);
+void gtk_menu_item_activate (GtkMenuItem *menu_item);
+void gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
+ guint16 *requisition);
+void gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
+ guint16 allocation);
+void gtk_menu_item_right_justify (GtkMenuItem *menu_item);
+
#ifdef __cplusplus
Index: gtk/gtkmenushell.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.c,v
retrieving revision 1.33
diff -u -p -r1.33 gtkmenushell.c
--- gtk/gtkmenushell.c 2000/07/26 11:32:45 1.33
+++ gtk/gtkmenushell.c 2000/10/23 16:21:30
@@ -129,12 +129,17 @@ static void gtk_menu_shell_forall
gboolean include_internals,
GtkCallback callback,
gpointer callback_data);
+static void gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
+ GtkWidget *child,
+ gint position);
static void gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell);
static gint gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
GtkWidget *child);
static GtkWidget *gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
GdkEvent *event);
static GtkType gtk_menu_shell_child_type (GtkContainer *container);
+static void gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item);
static void gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell);
static void gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
@@ -246,6 +251,8 @@ gtk_menu_shell_class_init (GtkMenuShellC
klass->move_current = gtk_real_menu_shell_move_current;
klass->activate_current = gtk_real_menu_shell_activate_current;
klass->cancel = gtk_real_menu_shell_cancel;
+ klass->select_item = gtk_menu_shell_real_select_item;
+ klass->insert = gtk_menu_shell_real_insert;
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set,
@@ -303,6 +310,24 @@ gtk_menu_shell_insert (GtkMenuShell *men
GtkWidget *child,
gint position)
{
+ GtkMenuShellClass *class;
+
+ g_return_if_fail (menu_shell != NULL);
+ g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+ g_return_if_fail (child != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (child));
+
+ class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
+
+ if (class->insert)
+ class->insert (menu_shell, child, position);
+}
+
+static void
+gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
+ GtkWidget *child,
+ gint position)
+{
g_return_if_fail (menu_shell != NULL);
g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
g_return_if_fail (child != NULL);
@@ -573,7 +598,7 @@ gtk_menu_shell_enter_notify (GtkWidget
if (!menu_item || !GTK_WIDGET_IS_SENSITIVE (menu_item))
return TRUE;
-
+
if ((menu_item->parent == widget) &&
(menu_shell->active_menu_item != menu_item) &&
GTK_IS_MENU_ITEM (menu_item))
@@ -775,6 +800,24 @@ gtk_menu_shell_get_item (GtkMenuShell *m
void
gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
GtkWidget *menu_item)
+{
+ GtkMenuShellClass *class;
+
+ g_return_if_fail (menu_shell != NULL);
+ g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+ g_return_if_fail (menu_item != NULL);
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
+
+ if (class->select_item)
+ class->select_item (menu_shell, menu_item);
+}
+
+
+static void
+gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item)
{
g_return_if_fail (menu_shell != NULL);
g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
Index: gtk/gtkmenushell.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.h,v
retrieving revision 1.10
diff -u -p -r1.10 gtkmenushell.h
--- gtk/gtkmenushell.h 2000/08/30 00:33:37 1.10
+++ gtk/gtkmenushell.h 2000/10/23 16:21:30
@@ -81,6 +81,11 @@ struct _GtkMenuShellClass
void (*activate_current) (GtkMenuShell *menu_shell,
gboolean force_hide);
void (*cancel) (GtkMenuShell *menu_shell);
+ void (*select_item) (GtkMenuShell *menu_shell,
+ GtkWidget *menu_item);
+ void (*insert) (GtkMenuShell *menu_shell,
+ GtkWidget *child,
+ gint position);
};
Index: gtk/gtkoptionmenu.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkoptionmenu.c,v
retrieving revision 1.32
diff -u -p -r1.32 gtkoptionmenu.c
--- gtk/gtkoptionmenu.c 2000/07/26 11:32:45 1.32
+++ gtk/gtkoptionmenu.c 2000/10/23 16:21:30
@@ -657,13 +657,10 @@ gtk_option_menu_position (GtkMenu *menu
GtkWidget *child;
GtkRequisition requisition;
GList *children;
- gint shift_menu;
gint screen_width;
- gint screen_height;
gint menu_xpos;
gint menu_ypos;
- gint width;
- gint height;
+ gint menu_width;
g_return_if_fail (user_data != NULL);
g_return_if_fail (GTK_IS_OPTION_MENU (user_data));
@@ -671,11 +668,9 @@ gtk_option_menu_position (GtkMenu *menu
option_menu = GTK_OPTION_MENU (user_data);
gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
- width = requisition.width;
- height = requisition.height;
+ menu_width = requisition.width;
active = gtk_menu_get_active (GTK_MENU (option_menu->menu));
- children = GTK_MENU_SHELL (option_menu->menu)->children;
gdk_window_get_origin (GTK_WIDGET (option_menu)->window, &menu_xpos, &menu_ypos);
menu_ypos += GTK_WIDGET (option_menu)->allocation.height / 2 - 2;
@@ -686,6 +681,7 @@ gtk_option_menu_position (GtkMenu *menu
menu_ypos -= requisition.height / 2;
}
+ children = GTK_MENU_SHELL (option_menu->menu)->children;
while (children)
{
child = children->data;
@@ -703,32 +699,11 @@ gtk_option_menu_position (GtkMenu *menu
}
screen_width = gdk_screen_width ();
- screen_height = gdk_screen_height ();
-
- shift_menu = FALSE;
- if (menu_ypos < 0)
- {
- menu_ypos = 0;
- shift_menu = TRUE;
- }
- else if ((menu_ypos + height) > screen_height)
- {
- menu_ypos -= ((menu_ypos + height) - screen_height);
- shift_menu = TRUE;
- }
-
- if (shift_menu)
- {
- if ((menu_xpos + GTK_WIDGET (option_menu)->allocation.width + width) <= screen_width)
- menu_xpos += GTK_WIDGET (option_menu)->allocation.width;
- else
- menu_xpos -= width;
- }
-
+
if (menu_xpos < 0)
menu_xpos = 0;
- else if ((menu_xpos + width) > screen_width)
- menu_xpos -= ((menu_xpos + width) - screen_width);
+ else if ((menu_xpos + menu_width) > screen_width)
+ menu_xpos -= ((menu_xpos + menu_width) - screen_width);
*x = menu_xpos;
*y = menu_ypos;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]