bevyengine / bevyengine/bevy

Headless Tooltips

Open
#20,601 16 comments 10 reactions 0 assignees View on GitHub
A-UI C-Feature D-Modest S-Ready-For-Implementation
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

The subject of tooltips has come up several times recently, so here's a quick design doc on how they might work.

## Part 1: Tooltip metadata

A `Tooltip` component can be used to indicate entities that should show a tooltip upon hover. This can either contain a text string, or, alternatively, the a11y label can be used.

In some cases, we may want to support tooltips with rich text, icons and so on. For these, the tooltip component can contain a callback which generates the tooltip content via BSN.

Note that any hoverable entity can have a tooltip, it doesn't have to be a UI widget. (Although figuring out the coordinates to display the tooltip might be complicated if it's not a UI node. Perhaps what we can do is for non-UI nodes just use the current pointer coords.)

## Part 2: Tooltip activation

Because tooltips can be distracting while moving the pointer, there is normally a delay - that is, you have to keep the pointer still for some amount of time before a tooltip will appear. This can be done with a timer that resets whenever the pointer moves.

When the timer goes off, we check the HoverMap to see whether the current hover entity, or one of its ancestors, has a `Tooltip` component. If it does, we spawn a tooltip entity on that window. There can only be one tooltip displayed at a time.

In some UI frameworks, displaying a tooltip causes the framework to go into a "tooltip mode" in which the delay time is reduced, allowing the user to display tooltips for multiple buttons in sequence without having to wait the normal length of time it would take for a tooltip to appear for each one. This can be done with a second timer. Let's call this "warm" mode, and call the two timers "warmup" and "cooldown".

## Part 3: Tooltip spawning

Because this is a headless widget, it's up to the user to actually spawn the tooltip entity with whatever stylistic details they need. Most likely we will require a global observer to be registered that listens for a `ShowTooltip` and `HideTooltip` event.

To ensure there is only one tooltip, the user can put the entity id of the tooltip in a Resource if they wish.

## Part 4: Tooltip placement

The `Popover` component (already written, used as part of core menus) can be used to intelligently place the tooltip rectangle relative to the anchor entity.

An optional feature will be tooltip arrows, similar to a callout box on an architectural diagram. This is indicated by creating a child entity with a `PopoverArrow` marker. If the `Popover` code detects the presence of this entity, it will automatically adjust the left/top position of the arrow entity relative to the tooltip rect. It will also insert a `PopoverDirection` component to indicate which direction the arrow should be pointing.

The `PopoverArrow` component contains a margin that indicates how close to the corner the arrow can be - this is needed in cases where the tooltip has rounded corners. `Popover` doesn't care how far out from the edge the arrow protrudes, although usually this will be similar to the popover offset distance. To make the calculations simple, we assume that `PopoverArrow` is a zero-size Node, and the actual arrow element is a child of that. The reason for this is that the dimensions of the arrow may change depending on whether the arrow is pointing in a vertical or horizontal direction.

With this approach, we can flip the position of the tooltip based on the available space, so that the tooltip is not clipped by the window edge if the anchor is near to it. A tooltip can even flip while visible, if for example the anchor scrolls too near the window edge.

Tooltips should generally opt-out of clipping using the `OverrideClip` marker component. This means that if the anchor widget is inside a scrolling region or clipped sub-window, the tooltip will not be clipped.

Tooltips should always opt-out of picking.

Tooltips should also use a positive global Z-index so that they appear on top of other elements. At some point we'll need to come up with a standard set of Z-index constants.

Contributor guide

Open the contributing guide

Research direction

No file or test is named. Start by reading the existing Popover and HoverMap concepts, then trace how ShowTooltip, HideTooltip, OverrideClip, PopoverArrow, and PopoverDirection could fit the four-part design; done means the proposed headless metadata, activation, spawning, and placement behavior is implemented and validated.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
design, frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.