diff --git a/crates/ui-egui/src/font_menu.rs b/crates/ui-egui/src/font_menu.rs index d08a0765f..24bf6188c 100644 --- a/crates/ui-egui/src/font_menu.rs +++ b/crates/ui-egui/src/font_menu.rs @@ -452,6 +452,13 @@ struct QueuedWheel { positions_after: Vec, } +#[derive(Clone)] +struct ReplayWheel { + target: egui::LayerId, + position: egui::Pos2, + event: egui::Event, +} + #[derive(Clone, Default)] struct WheelFields { pass: u64, @@ -462,7 +469,7 @@ struct WheelFields { fields: Vec, pending: HashMap>, queued: Vec, - replay: Vec, + replay: Vec, } #[cfg(not(target_arch = "wasm32"))] @@ -505,8 +512,9 @@ impl egui::Plugin for WheelLayerSnapshot { let id = event.field.with("wheel"); if !active { ctx.data_mut(|d| d.remove::(id)); - fields.replay.push(egui::Event::PointerMoved(event.position)); - fields.replay.push(event.event); + if let Some(target) = ctx.layer_id_at(event.position) { + fields.replay.push(ReplayWheel { target, position: event.position, event: event.event }); + } } else { let notches = ctx.data(|d| d.get_temp::(id)).unwrap_or(0.0) + event.amount; let whole = notches.trunc(); @@ -521,7 +529,6 @@ impl egui::Plugin for WheelLayerSnapshot { } } if fields.replay.len() > replayed { - fields.replay.push(fields.pointer.map_or(egui::Event::PointerGone, egui::Event::PointerMoved)); ctx.request_repaint(); } ctx.data_mut(|d| d.insert_temp(key, fields)); @@ -796,7 +803,21 @@ pub(crate) fn take_raw_wheel(ctx: &egui::Context, raw: &mut egui::RawInput) { true }); if !fields.replay.is_empty() { - raw.events.splice(0..0, std::mem::take(&mut fields.replay)); + let mut replay = Vec::new(); + for wheel in std::mem::take(&mut fields.replay) { + #[cfg(not(target_arch = "wasm32"))] + let target_still_visible = native_layers.as_ref().is_some_and(|layers| (layers.0)(wheel.position) == Some(wheel.target)); + #[cfg(target_arch = "wasm32")] + let target_still_visible = false; + if target_still_visible { + replay.push(egui::Event::PointerMoved(wheel.position)); + replay.push(wheel.event); + } + } + if !replay.is_empty() { + replay.push(pointer.map_or(egui::Event::PointerGone, egui::Event::PointerMoved)); + raw.events.splice(0..0, replay); + } } fields.pointer = pointer; ctx.data_mut(|d| d.insert_temp(key, fields)); diff --git a/crates/ui-egui/src/panel_docking/native_tests.rs b/crates/ui-egui/src/panel_docking/native_tests.rs index 223f98955..ca3ce7444 100644 --- a/crates/ui-egui/src/panel_docking/native_tests.rs +++ b/crates/ui-egui/src/panel_docking/native_tests.rs @@ -785,6 +785,49 @@ fn an_overlay_raised_with_the_wheel_event_does_not_change_the_font() { assert_eq!(h.app.run("text.fonts", json!({})).unwrap(), before); } +#[test] +fn a_closed_overlay_does_not_replay_its_wheel_into_the_panel() { + let mut h = Harness::new(); + h.app.run("text.create", json!({"x": 30, "y": 50, "text": "Hi", "font": "Source Sans 3"})).unwrap(); + let viewport = h.float("character"); + let field = h + .host + .borrow() + .windows + .get(&viewport) + .and_then(|w| w.texts.iter().find(|(text, _)| text == "Source Sans 3").map(|(_, rect)| *rect)) + .expect("the Character panel's font family field"); + let pointer = pos2(field.min.x + 4.0, field.center().y); + h.host.borrow_mut().child_raise_overlay = Some((viewport, field.expand(10.0), false)); + h.frames(2); + let field_layer = crate::font_menu::wheel_field_layer_for_test(&h.ctx, viewport, pointer).expect("the font field layer"); + h.host.borrow_mut().child_raise_layer = Some((viewport, field_layer)); + h.frame(vec![]); + h.host.borrow_mut().child_raise_layer = None; + h.frame(vec![]); + let before = h.app.run("text.fonts", json!({})).unwrap(); + h.host.borrow_mut().child_raise_overlay = Some((viewport, field.expand(10.0), true)); + h.host.borrow_mut().windows.get_mut(&viewport).unwrap().events.extend([ + Event::PointerMoved(pointer), + Event::MouseWheel { + unit: egui::MouseWheelUnit::Line, + delta: vec2(0.0, -1.0), + phase: egui::TouchPhase::Move, + modifiers: Default::default(), + source: egui::MouseWheelSource::Unknown, + }, + ]); + h.frame(vec![]); + h.host.borrow_mut().child_raise_overlay = None; + h.frame(vec![]); + h.frame(vec![]); + assert!( + !h.ctx.input_for(viewport, |input| input.raw.events.iter().any(|event| matches!(event, Event::MouseWheel { .. }))), + "a closed overlay cannot receive a later wheel event" + ); + assert_eq!(h.app.run("text.fonts", json!({})).unwrap(), before); +} + #[test] fn wheel_events_on_both_sides_of_an_overlay_are_checked_separately() { let mut h = Harness::new();