Take clip_rect into account when storing widget rects (#4020)
* Bug introduced in https://github.com/emilk/egui/pull/4013 * Closes https://github.com/emilk/egui/issues/4017 Unfortunately this is a breaking change, since it changes the fields of `Response`, so can't do a patch-release with this.
This commit is contained in:
parent
132d0ec430
commit
407224746d
|
|
@ -199,7 +199,9 @@ impl ContextImpl {
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct WidgetRect {
|
||||
/// Where the widget is.
|
||||
pub rect: Rect,
|
||||
///
|
||||
/// This is after clipping with the parent ui clip rect.
|
||||
pub interact_rect: Rect,
|
||||
|
||||
/// The globally unique widget id.
|
||||
///
|
||||
|
|
@ -234,13 +236,17 @@ impl WidgetRects {
|
|||
|
||||
/// Insert the given widget rect in the given layer.
|
||||
pub fn insert(&mut self, layer_id: LayerId, widget_rect: WidgetRect) {
|
||||
if !widget_rect.interact_rect.is_positive() {
|
||||
return;
|
||||
}
|
||||
|
||||
let layer_widgets = self.by_layer.entry(layer_id).or_default();
|
||||
|
||||
if let Some(last) = layer_widgets.last_mut() {
|
||||
if last.id == widget_rect.id {
|
||||
// e.g. calling `response.interact(…)` right after interacting.
|
||||
last.sense |= widget_rect.sense;
|
||||
last.rect = last.rect.union(widget_rect.rect);
|
||||
last.interact_rect = last.interact_rect.union(widget_rect.interact_rect);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1046,15 +1052,25 @@ impl Context {
|
|||
);
|
||||
}
|
||||
|
||||
self.interact_with_hovered(layer_id, id, rect, sense, enabled, contains_pointer)
|
||||
self.interact_with_hovered(
|
||||
layer_id,
|
||||
id,
|
||||
rect,
|
||||
interact_rect,
|
||||
sense,
|
||||
enabled,
|
||||
contains_pointer,
|
||||
)
|
||||
}
|
||||
|
||||
/// You specify if a thing is hovered, and the function gives a [`Response`].
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn interact_with_hovered(
|
||||
&self,
|
||||
layer_id: LayerId,
|
||||
id: Id,
|
||||
rect: Rect,
|
||||
interact_rect: Rect,
|
||||
sense: Sense,
|
||||
enabled: bool,
|
||||
contains_pointer: bool,
|
||||
|
|
@ -1065,6 +1081,7 @@ impl Context {
|
|||
layer_id,
|
||||
id,
|
||||
rect,
|
||||
interact_rect,
|
||||
sense,
|
||||
enabled,
|
||||
contains_pointer,
|
||||
|
|
@ -1108,9 +1125,14 @@ impl Context {
|
|||
// We add all widgets here, even non-interactive ones,
|
||||
// because we need this list not only for checking for blocking widgets,
|
||||
// but also to know when we have reached the widget we are checking for cover.
|
||||
viewport
|
||||
.layer_rects_this_frame
|
||||
.insert(layer_id, WidgetRect { id, rect, sense });
|
||||
viewport.layer_rects_this_frame.insert(
|
||||
layer_id,
|
||||
WidgetRect {
|
||||
id,
|
||||
interact_rect,
|
||||
sense,
|
||||
},
|
||||
);
|
||||
|
||||
let input = &viewport.input;
|
||||
let memory = &mut ctx.memory;
|
||||
|
|
@ -1857,7 +1879,7 @@ impl Context {
|
|||
} else {
|
||||
(Color32::from_rgb(0, 0, 0x88), "hover")
|
||||
};
|
||||
painter.debug_rect(rect.rect, color, text);
|
||||
painter.debug_rect(rect.interact_rect, color, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2314,13 +2336,13 @@ impl Context {
|
|||
layer_id: LayerId,
|
||||
id: Id,
|
||||
sense: Sense,
|
||||
rect: Rect,
|
||||
interact_rect: Rect,
|
||||
) -> bool {
|
||||
if !rect.is_positive() {
|
||||
if !interact_rect.is_positive() {
|
||||
return false; // don't even remember this widget
|
||||
}
|
||||
|
||||
let contains_pointer = self.rect_contains_pointer(layer_id, rect);
|
||||
let contains_pointer = self.rect_contains_pointer(layer_id, interact_rect);
|
||||
|
||||
let mut blocking_widget = None;
|
||||
|
||||
|
|
@ -2330,9 +2352,14 @@ impl Context {
|
|||
// We add all widgets here, even non-interactive ones,
|
||||
// because we need this list not only for checking for blocking widgets,
|
||||
// but also to know when we have reached the widget we are checking for cover.
|
||||
viewport
|
||||
.layer_rects_this_frame
|
||||
.insert(layer_id, WidgetRect { id, rect, sense });
|
||||
viewport.layer_rects_this_frame.insert(
|
||||
layer_id,
|
||||
WidgetRect {
|
||||
id,
|
||||
interact_rect,
|
||||
sense,
|
||||
},
|
||||
);
|
||||
|
||||
// Check if any other widget is covering us.
|
||||
// Whichever widget is added LAST (=on top) gets the input.
|
||||
|
|
@ -2347,7 +2374,7 @@ impl Context {
|
|||
// which means there are no widgets covering us.
|
||||
break;
|
||||
}
|
||||
if !blocking.rect.contains(pointer_pos) {
|
||||
if !blocking.interact_rect.contains(pointer_pos) {
|
||||
continue;
|
||||
}
|
||||
if sense.interactive() && !blocking.sense.interactive() {
|
||||
|
|
@ -2369,7 +2396,7 @@ impl Context {
|
|||
|
||||
if blocking.sense.interactive() {
|
||||
// Another widget is covering us at the pointer position
|
||||
blocking_widget = Some(blocking.rect);
|
||||
blocking_widget = Some(blocking.interact_rect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -2382,7 +2409,7 @@ impl Context {
|
|||
if let Some(blocking_rect) = blocking_widget {
|
||||
if sense.interactive() && self.memory(|m| m.options.style.debug.show_blocking_widget) {
|
||||
Self::layer_painter(self, LayerId::debug()).debug_rect(
|
||||
rect,
|
||||
interact_rect,
|
||||
Color32::GREEN,
|
||||
"Covered",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,15 @@ pub struct Response {
|
|||
/// The area of the screen we are talking about.
|
||||
pub rect: Rect,
|
||||
|
||||
/// The rectangle sensing interaction.
|
||||
///
|
||||
/// This is sometimes smaller than [`Self::rect`] because of clipping
|
||||
/// (e.g. when inside a scroll area).
|
||||
///
|
||||
/// The interact rect may also be slightly larger than the widget rect,
|
||||
/// because egui adds half if the item spacing to make the interact rect easier to hit.
|
||||
pub interact_rect: Rect,
|
||||
|
||||
/// The senses (click and/or drag) that the widget was interested in (if any).
|
||||
pub sense: Sense,
|
||||
|
||||
|
|
@ -605,6 +614,7 @@ impl Response {
|
|||
self.layer_id,
|
||||
self.id,
|
||||
self.rect,
|
||||
self.interact_rect,
|
||||
sense,
|
||||
self.enabled,
|
||||
self.contains_pointer,
|
||||
|
|
@ -799,6 +809,7 @@ impl Response {
|
|||
layer_id: self.layer_id,
|
||||
id: self.id,
|
||||
rect: self.rect.union(other.rect),
|
||||
interact_rect: self.interact_rect.union(other.interact_rect),
|
||||
sense: self.sense.union(other.sense),
|
||||
enabled: self.enabled || other.enabled,
|
||||
contains_pointer: self.contains_pointer || other.contains_pointer,
|
||||
|
|
|
|||
|
|
@ -650,10 +650,12 @@ impl Ui {
|
|||
id: Id,
|
||||
sense: Sense,
|
||||
) -> Response {
|
||||
let interact_rect = rect.intersect(self.clip_rect());
|
||||
self.ctx().interact_with_hovered(
|
||||
self.layer_id(),
|
||||
id,
|
||||
rect,
|
||||
interact_rect,
|
||||
sense,
|
||||
self.enabled,
|
||||
contains_pointer,
|
||||
|
|
|
|||
|
|
@ -49,9 +49,20 @@ impl eframe::App for MyApp {
|
|||
}
|
||||
ui.label(format!("Hello '{}', age {}", self.name, self.age));
|
||||
|
||||
ui.image(egui::include_image!(
|
||||
"../../../crates/egui/assets/ferris.png"
|
||||
));
|
||||
// ui.image(egui::include_image!(
|
||||
// "../../../crates/egui/assets/ferris.png"
|
||||
// ));
|
||||
|
||||
let (response, painter) =
|
||||
ui.allocate_painter(egui::vec2(300.0, 150.0), egui::Sense::click());
|
||||
painter.rect_filled(response.rect, 2.0, egui::Color32::BLACK);
|
||||
let clicked_pos = response.interact_pointer_pos();
|
||||
if response.clicked() {
|
||||
eprintln!("clicked_pos: {clicked_pos:?}");
|
||||
}
|
||||
if response.is_pointer_button_down_on() {
|
||||
eprintln!("down_pos: {clicked_pos:?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue