diff --git a/crates/ui-egui/src/font_menu.rs b/crates/ui-egui/src/font_menu.rs index d0750d373..19d103a74 100644 --- a/crates/ui-egui/src/font_menu.rs +++ b/crates/ui-egui/src/font_menu.rs @@ -12,8 +12,8 @@ //! the mouse wheel turned over the closed field applies the next family (or previous one) to the //! selected text, one undo step per notch. The menu's order at the first turn stays fixed until the //! pointer leaves the field or the text selection changes; steps stop at its first and last family. -//! The field takes the wheel only when the pointer was moved onto it after the field last moved and -//! the wheel stream began there. +//! The field takes the wheel only when the pointer was moved onto it after the field last moved or +//! was covered by a window or overlay, and the wheel stream began there. use std::collections::{HashMap, HashSet}; use std::sync::Arc; @@ -446,7 +446,7 @@ struct WheelField { /// The seconds without wheel input that end a wheel stream. This is VectorCraft's own value. pub(crate) const WHEEL_STREAM_GAP: f64 = 0.5; -/// The field the pointer was last moved onto, while that field stays where it was. +/// The field the pointer was last moved onto, while that field stays where it was and uncovered. #[derive(Clone, Copy)] struct ArmedWheelField { id: egui::Id, @@ -732,6 +732,9 @@ fn remember_wheel_field(ui: &Ui, resp: &egui::Response, enabled: bool) -> Vec) -> Vec { let key = wheel_fields_id(ui.ctx().viewport_id()); let pass = ui.ctx().cumulative_pass_nr(); + // A window or overlay over the pointer covers the field even when no input arrives. + let pointer = ui.data_mut(|d| d.get_temp_mut_or_default::(key).pointer).or_else(|| ui.ctx().input(|i| i.pointer.latest_pos())); + let covered = pointer.is_some_and(|p| resp.interact_rect.contains(p) && ui.ctx().layer_id_at(p) != Some(resp.layer_id)); ui.data_mut(|d| { let mut fields: WheelFields = d.get_temp(key).unwrap_or_default(); if fields.pass != pass { @@ -742,7 +745,7 @@ fn remember_owned_wheel_field(ui: &Ui, resp: &egui::Response, enabled: bool, men if let Some(armed) = &mut fields.armed && armed.id == resp.id { - if armed.rect == resp.interact_rect && pass.saturating_sub(armed.pass) <= 1 { + if armed.rect == resp.interact_rect && pass.saturating_sub(armed.pass) <= 1 && !covered { armed.pass = pass; } else { fields.armed = None; @@ -761,8 +764,8 @@ fn remember_owned_wheel_field(ui: &Ui, resp: &egui::Response, enabled: bool, men /// Take the closed font fields' wheel events before egui smooths and combines them. Other /// events stay in the input, so scrolling elsewhere keeps its own delta and event order. A field /// takes a wheel stream only when the stream begins with the pointer over it and the pointer was -/// moved onto it after the field last moved. A stream that began anywhere else stays there until -/// it ends, and a stream that leaves its field does not return to it. +/// moved onto it after the field last moved or was covered. A stream that began anywhere else +/// stays there until it ends, and a stream that leaves its field does not return to it. pub(crate) fn take_raw_wheel(ctx: &egui::Context, raw: &mut egui::RawInput) { let key = wheel_fields_id(raw.viewport_id); let Some(mut fields) = ctx.data(|d| d.get_temp::(key)) else { return }; diff --git a/crates/ui-egui/src/font_menu/tests.rs b/crates/ui-egui/src/font_menu/tests.rs index 6e0d3bba6..87c3feb06 100644 --- a/crates/ui-egui/src/font_menu/tests.rs +++ b/crates/ui-egui/src/font_menu/tests.rs @@ -358,6 +358,11 @@ fn activating_the_family_popup_leaves_its_wheel_input_available() { /// A family field at `top`, the input's clock at `time`. Returns the wheel events left in the /// input, the notches the field took and the point in the middle of the field. fn stream_frame(ctx: &egui::Context, top: Option, time: f64, events: Vec) -> (usize, i32, egui::Pos2) { + covered_stream_frame(ctx, top, false, time, events) +} + +/// [`stream_frame`], with an overlay over the whole field when `covered` is set. +fn covered_stream_frame(ctx: &egui::Context, top: Option, covered: bool, time: f64, events: Vec) -> (usize, i32, egui::Pos2) { let mut raw = egui::RawInput { time: Some(time), events, ..Default::default() }; super::take_raw_wheel(ctx, &mut raw); let left = raw.events.iter().filter(|e| matches!(e, egui::Event::MouseWheel { .. })).count(); @@ -373,6 +378,12 @@ fn stream_frame(ctx: &egui::Context, top: Option, time: f64, events: Vec