From ae1ba79f63168c4ad53b459a8bb8ef6a435c045f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 30 May 2019 20:10:03 +0000 Subject: [PATCH] menu: Use a box This makes some of the GtkMenuShell api no longer work, since we don't let the menu shell code maintain the list of children anymore. --- gtk/gtkmenu.c | 369 +++++++------------------------------------ gtk/gtkmenuprivate.h | 1 + 2 files changed, 55 insertions(+), 315 deletions(-) diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index fc9279c01d..7b78615c77 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -121,16 +121,8 @@ #include #include - -#define MENU_SCROLL_STEP1 8 -#define MENU_SCROLL_STEP2 15 -#define MENU_SCROLL_FAST_ZONE 8 -#define MENU_SCROLL_TIMEOUT1 50 -#define MENU_SCROLL_TIMEOUT2 20 - #define MENU_POPUP_DELAY 225 -#define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key" #define ATTACHED_MENUS "gtk-attached-menus" typedef struct _GtkMenuAttachData GtkMenuAttachData; @@ -148,13 +140,7 @@ struct _GtkMenuPopdownData GdkDevice *device; }; -typedef struct -{ - gint effective_top_attach; -} AttachInfo; - enum { - MOVE_SCROLL, POPPED_UP, LAST_SIGNAL }; @@ -252,96 +238,6 @@ menu_queue_resize (GtkMenu *menu) gtk_widget_queue_resize (GTK_WIDGET (menu)); } -static void -attach_info_free (AttachInfo *info) -{ - g_slice_free (AttachInfo, info); -} - -static AttachInfo * -get_attach_info (GtkWidget *child) -{ - GObject *object = G_OBJECT (child); - AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY); - - if (!ai) - { - ai = g_slice_new0 (AttachInfo); - g_object_set_data_full (object, I_(ATTACH_INFO_KEY), ai, - (GDestroyNotify) attach_info_free); - } - - return ai; -} - -static void -menu_ensure_layout (GtkMenu *menu) -{ - GtkMenuPrivate *priv = menu->priv; - - if (!priv->have_layout) - { - GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu); - GList *l; - gchar *row_occupied; - gint current_row; - gint max_bottom_attach; - - /* Find extents of gridded portion - */ - max_bottom_attach = 0; - - /* Find empty rows */ - row_occupied = g_malloc0 (max_bottom_attach); - - /* Lay non-grid-items out in those rows - */ - current_row = 0; - for (l = menu_shell->priv->children; l; l = l->next) - { - GtkWidget *child = l->data; - AttachInfo *ai = get_attach_info (child); - - while (current_row < max_bottom_attach && row_occupied[current_row]) - current_row++; - - ai->effective_top_attach = current_row; - - current_row++; - } - - g_free (row_occupied); - - priv->n_rows = MAX (current_row, max_bottom_attach); - priv->have_layout = TRUE; - } -} - -static gint -gtk_menu_get_n_rows (GtkMenu *menu) -{ - GtkMenuPrivate *priv = menu->priv; - - menu_ensure_layout (menu); - - return priv->n_rows; -} - -static void -get_effective_child_attach (GtkWidget *child, - int *t) -{ - GtkMenu *menu = GTK_MENU (gtk_widget_get_parent (child)); - AttachInfo *ai; - - menu_ensure_layout (menu); - - ai = get_attach_info (child); - - if (t) - *t = ai->effective_top_attach; -} - static void gtk_menu_class_init (GtkMenuClass *class) { @@ -792,6 +688,7 @@ gtk_menu_init (GtkMenu *menu) priv->toplevel = gtk_window_new (GTK_WINDOW_POPUP); gtk_container_add (GTK_CONTAINER (priv->toplevel), GTK_WIDGET (menu)); g_signal_connect (priv->toplevel, "destroy", G_CALLBACK (gtk_widget_destroyed), &priv->toplevel); + gtk_window_set_resizable (GTK_WINDOW (priv->toplevel), FALSE); gtk_window_set_mnemonic_modifier (GTK_WINDOW (priv->toplevel), 0); @@ -805,6 +702,9 @@ gtk_menu_init (GtkMenu *menu) g_object_force_floating (G_OBJECT (menu)); priv->needs_destruction_ref = TRUE; + priv->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_parent (priv->box, GTK_WIDGET (menu)); + priv->monitor_num = -1; priv->anchor_hints = GDK_ANCHOR_FLIP | GDK_ANCHOR_SLIDE | GDK_ANCHOR_RESIZE; @@ -878,6 +778,11 @@ gtk_menu_destroy (GtkWidget *widget) static void gtk_menu_finalize (GObject *object) { + GtkMenu *menu = GTK_MENU (object); + GtkMenuPrivate *priv = menu->priv; + + g_clear_pointer (&priv->box, gtk_widget_unparent); + G_OBJECT_CLASS (gtk_menu_parent_class)->finalize (object); } @@ -1073,9 +978,7 @@ gtk_menu_remove (GtkContainer *container, if (priv->old_active_menu_item == widget) g_clear_object (&priv->old_active_menu_item); - GTK_CONTAINER_CLASS (gtk_menu_parent_class)->remove (container, widget); - - g_object_set_data (G_OBJECT (widget), I_(ATTACH_INFO_KEY), NULL); + gtk_container_remove (GTK_CONTAINER (priv->box), widget); menu_queue_resize (menu); } @@ -1099,10 +1002,9 @@ gtk_menu_real_insert (GtkMenuShell *menu_shell, gint position) { GtkMenu *menu = GTK_MENU (menu_shell); + GtkMenuPrivate *priv = menu->priv; - gtk_widget_insert_before (child, GTK_WIDGET (menu), NULL); - - GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->insert (menu_shell, child, position); + gtk_container_add (GTK_CONTAINER (priv->box), child); menu_queue_resize (menu); } @@ -2034,135 +1936,30 @@ gtk_menu_unrealize (GtkWidget *widget) GTK_WIDGET_CLASS (gtk_menu_parent_class)->unrealize (widget); } -static gint -calculate_line_heights (GtkMenu *menu, - gint for_width, - guint **ret_min_heights, - guint **ret_nat_heights) -{ - GtkMenuPrivate *priv; - GtkMenuShell *menu_shell; - GtkWidget *child, *widget; - GList *children; - gint n_heights; - guint *min_heights; - guint *nat_heights; - gint avail_width; - - priv = menu->priv; - widget = GTK_WIDGET (menu); - menu_shell = GTK_MENU_SHELL (widget); - - 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); - avail_width = for_width - (2 * priv->toggle_size + priv->accel_size); - - for (children = menu_shell->priv->children; children; children = children->next) - { - gint part; - gint toggle_size; - gint t; - gint child_min, child_nat; - - child = children->data; - - if (!gtk_widget_get_visible (child)) - continue; - - get_effective_child_attach (child, &t); - - gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, - avail_width, - &child_min, &child_nat, - NULL, NULL); - - gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size); - - part = MAX (child_min, toggle_size); - min_heights[t] = MAX (min_heights[t], part); - - part = MAX (child_nat, toggle_size); - nat_heights[t] = MAX (nat_heights[t], part); - } - - if (ret_min_heights) - *ret_min_heights = min_heights; - else - g_free (min_heights); - - if (ret_nat_heights) - *ret_nat_heights = nat_heights; - else - g_free (nat_heights); - - return n_heights; -} - static void gtk_menu_size_allocate (GtkWidget *widget, - int widget_width, - int widget_height, + int width, + int height, int baseline) { - GtkMenu *menu; - GtkMenuPrivate *priv; - GtkMenuShell *menu_shell; - GtkWidget *child; - GtkAllocation child_allocation; - GList *children; - gint i; - int base_width; + GtkMenu *menu = GTK_MENU (widget); + GtkMenuPrivate *priv = menu->priv; + GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget); + GList *children, *l; - g_return_if_fail (GTK_IS_MENU (widget)); - - menu = GTK_MENU (widget); - menu_shell = GTK_MENU_SHELL (widget); - priv = menu->priv; - - g_free (priv->heights); - priv->heights_length = calculate_line_heights (menu, - widget_width, - &priv->heights, - NULL); - - /* refresh our cached height request */ - priv->requested_height = 0; - for (i = 0; i < priv->heights_length; i++) - priv->requested_height += priv->heights[i]; - - base_width = widget_width; - children = menu_shell->priv->children; - while (children) + children = gtk_container_get_children (GTK_CONTAINER (priv->box)); + for (l = children; l; l = l->next) { - child = children->data; - children = children->next; + GtkWidget *child = l->data; - if (gtk_widget_get_visible (child)) - { - gint t; - - get_effective_child_attach (child, &t); - - child_allocation.width = base_width; - child_allocation.height = 0; - child_allocation.x = 0; - child_allocation.y = 0; - - for (i = 0; i < t + 1; i++) - { - if (i < t) - child_allocation.y += priv->heights[i]; - else - child_allocation.height += priv->heights[i]; - } - - gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child), - priv->toggle_size); - - gtk_widget_size_allocate (child, &child_allocation, -1); - } + gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child), + priv->toggle_size); } + g_list_free (children); + + gtk_widget_size_allocate (priv->box, + &(GtkAllocation) { 0, 0, width, height }, + baseline); } static void @@ -2175,59 +1972,50 @@ gtk_menu_show (GtkWidget *widget) GTK_WIDGET_CLASS (gtk_menu_parent_class)->show (widget); } -static void gtk_menu_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) +static void +gtk_menu_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) { GtkMenu *menu = GTK_MENU (widget); - GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget); GtkMenuPrivate *priv = gtk_menu_get_instance_private (menu); - GtkWidget *child; + GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget); + + + gtk_widget_measure (priv->box, + orientation, + for_size, + minimum, natural, + minimum_baseline, natural_baseline); if (orientation == GTK_ORIENTATION_HORIZONTAL) { - GList *children; - guint max_toggle_size; - guint max_accel_width; - gint child_min, child_nat; - gint min_width, nat_width; - - min_width = nat_width = 0; + GList *children, *l; + guint max_toggle_size; + guint max_accel_width; max_toggle_size = 0; max_accel_width = 0; - children = menu_shell->priv->children; - while (children) + children = gtk_container_get_children (GTK_CONTAINER (priv->box)); + for (l = children; l; l = l->next) { + GtkWidget *child = l->data; gint toggle_size; - child = children->data; - children = children->next; - - if (! gtk_widget_get_visible (child)) + if (!gtk_widget_get_visible (child)) continue; - /* 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_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, - -1, &child_min, &child_nat, NULL, NULL); - 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)->priv->accelerator_width); - - min_width = MAX (min_width, child_min); - nat_width = MAX (nat_width, child_min); } + g_list_free (children); /* If the menu doesn't include any images or check items * reserve the space so that all menus are consistent. @@ -2256,60 +2044,11 @@ static void gtk_menu_measure (GtkWidget *widget, g_object_unref (menu_item); } - min_width += 2 * max_toggle_size + max_accel_width; - nat_width += 2 * max_toggle_size + max_accel_width; - priv->toggle_size = max_toggle_size; priv->accel_size = max_accel_width; - *minimum = min_width; - *natural = nat_width; - - } - else /* VERTICAL */ - { - if (for_size < 0) - { - gint min_width, nat_width; - - /* Menus are height-for-width only, just return the height - * for the minimum width - */ - GTK_WIDGET_GET_CLASS (widget)->measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, - &min_width, &nat_width, NULL, NULL); - GTK_WIDGET_GET_CLASS (widget)->measure (widget, GTK_ORIENTATION_VERTICAL, min_width, - minimum, natural, - minimum_baseline, natural_baseline); - } - else - { - guint *min_heights, *nat_heights; - gint n_heights, i; - gint min_height, single_height, nat_height; - - min_height = nat_height = 0; - single_height = 0; - - n_heights = - calculate_line_heights (menu, for_size, &min_heights, &nat_heights); - - for (i = 0; i < n_heights; i++) - { - min_height += min_heights[i]; - single_height = MAX (single_height, min_heights[i]); - nat_height += nat_heights[i]; - } - - min_height = MIN (min_height, single_height); - - *minimum = min_height; - *natural = nat_height; - - g_free (min_heights); - g_free (nat_heights); - - - } + *minimum += 2 * max_toggle_size + max_accel_width; + *natural += 2 * max_toggle_size + max_accel_width; } } diff --git a/gtk/gtkmenuprivate.h b/gtk/gtkmenuprivate.h index 02bff97776..62e8da8cba 100644 --- a/gtk/gtkmenuprivate.h +++ b/gtk/gtkmenuprivate.h @@ -75,6 +75,7 @@ struct _GtkMenuPrivate * before operating on these widgets */ GtkWidget *toplevel; + GtkWidget *box; guint needs_destruction_ref : 1;