When do we really have to use `gtk_popover_present()` to show a Popover at a given widget?

Is it really the case that to have a GtkPopover on any widget, that widget must be a subclass that specifically overrides size_allocate to call present() on its Popover(s)? References:

I ask because in GTK4 code, a GtkMenuButton with a create_popup_func does not do that - it simply waits for the popover to be created, and immediately calls popup() on it. Said popup() via gtk_popover_show() then calls present() on itself anyway. I would love to be able to do this with all the popovers I need to migrate, instead of having to add size_allocate_vfunc() overrides and call all_popovers.present() in each…

There’s a note in the docs saying “When using a layout manager, this is happening automatically”, but it’s not too clear to me what that means . . . Does it mean that out-of-the-box (standard, GTK-provided) widgets, which presumably all have layout managers, handle this for us?

In short: The GtkMenuButton case to me implies that set_parent() and popup() should be enough (as set_relative_to() and popup() were in GTK3) . . . but is this a case of ‘do as we say, not as we do’, or what? :wink:

Yes, it is mandatory to call gtk_popover_present() in your custom size_allocate virtual function if you are writing a custom widget that has a popover child.

If your widget is using a GtkLayoutManager then you don’t need to override size_allocate, and GtkLayoutManager will deal with popover children for you.

GtkMenuButton call gtk_popover_present() inside its size_allocate() implementation:

static void
gtk_menu_button_size_allocate (GtkWidget *widget,
                               int        width,
                               int        height,
                               int        baseline)
{
  GtkMenuButton *self= GTK_MENU_BUTTON (widget);

  gtk_widget_size_allocate (self->button,
                            &(GtkAllocation) { 0, 0, width, height },
                            baseline);
  if (self->popover)
    gtk_popover_present (GTK_POPOVER (self->popover));
}

You can avoid doing that if your widget-with-a-button-and-a-popover composite widget uses a layout manager, like GtkBinLayout. Otherwise, no: it’s not enough to call present() on popup(), because widgets need to have an allocation, and present() does that; it’s not called gtk_popover_allocate() because it is an async operation that involves the windowing system.

I know, but I was referring to gtk_menu_button_toggled(), which just calls self->create_popup_func and then calls gtk_popover_popup() on the resulting self->popover. There’s no size-allocate happening there… or is there?

Then gtk_popover_popup() calls gtk_popover_show() calls present_popup(), so it seems the present is happening anyway, without needing to be in an (in this case unrelated) size-allocate handler.

If your widget is using a GtkLayoutManager then you don’t need to override size_allocate, and GtkLayoutManager will deal with popover children for you.

So, if I just want to show a popover over a standard GTK widget, not a custom one - e.g. a GtkNotebook or GtkTreeView (bad luck that these are both deprecated, but that’s Inkscape for ya) - do I need to present()? Or do those have a default layout manager and will handle that for me? In short: will standard widgets with LayoutManagers be OK? And is there any documentation reference on which widgets use which (if any) layouts?

I feel like the docs could be clearer either way, but want to be sure I understand them before I try to help improve.

The allocation will happen before the next frame, same as the popup; but GTK must be able to contact the windowing system to determine the position and size of the popover within the same frame processing sequence, which means that the parent container of the popover must call gtk_popover_present() during its own allocation.

You cannot add a random GtkPopover to unsuspecting widgets, because you cannot add random children to them, and GtkPopover must be a child of the widget that controls it.

You always need to create a new composite widget that contains the widget you want to present, and the popover; at that point you can either implement custom measure() and size_allocate() virtual functions, or you can use a ready-made layout manager, like GtkBinLayout.

Ideally, we should have a GtkPopoverBin widget that you can use to put a widget and build a popover out of a GMenu (or set a custom GtkPopover); we currently don’t, but I hope to finish a branch for it in time for GTK 4.14.

2 Likes

I think I get it now, but just to be sure - you mean that, after the create_popup_func sets self->popover, a size_allocate() will occur on the MenuButton, which will present() the popover at that point?

Ideally, we should have a GtkPopoverBin widget that you can use to put a widget and build a popover out of a GMenu (or set a custom GtkPopover); we currently don’t, but I hope to finish a branch for it in time for GTK 4.14.

That would be a huge time-saver! I don’t know if Inkscape will be able to depend on GTK 4.14 in that case - I hope so - but otherwise at least we’ll have a canonical implementation we can adapt, if need be. Thanks for working on that!

Yes: the create_popup_func() function will either call gtk_menu_button_set_popover() or gtk_menu_button_set_menu_model(); in both cases, a GtkPopover will be parented to the menu button, and will cause a layout to be queued for the next frame.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.