mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Make non-interactive tooltips not interactable (#8362)
Fixes two bugs around tooltips: - During sizing pass tooltips would render at a different size, at which point they might overlap the interacted widget causing the hover state to change and the tooltip to never be shown - Fix: During sizing pass make the area `interactable: false` so events pass through - When hovering close to the boarder of a widget (in the area where `interact_radius` takes effect), there could be a feedback loop where the tooltip is shown one frame and hidden the next. - Fix: Always make tooltips `interactable: false` when they don't have interactive contents This PR changes the behavior of `Area::interactable` to it's original behavior: Now interactions will pass through the area background _and_ it's containing widgets. It worked this way initially but got changed in #4026 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use egui::ScrollArea;
|
||||
use egui::accesskit::Role;
|
||||
#[cfg(debug_assertions)]
|
||||
use egui::epaint::Shape;
|
||||
use egui::style::ScrollAnimation;
|
||||
use egui::text::{LayoutJob, TextWrapping};
|
||||
use egui::{
|
||||
Align, Button, Color32, FontFamily, FontId, Image, Label, Layout, RichText, Sense, TextBuffer,
|
||||
TextFormat, TextWrapMode, Ui, include_image, vec2,
|
||||
Align, Button, Color32, FontFamily, FontId, Image, Label, Layout, Rect, RichText, Sense,
|
||||
TextBuffer, TextFormat, TextWrapMode, Ui, Vec2, include_image, vec2,
|
||||
};
|
||||
use egui::{Pos2, ScrollArea};
|
||||
use egui_kittest::Harness;
|
||||
use egui_kittest::kittest::{NodeT as _, Queryable as _};
|
||||
|
||||
@@ -481,3 +481,81 @@ fn animated_scroll_beats_sticky_bottom() {
|
||||
"animated explicit scroll should leave the sticky bottom"
|
||||
);
|
||||
}
|
||||
|
||||
/// Tests that tooltips are shown correctly for buttons that are only shown on hover.
|
||||
///
|
||||
/// Basically, this tests that a tooltip overlapping the mouse cursor does not interfere with a
|
||||
/// buttons hover state.
|
||||
#[test]
|
||||
fn tooltip_should_work_for_hover_button() {
|
||||
let button_rect = Rect::from_min_size(Pos2::new(4.0, 4.0), Vec2::new(80.0, 20.0));
|
||||
let mut harness = Harness::builder().with_size((320.0, 80.0)).build_ui(|ui| {
|
||||
if ui.rect_contains_pointer(button_rect) {
|
||||
ui.button("A tooltip should be shown")
|
||||
.on_hover_text("My tooltip");
|
||||
}
|
||||
});
|
||||
|
||||
harness.hover_at(button_rect.center());
|
||||
|
||||
harness.run();
|
||||
|
||||
harness.snapshot("test_tooltip_hover_regression");
|
||||
}
|
||||
|
||||
/// Ensure that hovering close to a widget doesn't cause a tooltip feedback loop (due to a
|
||||
/// difference between `hovered` and `contains_pointer` caused by the interact radius).
|
||||
#[test]
|
||||
fn tooltip_covering_button_should_not_cause_feedback_loop() {
|
||||
let mut harness = Harness::builder().with_size((200.0, 30.0)).build_ui(|ui| {
|
||||
ui.button("A tooltip should be shown")
|
||||
.on_hover_text("This tooltip is larger than the button");
|
||||
});
|
||||
|
||||
harness.hover_at(
|
||||
harness
|
||||
.get_by_label("A tooltip should be shown")
|
||||
.rect()
|
||||
.left_center()
|
||||
- Vec2::X,
|
||||
);
|
||||
|
||||
harness.run();
|
||||
|
||||
harness.snapshot("tooltip_covering_button_should_not_cause_feedback_loop");
|
||||
}
|
||||
|
||||
/// Tests that a tooltip closes when the pointer moves onto a neighboring widget,
|
||||
/// so that the neighbor can show its own tooltip.
|
||||
///
|
||||
/// The two buttons are only `item_spacing.y` (3 pt) apart, which is less than the
|
||||
/// hit-test `interact_radius` (5 pt), so the first button is still close enough to
|
||||
/// interact with when the pointer is on the second one.
|
||||
#[test]
|
||||
fn tooltip_should_hand_over_to_neighboring_widget() {
|
||||
let mut harness = Harness::builder().with_size((300.0, 200.0)).build_ui(|ui| {
|
||||
ui.button("Button A").on_hover_text("Tooltip A");
|
||||
ui.button("Button B").on_hover_text("Tooltip B");
|
||||
});
|
||||
|
||||
let a_rect = harness.get_by_label("Button A").rect();
|
||||
let b_rect = harness.get_by_label("Button B").rect();
|
||||
|
||||
harness.hover_at(a_rect.center_bottom() - Vec2::Y);
|
||||
harness.run();
|
||||
assert!(
|
||||
harness.query_by_label("Tooltip A").is_some(),
|
||||
"Tooltip A should be shown when hovering Button A"
|
||||
);
|
||||
|
||||
harness.hover_at(b_rect.center_top() + Vec2::Y);
|
||||
harness.run();
|
||||
assert!(
|
||||
harness.query_by_label("Tooltip B").is_some(),
|
||||
"Tooltip B should be shown when hovering Button B"
|
||||
);
|
||||
assert!(
|
||||
harness.query_by_label("Tooltip A").is_none(),
|
||||
"Tooltip A should be hidden when hovering Button B"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:59bef0e3593896985988c03171c710d8ed31ed8bb89c7a3f63c060c573e4fb74
|
||||
size 6510
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ed2f80921ab864b0df3303834a8ab943a3aa5e10456896991c2dca81be0c4968
|
||||
size 3968
|
||||
Reference in New Issue
Block a user