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();