Handle context-help mouse capture loss

End wxContextHelp mode when mouse capture is lost and only release the
mouse if the help window still owns capture after the modal help loop.
Add coverage for the capture-loss path so context help exits without
using another input event.

Fixes #21534.

Closes #26674.
This commit is contained in:
Richard
2026-07-20 20:35:07 +02:00
committed by Vadim Zeitlin
parent 4e9b924ec6
commit e04e3260fc
2 changed files with 102 additions and 5 deletions
+8 -5
View File
@@ -4,6 +4,7 @@
// Author: Julian Smart, Vadim Zeitlin
// Created: 08/09/2000
// Copyright: (c) 2000 Julian Smart, Vadim Zeitlin
// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
@@ -108,7 +109,8 @@ bool wxContextHelp::BeginContextHelp(wxWindow* win)
EventLoop();
win->ReleaseMouse();
if ( win->HasCapture() )
win->ReleaseMouse();
win->PopEventHandler(true);
@@ -173,10 +175,11 @@ bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
return true;
}
if ((event.GetEventType() == wxEVT_CHAR) ||
(event.GetEventType() == wxEVT_KEY_DOWN) ||
(event.GetEventType() == wxEVT_ACTIVATE) ||
(event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED))
if ( (event.GetEventType() == wxEVT_CHAR) ||
(event.GetEventType() == wxEVT_KEY_DOWN) ||
(event.GetEventType() == wxEVT_ACTIVATE) ||
(event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED) ||
(event.GetEventType() == wxEVT_MOUSE_CAPTURE_LOST) )
{
// May have already been set to true by a left-click
//m_contextHelp->SetStatus(false);
+94
View File
@@ -4,6 +4,7 @@
// Author: Steven Lamerton
// Created: 2010-07-10
// Copyright: (c) 2010 Steven Lamerton
// (c) 2026 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////
#include "testprec.h"
@@ -25,6 +26,7 @@
#include "wx/caret.h"
#include "wx/cshelp.h"
#include "wx/dcclient.h"
#include "wx/timer.h"
#include "wx/tooltip.h"
#include "wx/wupdlock.h"
@@ -55,6 +57,78 @@ protected:
wxDECLARE_NO_COPY_CLASS(WindowTestCase);
};
#if wxUSE_HELP
class ContextHelpCaptureLostTester : public wxWindow
{
public:
ContextHelpCaptureLostTester(wxWindow* parent)
: wxWindow(parent, wxID_ANY)
{
}
void SimulateCaptureLost()
{
DoReleaseMouse();
NotifyCaptureLost();
}
};
class ContextHelpCaptureLostState : public wxEvtHandler
{
public:
explicit ContextHelpCaptureLostState(ContextHelpCaptureLostTester* win)
: m_win(win),
m_captureLostTimer(this),
m_fallbackTimer(this)
{
Bind(wxEVT_TIMER, &ContextHelpCaptureLostState::OnTimer, this);
}
void Start()
{
m_captureLostTimer.StartOnce(1);
}
void Done()
{
m_done = true;
m_captureLostTimer.Stop();
m_fallbackTimer.Stop();
}
bool WasCaptureLostSent() const { return m_captureLostSent; }
bool WasFallbackUsed() const { return m_fallbackUsed; }
private:
void OnTimer(wxTimerEvent& event)
{
if ( m_done )
return;
if ( &event.GetTimer() == &m_captureLostTimer )
{
m_captureLostSent = true;
m_win->SimulateCaptureLost();
m_fallbackTimer.StartOnce(100);
return;
}
m_fallbackUsed = true;
wxKeyEvent eventKey(wxEVT_KEY_DOWN);
eventKey.SetEventObject(m_win);
m_win->GetEventHandler()->ProcessEvent(eventKey);
}
ContextHelpCaptureLostTester* const m_win;
wxTimer m_captureLostTimer;
wxTimer m_fallbackTimer;
bool m_done = false;
bool m_captureLostSent = false;
bool m_fallbackUsed = false;
};
#endif // wxUSE_HELP
static void DoTestShowHideEvent(wxWindow* window)
{
EventCounter show(window, wxEVT_SHOW);
@@ -179,6 +253,26 @@ TEST_CASE_METHOD(WindowTestCase, "Window::Mouse", "[window]")
CHECK(!m_window->HasCapture());
}
TEST_CASE_METHOD(WindowTestCase, "Window::ContextHelpCaptureLost",
"[window][help]")
{
#if wxUSE_HELP
ContextHelpCaptureLostTester* const win =
new ContextHelpCaptureLostTester(wxTheApp->GetTopWindow());
ContextHelpCaptureLostState state(win);
state.Start();
wxContextHelp contextHelp(win, false);
CHECK(contextHelp.BeginContextHelp(win));
state.Done();
CHECK(state.WasCaptureLostSent());
CHECK(!state.WasFallbackUsed());
CHECK(!win->HasCapture());
#endif // wxUSE_HELP
}
TEST_CASE_METHOD(WindowTestCase, "Window::Properties", "[window]")
{
m_window->SetLabel("label");