Fix buggy interaction with widgets outside of clip rect (#4675)
This fixes a bug which sometimes would make it possible to interact with widgets that were outside the parent clip_rect. Interaction with a widget is done with the `interact_rect`, which is the intersection of the widget rect and the parent clip rect. If these rectangles are disjoint (the widget is outside the parent clip rect), this results in a _negative rectangle_ (a rectangle with a negative width and/or height). The distance tests for negative rectangles were broken, causing the bug. * This is part of solving https://github.com/emilk/egui/issues/4475 * It is also likely this would have solved https://github.com/emilk/egui/issues/4349 (which now has another fix for it) ### Breaking changes `Rect::distance_to_pos`, `distance_sq_to_pos`, `signed_distance_to_pos` now all return `f32::INFINITY` if the rectangle is negative.
This commit is contained in:
parent
00ac5b2015
commit
598dd53059
|
|
@ -58,6 +58,10 @@ pub fn hit_test(
|
|||
.filter(|layer| layer.order.allow_interaction())
|
||||
.flat_map(|&layer_id| widgets.get_layer(layer_id))
|
||||
.filter(|&w| {
|
||||
if w.interact_rect.is_negative() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let pos_in_layer = pos_in_layers.get(&w.layer_id).copied().unwrap_or(pos);
|
||||
let dist_sq = w.interact_rect.distance_sq_to_pos(pos_in_layer);
|
||||
|
||||
|
|
@ -311,6 +315,10 @@ fn find_closest(widgets: impl Iterator<Item = WidgetRect>, pos: Pos2) -> Option<
|
|||
let mut closest = None;
|
||||
let mut closest_dist_sq = f32::INFINITY;
|
||||
for widget in widgets {
|
||||
if widget.interact_rect.is_negative() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let dist_sq = widget.interact_rect.distance_sq_to_pos(pos);
|
||||
|
||||
// In case of a tie, take the last one = the one on top.
|
||||
|
|
|
|||
|
|
@ -361,6 +361,8 @@ impl Rect {
|
|||
/// The distance from the rect to the position.
|
||||
///
|
||||
/// The distance is zero when the position is in the interior of the rectangle.
|
||||
///
|
||||
/// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
|
||||
#[inline]
|
||||
pub fn distance_to_pos(&self, pos: Pos2) -> f32 {
|
||||
self.distance_sq_to_pos(pos).sqrt()
|
||||
|
|
@ -369,8 +371,14 @@ impl Rect {
|
|||
/// The distance from the rect to the position, squared.
|
||||
///
|
||||
/// The distance is zero when the position is in the interior of the rectangle.
|
||||
///
|
||||
/// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
|
||||
#[inline]
|
||||
pub fn distance_sq_to_pos(&self, pos: Pos2) -> f32 {
|
||||
if self.is_negative() {
|
||||
return f32::INFINITY;
|
||||
}
|
||||
|
||||
let dx = if self.min.x > pos.x {
|
||||
self.min.x - pos.x
|
||||
} else if pos.x > self.max.x {
|
||||
|
|
@ -394,6 +402,8 @@ impl Rect {
|
|||
///
|
||||
/// Negative inside the box.
|
||||
///
|
||||
/// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
|
||||
///
|
||||
/// ```
|
||||
/// # use emath::{pos2, Rect};
|
||||
/// let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
|
||||
|
|
@ -402,6 +412,10 @@ impl Rect {
|
|||
/// assert_eq!(rect.signed_distance_to_pos(pos2(1.50, 0.50)), 0.50);
|
||||
/// ```
|
||||
pub fn signed_distance_to_pos(&self, pos: Pos2) -> f32 {
|
||||
if self.is_negative() {
|
||||
return f32::INFINITY;
|
||||
}
|
||||
|
||||
let edge_distances = (pos - self.center()).abs() - self.size() * 0.5;
|
||||
let inside_dist = edge_distances.max_elem().min(0.0);
|
||||
let outside_dist = edge_distances.max(Vec2::ZERO).length();
|
||||
|
|
|
|||
Loading…
Reference in New Issue