I am trying to port a context menu functionality for a tree view from Gtk3 to Gtk4. I've managed up to the point that the menu is shown, but the outer parts are cut off so that the menu is shown incomplete. It looks like this:
Example program to reproduce the effect is here:
#include <gtk/gtk.h>
GtkApplication *app;
GtkWidget *window;
GtkWidget *treeview;
GMenu *context_menu;
GtkWidget *popover;
static void general_initialisiation (gchar *argument);
static void mouse_pressed (GtkGestureClick *gesture, int n_press, double x, double y);
int main (gint argc, gchar *argv[])
{
app = gtk_application_new ("this.is.a.test", G_APPLICATION_FLAGS_NONE);
gint status;
g_signal_connect_swapped (app, "activate", G_CALLBACK (general_initialisiation), NULL);
status = g_application_run (G_APPLICATION (app), 0, NULL);
g_object_unref (app);
return status;
}
static void general_initialisiation (gchar *argument)
{
window = gtk_application_window_new (GTK_APPLICATION (app));
GtkTreeStore *treestore;
GtkTreeModel *model;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
treeview = gtk_tree_view_new ();
treestore = gtk_tree_store_new (1, GDK_TYPE_PIXBUF);
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (treestore));
model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
g_object_unref (treestore);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, "Pix");
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, renderer, FALSE);
gtk_tree_view_column_set_attributes (column, renderer, "pixbuf", 0, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
gtk_window_set_child (GTK_WINDOW (window), treeview);
GtkIconTheme *icon_theme;
GdkPixbuf *pixbuf_smaller[2];
GError *error = NULL;
icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (window));
for (gint i = 0; i < 2; i++) {
g_autoptr(GtkIconPaintable) icon;
g_autoptr(GFile) file;
g_autofree gchar *file_path;
g_autoptr(GdkPixbuf) pixbuf;
icon = gtk_icon_theme_lookup_icon (icon_theme,
(i == 0) ? "dialog-question" : "image-missing", // icon name
NULL,
48, // icon size
1, // scale
GTK_TEXT_DIR_LTR,
0 // flags
);
file = gtk_icon_paintable_get_file (icon);
file_path = g_file_get_path (file);
pixbuf = gdk_pixbuf_new_from_file (file_path, &error);
pixbuf_smaller[i] = gdk_pixbuf_scale_simple (pixbuf, 39, 39, GDK_INTERP_BILINEAR);
}
GtkTreeIter new_iter;
gtk_tree_store_insert_with_values (treestore, &new_iter, NULL, -1, 0, pixbuf_smaller[0], -1);
gtk_tree_store_insert_with_values (treestore, &new_iter, NULL, -1, 0, pixbuf_smaller[1], -1);
context_menu = g_menu_new ();
popover = gtk_popover_menu_new_from_model_full (G_MENU_MODEL (context_menu), GTK_POPOVER_MENU_NESTED);
gtk_popover_set_has_arrow (GTK_POPOVER (popover), FALSE);
gtk_widget_set_parent (popover, treeview);
GtkGesture *gesture = gtk_gesture_click_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
GtkEventController *controller = GTK_EVENT_CONTROLLER (gesture);
g_signal_connect (controller, "pressed", G_CALLBACK (mouse_pressed), NULL);
gtk_widget_add_controller (treeview, controller);
gtk_widget_show (window);
}
static void mouse_pressed (GtkGestureClick *gesture, int n_press, double x, double y)
{
g_autoptr(GtkTreePath) path;
gint x_bin_window, y_bin_window;
gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (treeview), (gint) x, (gint) y, &x_bin_window, &y_bin_window);
gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview), x_bin_window, y_bin_window, &path, NULL, NULL, NULL);
if (!path) {
return;
}
g_menu_remove_all (context_menu);
GMenu *items1 = g_menu_new ();
g_menu_append (items1, "Item 1a", NULL);
g_menu_append (items1, "Item 1b", NULL);
g_menu_append_section (context_menu, "Headline 1", G_MENU_MODEL (items1));
g_object_unref (items1);
GMenu *items2 = g_menu_new ();
g_menu_append (items2, "Item 2a", NULL);
g_menu_append (items2, "Item 2b", NULL);
g_menu_append_section (context_menu, "Headline 2", G_MENU_MODEL (items2));
g_object_unref (items2);
GdkRectangle rectangle;
rectangle.x = x;
rectangle.y = y;
rectangle.width = 1;
rectangle.height = 1;
gtk_popover_set_pointing_to (GTK_POPOVER (popover), &rectangle);
gtk_popover_popup (GTK_POPOVER (popover));
}
As for
rectangle.width = 1;
rectangle.height = 1;
I took this from example code, but whatever I set there results in the same effect.
I receive warnings/error messages like the following in the terminal:
- gtk_css_node_insert_after: assertion 'previous_sibling == NULL ||
previous_sibling->parent == parent' failed
- Gtk-WARNING **: 21:05:17.956: Allocating size to GtkPopoverMenu
0x55cf605001f0 without calling gtk_widget_measure(). How does the
code know the size to allocate?
- Allocation height too small. Tried to allocate 89x132, but GtkPopoverMenu 0x55cf605001f0 needs at least 89x201.
What do I need to change so the context menu is shown properly? I've checked in two different Linux distributions, Manjaro and OpenSuse Tumbleweed.
EDIT
After reading Create popover that can overpass window area, I thought I had found the solution and created a large enough window, but other than the messages about allocation problems going away the problem remains. I've noticed though that if I leave out the headlines for the sections, the menu is displayed properly. Adding the section headlines leads to an undersized menu again.