Adjusting margin and padding of GtkPopoverMenu items

I need to adjust the padding and margins for menu items in a GtkPopoverMenu. I’m creating a GMenu by adding items using g_menu_item_new and g_simple_action_new. After that, I create a GtkPopoverMenu from the menu model like this:


GtkWidget *popoverMenu = gtk_popover_menu_new_from_model_full(model, GTK_POPOVER_MENU_NESTED);

When the popover menu is displayed for the first time, it shows a vertical scrollbar. I’d like to adjust the padding and margins of the menu to suit my needs, but I’m not sure how to do it.

I’ve tried applying various CSS rules, but none of them seem to work. Here are the styles I attempted:

.popMenu {
    margin: 0px;
    padding: 0px;
    border-width: 0px;
    min-height: 14px;
}

popover {
    padding: 0px;
}

popover .menu {
    padding: 0px;
}

popover button.model {
    padding: 1px 1px;
    margin: 0px;
    min-height: 14px;
}

popover button.model .label {
    padding: 0px;
    margin: 0px;
}

popover box.horizontal.inline-buttons button.model {
    padding: 1px 1px;
    margin: 0px;
    min-height: 14px;
}

Can anyone help me figure out how to properly adjust these properties?

Hi,

For adding space around the whole menu, you can use:

popover.menu contents {
	padding: 20px;
}

For individual items, use:

popover.menu modelbutton {
	padding: 20px;
}

(note: looks like there is a bug in the docs, they say button.model instead of modelbutton…)

Great! It works well overall. However, there is an issue when the popover menu is shown for the first time - it appears with scrollbars. After the first appearance, it looks perfect every other time.

Any idea how to show the popover menu to appear in full size on its first appearance (without scrollbars)?

Strange, I never saw this sizing issue with scrollbars…

Which version of gtk4 do you use? and which display server ? (x11, wayland, win32, macos, …)

4.14.5. It happens on ubuntu 22.04, windows 10 & 11, macos 14.6.1 qrm & intel.

This issue actually occurs on all GTK versions starting from 4.6. I’m using version 4.14.3 on Ubuntu 24.04 (installed via Ubuntu packages). For Windows, I compiled it with Visual Studio 2022 and Meson, and did the same for macOS.

By the way, I have these popover menus in the toolbar, but I only experience the scrollbar effect when displaying it as a context menu. It only shows scrollbars on its first appearance; after that, it works fine. Very strange.

Attached is a small video captured on macOS demonstrating the code I use to display the context menu:

//menu model already created
GMenuModel* model = G_MENU_MODEL(menuModelHandle);
assert(model);

GtkWidget* popoverMenu = gtk_popover_menu_new_from_model_full(model, GTK_POPOVER_MENU_NESTED);
GtkWidget* parent = GTK_WIDGET(pFrame->getHandle());
gtk_widget_set_parent(popoverMenu, parent);

GtkPopover* popover = GTK_POPOVER (popoverMenu);
gtk_popover_set_position (popover, GTK_POS_BOTTOM);
gtk_popover_set_has_arrow (popover, FALSE);
gtk_widget_set_halign (popoverMenu, GTK_ALIGN_START);

GdkRectangle rect = { (int) pointOnFrame.x, (int) pointOnFrame.y, 1, 1 };
gtk_popover_set_pointing_to (popOver, &rect);
gtk_popover_popup (popOver);

Link to the video demonstration:

[Dropbox](https://Video demo)

Do you call Gtk.Popover.present from the size_allocate method of the parent?

That would be nice if you could provide a minimal reproducible example, so we can debug on our side.

(side note: this topic is marked “solved”, you will get more attention if you create a new dedicated topic for the sizing issue)

This is a minimal example that displays Popovermenu with scrollbars…


#include <gtk/gtk.h>

static GtkWidget* popoverMenu = nullptr;

static GMenu* subMenu = nullptr;

void createContextMenu()
{
    subMenu = g_menu_new();
    GMenu* section = g_menu_new();
    const char* pID = "f";
    //GSimpleAction* sa = g_simple_action_new ("saSetFront", NULL);
    //g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (sa));
    //g_signal_connect (sa, "activate", G_CALLBACK (itemActivated), &item);

    GMenuItem* gMI = g_menu_item_new ("SetFront", pID);
    g_menu_append_item (section, gMI);

    g_object_unref (gMI);

    pID = "b";
    //sa = g_simple_action_new ("saSetBack", NULL);
    //g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (sa));
    //g_signal_connect (sa, "activate", G_CALLBACK (itemActivated), &item);

    gMI = g_menu_item_new ("SetBack", pID);
    g_menu_append_item (section, gMI);
    g_object_unref (gMI);

    //complete first section (2 items)
    g_menu_append_section (subMenu, NULL, G_MENU_MODEL(section));

    //add second section (1 item)
    section = g_menu_new();
    pID = nullptr;
    //sa = g_simple_action_new ("saOptions", NULL);

    // g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (sa));
    // g_signal_connect (sa, "activate", G_CALLBACK (itemActivated), &item);

    gMI = g_menu_item_new ("Options", pID);

    g_menu_append_item (section, gMI);
    g_object_unref (gMI);

    //complete second section
    g_menu_append_section (subMenu, NULL, G_MENU_MODEL(section));
}

void handleMenu(GtkWidget* parent, int x, int y)
{
    if(!subMenu)
        createContextMenu();

    GMenuModel* model = G_MENU_MODEL(subMenu);

    if (!popoverMenu)
    {
        popoverMenu = gtk_popover_menu_new_from_model_full(model, GTK_POPOVER_MENU_NESTED);
        gtk_widget_set_parent(popoverMenu, parent);

        GtkPopover* popover = GTK_POPOVER (popoverMenu);
        gtk_popover_set_position (popover, GTK_POS_BOTTOM);
        gtk_popover_set_has_arrow (popover, FALSE);
        gtk_widget_set_halign (popoverMenu, GTK_ALIGN_START);
        gtk_widget_set_valign(popoverMenu, GTK_ALIGN_START);
    }
    GtkPopover* popOver = GTK_POPOVER(popoverMenu);
    GdkRectangle rect = { x, y, 1, 1 };
    gtk_popover_set_pointing_to (popOver, &rect);
    gtk_popover_popup (popOver);
}


static void onRightClick(GtkGestureClick *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data)
{
    if (n_press == 1 && gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(gesture)) == GDK_BUTTON_SECONDARY)
    {
        GtkWidget *parent = (GtkWidget *) user_data;
        handleMenu(parent, (int) x, (int) y);
    }
}


static void activate (GtkApplication* app, gpointer        user_data)
{
    GtkWidget *window;

    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Window");
    gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);

    // Create a GtkGestureClick controller for right-clicks
    GtkGesture* rightClick = gtk_gesture_click_new();
    gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(rightClick), GDK_BUTTON_SECONDARY);
    gtk_widget_add_controller (window, GTK_EVENT_CONTROLLER(rightClick));
    g_signal_connect (rightClick, "pressed", G_CALLBACK (onRightClick), window);

    gtk_window_present (GTK_WINDOW (window));
}

int main (int    argc, char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

If I keep only 1 section (without separator) everything looks fine…

OK, in your example above, the popovermenu’s parent is a simple Gtk(Application)Window, which doesn’t have a layout manager. That’s why the menu can’t properly allocate.

If you want to attach the menu to a Window, then subclass with a custom type that overrides the virtual size_allocate method to call gtk_popover_present.

Or, use an intermediate widget that already implements a layout manager, like GtkBox, and attach the menu to it.

For example:

static void activate (GtkApplication* app, gpointer        user_data)
{
    GtkWidget *window;
    GtkWidget *box;

    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Window");
    gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);

    /* boxes have a layout manager so will automatically manage popovers allocation */
    box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_window_set_child (GTK_WINDOW (window), box);

    // Create a GtkGestureClick controller for right-clicks
    GtkGesture* rightClick = gtk_gesture_click_new();
    gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(rightClick), GDK_BUTTON_SECONDARY);
    gtk_widget_add_controller (box, GTK_EVENT_CONTROLLER(rightClick));
    g_signal_connect (rightClick, "pressed", G_CALLBACK (onRightClick), box);

    gtk_window_present (GTK_WINDOW (window));
}

In my case, popover menus are used as context menus for widgets like GtkGLArea and GtkDrawingArea. So the gtkbox solution is not applicable.

Also, I don’t use GTK’s layout managers; instead, I use my own custom layout managers. Could you please elaborate on the first solution approach? I didn’t quite understand it.

Will it work if I place the popover menu widget inside a GtkBox and then pass the GtkBox to the popover.

Using GtkBox and displaying it doesn’t seem to work. In general, I believe the popover menu should function properly in any case. Currently, it appears that the issue with measuring is specifically related to the height required for the separator. If I were to implement a custom popover menu using size_allocate, I would need to manually calculate its size. I tried using gtk_widget_get_preferred_size, but it returns incorrect values.

Your proposal with adding GtkBox to parent window produces same wrong results.


static void activate (GtkApplication* app, gpointer        user_data)
{
    GtkWidget *window;

    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Window");
    gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);

    GtkWidget* box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_window_set_child (GTK_WINDOW (window), box);

    // Create a GtkGestureClick controller for right-clicks
    GtkGesture* rightClick = gtk_gesture_click_new();
    gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(rightClick), GDK_BUTTON_SECONDARY);
    gtk_widget_add_controller (window, GTK_EVENT_CONTROLLER(rightClick));
    g_signal_connect (rightClick, "pressed", G_CALLBACK (onRightClick), window);

    gtk_window_present (GTK_WINDOW (window));
}

Then either subclass the GtkDrawingArea to override size_allocate, of pack the GtkDrawingArea in a GtkBox.

Does it inherit from GtkLayoutManager? If yes, the popover handling should be automatic, otherwise see proposals just above.

For inheriting, you can look at some gtk demos, like this one, how they create their own Demo3Widget override size_allocate to call gtk_popover_present.

Do you mean to pass the GtkBox as parent of the popover? Yes, that’s the point, see the example in my previous message.

Not at all. Just call gtk_popover_present, it will do it for you.

This is not my proposal :slight_smile: You did not copy correctly and forgot the most important part, i.e. passing box instead of window to connect the controller and setting the popover parent:

    gtk_widget_add_controller (box, GTK_EVENT_CONTROLLER(rightClick));
    g_signal_connect (rightClick, "pressed", G_CALLBACK (onRightClick), box);
1 Like

The approach using GtkBox would require significant changes to my code, which isn’t feasible right now.

I already know how to subclass GtkDrawingArea and am currently doing so.

What exactly do I need to adjust in the size_allocate method of my custom GtkDrawingArea to ensure the popover appears at full size?

Do I need to call gtk_native_check_resize(GTK_NATIVE(self->menu)) for each context menu I have?

OK. I need to call gtk_popover_present.

Great! This one is also fixed. Thanks again!

1 Like