diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 81907cfd06..c8485836b6 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -2750,8 +2750,116 @@ gtk_menu_position (GtkMenu *menu) (* menu->position_func) (menu, &x, &y, &push_in, menu->position_func_data); else { - x = CLAMP (x - 2, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width)); - y = CLAMP (y - 2, monitor.y, MAX (monitor.y, monitor.y + monitor.height - requisition.height)); + gint space_left, space_right, space_above, space_below; + gint needed_width; + gint needed_height; + gint xthickness = widget->style->xthickness; + gint ythickness = widget->style->ythickness; + gboolean rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + + /* The placement of popup menus horizontally works like this (with + * RTL in parentheses) + * + * - If there is enough room to the right (left) of the mouse cursor, + * position the menu there. + * + * - Otherwise, if if there is enough room to the left (right) of the + * mouse cursor, position the menu there. + * + * - Otherwise if the menu is smaller than the monitor, position it + * on the side of the mouse cursor that has the most space available + * + * - Otherwise (if there is simply not enough room for the menu on the + * monitor), position it as far left (right) as possible. + * + * Positioning in the vertical direction is similar: first try below + * mouse cursor, then above. + */ + space_left = x - monitor.x; + space_right = monitor.x + monitor.width - x - 1; + space_above = y - monitor.y; + space_below = monitor.y + monitor.height - y - 1; + + /* position horizontally */ + + /* the amount of space we need to position the menu. Note the + * menu is offset "xthickness" pixels + */ + needed_width = requisition.width - xthickness; + + if (needed_width <= space_left || + needed_width <= space_right) + { + if ((rtl && needed_width <= space_left) || + (!rtl && needed_width > space_right)) + { + /* position left */ + x = x + xthickness - requisition.width + 1; + } + else + { + /* position right */ + x = x - xthickness; + } + + /* x is clamped on-screen further down */ + } + else if (requisition.width <= monitor.width) + { + /* the menu is too big to fit on either side of the mouse + * cursor, but smaller than the monitor. Position it on + * the side that has the most space + */ + if (space_left > space_right) + { + /* left justify */ + x = monitor.x; + } + else + { + /* right justify */ + x = monitor.x + monitor.width - requisition.width; + } + } + else /* menu is simply too big for the monitor */ + { + if (rtl) + { + /* right justify */ + x = monitor.x + monitor.width - requisition.width; + } + else + { + /* left justify */ + x = monitor.x; + } + } + + /* Position vertically. The algorithm is the same as above, but + * simpler because we don't have to take RTL into account. + */ + needed_height = requisition.height - ythickness; + if (requisition.height > monitor.height) + { + y = monitor.y; + } + else if (needed_height > space_below && needed_height > space_above) + { + if (space_below >= space_above) + y = monitor.y + monitor.height - requisition.height; + else + y = monitor.y; + } + else + { + if (needed_height <= space_below) + y = y - ythickness; + else + y = y + ythickness - requisition.height + 1; + + y = CLAMP (y, monitor.y, + monitor.y + monitor.height - requisition.height); + } } scroll_offset = 0; diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c index eca9b46629..2fa7c5605e 100644 --- a/gtk/gtkmenuitem.c +++ b/gtk/gtkmenuitem.c @@ -37,6 +37,8 @@ #define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass) +#define MIN_SUBMENU_ITEM_WIDTH 100 + enum { ACTIVATE, ACTIVATE_ITEM, @@ -508,6 +510,8 @@ gtk_menu_item_size_request (GtkWidget *widget, requisition->width += child_requisition.height; requisition->width += arrow_spacing; + + requisition->width = MAX (requisition->width, MIN_SUBMENU_ITEM_WIDTH); } } else