Tooltips
A tooltip is a small box of text that appears next to a control when the pointer rests on it, to say what the control does. The control it belongs to is its target.
void SetTooltip(ID targetID, std::string_view text);
void SetTooltip(ID targetID, IconID icon, std::string_view text);
void SetTooltip(ID targetID, std::string_view text, std::string_view shortcut);
void SetTooltip(ID targetID, IconID icon, std::string_view text, std::string_view shortcut);
void SetTooltip(std::string_view text);
void SetTooltip(std::string_view text, std::string_view shortcut);
void SetTooltip(ID targetID, const PropertyHelp& help);
void SetTooltip(const PropertyHelp& help);
void SetErrorTooltip(ID targetID, std::string_view message);
void SetErrorTooltip(std::string_view message);
void SetDisabledTooltip(ID targetID, std::string_view text, std::string_view reason);
void SetDisabledTooltip(std::string_view text, std::string_view reason);
bool BeginTooltip(ID targetID);
bool BeginTooltip();
void EndTooltip();
- SetTooltip shows text, and an optional icon.
- It shows when the pointer rests on the target.
- It shows at once when Tab moves the keyboard focus to the target.
- shortcut is the key that does the same thing, shown in a small box after the text.
- CommandButton fills it in from its command.
- A GUIContent with a tooltip also sets one.
- No tooltip shows while a mouse button is held down. A press, a key, scrolling or an open menu hides it.
Button(saveIcon, [this] { Save(); });
SetTooltip("Save scene", "Ctrl+S");
Button({ saveIcon, "Save", "Write the file to disk" }, [this] { Save(); });
BeginTooltip is for a tooltip with any content, not only text.
- It returns true while the tooltip is showing. Build the content only then.
- Always call EndTooltip.
Image(preview, LayoutOverride::FixedSize(48, 48));
if (BeginTooltip())
{
Text("forest_floor.png");
Image(preview, LayoutOverride::FixedSize(256, 256));
}
EndTooltip();
Without a target, the tooltip belongs to the control just drawn:
- its outer element
- the parts drawn after it, such as a checkbox's label or a slider's value
- but not a popup it opened
- Controls made of several parts, such as a color field or a breadcrumb, show it over every part.
When the hovered element and a control around it both have a tooltip, the one declared first wins. So a control's tooltip beats the tooltip of the row it's in.
Button(gridIcon, [this] { showGrid = !showGrid; });
SetTooltip(showGrid ? "Grid on" : "Grid off");
Where it appears:
- below the target, or above it if there's no room below
- kept on screen, with its arrow still pointing at the target's text
- lined up sideways with the target's text:
- start-aligned text: at the text's start
- end-aligned text: at the text's end
- centered text, no text, or a target on a canvas that is panned or zoomed: centered
It fades in over 100 ms and out over 80 ms, but not with reduced motion.
How long the tooltips wait is set on tooltipManager, a member of GUIApplication:
| TooltipManager (tooltipManager) | Default | Meaning |
| delaySeconds | 0.5 | Rest time before a tooltip shows |
| warmSeconds | 0.5 | Moving to another target within this time shows its tooltip at once |
| hideSeconds | 0.1 | Time a tooltip stays after the pointer leaves, unless the next target has one |
tooltipManager.delaySeconds = 0.25f;
The CSS classes:
- tooltip-frame for the see-through window that holds it
- tooltip for the box
- tooltip-text
- tooltip-shortcut
The colors: --tooltip-bg, --tooltip-border, --tooltip-text, --tooltip-chip, --tooltip-chip-text and --shadow-tooltip.
Property help
Property help is a larger tooltip that explains a setting, such as a field in an inspector. A PropertyHelp has three parts, and empty ones are left out:
- title
- description
- footer, for the property's API path and type. It's in a monospace font, under a line.
It shows in a 268 px wide tooltip, on the popup surface.
A Label or DragLabel with help gets a dotted underline, unless you gave it your own style.
- The underline sits just below the text.
- A label alone on its line is only as tall as its text, so the underline is cut off.
- In a row with a control, it shows.
The CSS classes:
- tooltip-rich, tooltip-title, tooltip-description and tooltip-footer for the tooltip
- label-help, group-label-help, drag-label-help and group-drag-label-help for the labels
- The shadow color is --shadow-popup.
Label("Cast shadows");
SetTooltip(PropertyHelp{ "Cast shadows",
"Whether this mesh casts shadows from directional, spot and point lights.",
"MeshRenderer.castShadows (bool)" });
Checkbox(castShadows);
A value that isn't accepted
SetErrorTooltip says why a value is rejected.
- It shows on the popup surface, with a 1 px border in the error color.
- Pair it with BeginInvalid, which gives the control an error border and a glow around it.
- Text and number fields also get an error icon at their end.
- The CSS classes: tooltip-error and tooltip-error-text.
BeginInvalid();
Field(opacity);
SetErrorTooltip("Value must be between 0 and 1");
EndInvalid();
A disabled control
- A disabled control still shows its tooltip.
- Under the pointer it isn't hovered: it's InteractionState::GetHoveredDisabledID.
- SetDisabledTooltip adds the reason it's disabled, on a second, muted line.
- When the control is enabled, it's an ordinary tooltip.
- A tooltip declared inside BeginDisabled isn't itself disabled. The same goes for BeginSelected and BeginInvalid.
- The CSS classes: tooltip-lines for the box with several lines, tooltip-note for the muted line.
- The muted line's color is --tooltip-muted.
BeginDisabled();
Button("Publish", [this] { Publish(); });
SetDisabledTooltip("Publish to asset store", "Sign in to enable publishing");
EndDisabled();
Node pins have their own tooltips: see Node view.
Text cut short
When text doesn't fit and is cut short, hovering it shows the whole text. This is automatic: there's nothing to declare.
- The text can be cut by its own ellipsis, or by a box around it, such as a narrow row.
- The full text shows right over the cut text, in the same font, position and alignment.
- An icon beside the text is included.
- Under or over an icon, like a grid item's name, it's just the text's line, without the icon.
- It works for the part under the pointer, and for the focused control after Tab.
- The target's own tooltip takes precedence.
- It's not shown for:
- text in fields
- wrapping text
- text out of sight
- text on a canvas that is panned or zoomed
- The CSS classes:
- tooltip-expand for the box: tooltip colors, 4 px radius, 6 px on each side, no arrow
- tooltip-expand-text, for the text color only
Message dialogs
MessageDialog
A message dialog asks the user a question, with a button for each answer, such as "Cancel" and "Quit".
int MessageDialog(ID id, const MessageDialogDesc& desc, StyleRef style);
- It's centered, and modal: the user can't use the rest of the app until they answer.
- Call it every frame for as long as you're asking.
- It returns the index of the chosen button once, in the frame after the click. Otherwise it returns -1.
Keyboard:
- Esc and the close button choose cancelButton.
- Alt plus a button's first letter chooses that button.
- The default button starts with the focus. Tab cycles through the dialog.
| MessageDialogDesc field | Meaning |
| icon | MessageIcon::Warning, Quit, Question, Info or Error |
| title | The question |
| message | One or two sentences on what happens; a long one wraps beside the icon |
| buttons | Span of MessageButton: label, kind (Secondary, Primary, Danger), alignLeft |
| defaultButton | |
| cancelButton | -1 means the default button |
| items | Affected items, each with an optional color dot, note and checkbox |
| dontAskAgain | bool*; when set, the dialog shows the checkbox |
| details | Expandable technical text |
The CSS classes:
- message-box, and message-box-* for its parts
- dialog-button, dialog-button-primary and dialog-button-danger for the buttons
static constexpr MessageButton kButtons[] = {
{ .label = "Cancel" },
{ .label = "Quit", .kind = MessageButtonKind::Danger },
};
const int choice = MessageDialog(quitDialogID, MessageDialogDesc{
.icon = MessageIcon::Quit, .title = "Quit?", .message = "Unsaved edits will be lost.",
.buttons = kButtons, .defaultButton = 0, .cancelButton = 0 });
if (choice == 1) applicationQuit = true;
if (choice >= 0) asking = false;
A dialog that lists the affected files and offers "Don't ask again":
const MessageDialogItem affected[] = {
{ .name = "Forest.level", .meta = "12 edits", .checked = &saveScene },
};
static constexpr MessageButton kSaveButtons[] = {
{ .label = "Don't save", .alignLeft = true },
{ .label = "Cancel" },
{ .label = "Save", .kind = MessageButtonKind::Primary },
};
const int answer = MessageDialog(MessageDialogDesc{
.icon = MessageIcon::Warning, .title = "Save changes before closing?",
.buttons = kSaveButtons, .defaultButton = 2, .cancelButton = 1,
.items = affected, .dontAskAgain = &dontAskAgain });
if (answer >= 0) asking = false;
The color picker and gradient editor dialogs open from ColorField and GradientField.
Toasts
A toast is a short message that pops up in a corner of the window and goes away by itself, such as "Build succeeded". It doesn't interrupt the user.
ToastID ShowToast(ToastDesc desc);
ToastDesc* FindToast(ToastID toast);
void DismissToast(ToastID toast);
void Toasts();
- ShowToast adds a toast.
- Call Toasts() last in a window. It draws the toasts:
- in the bottom-right corner, 16 px from the edges
- over the content
- with the newest nearest the corner
- Past four toasts, the older ones fold into a "+N earlier" pill.
- How long a toast stays:
- An error toast stays until it's dismissed, by its close button or by DismissToast.
- Others go after about 6 s, counted only while the pointer isn't over them.
- A toast with a progress below 1 stays.
- FindToast returns the toast's ToastDesc, to update its progress, body or title. It returns null once the toast is gone.
- Clicking an action runs it, and dismisses the toast.
- The body wraps. The title is one line, cut short with an ellipsis, with the full text in a tooltip.
- Set age to show how long ago it happened ("4 min").
| ToastDesc field | Meaning |
| severity | Severity::Info, Success, Warning, Error: icon and circle color |
| title, body | |
| monospaceBody | For code or file locations |
| progress | Optional bar, 0 to 1 |
| actions | ToastAction list (label, onClick); the first uses the link color |
The CSS classes:
- toast-stack
- toast
- toast-circle-<severity>
- toast-title, toast-time, toast-body and toast-body-mono
- toast-progress and toast-progress-fill
- toast-action and toast-action-secondary
- toast-dismiss
- toast-earlier
ShowToast(ToastDesc{ .severity = Severity::Error, .title = "Shader compile failed",
.body = "Terrain.shader:118", .monospaceBody = true,
.actions = { { "Show log", [this] { ShowLog(); } } } });
A toast with a progress bar, updated every frame, and drawn last in the window:
uploadToast = ShowToast(ToastDesc{ .title = "Uploading build", .progress = 0.0f });
if (ToastDesc* upload = FindToast(uploadToast))
{
upload->progress = uploadDone;
if (uploadDone >= 1.0f)
upload->title = "Uploaded build";
}
Toasts();
Button("Cancel upload", [this] { DismissToast(uploadToast); });
Banner
A banner is a message across the top of a panel, tinted in the color of its severity.
void Banner(ID id, Severity severity, const GUIContent& message, std::span<const BannerAction> actions = {},
LayoutOverride&& constraints = {});
- Its icon is message.icon, or else the severity's icon.
- Its actions sit on the right. Each is one of:
- BannerActionKind::Primary: an accent button
- Secondary: a plain button
- Link
- The CSS classes:
- banner-<severity>
- banner-message-<severity>
- banner-primary, banner-secondary and banner-link
const BannerAction actions[] = {
{ "Discard", [this] { Discard(); } },
{ "Save", [this] { Save(); }, BannerActionKind::Primary },
};
Banner(idManager.AllocateDynamicID("unsaved"), Severity::Warning, "Scene has unsaved changes", actions);
const BannerAction links[] = { { "Learn more", [this] { showHelp = true; }, BannerActionKind::Link } };
Banner(Severity::Info, "This project uses an older file format.", links);
Previous: Menus | Controls | Next: Lists and reorderable groups