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) .chain(&long_touched)
.copied() .copied()
.collect() .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 { } else {
// Whatever is topmost is what we are hovering. // We may be hovering a an interactive widget or two.
// TODO(emilk): consider handle hovering over multiple top-most widgets? // We must also consider the case where non-interactive widgets
// TODO(emilk): allow hovering close widgets? // are _on top_ of an interactive widget.
hits.contains_pointer // For instance: a label in a draggable window.
.last() // In that case we want to hover _both_ widgets,
.map(|w| w.id) // otherwise we won't see tooltips for the label.
.into_iter() //
.collect() // 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 { InteractionSnapshot {

View File

@ -72,6 +72,11 @@ impl WidgetRects {
self.by_id.get(&id).map(|(_, w)| w) 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] #[inline]
pub fn contains(&self, id: Id) -> bool { pub fn contains(&self, id: Id) -> bool {
self.by_id.contains_key(&id) self.by_id.contains_key(&id)