Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tooltips for non-interactive widgets #4291

Merged
merged 1 commit into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 32 additions & 11 deletions crates/egui/src/interaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions crates/egui/src/widget_rect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading