Consider all non-interactie widgets under the mouse pointer hovered (#4291)

At least all those above any interactive widget.

* Closes https://github.com/emilk/egui/issues/4286

I feel there is still more thinking to be done about what is considered
`hovered` and how it relates to `contains_pointer`, but this PR at least
fixes tooltips for uninteractive widgets
This commit is contained in:
Emil Ernerfeldt 2024-03-31 20:06:25 +02:00 committed by GitHub
parent 21835c3176
commit bb06befef1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 37 additions and 11 deletions

View File

@ -242,18 +242,39 @@ pub(crate) fn interact(
.chain(&long_touched)
.copied()
.collect()
} else if hits.click.is_some() || hits.drag.is_some() {
// We are hovering over an interactive widget or two.
hits.click.iter().chain(&hits.drag).map(|w| w.id).collect()
} else {
// Whatever is topmost is what we are hovering.
// TODO(emilk): consider handle hovering over multiple top-most widgets?
// TODO(emilk): allow hovering close widgets?
hits.contains_pointer
.last()
.map(|w| w.id)
.into_iter()
.collect()
// We may be hovering a an interactive widget or two.
// We must also consider the case where non-interactive widgets
// are _on top_ of an interactive widget.
// For instance: a label in a draggable window.
// In that case we want to hover _both_ widgets,
// otherwise we won't see tooltips for the label.
//
// Because of how `Ui` work, we will often allocate the `Ui` rect
// _after_ adding the children in it (once we know the size it will occopy)
// so we will also have a lot of such `Ui` widgets rects covering almost any widget.
//
// So: we want to hover _all_ widgets above the interactive widget (if any),
// but none below it (an interactive widget stops the hover search).
//
// To know when to stop we need to first know the order of the widgets,
// which luckily we have in the `WidgetRects`.
let order = |id| widgets.order(id).map(|(_layer, order)| order); // we ignore the layer, since all widgets at this point is in the same layer
let click_order = hits.click.and_then(|w| order(w.id)).unwrap_or(0);
let drag_order = hits.drag.and_then(|w| order(w.id)).unwrap_or(0);
let top_interactive_order = click_order.max(drag_order);
let mut hovered: IdSet = hits.click.iter().chain(&hits.drag).map(|w| w.id).collect();
for w in &hits.contains_pointer {
if top_interactive_order <= order(w.id).unwrap_or(0) {
hovered.insert(w.id);
}
}
hovered
};
InteractionSnapshot {

View File

@ -72,6 +72,11 @@ impl WidgetRects {
self.by_id.get(&id).map(|(_, w)| w)
}
/// In which layer, and in which order in that layer?
pub fn order(&self, id: Id) -> Option<(LayerId, usize)> {
self.by_id.get(&id).map(|(idx, w)| (w.layer_id, *idx))
}
#[inline]
pub fn contains(&self, id: Id) -> bool {
self.by_id.contains_key(&id)