Skip to content

Popover

popover · Overlays · container · interactive

An empty floating surface the caller fills.

Import

cpp
#include "gbui/widgets/popover.hpp"

Example

Nothing is downloaded until you press it.

cpp
Ui::Scope popover(Ui& ui, const Interaction& input, std::string_view id, std::string_view anchorId, const PopoverOptions& options = {});
cpp
Ui::Scope popover(Ui& ui, const Interaction& input, std::string_view id, Rect anchor, const PopoverOptions& options = {});

Returns a scope, so its contents are written inside the braces like any other container.

The same box, anchored to a rectangle the caller has rather than to a tag. For the two cases a tag cannot express: a menu at the pointer, where the anchor is a point and nothing was ever laid out there, and a box hanging off geometry the caller worked out itself. A zero-sized rectangle is a legitimate anchor — the placement engine only ever reads its edges. The tagged form is this one with input.frameOf(anchorId) passed in, which is why there is one implementation and not two.

API

PopoverOptions

OptionTypeDefaultWhat it does
minWidthfloat160.0f
maxWidthfloat420.0f
minHeightfloatkAutoA ceiling on how tall it may grow. kAuto does not mean unbounded: it means the room actually available on the side it lands on, less the margin. A popup that would run past the bottom of the window stops at it and scrolls inside instead — which is what JetBrains' popups do, and the only behaviour that works when the anchor is near an edge. A number overrides that, and a smaller one wins.
maxHeightfloatkAuto
allowOverflowboolfalseLets a popup grow past the window's edge, for a caller that would rather clip than scroll. Off, because the default should be reachable.
scrollScrollAxis
None · Vertical · Horizontal
ScrollAxis::VerticalScrolls its own contents once maxHeight bites, rather than making the caller wrap everything in a scroll view. The axis is the caller's: ScrollAxis::None clips instead, which is what a popover that must never scroll wants. scrollState has to be set too, or this does nothing. Both of them together are what turns the ceiling into a scroll; an axis on its own leaves the box clipped, which looks like the popup simply lost its last rows. Three callers had set exactly this and only this, and the calendar opened near the bottom of a window was missing a week with no way to reach it. There is no default state to fall back on: state belongs to the application here as it does everywhere else.
scrollStateScrollState*nullptrWhere the scroll position lives, when the popover scrolls its own contents. Null means it does not: the box is still bounded by maxHeight and still clips, but nothing moves. State belongs to the application here as it does everywhere else — a popover that kept its own offset would be a component with memory, and the tree is rebuilt every frame.
matchAnchorWidthboolfalseMatches the anchor's width — what a select's list wants.
paddingEdgesEdges::all(6.0f)
gapBetweenItemsfloat2.0fBetween the things inside the surface, as distinct from the padding around them. A menu wants a couple of pixels and a calendar wants none.
roleRoleRole::NoneWhat the surface is, to a reader who cannot see it float. None by default and on purpose: a popover is a placement, not a kind of thing, and the same box holds a menu, a list of values and a calendar. Whoever opened it knows which — select says ListBox, a context menu says Menu — and a default of "menu" would put that word in front of every date picker in the tree.
namestd::string_viewWhat it is called, when the role is worth announcing.

Released under the LGPL-3.0-or-later licence.