GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
fix: don't clear hover on pointer Leave while a button is held
Mid-drag the compositor may unfocus the surface (pointer past the input
region), and the synthetic off-screen move corrupted the drag — a ramp
key snapped to the graph corner. Track held buttons and gate the Leave
hover-clear on none held.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/backend/window_runner.rs | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 8e8198e..b3cd5bc 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -2690,6 +2690,11 @@ pub struct EngineState<A: Application> {
/// Serial of the most recent pointer press, kept for
/// [`Application::take_window_action`] move/resize grabs.
pub last_press_serial: Option<u32>,
+ /// Mouse buttons currently held (tracked Press/Release pairs). Gates the
+ /// Leave hover-clear: mid-drag the compositor may unfocus the surface
+ /// (pointer past the input region), and the synthetic off-screen move
+ /// would corrupt the drag (a ramp key snapped to the graph corner).
+ pub buttons_down: u32,
/// This frame's display-list text, shaped and held here so the glyphon `TextArea`s built
/// in the render pass can borrow the buffers (Phase 6 —
/// [`Application::display_list_text`]).
@@ -3375,10 +3380,15 @@ impl<A: Application> PointerHandler for EngineState<A> {
}
PointerEventKind::Leave { .. } => {
self.current_cursor_icon = None;
- let mut rebuild = false;
- self.inner.as_mut().unwrap().handle_pointer_move(LogicalPosition::new(-10000.0, -10000.0), &mut rebuild);
- if rebuild {
- self.redraw = true;
+ // Clear hover with an off-screen move — but not while a
+ // button is held: a drag in progress must not see the
+ // sentinel position as cursor motion.
+ if self.buttons_down == 0 {
+ let mut rebuild = false;
+ self.inner.as_mut().unwrap().handle_pointer_move(LogicalPosition::new(-10000.0, -10000.0), &mut rebuild);
+ if rebuild {
+ self.redraw = true;
+ }
}
}
PointerEventKind::Motion { .. } => {
@@ -3405,6 +3415,7 @@ impl<A: Application> PointerHandler for EngineState<A> {
_ => continue,
};
self.last_press_serial = Some(*serial);
+ self.buttons_down = self.buttons_down.saturating_add(1);
// Client-Side Decorations (CSD) Drag & Resize Handling
let is_status_bar = self.inner.as_ref().unwrap().settings().app_id.starts_with("cce-status");
@@ -3494,6 +3505,7 @@ impl<A: Application> PointerHandler for EngineState<A> {
274 => MouseButton::Middle,
_ => continue,
};
+ self.buttons_down = self.buttons_down.saturating_sub(1);
let mut rebuild = false;
if let Some(msg) = self.inner.as_mut().unwrap().handle_mouse_input(btn, ElementState::Released, LogicalPosition::new(lx, ly), &mut rebuild) {
let mut update_rebuild = false;
@@ -3964,6 +3976,7 @@ pub fn run<A: Application>() {
last_pinch_scale: 1.0,
cursor_pos: (0.0, 0.0),
last_press_serial: None,
+ buttons_down: 0,
dl_text_items: Vec::new(),
};