From b9327dbe7ea2511027c681a1dfc8349d37093c82 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Mon, 26 Jan 2026 20:14:02 -0800 Subject: [PATCH] Fix incorrect ordering of mouse click and escape key events When pressing escape soon after left clicking (within ~60ms), KiCad was processing the escape key before the mouse button release event due to wxWidgets event queue ordering. This caused operations to be canceled instead of completed. The fix adds flushPendingClicks() which checks button state via polling when an escape key event arrives. If a button was logically pressed but has been physically released, the click event is generated before the escape event is processed, maintaining proper ordering. Fixes https://gitlab.com/kicad/code/kicad/-/issues/20527 --- common/tool/tool_dispatcher.cpp | 28 ++++++++++++++++++++++++++++ include/tool/tool_dispatcher.h | 5 +++++ 2 files changed, 33 insertions(+) diff --git a/common/tool/tool_dispatcher.cpp b/common/tool/tool_dispatcher.cpp index 98b08d9944..19820e431c 100644 --- a/common/tool/tool_dispatcher.cpp +++ b/common/tool/tool_dispatcher.cpp @@ -474,6 +474,28 @@ std::optional TOOL_DISPATCHER::GetToolEvent( wxKeyEvent* aKeyEvent, } +void TOOL_DISPATCHER::flushPendingClicks() +{ + // When an escape key event arrives, keyboard events can be processed before mouse button + // events due to wxWidgets event queue ordering. If a mouse button was pressed and has since + // been released (detected via polling), we need to process that click before handling the + // escape to maintain proper event ordering. + for( BUTTON_STATE* st : m_buttons ) + { + if( st->pressed && !st->GetState() ) + { + st->pressed = false; + + TOOL_EVENT clickEvt( TC_MOUSE, TA_MOUSE_CLICK, st->button ); + clickEvt.SetMousePosition( st->downPosition ); + m_toolMgr->ProcessEvent( clickEvt ); + + st->dragging = false; + } + } +} + + void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent ) { bool motion = false; @@ -592,6 +614,12 @@ void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent ) keyIsEscape = ( ke->GetKeyCode() == WXK_ESCAPE ); + // When escape is pressed shortly after a mouse click, the keyboard event can be + // processed before the mouse button release event. Flush any pending clicks first + // to ensure proper event ordering. + if( keyIsEscape ) + flushPendingClicks(); + if( KIUI::IsInputControlFocused( focus ) ) { bool enabled = KIUI::IsInputControlEditable( focus ); diff --git a/include/tool/tool_dispatcher.h b/include/tool/tool_dispatcher.h index 9307890ef6..4571287f30 100644 --- a/include/tool/tool_dispatcher.h +++ b/include/tool/tool_dispatcher.h @@ -81,6 +81,11 @@ private: /// Handles mouse related events (click, motion, dragging). bool handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion ); + /// Processes any pending mouse clicks that have been physically completed but not yet + /// dispatched. This ensures clicks are processed before cancel events when both happen + /// in quick succession. + void flushPendingClicks(); + /// Returns the instance of VIEW, used by the application. KIGFX::VIEW* getView();