[gtk+] Implemented height-for-width geometry management for menus
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] Implemented height-for-width geometry management for menus
- Date: Thu, 19 Aug 2010 00:00:51 +0000 (UTC)
commit 52e5f36dc30fd61d65b2a40622bab44e74884e31
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date: Wed Aug 18 19:42:02 2010 -0400
Implemented height-for-width geometry management for menus
Now GtkMenu/GtkMenuItem request/allocate in height-for-width
manner... to reduce the height of the menu one must explicitly
set the requested minimum width of the menu to a greater value
(using gtk_widget_set_size_request()).
gtk/gtkmenu.c | 412 ++++++++++++++++++++++++++++------------
gtk/gtkmenuitem.c | 554 ++++++++++++++++++++++++++++++++++++++---------------
2 files changed, 689 insertions(+), 277 deletions(-)
---
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index 39c83f2..03aff55 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -41,6 +41,7 @@
#include "gtkhbox.h"
#include "gtkvscrollbar.h"
#include "gtksettings.h"
+#include "gtksizerequest.h"
#include "gtkprivate.h"
#include "gtkintl.h"
@@ -79,6 +80,7 @@ struct _GtkMenuPrivate
/* info used for the table */
guint *heights;
gint heights_length;
+ gint requested_height;
gint monitor_num;
@@ -86,6 +88,8 @@ struct _GtkMenuPrivate
gint n_rows;
gint n_columns;
+ guint accel_size;
+
gchar *title;
/* Arrow states */
@@ -169,8 +173,6 @@ static void gtk_menu_get_child_property(GtkContainer *container,
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,
@@ -258,6 +260,19 @@ static gboolean gtk_menu_real_can_activate_accel (GtkWidget *widget,
static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
gboolean group_changed);
+static void gtk_menu_size_request_init (GtkSizeRequestIface *iface);
+static void gtk_menu_get_width (GtkSizeRequest *widget,
+ gint *minimum_size,
+ gint *natural_size);
+static void gtk_menu_get_height (GtkSizeRequest *widget,
+ gint *minimum_size,
+ gint *natural_size);
+static void gtk_menu_get_height_for_width (GtkSizeRequest *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size);
+
+
static const gchar attach_data_key[] = "gtk-menu-attach-data";
static guint menu_signals[LAST_SIGNAL] = { 0 };
@@ -268,7 +283,9 @@ gtk_menu_get_private (GtkMenu *menu)
return G_TYPE_INSTANCE_GET_PRIVATE (menu, GTK_TYPE_MENU, GtkMenuPrivate);
}
-G_DEFINE_TYPE (GtkMenu, gtk_menu, GTK_TYPE_MENU_SHELL)
+G_DEFINE_TYPE_WITH_CODE (GtkMenu, gtk_menu, GTK_TYPE_MENU_SHELL,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_SIZE_REQUEST,
+ gtk_menu_size_request_init))
static void
menu_queue_resize (GtkMenu *menu)
@@ -459,7 +476,6 @@ gtk_menu_class_init (GtkMenuClass *class)
widget_class->realize = gtk_menu_realize;
widget_class->unrealize = gtk_menu_unrealize;
- widget_class->size_request = gtk_menu_size_request;
widget_class->size_allocate = gtk_menu_size_allocate;
widget_class->show = gtk_menu_show;
widget_class->expose_event = gtk_menu_expose;
@@ -1625,7 +1641,7 @@ gtk_menu_popup_for_device (GtkMenu *menu,
GtkRequisition tmp_request;
GtkAllocation tmp_allocation = { 0, };
- gtk_widget_size_request (menu->toplevel, &tmp_request);
+ gtk_size_request_get_size (GTK_SIZE_REQUEST (menu->toplevel), NULL, &tmp_request);
tmp_allocation.width = tmp_request.width;
tmp_allocation.height = tmp_request.height;
@@ -2042,10 +2058,13 @@ gtk_menu_set_tearoff_hints (GtkMenu *menu,
gint width)
{
GdkGeometry geometry_hints;
-
+ GtkMenuPrivate *priv;
+
if (!menu->tearoff_window)
return;
+ priv = gtk_menu_get_private (menu);
+
if (gtk_widget_get_visible (menu->tearoff_scrollbar))
{
gtk_widget_size_request (menu->tearoff_scrollbar, NULL);
@@ -2056,7 +2075,7 @@ gtk_menu_set_tearoff_hints (GtkMenu *menu,
geometry_hints.max_width = width;
geometry_hints.min_height = 0;
- geometry_hints.max_height = GTK_WIDGET (menu)->requisition.height;
+ geometry_hints.max_height = priv->requested_height;
gtk_window_set_geometry_hints (GTK_WINDOW (menu->tearoff_window),
NULL,
@@ -2122,10 +2141,13 @@ void
gtk_menu_set_tearoff_state (GtkMenu *menu,
gboolean torn_off)
{
- gint width, height;
+ gint width, height;
+ GtkMenuPrivate *priv;
g_return_if_fail (GTK_IS_MENU (menu));
+ priv = gtk_menu_get_private (menu);
+
if (menu->torn_off != torn_off)
{
menu->torn_off = torn_off;
@@ -2170,7 +2192,7 @@ gtk_menu_set_tearoff_state (GtkMenu *menu,
menu->tearoff_adjustment =
GTK_ADJUSTMENT (gtk_adjustment_new (0,
0,
- GTK_WIDGET (menu)->requisition.height,
+ priv->requested_height,
MENU_SCROLL_STEP2,
height/2,
height));
@@ -2371,6 +2393,7 @@ gtk_menu_realize (GtkWidget *widget)
gint attributes_mask;
gint border_width;
GtkMenu *menu;
+ GtkMenuPrivate *priv;
GtkWidget *child;
GList *children;
guint vertical_padding;
@@ -2380,7 +2403,8 @@ gtk_menu_realize (GtkWidget *widget)
g_return_if_fail (GTK_IS_MENU (widget));
menu = GTK_MENU (widget);
-
+ priv = gtk_menu_get_private (menu);
+
gtk_widget_set_realized (widget, TRUE);
attributes.window_type = GDK_WINDOW_CHILD;
@@ -2424,7 +2448,7 @@ gtk_menu_realize (GtkWidget *widget)
attributes.x = 0;
attributes.y = 0;
attributes.width = MAX (1, widget->allocation.width - (border_width + widget->style->xthickness + horizontal_padding) * 2);
- attributes.height = MAX (1, widget->requisition.height - (border_width + widget->style->ythickness + vertical_padding) * 2);
+ attributes.height = MAX (1, priv->requested_height - (border_width + widget->style->ythickness + vertical_padding) * 2);
menu->bin_window = gdk_window_new (menu->view_window, &attributes, attributes_mask);
gdk_window_set_user_data (menu->bin_window, menu);
@@ -2525,118 +2549,82 @@ gtk_menu_unrealize (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_menu_parent_class)->unrealize (widget);
}
-static void
-gtk_menu_size_request (GtkWidget *widget,
- GtkRequisition *requisition)
+
+static gint
+calculate_line_heights (GtkMenu *menu,
+ gint for_width,
+ guint **ret_min_heights,
+ guint **ret_nat_heights)
{
- gint i;
- GtkMenu *menu;
- GtkMenuShell *menu_shell;
- GtkWidget *child;
- GList *children;
- guint max_toggle_size;
- guint max_accel_width;
- guint vertical_padding;
- guint horizontal_padding;
- guint border_width;
- GtkRequisition child_requisition;
+ GtkMenuShell *menu_shell;
GtkMenuPrivate *priv;
+ GtkWidget *child, *widget;
+ GList *children;
+ guint horizontal_padding;
+ guint border_width;
+ guint n_columns;
+ gint n_heights;
+ guint *min_heights;
+ guint *nat_heights;
+ gint avail_width;
+
+ widget = GTK_WIDGET (menu);
+ menu_shell = GTK_MENU_SHELL (widget);
+ priv = gtk_menu_get_private (menu);
- g_return_if_fail (GTK_IS_MENU (widget));
- g_return_if_fail (requisition != NULL);
-
- menu = GTK_MENU (widget);
- menu_shell = GTK_MENU_SHELL (widget);
- priv = gtk_menu_get_private (menu);
-
- requisition->width = 0;
- requisition->height = 0;
-
- max_toggle_size = 0;
- max_accel_width = 0;
-
- g_free (priv->heights);
- priv->heights = g_new0 (guint, gtk_menu_get_n_rows (menu));
- priv->heights_length = gtk_menu_get_n_rows (menu);
+ min_heights = g_new0 (guint, gtk_menu_get_n_rows (menu));
+ nat_heights = g_new0 (guint, gtk_menu_get_n_rows (menu));
+ n_heights = gtk_menu_get_n_rows (menu);
+ n_columns = gtk_menu_get_n_columns (menu);
+ avail_width = for_width - (2 * menu->toggle_size + priv->accel_size) * n_columns;
- children = menu_shell->children;
- while (children)
+ gtk_widget_style_get (GTK_WIDGET (menu),
+ "horizontal-padding", &horizontal_padding,
+ NULL);
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
+ avail_width -= (border_width + horizontal_padding + widget->style->xthickness) * 2;
+
+ for (children = menu_shell->children; children; children = children->next)
{
gint part;
gint toggle_size;
gint l, r, t, b;
+ gint child_min, child_nat;
child = children->data;
- children = children->next;
if (! gtk_widget_get_visible (child))
continue;
get_effective_child_attach (child, &l, &r, &t, &b);
- /* It's important to size_request the child
- * before doing the toggle size request, in
- * case the toggle size request depends on the size
- * request of a child of the child (e.g. for ImageMenuItem)
- */
-
- GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
- gtk_widget_size_request (child, &child_requisition);
+ part = avail_width / (r - l);
- 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);
+ gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child), part,
+ &child_min, &child_nat);
- part = child_requisition.width / (r - l);
- requisition->width = MAX (requisition->width, part);
-
- part = MAX (child_requisition.height, toggle_size) / (b - t);
- priv->heights[t] = MAX (priv->heights[t], part);
- }
-
- /* If the menu doesn't include any images or check items
- * reserve the space so that all menus are consistent.
- * We only do this for 'ordinary' menus, not for combobox
- * menus or multi-column menus
- */
- if (max_toggle_size == 0 &&
- gtk_menu_get_n_columns (menu) == 1 &&
- !priv->no_toggle_size)
- {
- guint toggle_spacing;
- guint indicator_size;
+ gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
+
+ part = MAX (child_min, toggle_size) / (b - t);
+ min_heights[t] = MAX (min_heights[t], part);
- gtk_style_get (widget->style,
- GTK_TYPE_CHECK_MENU_ITEM,
- "toggle-spacing", &toggle_spacing,
- "indicator-size", &indicator_size,
- NULL);
+ part = MAX (child_nat, toggle_size) / (b - t);
+ nat_heights[t] = MAX (nat_heights[t], part);
- max_toggle_size = indicator_size + toggle_spacing;
}
- for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
- requisition->height += priv->heights[i];
-
- requisition->width += 2 * max_toggle_size + max_accel_width;
- requisition->width *= gtk_menu_get_n_columns (menu);
-
- gtk_widget_style_get (GTK_WIDGET (menu),
- "vertical-padding", &vertical_padding,
- "horizontal-padding", &horizontal_padding,
- NULL);
+ if (ret_min_heights)
+ *ret_min_heights = min_heights;
+ else
+ g_free (min_heights);
- border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
- requisition->width += (border_width + horizontal_padding + widget->style->xthickness) * 2;
- requisition->height += (border_width + vertical_padding + widget->style->ythickness) * 2;
+ if (ret_nat_heights)
+ *ret_nat_heights = nat_heights;
+ else
+ g_free (nat_heights);
- menu->toggle_size = max_toggle_size;
-
- /* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap).
- */
- if (menu->tearoff_active)
- gtk_menu_set_tearoff_hints (menu, requisition->width);
+ return n_heights;
}
static void
@@ -2647,10 +2635,9 @@ gtk_menu_size_allocate (GtkWidget *widget,
GtkMenuShell *menu_shell;
GtkWidget *child;
GtkAllocation child_allocation;
- GtkRequisition child_requisition;
GtkMenuPrivate *priv;
GList *children;
- gint x, y;
+ gint x, y, i;
gint width, height;
guint border_width;
guint vertical_padding;
@@ -2664,23 +2651,31 @@ gtk_menu_size_allocate (GtkWidget *widget,
priv = gtk_menu_get_private (menu);
widget->allocation = *allocation;
- gtk_widget_get_child_requisition (GTK_WIDGET (menu), &child_requisition);
gtk_widget_style_get (GTK_WIDGET (menu),
"vertical-padding", &vertical_padding,
"horizontal-padding", &horizontal_padding,
NULL);
-
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
+
+ g_free (priv->heights);
+ priv->heights_length =
+ calculate_line_heights (menu,
+ allocation->width,
+ &priv->heights,
+ NULL);
+
+ /* refresh our cached height request */
+ priv->requested_height = (border_width + vertical_padding + GTK_WIDGET (widget)->style->ythickness) * 2;
+ for (i = 0; i < priv->heights_length; i++)
+ priv->requested_height += priv->heights[i];
+
x = border_width + widget->style->xthickness + horizontal_padding;
y = border_width + widget->style->ythickness + vertical_padding;
width = MAX (1, allocation->width - x * 2);
height = MAX (1, allocation->height - y * 2);
- child_requisition.width -= x * 2;
- child_requisition.height -= y * 2;
-
if (menu_shell->active)
gtk_menu_scroll_to (menu, menu->scroll_offset);
@@ -2769,7 +2764,7 @@ gtk_menu_size_allocate (GtkWidget *widget,
if (menu->tearoff_active)
{
- if (allocation->height >= widget->requisition.height)
+ if (allocation->height >= priv->requested_height)
{
if (gtk_widget_get_visible (menu->tearoff_scrollbar))
{
@@ -2781,7 +2776,7 @@ gtk_menu_size_allocate (GtkWidget *widget,
}
else
{
- menu->tearoff_adjustment->upper = widget->requisition.height;
+ menu->tearoff_adjustment->upper = priv->requested_height;
menu->tearoff_adjustment->page_size = allocation->height;
if (menu->tearoff_adjustment->value + menu->tearoff_adjustment->page_size >
@@ -3009,6 +3004,178 @@ gtk_menu_show (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_menu_parent_class)->show (widget);
}
+
+
+static void
+gtk_menu_size_request_init (GtkSizeRequestIface *iface)
+{
+ iface->get_width = gtk_menu_get_width;
+ iface->get_height = gtk_menu_get_height;
+ iface->get_height_for_width = gtk_menu_get_height_for_width;
+}
+
+static void
+gtk_menu_get_width (GtkSizeRequest *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkMenu *menu;
+ GtkMenuShell *menu_shell;
+ GtkMenuPrivate *priv;
+ GtkWidget *child;
+ GList *children;
+ guint max_toggle_size;
+ guint max_accel_width;
+ guint horizontal_padding;
+ guint border_width;
+ gint child_min, child_nat;
+ gint min_width, nat_width;
+
+ menu = GTK_MENU (widget);
+ menu_shell = GTK_MENU_SHELL (widget);
+ priv = gtk_menu_get_private (menu);
+
+ min_width = nat_width = 0;
+
+ max_toggle_size = 0;
+ max_accel_width = 0;
+
+ children = menu_shell->children;
+ while (children)
+ {
+ gint part;
+ gint toggle_size;
+ gint l, r, t, b;
+
+ child = children->data;
+ children = children->next;
+
+ if (! gtk_widget_get_visible (child))
+ continue;
+
+ get_effective_child_attach (child, &l, &r, &t, &b);
+
+ /* It's important to size_request the child
+ * before doing the toggle size request, in
+ * case the toggle size request depends on the size
+ * request of a child of the child (e.g. for ImageMenuItem)
+ */
+
+ GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
+ gtk_size_request_get_width (GTK_SIZE_REQUEST (child), &child_min, &child_nat);
+
+ 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);
+
+ part = child_min / (r - l);
+ min_width = MAX (min_width, part);
+
+ part = child_nat / (r - l);
+ nat_width = MAX (nat_width, part);
+ }
+
+ /* If the menu doesn't include any images or check items
+ * reserve the space so that all menus are consistent.
+ * We only do this for 'ordinary' menus, not for combobox
+ * menus or multi-column menus
+ */
+ if (max_toggle_size == 0 &&
+ gtk_menu_get_n_columns (menu) == 1 &&
+ !priv->no_toggle_size)
+ {
+ guint toggle_spacing;
+ guint indicator_size;
+
+ gtk_style_get (GTK_WIDGET (widget)->style,
+ GTK_TYPE_CHECK_MENU_ITEM,
+ "toggle-spacing", &toggle_spacing,
+ "indicator-size", &indicator_size,
+ NULL);
+
+ max_toggle_size = indicator_size + toggle_spacing;
+ }
+
+ min_width += 2 * max_toggle_size + max_accel_width;
+ min_width *= gtk_menu_get_n_columns (menu);
+
+ nat_width += 2 * max_toggle_size + max_accel_width;
+ nat_width *= gtk_menu_get_n_columns (menu);
+
+ gtk_widget_style_get (GTK_WIDGET (menu),
+ "horizontal-padding", &horizontal_padding,
+ NULL);
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
+ min_width += (border_width + horizontal_padding + GTK_WIDGET (widget)->style->xthickness) * 2;
+ nat_width += (border_width + horizontal_padding + GTK_WIDGET (widget)->style->xthickness) * 2;
+
+ menu->toggle_size = max_toggle_size;
+ priv->accel_size = max_accel_width;
+
+ if (minimum_size)
+ *minimum_size = min_width;
+
+ if (natural_size)
+ *natural_size = nat_width;
+
+ /* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap).
+ */
+ if (menu->tearoff_active)
+ gtk_menu_set_tearoff_hints (menu, min_width);
+}
+
+static void
+gtk_menu_get_height (GtkSizeRequest *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ gint min_width;
+
+ /* Menus are height-for-width only, just return the height for the minimum width */
+ gtk_size_request_get_width (widget, &min_width, NULL);
+ gtk_size_request_get_height_for_width (widget, min_width, minimum_size, natural_size);
+}
+
+static void
+gtk_menu_get_height_for_width (GtkSizeRequest *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkMenu *menu = GTK_MENU (widget);
+ guint *min_heights, *nat_heights;
+ guint vertical_padding, border_width;
+ gint n_heights, i;
+ gint min_height, nat_height;
+
+ gtk_widget_style_get (GTK_WIDGET (menu), "vertical-padding", &vertical_padding, NULL);
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
+
+ min_height = nat_height = (border_width + vertical_padding + GTK_WIDGET (widget)->style->ythickness) * 2;
+
+ n_heights =
+ calculate_line_heights (menu, for_size, &min_heights, &nat_heights);
+
+ for (i = 0; i < n_heights; i++)
+ {
+ min_height += min_heights[i];
+ nat_height += nat_heights[i];
+ }
+
+ if (minimum_size)
+ *minimum_size = min_height;
+
+ if (natural_size)
+ *natural_size = nat_height;
+
+ g_free (min_heights);
+ g_free (nat_heights);
+}
+
+
+
static gboolean
gtk_menu_button_scroll (GtkMenu *menu,
GdkEventButton *event)
@@ -3471,9 +3638,11 @@ gtk_menu_scroll_by (GtkMenu *menu,
gint view_width, view_height;
gboolean double_arrows;
GtkBorder arrow_border;
+ GtkMenuPrivate *priv;
widget = GTK_WIDGET (menu);
offset = menu->scroll_offset + step;
+ priv = gtk_menu_get_private (menu);
get_arrows_border (menu, &arrow_border);
@@ -3495,7 +3664,7 @@ gtk_menu_scroll_by (GtkMenu *menu,
gdk_drawable_get_size (widget->window, &view_width, &view_height);
if (menu->scroll_offset == 0 &&
- view_height >= widget->requisition.height)
+ view_height >= priv->requested_height)
return;
/* Don't scroll past the bottom if we weren't before: */
@@ -3508,9 +3677,9 @@ gtk_menu_scroll_by (GtkMenu *menu,
if (double_arrows)
view_height -= arrow_border.bottom;
- if ((menu->scroll_offset + view_height <= widget->requisition.height) &&
- (offset + view_height > widget->requisition.height))
- offset = widget->requisition.height - view_height;
+ if ((menu->scroll_offset + view_height <= priv->requested_height) &&
+ (offset + view_height > priv->requested_height))
+ offset = priv->requested_height - view_height;
if (offset != menu->scroll_offset)
gtk_menu_scroll_to (menu, offset);
@@ -4308,12 +4477,10 @@ gtk_menu_position (GtkMenu *menu)
gdk_display_get_device_state (gdk_screen_get_display (screen),
pointer, &pointer_screen, &x, &y, NULL);
- /* We need the requisition to figure out the right place to
- * popup the menu. In fact, we always need to ask here, since
- * if a size_request was queued while we weren't popped up,
- * the requisition won't have been recomputed yet.
+ /* Get the minimum height for minimum width to figure out
+ * the right place to popup the menu.
*/
- gtk_widget_size_request (widget, &requisition);
+ gtk_size_request_get_size (GTK_SIZE_REQUEST (widget), &requisition, NULL);
if (pointer_screen != screen)
{
@@ -4467,7 +4634,7 @@ gtk_menu_position (GtkMenu *menu)
if (private->initially_pushed_in)
{
- menu_height = GTK_WIDGET (menu)->requisition.height;
+ menu_height = requisition.height;
if (y + menu_height > monitor.y + monitor.height)
{
@@ -4563,8 +4730,10 @@ gtk_menu_scroll_to (GtkMenu *menu,
guint horizontal_padding;
gboolean double_arrows;
GtkBorder arrow_border;
+ GtkMenuPrivate *priv;
widget = GTK_WIDGET (menu);
+ priv = gtk_menu_get_private (menu);
if (menu->tearoff_active &&
menu->tearoff_adjustment &&
@@ -4590,7 +4759,7 @@ gtk_menu_scroll_to (GtkMenu *menu,
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
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 -
+ menu_height = priv->requested_height -
(border_width + widget->style->ythickness + vertical_padding) * 2;
x = border_width + widget->style->xthickness + horizontal_padding;
@@ -5244,8 +5413,11 @@ get_menu_height (GtkMenu *menu)
{
gint height;
GtkWidget *widget = GTK_WIDGET (menu);
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
- height = widget->requisition.height;
+ height = allocation.height;
height -= gtk_container_get_border_width (GTK_CONTAINER (widget) + widget->style->ythickness) * 2;
if (!menu->tearoff_active)
diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c
index ded538c..10c83bc 100644
--- a/gtk/gtkmenuitem.c
+++ b/gtk/gtkmenuitem.c
@@ -38,6 +38,7 @@
#include "gtkprivate.h"
#include "gtkbuildable.h"
#include "gtkactivatable.h"
+#include "gtksizerequest.h"
#include "gtkintl.h"
@@ -78,8 +79,6 @@ static void gtk_menu_item_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
static void gtk_menu_item_destroy (GtkObject *object);
-static void gtk_menu_item_size_request (GtkWidget *widget,
- GtkRequisition *requisition);
static void gtk_menu_item_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_menu_item_realize (GtkWidget *widget);
@@ -125,6 +124,17 @@ static void gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
const gchar *label);
static G_CONST_RETURN gchar * gtk_real_menu_item_get_label (GtkMenuItem *menu_item);
+static void gtk_menu_item_size_request_init (GtkSizeRequestIface *iface);
+static void gtk_menu_item_get_width (GtkSizeRequest *widget,
+ gint *minimum_size,
+ gint *natural_size);
+static void gtk_menu_item_get_height (GtkSizeRequest *widget,
+ gint *minimum_size,
+ gint *natural_size);
+static void gtk_menu_item_get_height_for_width (GtkSizeRequest *widget,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size);
static void gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface);
static void gtk_menu_item_buildable_add_child (GtkBuildable *buildable,
@@ -157,7 +167,9 @@ G_DEFINE_TYPE_WITH_CODE (GtkMenuItem, gtk_menu_item, GTK_TYPE_ITEM,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
gtk_menu_item_buildable_interface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
- gtk_menu_item_activatable_interface_init))
+ gtk_menu_item_activatable_interface_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_SIZE_REQUEST,
+ gtk_menu_item_size_request_init))
#define GET_PRIVATE(object) \
(G_TYPE_INSTANCE_GET_PRIVATE ((object), GTK_TYPE_MENU_ITEM, GtkMenuItemPrivate))
@@ -177,7 +189,6 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass)
object_class->destroy = gtk_menu_item_destroy;
- widget_class->size_request = gtk_menu_item_size_request;
widget_class->size_allocate = gtk_menu_item_size_allocate;
widget_class->expose_event = gtk_menu_item_expose;
widget_class->realize = gtk_menu_item_realize;
@@ -558,6 +569,384 @@ gtk_menu_item_detacher (GtkWidget *widget,
}
static void
+get_arrow_size (GtkWidget *widget,
+ GtkWidget *child,
+ gint *size)
+{
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ gfloat arrow_scaling;
+
+ g_assert (size);
+
+ gtk_widget_style_get (widget,
+ "arrow-scaling", &arrow_scaling,
+ NULL);
+
+ context = gtk_widget_get_pango_context (child);
+ metrics = pango_context_get_metrics (context,
+ child->style->font_desc,
+ pango_context_get_language (context));
+
+ *size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
+ pango_font_metrics_get_descent (metrics)));
+
+ pango_font_metrics_unref (metrics);
+
+ *size = *size * arrow_scaling;
+}
+
+
+static void
+gtk_menu_item_accel_width_foreach (GtkWidget *widget,
+ gpointer data)
+{
+ guint *width = data;
+
+ if (GTK_IS_ACCEL_LABEL (widget))
+ {
+ guint w;
+
+ w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
+ *width = MAX (*width, w);
+ }
+ else if (GTK_IS_CONTAINER (widget))
+ gtk_container_foreach (GTK_CONTAINER (widget),
+ gtk_menu_item_accel_width_foreach,
+ data);
+}
+
+static gint
+get_minimum_width (GtkWidget *widget)
+{
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ gint width;
+ gint width_chars;
+
+ context = gtk_widget_get_pango_context (widget);
+ metrics = pango_context_get_metrics (context,
+ widget->style->font_desc,
+ pango_context_get_language (context));
+
+ width = pango_font_metrics_get_approximate_char_width (metrics);
+
+ pango_font_metrics_unref (metrics);
+
+ gtk_widget_style_get (widget, "width-chars", &width_chars, NULL);
+
+ return PANGO_PIXELS (width_chars * width);
+}
+
+static void
+gtk_menu_item_size_request_init (GtkSizeRequestIface *iface)
+{
+ iface->get_width = gtk_menu_item_get_width;
+ iface->get_height = gtk_menu_item_get_height;
+ iface->get_height_for_width = gtk_menu_item_get_height_for_width;
+}
+
+static void
+gtk_menu_item_get_width (GtkSizeRequest *request,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkMenuItem *menu_item;
+ GtkBin *bin;
+ GtkWidget *child, *widget = GTK_WIDGET (request);
+ guint accel_width;
+ guint horizontal_padding;
+ guint border_width;
+ GtkPackDirection pack_dir;
+ GtkPackDirection child_pack_dir;
+ gint min_width, nat_width;
+
+ min_width = nat_width = 0;
+
+ gtk_widget_style_get (widget,
+ "horizontal-padding", &horizontal_padding,
+ NULL);
+
+ bin = GTK_BIN (widget);
+ menu_item = GTK_MENU_ITEM (widget);
+
+ if (GTK_IS_MENU_BAR (widget->parent))
+ {
+ pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
+ child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
+ }
+ else
+ {
+ pack_dir = GTK_PACK_DIRECTION_LTR;
+ child_pack_dir = GTK_PACK_DIRECTION_LTR;
+ }
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+ min_width = (border_width + widget->style->xthickness) * 2;
+
+ if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
+ (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
+ min_width += 2 * horizontal_padding;
+
+ nat_width = min_width;
+
+ child = gtk_bin_get_child (bin);
+
+ if (child != NULL && gtk_widget_get_visible (child))
+ {
+ gint child_min, child_nat;
+
+ gtk_size_request_get_width (GTK_SIZE_REQUEST (child), &child_min, &child_nat);
+
+ if (menu_item->submenu && menu_item->show_submenu_indicator)
+ {
+ guint arrow_spacing;
+ gint arrow_size;
+
+ gtk_widget_style_get (widget,
+ "arrow-spacing", &arrow_spacing,
+ NULL);
+
+ get_arrow_size (widget, child, &arrow_size);
+
+ min_width += arrow_size;
+ min_width += arrow_spacing;
+
+ min_width = MAX (min_width, get_minimum_width (widget));
+
+ nat_width = min_width;
+ }
+
+
+ min_width += child_min;
+ nat_width += child_nat;
+
+
+ }
+
+ accel_width = 0;
+ gtk_container_foreach (GTK_CONTAINER (menu_item),
+ gtk_menu_item_accel_width_foreach,
+ &accel_width);
+ menu_item->accelerator_width = accel_width;
+
+ if (minimum_size)
+ *minimum_size = min_width;
+
+ if (natural_size)
+ *natural_size = nat_width;
+}
+
+static void
+gtk_menu_item_get_height (GtkSizeRequest *request,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkMenuItem *menu_item;
+ GtkBin *bin;
+ GtkWidget *child, *widget = GTK_WIDGET (request);
+ guint accel_width;
+ guint horizontal_padding;
+ guint border_width;
+ GtkPackDirection pack_dir;
+ GtkPackDirection child_pack_dir;
+ gint min_height, nat_height;
+
+ min_height = nat_height = 0;
+
+ gtk_widget_style_get (widget,
+ "horizontal-padding", &horizontal_padding,
+ NULL);
+
+ bin = GTK_BIN (widget);
+ menu_item = GTK_MENU_ITEM (widget);
+
+ if (GTK_IS_MENU_BAR (widget->parent))
+ {
+ pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
+ child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
+ }
+ else
+ {
+ pack_dir = GTK_PACK_DIRECTION_LTR;
+ child_pack_dir = GTK_PACK_DIRECTION_LTR;
+ }
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+ min_height = (border_width + widget->style->ythickness) * 2;
+
+ if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
+ (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
+ min_height += 2 * horizontal_padding;
+
+ nat_height = min_height;
+
+ child = gtk_bin_get_child (bin);
+
+ if (child != NULL && gtk_widget_get_visible (child))
+ {
+ gint child_min, child_nat;
+
+ gtk_size_request_get_height (GTK_SIZE_REQUEST (child), &child_min, &child_nat);
+
+ min_height += child_min;
+ nat_height += child_nat;
+
+ if (menu_item->submenu && menu_item->show_submenu_indicator)
+ {
+ gint arrow_size;
+
+ get_arrow_size (widget, child, &arrow_size);
+
+ min_height = MAX (min_height, arrow_size);
+ nat_height = MAX (nat_height, arrow_size);
+ }
+ }
+ else /* separator item */
+ {
+ gboolean wide_separators;
+ gint separator_height;
+
+ gtk_widget_style_get (widget,
+ "wide-separators", &wide_separators,
+ "separator-height", &separator_height,
+ NULL);
+
+ if (wide_separators)
+ min_height += separator_height + widget->style->ythickness;
+ else
+ min_height += widget->style->ythickness * 2;
+
+ nat_height = min_height;
+ }
+
+ accel_width = 0;
+ gtk_container_foreach (GTK_CONTAINER (menu_item),
+ gtk_menu_item_accel_width_foreach,
+ &accel_width);
+ menu_item->accelerator_width = accel_width;
+
+ if (minimum_size)
+ *minimum_size = min_height;
+
+ if (natural_size)
+ *natural_size = nat_height;
+}
+
+static void
+gtk_menu_item_get_height_for_width (GtkSizeRequest *request,
+ gint for_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkMenuItem *menu_item;
+ GtkBin *bin;
+ GtkWidget *child, *widget = GTK_WIDGET (request);
+ guint horizontal_padding;
+ guint border_width;
+ GtkPackDirection pack_dir;
+ GtkPackDirection child_pack_dir;
+ gint min_height, nat_height;
+ gint avail_size;
+
+ min_height = nat_height = 0;
+
+ gtk_widget_style_get (widget,
+ "horizontal-padding", &horizontal_padding,
+ NULL);
+
+ bin = GTK_BIN (widget);
+ menu_item = GTK_MENU_ITEM (widget);
+
+ if (GTK_IS_MENU_BAR (widget->parent))
+ {
+ pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
+ child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
+ }
+ else
+ {
+ pack_dir = GTK_PACK_DIRECTION_LTR;
+ child_pack_dir = GTK_PACK_DIRECTION_LTR;
+ }
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+ min_height = (border_width + widget->style->ythickness) * 2;
+
+ avail_size = for_size;
+ avail_size -= (border_width + widget->style->xthickness) * 2;
+
+ if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
+ (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
+ min_height += 2 * horizontal_padding;
+
+ if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
+ (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
+ avail_size -= 2 * horizontal_padding;
+
+ nat_height = min_height;
+
+ child = gtk_bin_get_child (bin);
+
+ if (child != NULL && gtk_widget_get_visible (child))
+ {
+ gint child_min, child_nat;
+ gint arrow_size = 0;
+
+ if (menu_item->submenu && menu_item->show_submenu_indicator)
+ {
+
+ guint arrow_spacing;
+
+ gtk_widget_style_get (widget,
+ "arrow-spacing", &arrow_spacing,
+ NULL);
+
+ get_arrow_size (widget, child, &arrow_size);
+
+ avail_size -= arrow_size;
+ avail_size -= arrow_spacing;
+ }
+
+ gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child), avail_size, &child_min, &child_nat);
+
+ min_height += child_min;
+ nat_height += child_nat;
+
+ if (menu_item->submenu && menu_item->show_submenu_indicator)
+ {
+ min_height = MAX (min_height, arrow_size);
+ nat_height = MAX (nat_height, arrow_size);
+ }
+ }
+ else /* separator item */
+ {
+ gboolean wide_separators;
+ gint separator_height;
+
+ gtk_widget_style_get (widget,
+ "wide-separators", &wide_separators,
+ "separator-height", &separator_height,
+ NULL);
+
+ if (wide_separators)
+ min_height += separator_height + widget->style->ythickness;
+ else
+ min_height += widget->style->ythickness * 2;
+
+ nat_height = min_height;
+ }
+
+ if (minimum_size)
+ *minimum_size = min_height;
+
+ if (natural_size)
+ *natural_size = nat_height;
+}
+
+
+
+static void
gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface)
{
parent_buildable_iface = g_type_interface_peek_parent (iface);
@@ -887,140 +1276,6 @@ gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
}
static void
-gtk_menu_item_accel_width_foreach (GtkWidget *widget,
- gpointer data)
-{
- guint *width = data;
-
- if (GTK_IS_ACCEL_LABEL (widget))
- {
- guint w;
-
- w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
- *width = MAX (*width, w);
- }
- else if (GTK_IS_CONTAINER (widget))
- gtk_container_foreach (GTK_CONTAINER (widget),
- gtk_menu_item_accel_width_foreach,
- data);
-}
-
-static gint
-get_minimum_width (GtkWidget *widget)
-{
- PangoContext *context;
- PangoFontMetrics *metrics;
- gint width;
- gint width_chars;
-
- context = gtk_widget_get_pango_context (widget);
- metrics = pango_context_get_metrics (context,
- widget->style->font_desc,
- pango_context_get_language (context));
-
- width = pango_font_metrics_get_approximate_char_width (metrics);
-
- pango_font_metrics_unref (metrics);
-
- gtk_widget_style_get (widget, "width-chars", &width_chars, NULL);
-
- return PANGO_PIXELS (width_chars * width);
-}
-
-static void
-gtk_menu_item_size_request (GtkWidget *widget,
- GtkRequisition *requisition)
-{
- GtkMenuItem *menu_item;
- GtkBin *bin;
- GtkWidget *child;
- guint accel_width;
- guint horizontal_padding;
- guint border_width;
- GtkPackDirection pack_dir;
- GtkPackDirection child_pack_dir;
-
- g_return_if_fail (GTK_IS_MENU_ITEM (widget));
- g_return_if_fail (requisition != NULL);
-
- gtk_widget_style_get (widget,
- "horizontal-padding", &horizontal_padding,
- NULL);
-
- bin = GTK_BIN (widget);
- menu_item = GTK_MENU_ITEM (widget);
-
- if (GTK_IS_MENU_BAR (widget->parent))
- {
- pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
- child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
- }
- else
- {
- pack_dir = GTK_PACK_DIRECTION_LTR;
- child_pack_dir = GTK_PACK_DIRECTION_LTR;
- }
-
- border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
- requisition->width = (border_width + widget->style->xthickness) * 2;
- requisition->height = (border_width + widget->style->ythickness) * 2;
-
- if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
- (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
- requisition->width += 2 * horizontal_padding;
- else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
- (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
- requisition->height += 2 * horizontal_padding;
-
- child = gtk_bin_get_child (bin);
-
- if (child != NULL && gtk_widget_get_visible (child))
- {
- GtkRequisition child_requisition;
-
- gtk_widget_size_request (child, &child_requisition);
-
- requisition->width += child_requisition.width;
- requisition->height += child_requisition.height;
-
- if (menu_item->submenu && menu_item->show_submenu_indicator)
- {
- guint arrow_spacing;
-
- gtk_widget_style_get (widget,
- "arrow-spacing", &arrow_spacing,
- NULL);
-
- requisition->width += child_requisition.height;
- requisition->width += arrow_spacing;
-
- requisition->width = MAX (requisition->width, get_minimum_width (widget));
- }
- }
- else /* separator item */
- {
- gboolean wide_separators;
- gint separator_height;
-
- gtk_widget_style_get (widget,
- "wide-separators", &wide_separators,
- "separator-height", &separator_height,
- NULL);
-
- if (wide_separators)
- requisition->height += separator_height + widget->style->ythickness;
- else
- requisition->height += widget->style->ythickness * 2;
- }
-
- accel_width = 0;
- gtk_container_foreach (GTK_CONTAINER (menu_item),
- gtk_menu_item_accel_width_foreach,
- &accel_width);
- menu_item->accelerator_width = accel_width;
-}
-
-static void
gtk_menu_item_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
@@ -1224,32 +1479,17 @@ gtk_menu_item_paint (GtkWidget *widget,
{
gint arrow_x, arrow_y;
gint arrow_size;
- gint arrow_extent;
guint horizontal_padding;
- gfloat arrow_scaling;
GtkTextDirection direction;
GtkArrowType arrow_type;
- PangoContext *context;
- PangoFontMetrics *metrics;
direction = gtk_widget_get_direction (widget);
gtk_widget_style_get (widget,
"horizontal-padding", &horizontal_padding,
- "arrow-scaling", &arrow_scaling,
NULL);
-
- context = gtk_widget_get_pango_context (child);
- metrics = pango_context_get_metrics (context,
- child->style->font_desc,
- pango_context_get_language (context));
-
- arrow_size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
- pango_font_metrics_get_descent (metrics)));
-
- pango_font_metrics_unref (metrics);
- arrow_extent = arrow_size * arrow_scaling;
+ get_arrow_size (widget, child, &arrow_size);
shadow_type = GTK_SHADOW_OUT;
if (state_type == GTK_STATE_PRELIGHT)
@@ -1257,7 +1497,7 @@ gtk_menu_item_paint (GtkWidget *widget,
if (direction == GTK_TEXT_DIR_LTR)
{
- arrow_x = x + width - horizontal_padding - arrow_extent;
+ arrow_x = x + width - horizontal_padding - arrow_size;
arrow_type = GTK_ARROW_RIGHT;
}
else
@@ -1266,14 +1506,14 @@ gtk_menu_item_paint (GtkWidget *widget,
arrow_type = GTK_ARROW_LEFT;
}
- arrow_y = y + (height - arrow_extent) / 2;
+ arrow_y = y + (height - arrow_size) / 2;
gtk_paint_arrow (widget->style, widget->window,
state_type, shadow_type,
area, widget, "menuitem",
arrow_type, TRUE,
arrow_x, arrow_y,
- arrow_extent, arrow_extent);
+ arrow_size, arrow_size);
}
else if (!child)
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]