Skip to content

Commit 0ef59af

Browse files
committed
Bug fixes
1 parent 5cb984e commit 0ef59af

File tree

6 files changed

+104
-47
lines changed

6 files changed

+104
-47
lines changed

crates/egui/src/context.rs

+46-26
Original file line numberDiff line numberDiff line change
@@ -254,17 +254,6 @@ impl WidgetRects {
254254

255255
let layer_widgets = self.by_layer.entry(layer_id).or_default();
256256

257-
if let Some(last) = layer_widgets.last_mut() {
258-
if last.id == widget_rect.id {
259-
// e.g. calling `response.interact(…)` right after interacting.
260-
last.sense |= widget_rect.sense;
261-
last.interact_rect = last.interact_rect.union(widget_rect.interact_rect);
262-
return;
263-
}
264-
}
265-
266-
layer_widgets.push(widget_rect);
267-
268257
match self.by_id.entry(widget_rect.id) {
269258
std::collections::hash_map::Entry::Vacant(entry) => {
270259
entry.insert(widget_rect);
@@ -276,6 +265,17 @@ impl WidgetRects {
276265
existing.interact_rect = existing.interact_rect.union(widget_rect.interact_rect);
277266
}
278267
}
268+
269+
if let Some(last) = layer_widgets.last_mut() {
270+
if last.id == widget_rect.id {
271+
// e.g. calling `response.interact(…)` right after interacting.
272+
last.sense |= widget_rect.sense;
273+
last.interact_rect = last.interact_rect.union(widget_rect.interact_rect);
274+
return;
275+
}
276+
}
277+
278+
layer_widgets.push(widget_rect);
279279
}
280280
}
281281

@@ -1105,9 +1105,14 @@ impl Context {
11051105
layer_id: LayerId,
11061106
id: Id,
11071107
rect: Rect,
1108-
sense: Sense,
1108+
mut sense: Sense,
11091109
enabled: bool,
11101110
) -> Response {
1111+
if !enabled {
1112+
sense.click = false;
1113+
sense.drag = false;
1114+
}
1115+
11111116
// Respect clip rectangle when interacting:
11121117
let interact_rect = clip_rect.intersect(rect);
11131118

@@ -1126,7 +1131,7 @@ impl Context {
11261131
);
11271132
}
11281133

1129-
self.interact_with_hovered(
1134+
self.interact_with_existing(
11301135
layer_id,
11311136
id,
11321137
rect,
@@ -1139,16 +1144,21 @@ impl Context {
11391144

11401145
/// You specify if a thing is hovered, and the function gives a [`Response`].
11411146
#[allow(clippy::too_many_arguments)]
1142-
pub(crate) fn interact_with_hovered(
1147+
pub(crate) fn interact_with_existing(
11431148
&self,
11441149
layer_id: LayerId,
11451150
id: Id,
11461151
rect: Rect,
11471152
interact_rect: Rect,
1148-
sense: Sense,
1153+
mut sense: Sense,
11491154
enabled: bool,
11501155
contains_pointer: bool,
11511156
) -> Response {
1157+
if !enabled {
1158+
sense.click = false;
1159+
sense.drag = false;
1160+
}
1161+
11521162
// This is the start - we'll fill in the fields below:
11531163
let mut res = Response {
11541164
ctx: self.clone(),
@@ -1159,7 +1169,7 @@ impl Context {
11591169
sense,
11601170
enabled,
11611171
contains_pointer,
1162-
hovered: contains_pointer && enabled,
1172+
hovered: false,
11631173
highlighted: self.frame_state(|fs| fs.highlight_this_frame.contains(&id)),
11641174
clicked: Default::default(),
11651175
double_clicked: Default::default(),
@@ -1235,11 +1245,14 @@ impl Context {
12351245
res.is_pointer_button_down_on =
12361246
interaction.click_id == Some(id) || interaction.drag_id == Some(id);
12371247

1238-
res.dragged = Some(id) == viewport.interact_widgets.dragged.map(|w| w.id);
1239-
res.drag_started = Some(id) == viewport.interact_widgets.drag_started.map(|w| w.id);
1240-
res.drag_released = Some(id) == viewport.interact_widgets.drag_ended.map(|w| w.id);
1248+
if enabled {
1249+
res.hovered = viewport.interact_widgets.hovered.contains_key(&id);
1250+
res.dragged = Some(id) == viewport.interact_widgets.dragged.map(|w| w.id);
1251+
res.drag_started = Some(id) == viewport.interact_widgets.drag_started.map(|w| w.id);
1252+
res.drag_released = Some(id) == viewport.interact_widgets.drag_ended.map(|w| w.id);
1253+
}
12411254

1242-
let clicked = viewport.interact_widgets.clicked.iter().any(|w| w.id == id);
1255+
let clicked = Some(id) == viewport.interact_widgets.clicked.map(|w| w.id);
12431256

12441257
if sense.click && clicked {
12451258
// We were clicked - what kind of click?
@@ -1922,10 +1935,13 @@ impl Context {
19221935
top,
19231936
click,
19241937
drag,
1938+
closest_interactive: _,
19251939
} = hits;
19261940

1927-
for widget in &contains_pointer {
1928-
paint(widget, "contains_pointer", Color32::BLUE);
1941+
if false {
1942+
for widget in &contains_pointer {
1943+
paint(widget, "contains_pointer", Color32::BLUE);
1944+
}
19291945
}
19301946
for widget in &top {
19311947
paint(widget, "top", Color32::WHITE);
@@ -1949,11 +1965,15 @@ impl Context {
19491965
hovered,
19501966
} = interact_widgets;
19511967

1952-
for widget in contains_pointer.values() {
1953-
paint(widget, "contains_pointer", Color32::BLUE);
1968+
if false {
1969+
for widget in contains_pointer.values() {
1970+
paint(widget, "contains_pointer", Color32::BLUE);
1971+
}
19541972
}
1955-
for widget in hovered.values() {
1956-
paint(widget, "hovered", Color32::WHITE);
1973+
if true {
1974+
for widget in hovered.values() {
1975+
paint(widget, "hovered", Color32::WHITE);
1976+
}
19571977
}
19581978
for widget in &clicked {
19591979
paint(widget, "clicked", Color32::RED);

crates/egui/src/hit_test.rs

+43-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
use crate::*;
22

3-
/// Result of a hit-test agains [`WidgetRects`].
3+
/// Result of a hit-test against [`WidgetRects`].
44
///
55
/// Answers the question "what is under the mouse pointer?".
66
///
77
/// Note that this doesn't care if the mouse button is pressed or not,
88
/// or if we're currently already dragging something.
99
///
10-
/// For that you need the `InteractionState`.
10+
/// For that you need the [`crate::InteractionState`].
1111
#[derive(Clone, Debug, Default)]
1212
pub struct WidgetHits {
1313
/// All widgets that contains the pointer, back-to-front.
1414
///
15-
/// i.e. both a Window and the button in it can ontain the pointer.
15+
/// i.e. both a Window and the button in it can contain the pointer.
1616
///
1717
/// Some of these may be widgets in a layer below the top-most layer.
1818
pub contains_pointer: Vec<WidgetRect>,
@@ -31,6 +31,11 @@ pub struct WidgetHits {
3131
///
3232
/// This is the top one under the pointer, or closest one of the top-most.
3333
pub drag: Option<WidgetRect>,
34+
35+
/// The closest interactive widget under the pointer.
36+
///
37+
/// This is either the same as [`Self::click`] or [`Self::drag`], or both.
38+
pub closest_interactive: Option<WidgetRect>,
3439
}
3540

3641
/// Find the top or closest widgets to the given position,
@@ -43,16 +48,14 @@ pub fn hit_test(
4348
) -> WidgetHits {
4449
crate::profile_function!();
4550

46-
let hit_rect = Rect::from_center_size(pos, Vec2::splat(2.0 * search_radius));
47-
4851
let search_radius_sq = search_radius * search_radius;
4952

5053
// First pass: find the few widgets close to the given position, sorted back-to-front.
5154
let mut close: Vec<WidgetRect> = layer_order
5255
.iter()
56+
.filter(|layer| layer.order.allow_interaction())
5357
.filter_map(|layer_id| widgets.by_layer.get(layer_id))
5458
.flatten()
55-
.filter(|widget| widget.interact_rect.intersects(hit_rect))
5659
.filter(|w| w.interact_rect.distance_sq_to_pos(pos) <= search_radius_sq)
5760
.copied()
5861
.collect();
@@ -68,7 +71,7 @@ pub fn hit_test(
6871
let top_layer = top_hit.map(|w| w.layer_id);
6972

7073
if let Some(top_layer) = top_layer {
71-
// Ignore everything in a layer below the top-most layer:
74+
// Ignore all layers not in the same layer as the top hit.
7275
close.retain(|w| w.layer_id == top_layer);
7376
hits.retain(|w| w.layer_id == top_layer);
7477
}
@@ -81,26 +84,54 @@ pub fn hit_test(
8184
let closest_drag = find_closest(close.iter().copied().filter(|w| w.sense.drag), pos);
8285

8386
let top = top_hit.or(closest);
84-
let click = hit_click.or(closest_click);
85-
let drag = hit_drag.or(closest_drag);
87+
let mut click = hit_click.or(closest_click);
88+
let mut drag = hit_drag.or(closest_drag);
89+
90+
if let (Some(click), Some(drag)) = (&mut click, &mut drag) {
91+
// If one of the widgets is interested in both click and drags, let it win.
92+
// Otherwise we end up in weird situations where both widgets respond to hover,
93+
// but one of the widgets only responds to _one_ of the events.
94+
95+
if click.sense.click && click.sense.drag {
96+
*drag = *click;
97+
} else if drag.sense.click && drag.sense.drag {
98+
*click = *drag;
99+
}
100+
}
101+
102+
let closest_interactive = match (click, drag) {
103+
(Some(click), Some(drag)) => {
104+
if click.interact_rect.distance_sq_to_pos(pos)
105+
< drag.interact_rect.distance_sq_to_pos(pos)
106+
{
107+
Some(click)
108+
} else {
109+
Some(drag)
110+
}
111+
}
112+
(Some(click), None) => Some(click),
113+
(None, Some(drag)) => Some(drag),
114+
(None, None) => None,
115+
};
86116

87117
WidgetHits {
88118
contains_pointer: hits,
89119
top,
90120
click,
91121
drag,
122+
closest_interactive,
92123
}
93124
}
94125

95126
fn find_closest(widgets: impl Iterator<Item = WidgetRect>, pos: Pos2) -> Option<WidgetRect> {
96127
let mut closest = None;
97-
let mut cloest_dist_sq = f32::INFINITY;
128+
let mut closest_dist_sq = f32::INFINITY;
98129
for widget in widgets {
99130
let dist_sq = widget.interact_rect.distance_sq_to_pos(pos);
100131

101132
// In case of a tie, take the last one = the one on top.
102-
if dist_sq <= cloest_dist_sq {
103-
cloest_dist_sq = dist_sq;
133+
if dist_sq <= closest_dist_sq {
134+
closest_dist_sq = dist_sq;
104135
closest = Some(widget);
105136
}
106137
}

crates/egui/src/interaction.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ pub(crate) fn interact(
4242
input: &InputState,
4343
interaction: &mut InteractionState,
4444
) -> InteractionSnapshot {
45+
crate::profile_function!();
46+
4547
if let Some(id) = interaction.click_id {
4648
if !widgets.by_id.contains_key(&id) {
4749
// The widget we were interested in clicking is gone.
@@ -120,10 +122,10 @@ pub(crate) fn interact(
120122
.collect();
121123

122124
let hovered = if clicked.is_some() || dragged.is_some() {
123-
// If currently clicking or dragging, nother else is hovered.
125+
// If currently clicking or dragging, nothing else is hovered.
124126
clicked.iter().chain(&dragged).map(|w| (w.id, *w)).collect()
125127
} else if hits.click.is_some() || hits.drag.is_some() {
126-
// We are hovering over an interactive widget or two. Just highlight these two.
128+
// We are hovering over an interactive widget or two.
127129
hits.click
128130
.iter()
129131
.chain(&hits.drag)

crates/egui/src/response.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ impl Response {
610610
/// ```
611611
#[must_use]
612612
pub fn interact(&self, sense: Sense) -> Self {
613-
self.ctx.interact_with_hovered(
613+
self.ctx.interact_with_existing(
614614
self.layer_id,
615615
self.id,
616616
self.rect,

crates/egui/src/style.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -715,10 +715,10 @@ pub struct Interaction {
715715
/// which is important for e.g. touch screens.
716716
pub interact_radius: f32,
717717

718-
/// Mouse must be this close to the side of a window to resize
718+
/// Radius of the interactive area of the side of a window during drag-to-resize.
719719
pub resize_grab_radius_side: f32,
720720

721-
/// Mouse must be this close to the corner of a window to resize
721+
/// Radius of the interactive area of the corner of a window during drag-to-resize.
722722
pub resize_grab_radius_corner: f32,
723723

724724
/// If `false`, tooltips will show up anytime you hover anything, even is mouse is still moving
@@ -1135,9 +1135,9 @@ impl Default for Spacing {
11351135
impl Default for Interaction {
11361136
fn default() -> Self {
11371137
Self {
1138-
interact_radius: 3.0,
11391138
resize_grab_radius_side: 5.0,
11401139
resize_grab_radius_corner: 10.0,
1140+
interact_radius: 8.0,
11411141
show_tooltips_only_when_still: true,
11421142
tooltip_delay: 0.3,
11431143
selectable_labels: true,
@@ -1612,7 +1612,7 @@ impl Interaction {
16121612
multi_widget_text_select,
16131613
} = self;
16141614
ui.add(Slider::new(interact_radius, 0.0..=20.0).text("interact_radius"))
1615-
.on_hover_text("Interact witgh ghe closest widget within this radius.");
1615+
.on_hover_text("Interact with the closest widget within this radius.");
16161616
ui.add(Slider::new(resize_grab_radius_side, 0.0..=20.0).text("resize_grab_radius_side"));
16171617
ui.add(
16181618
Slider::new(resize_grab_radius_corner, 0.0..=20.0).text("resize_grab_radius_corner"),
@@ -1621,7 +1621,11 @@ impl Interaction {
16211621
show_tooltips_only_when_still,
16221622
"Only show tooltips if mouse is still",
16231623
);
1624-
ui.add(Slider::new(tooltip_delay, 0.0..=1.0).text("tooltip_delay"));
1624+
ui.add(
1625+
Slider::new(tooltip_delay, 0.0..=1.0)
1626+
.suffix(" s")
1627+
.text("tooltip_delay"),
1628+
);
16251629

16261630
ui.horizontal(|ui| {
16271631
ui.checkbox(selectable_labels, "Selectable text in labels");

crates/egui/src/ui.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ impl Ui {
650650
sense: Sense,
651651
) -> Response {
652652
let interact_rect = rect.intersect(self.clip_rect());
653-
self.ctx().interact_with_hovered(
653+
self.ctx().interact_with_existing(
654654
self.layer_id(),
655655
id,
656656
rect,

0 commit comments

Comments
 (0)