toolbar: Use a popover for overflow

We are phasing out menus.

This is not quite complete, toolitems still
create menuitems, which we translate on the fly.
This commit is contained in:
Matthias Clasen 2019-09-14 16:55:06 -04:00
parent 1b271f3335
commit b14b0efefe

View File

@ -45,10 +45,14 @@
#include "gtklabel.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkmenu.h"
#include "gtkorientable.h"
#include "gtkorientableprivate.h"
#include "gtkprivate.h"
#include "gtkpopovermenu.h"
#include "gtkmodelbutton.h"
#include "gtkseparator.h"
#include "gtkradiomenuitem.h"
#include "gtkcheckmenuitem.h"
#include "gtkradiobutton.h"
#include "gtkradiotoolbutton.h"
#include "gtkseparatormenuitem.h"
@ -132,7 +136,8 @@ struct _GtkToolbarClass
struct _GtkToolbarPrivate
{
GtkMenu *menu;
GtkWidget *menu;
GtkWidget *menu_box;
GtkSettings *settings;
GtkToolbarStyle style;
@ -429,6 +434,7 @@ gtk_toolbar_class_init (GtkToolbarClass *klass)
NULL,
G_TYPE_NONE, 1,
GTK_TYPE_TOOLBAR_STYLE);
/**
* GtkToolbar::popup-context-menu:
* @toolbar: the #GtkToolbar which emitted the signal
@ -1108,77 +1114,97 @@ menu_deactivated (GtkWidget *menu,
}
static void
menu_detached (GtkWidget *widget,
GtkMenu *menu)
button_clicked (GtkWidget *button,
GtkWidget *item)
{
GtkToolbar *toolbar = GTK_TOOLBAR (widget);
GtkToolbarPrivate *priv = toolbar->priv;
priv->menu = NULL;
gtk_popover_popdown (GTK_POPOVER (gtk_widget_get_ancestor (button, GTK_TYPE_POPOVER)));
if (GTK_IS_TOOL_BUTTON (item))
g_signal_emit_by_name (_gtk_tool_button_get_button (GTK_TOOL_BUTTON (item)), "clicked");
}
static void
rebuild_menu (GtkToolbar *toolbar)
{
GtkToolbarPrivate *priv = toolbar->priv;
GList *list, *children;
GList *list;
if (!priv->menu)
{
priv->menu = GTK_MENU (gtk_menu_new ());
gtk_menu_attach_to_widget (priv->menu,
GTK_WIDGET (toolbar),
menu_detached);
priv->menu = gtk_popover_menu_new (priv->arrow_button);
priv->menu_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_popover_menu_add_submenu (GTK_POPOVER_MENU (priv->menu), priv->menu_box, "main");
g_signal_connect (priv->menu, "deactivate",
g_signal_connect (priv->menu, "closed",
G_CALLBACK (menu_deactivated), toolbar);
}
gtk_container_foreach (GTK_CONTAINER (priv->menu), remove_item, NULL);
gtk_container_foreach (GTK_CONTAINER (priv->menu_box), remove_item, NULL);
for (list = priv->content; list != NULL; list = list->next)
{
ToolbarContent *content = list->data;
if (toolbar_content_get_state (content) == OVERFLOWN &&
!toolbar_content_is_placeholder (content))
{
GtkWidget *menu_item = toolbar_content_retrieve_menu_item (content);
if (menu_item)
{
GtkWidget *button, *widget;
const char *text;
GtkButtonRole role;
gboolean active;
g_assert (GTK_IS_MENU_ITEM (menu_item));
gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), menu_item);
text = gtk_menu_item_get_label (GTK_MENU_ITEM (menu_item));
if (text == NULL)
{
GtkWidget *box, *child;
box = gtk_bin_get_child (GTK_BIN (menu_item));
for (child = gtk_widget_get_first_child (box);
child;
child = gtk_widget_get_next_sibling (child))
{
if (GTK_IS_LABEL (child))
{
text = gtk_label_get_label (GTK_LABEL (child));
break;
}
}
}
if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item))
{
gtk_container_add (GTK_CONTAINER (priv->menu_box), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
continue;
}
else if (GTK_IS_RADIO_MENU_ITEM (menu_item))
role = GTK_BUTTON_ROLE_RADIO;
else if (GTK_IS_CHECK_MENU_ITEM (menu_item))
role = GTK_BUTTON_ROLE_CHECK;
else
role = GTK_BUTTON_ROLE_NORMAL;
if (GTK_IS_CHECK_MENU_ITEM (menu_item))
active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menu_item));
else
active = FALSE;
button = gtk_model_button_new ();
g_object_set (button,
"text", text,
"role", role,
"active", active,
NULL);
widget = toolbar_content_get_widget (content);
g_signal_connect (button, "clicked",
G_CALLBACK (button_clicked), widget);
gtk_container_add (GTK_CONTAINER (priv->menu_box), button);
}
}
}
/* Remove leading and trailing separator items */
children = gtk_container_get_children (GTK_CONTAINER (priv->menu));
list = children;
while (list && GTK_IS_SEPARATOR_MENU_ITEM (list->data))
{
GtkWidget *child = list->data;
gtk_container_remove (GTK_CONTAINER (priv->menu), child);
list = list->next;
}
g_list_free (children);
/* Regenerate the list of children so we don't try to remove items twice */
children = gtk_container_get_children (GTK_CONTAINER (priv->menu));
list = g_list_last (children);
while (list && GTK_IS_SEPARATOR_MENU_ITEM (list->data))
{
GtkWidget *child = list->data;
gtk_container_remove (GTK_CONTAINER (priv->menu), child);
list = list->prev;
}
g_list_free (children);
priv->need_rebuild = FALSE;
}
@ -2124,52 +2150,10 @@ show_menu (GtkToolbar *toolbar,
GdkEventButton *event)
{
GtkToolbarPrivate *priv = toolbar->priv;
GtkRequisition minimum_size;
rebuild_menu (toolbar);
gtk_widget_show (GTK_WIDGET (priv->menu));
switch (priv->orientation)
{
case GTK_ORIENTATION_HORIZONTAL:
gtk_widget_get_preferred_size (priv->arrow_button, &minimum_size, NULL);
g_object_set (priv->menu,
"anchor-hints", (GDK_ANCHOR_FLIP_Y |
GDK_ANCHOR_SLIDE |
GDK_ANCHOR_RESIZE),
"menu-type-hint", GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU,
"rect-anchor-dx", -minimum_size.width,
NULL);
gtk_menu_popup_at_widget (priv->menu,
priv->arrow_button,
GDK_GRAVITY_SOUTH_EAST,
GDK_GRAVITY_NORTH_WEST,
(GdkEvent *) event);
break;
case GTK_ORIENTATION_VERTICAL:
g_object_set (priv->menu,
"anchor-hints", (GDK_ANCHOR_FLIP_X |
GDK_ANCHOR_SLIDE |
GDK_ANCHOR_RESIZE),
NULL);
gtk_menu_popup_at_widget (priv->menu,
priv->arrow_button,
GDK_GRAVITY_NORTH_EAST,
GDK_GRAVITY_NORTH_WEST,
(GdkEvent *) event);
break;
default:
g_assert_not_reached ();
break;
}
gtk_popover_popup (GTK_POPOVER (priv->menu));
}
static void