Popovers

Create menus and popovers manually or automatically using a menu model

Before beginning this section, create a new Gtk.Application complete with a desktop launcher file, packaging, etc and add a Quit action as covered in the Actions section.

Gtk.Popover

Popovers are a very flexible container widget that can contain any other widgets, just like your main window. In this section we'll be opening a popover by clicking on a Gtk.MenuButton.

Create a new MenuButton and add it to the end of the HeaderBar. Use the icon-name property to give it an icon, and add Granite.STYLE_CLASS_LARGE_ICONS to make it bigger just like we did in the Actions section with our image Button. You can also set the primary property to true which will make this menu open when pressing the keyboard shortcut F10. Finally, add a tooltip to make the keyboard shortcut more discoverable.

Application.vala
protected override void activate () {
    var menu_button = new Gtk.MenuButton () {
        icon_name = "open-menu",
        primary = true,
        tooltip_markup = Granite.markup_accel_tooltip ({"F10"}, "Menu")
    };
    menu_button.add_css_class (Granite.STYLE_CLASS_LARGE_ICONS);

    var headerbar = new Gtk.HeaderBar () {
        show_title_buttons = true
    };
    headerbar.pack_end (menu_button);

    var main_window = new Gtk.ApplicationWindow (this) {
        default_height = 300,
        default_width = 300,
        title = "MyApp",
        titlebar = headerbar
    };
    main_window.present ();
}

Now create a new Button with the action app.quit, but instead of giving it a label, set a Granite.AccelLabel as its child; this will allow us to show the associated keyboard shortcut for the menu item. Finally, add Granite.STYLE_CLASS_MENUITEM so that the button shows as a borderless menu item.

Next create a Popover and set the quit Button as its child. Adding Granite.STYLE_CLASS_MENU will automatically set up some margins and sizing for us. Finally, set the popover property of the MenuButton to point to our Popover.

Application.vala
protected override void activate () {
    var quit_button = new Gtk.Button () {
        action_name = "app.quit",
        child = new Granite.AccelLabel.from_action_name ("Quit", "app.quit")
    };
    quit_button.add_css_class (Granite.STYLE_CLASS_MENUITEM);

    var popover = new Gtk.Popover () {
        child = quit_button
    };
    popover.add_css_class (Granite.STYLE_CLASS_MENU);

    var menu_button = new Gtk.MenuButton () {
        icon_name = "open-menu",
        popover = popover,
        primary = true
    };
    menu_button.add_css_class (Granite.STYLE_CLASS_LARGE_ICONS);

    var headerbar = new Gtk.HeaderBar () {
        show_title_buttons = true
    };
    headerbar.pack_end (menu_button);

    var main_window = new Gtk.ApplicationWindow (this) {
        default_height = 300,
        default_width = 300,
        title = "MyApp",
        titlebar = headerbar
    };
    main_window.present ();
}

Build and run your app. Notice that you can open and close the Popover automatically by activating the MenuButton with your pointer or by using the keyboard shortcut F10. The Popover is styled correctly as a menu and clicking the quit Button closes your app.

GLib.Menu

You can also create Popover menus automatically from Actions. In this section we'll be creating a Gtk.PopoverMenu with a GLib.Menu and opening it with a secondary click.

Start by creating a new Menu and add an item "Quit" with the action name "app.quit". Then create a new PopoverMenu with the Menu we just created as its model. We can position and halign the PopoverMenu so that it appears where we expect in relation to the pointer, and we'll set has_arrow to false, since this menu won't be pointing to a button.

Application.vala
protected override void activate () {
    var headerbar = new Gtk.HeaderBar () {
        show_title_buttons = true
    };

    var menu = new Menu ();
    menu.append ("Quit", "app.quit");

    var popover_menu = new Gtk.PopoverMenu.from_model (menu) {
        halign = Gtk.Align.START,
        has_arrow = false,
        position = Gtk.PositionType.BOTTOM
    };

    var main_window = new Gtk.ApplicationWindow (this) {
        default_height = 300,
        default_width = 300,
        title = "MyApp",
        titlebar = headerbar
    };
    main_window.present ();
}

Now let's add a new Gtk.GestureClick and set Gdk.BUTTON_SECONDARY as the button it responds to. Then at the end of the activate () function, we'll connect to the release () signal of that gesture.

When the GestureClick is released, it tells us the coordinates of where that gesture took place in our app, which we'll store in a Gdk.Rectangle. Then we can set the pointing_to property of our popover to that rectangle, and open it with the popup () function.

Application.vala
protected override void activate () {
    var headerbar = new Gtk.HeaderBar () {
        show_title_buttons = true
    };

    var menu = new Menu ();
    menu.append ("Quit", "app.quit");

    var popover_menu = new Gtk.PopoverMenu.from_model (menu) {
        halign = Gtk.Align.START,
        has_arrow = false,
        position = Gtk.PositionType.BOTTOM
    };

    var secondary_click_gesture = new Gtk.GestureClick () {
        button = Gdk.BUTTON_SECONDARY
    };

    var main_window = new Gtk.ApplicationWindow (this) {
        default_height = 300,
        default_width = 300,
        title = "MyApp",
        titlebar = headerbar
    };
    main_window.present ();

    secondary_click_gesture.released.connect ((n_press, x, y) => {
        var rect = Gdk.Rectangle () {
            x = (int) x,
            y = (int) y
        };

        popover_menu.pointing_to = rect;
        popover_menu.popup ();
    });
}

There's just one more thing to do for our secondary click menu, and that's to add a widget that will be the parent of our PopoverMenu and receive the GestureClick. Add a Box to the main window, connect the GestureClick using add_controller (), and parent the PopoverMenu with set_parent ().

Application.vala
protected override void activate () {
    var headerbar = new Gtk.HeaderBar () {
        show_title_buttons = true
    };

    var menu = new Menu ();
    menu.append ("Quit", "app.quit");

    var popover_menu = new Gtk.PopoverMenu.from_model (menu) {
        halign = Gtk.Align.START,
        has_arrow = false,
        position = Gtk.PositionType.BOTTOM
    };

    var secondary_click_gesture = new Gtk.GestureClick () {
        button = Gdk.BUTTON_SECONDARY
    };

    var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
    box.add_controller (secondary_click_gesture);
    popover_menu.set_parent (box);

    var main_window = new Gtk.ApplicationWindow (this) {
        child = box,
        default_height = 300,
        default_width = 300,
        title = "MyApp",
        titlebar = headerbar
    };
    main_window.present ();

    secondary_click_gesture.released.connect ((n_press, x, y) => {
        var rect = Gdk.Rectangle () {
            x = (int) x,
            y = (int) y
        };

        popover_menu.pointing_to = rect;
        popover_menu.popup ();
    });
}

Build and run your app, then use a secondary click to open the PopoverMenu at your pointer. You'll notice that the keyboard shortcut is shown automatically in the menu, everything is styled correctly without having to manually add any style classes, and clicking the menu item closes your app.

If you're having trouble, you can view the full example code here on GitHub. You can also learn more from Gtk.Popover reference documentation.

Last updated

Made with ❤️ by contributors from all over the world