Headless Tooltips
- 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
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