mirror of
https://github.com/wxWidgets/wxWidgets.git
synced 2026-08-17 17:02:51 +08:00
Merge branch 'qt-enter-leave-events' of https://github.com/AliKet/wxWidgets
Fix wxEVT_{ENTER,LEAVE}_WINDOW events generation in wxQt and add a test
for them.
See #24883.
This commit is contained in:
+73
-23
@@ -1728,7 +1728,7 @@ bool wxWindowQt::QtHandleMouseEvent ( QWidget *handler, QMouseEvent *event )
|
||||
|
||||
// Use screen position as the event might originate from a different
|
||||
// Qt window than this one.
|
||||
wxPoint mousePos = ScreenToClient(wxQtConvertPoint(event->globalPos()));
|
||||
const wxPoint mousePos = ScreenToClient(wxQtConvertPoint(event->globalPos()));
|
||||
|
||||
wxMouseEvent e( wxType );
|
||||
e.SetEventObject(this);
|
||||
@@ -1742,42 +1742,92 @@ bool wxWindowQt::QtHandleMouseEvent ( QWidget *handler, QMouseEvent *event )
|
||||
// Keyboard modifiers
|
||||
wxQtFillKeyboardModifiers( event->modifiers(), &e );
|
||||
|
||||
bool handled = ProcessWindowEvent( e );
|
||||
bool processed = ProcessWindowEvent( e );
|
||||
|
||||
// Determine if mouse is inside the widget
|
||||
bool mouseInside = true;
|
||||
if ( mousePos.x < 0 || mousePos.x > handler->width() ||
|
||||
mousePos.y < 0 || mousePos.y > handler->height() )
|
||||
mouseInside = false;
|
||||
|
||||
if ( e.GetEventType() == wxEVT_MOTION )
|
||||
if ( wxType == wxEVT_MOTION && QtGetParentWidget() == handler )
|
||||
{
|
||||
/* Qt doesn't emit leave/enter events while the mouse is grabbed
|
||||
* and it automatically grabs the mouse while dragging. In that cases
|
||||
* we emulate the enter and leave events */
|
||||
|
||||
// Mouse enter/leaves
|
||||
if ( m_mouseInside != mouseInside )
|
||||
{
|
||||
if ( mouseInside )
|
||||
e.SetEventType( wxEVT_ENTER_WINDOW );
|
||||
else
|
||||
e.SetEventType( wxEVT_LEAVE_WINDOW );
|
||||
|
||||
ProcessWindowEvent( e );
|
||||
}
|
||||
static QWidget* s_targetHandler = nullptr;
|
||||
|
||||
QtSendSetCursorEvent(this, mousePos);
|
||||
|
||||
const auto qtMousePos = wxQtConvertPoint(mousePos);
|
||||
|
||||
// Determine if mouse is inside the widget, see below...
|
||||
bool mouseInside = handler->rect().contains(qtMousePos);
|
||||
|
||||
if ( !s_targetHandler && mouseInside )
|
||||
{
|
||||
s_targetHandler = handler;
|
||||
}
|
||||
|
||||
if ( QApplication::mouseButtons() != Qt::NoButton )
|
||||
{
|
||||
if ( mouseInside )
|
||||
{
|
||||
// Quoting wx docs: the mouse is considered to be inside the window if
|
||||
// it is in the window client area and not inside one of its children.
|
||||
auto rgn = handler->childrenRegion();
|
||||
if ( rgn.rectCount() > 1 )
|
||||
{
|
||||
rgn = QRegion(handler->rect()) - rgn;
|
||||
mouseInside = rgn.contains(qtMousePos);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate mouse enter/leaves for target handler only
|
||||
if ( m_mouseInside != mouseInside && s_targetHandler == handler )
|
||||
{
|
||||
e.SetEventType(mouseInside ? wxEVT_ENTER_WINDOW : wxEVT_LEAVE_WINDOW);
|
||||
|
||||
processed = ProcessWindowEvent( e ) && processed;
|
||||
|
||||
}
|
||||
}
|
||||
else // No mouse button is pressed
|
||||
{
|
||||
s_targetHandler = nullptr; // reset
|
||||
}
|
||||
|
||||
m_mouseInside = mouseInside;
|
||||
}
|
||||
|
||||
m_mouseInside = mouseInside;
|
||||
|
||||
return handled;
|
||||
return processed;
|
||||
}
|
||||
|
||||
bool wxWindowQt::QtHandleEnterEvent ( QWidget *handler, QEvent *event )
|
||||
{
|
||||
wxMouseEvent e( event->type() == QEvent::Enter ? wxEVT_ENTER_WINDOW : wxEVT_LEAVE_WINDOW );
|
||||
static QWidget* s_handlerParent = nullptr;
|
||||
|
||||
const bool isEnterEvent = event->type() == QEvent::Enter;
|
||||
|
||||
// Notice that Qt doesn't generate Enter/Leave events for parent widget when
|
||||
// the mouse enters/leaves a child widget. And for consistency with the wx
|
||||
// documentation, we should generate the events manually for s_handlerParent.
|
||||
if ( s_handlerParent != handler )
|
||||
{
|
||||
s_handlerParent = handler->parentWidget();
|
||||
|
||||
if ( s_handlerParent )
|
||||
{
|
||||
QEvent qtEvent(isEnterEvent ? QEvent::Leave : QEvent::Enter);
|
||||
QApplication::sendEvent(s_handlerParent, &qtEvent);
|
||||
}
|
||||
}
|
||||
else // s_handlerParent == handler
|
||||
{
|
||||
if ( isEnterEvent && !s_handlerParent->underMouse() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
s_handlerParent = nullptr;
|
||||
|
||||
wxMouseEvent e( isEnterEvent ? wxEVT_ENTER_WINDOW : wxEVT_LEAVE_WINDOW );
|
||||
e.m_clickCount = 0;
|
||||
e.SetPosition( wxQtConvertPoint( handler->mapFromGlobal( QCursor::pos() ) ) );
|
||||
e.SetEventObject(this);
|
||||
|
||||
@@ -251,6 +251,7 @@ TEST_GUI_OBJECTS = \
|
||||
test_gui_windowtest.o \
|
||||
test_gui_dialogtest.o \
|
||||
test_gui_clone.o \
|
||||
test_gui_enterleave.o \
|
||||
test_gui_evtlooptest.o \
|
||||
test_gui_propagation.o \
|
||||
test_gui_keyboard.o \
|
||||
@@ -1165,6 +1166,9 @@ test_gui_dialogtest.o: $(srcdir)/controls/dialogtest.cpp $(TEST_GUI_ODEP)
|
||||
test_gui_clone.o: $(srcdir)/events/clone.cpp $(TEST_GUI_ODEP)
|
||||
$(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/events/clone.cpp
|
||||
|
||||
test_gui_enterleave.o: $(srcdir)/events/enterleave.cpp $(TEST_GUI_ODEP)
|
||||
$(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/events/enterleave.cpp
|
||||
|
||||
test_gui_evtlooptest.o: $(srcdir)/events/evtlooptest.cpp $(TEST_GUI_ODEP)
|
||||
$(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/events/evtlooptest.cpp
|
||||
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: tests/events/enterleave.cpp
|
||||
// Purpose: Test wxEVT_ENTER_WINDOW and wxEVT_LEAVE_WINDOW events
|
||||
// Author: Ali Kettab
|
||||
// Created: 2024-10-16
|
||||
// Copyright: (c) 2024 wxWidgets team
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// headers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "testprec.h"
|
||||
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/app.h"
|
||||
#include "wx/button.h"
|
||||
#include "wx/panel.h"
|
||||
#include "wx/textctrl.h"
|
||||
#include "wx/window.h"
|
||||
#endif // WX_PRECOMP
|
||||
|
||||
#include "wx/uiaction.h"
|
||||
|
||||
#include "asserthelper.h"
|
||||
#include "testableframe.h"
|
||||
#include "waitfor.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// tests themselves
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if wxUSE_UIACTIONSIMULATOR
|
||||
|
||||
TEST_CASE("EnterLeaveEvents", "[wxEvent][enter-leave]")
|
||||
{
|
||||
if ( !EnableUITests() )
|
||||
{
|
||||
WARN("Skipping wxEVT_{ENTER,LEAVE}_WINDOW tests: wxUIActionSimulator not available");
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<wxPanel>
|
||||
panel(new wxPanel(wxTheApp->GetTopWindow(), wxID_ANY));
|
||||
auto button = new wxButton(panel.get(), wxID_ANY, "button", {50, 50});
|
||||
auto textctrl = new wxTextCtrl(panel.get(), wxID_ANY, "", {160, 50});
|
||||
|
||||
EventCounter enter(panel.get(), wxEVT_ENTER_WINDOW);
|
||||
EventCounter leave(panel.get(), wxEVT_LEAVE_WINDOW);
|
||||
|
||||
// Wait for the first paint event to be sure that panel really
|
||||
// has its final size.
|
||||
WaitForPaint waitForPaint(panel.get());
|
||||
panel->SendSizeEventToParent();
|
||||
waitForPaint.YieldUntilPainted();
|
||||
|
||||
wxUIActionSimulator sim;
|
||||
|
||||
SECTION("Without mouse capture")
|
||||
{
|
||||
sim.MouseMove(panel->GetScreenPosition() + wxPoint(5, 5));
|
||||
wxYield();
|
||||
|
||||
CHECK( enter.GetCount() == 1 );
|
||||
CHECK( leave.GetCount() == 0 );
|
||||
|
||||
enter.Clear();
|
||||
|
||||
sim.MouseMove(button->GetScreenPosition() + wxPoint(5, 5));
|
||||
wxYield();
|
||||
|
||||
// The parent window (panel) should receive wxEVT_LEAVE_WINDOW event
|
||||
// when mouse enters the child window (button)
|
||||
CHECK( enter.GetCount() == 0 );
|
||||
CHECK( leave.GetCount() == 1 );
|
||||
|
||||
leave.Clear();
|
||||
|
||||
sim.MouseMove(panel->GetScreenPosition() + wxPoint(5, 5));
|
||||
wxYield();
|
||||
|
||||
// Now it (panel) should receive wxEVT_ENTER_WINDOW event when
|
||||
// the mouse leaves the button and enters the panel again.
|
||||
CHECK( enter.GetCount() == 1 );
|
||||
CHECK( leave.GetCount() == 0 );
|
||||
}
|
||||
|
||||
SECTION("With (implicit) mouse capture")
|
||||
{
|
||||
// Just to be sure that the button is really shown
|
||||
EventCounter clicked(button, wxEVT_BUTTON);
|
||||
|
||||
sim.MouseMove(button->GetScreenPosition() + wxPoint(5, 5));
|
||||
wxYield();
|
||||
|
||||
sim.MouseClick();
|
||||
wxYield();
|
||||
|
||||
CHECK( clicked.GetCount() == 1 );
|
||||
|
||||
enter.Clear();
|
||||
leave.Clear();
|
||||
|
||||
sim.MouseDown();
|
||||
wxYield();
|
||||
|
||||
#if defined(__WXGTK__) && !defined(__WXGTK3__)
|
||||
if ( IsAutomaticTest() )
|
||||
{
|
||||
WARN("Skipping tests known to fail under GitHub Actions");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
sim.MouseMove(button->GetScreenPosition() + wxPoint(10, 5));
|
||||
wxYield();
|
||||
|
||||
// Holding the mouse button down (initiated on the button) and then
|
||||
// hovering over the panel should not generate any events (enter/leave)
|
||||
// Additionally, entering and leaving another child (textctrl) while the
|
||||
// mouse is still held down should also not generate any events.
|
||||
|
||||
sim.MouseMove(panel->GetScreenPosition() + wxPoint(5, 5));
|
||||
wxYield();
|
||||
|
||||
CHECK( enter.GetCount() == 0 );
|
||||
CHECK( leave.GetCount() == 0 );
|
||||
|
||||
sim.MouseMove(textctrl->GetScreenPosition() + wxPoint(5, 5));
|
||||
wxYield();
|
||||
|
||||
CHECK( enter.GetCount() == 0 );
|
||||
CHECK( leave.GetCount() == 0 );
|
||||
|
||||
sim.MouseMove(panel->GetScreenPosition() + wxPoint(5, 5));
|
||||
wxYield();
|
||||
|
||||
CHECK( enter.GetCount() == 0 );
|
||||
CHECK( leave.GetCount() == 0 );
|
||||
|
||||
sim.MouseUp();
|
||||
wxYield();
|
||||
|
||||
// wxGTK behaves differently here, as it does not generate a
|
||||
// wxEVT_ENTER_WINDOW event when we release the mouse button.
|
||||
|
||||
#ifndef __WXGTK__
|
||||
CHECK( enter.GetCount() == 1 );
|
||||
#else
|
||||
CHECK( enter.GetCount() == 0 );
|
||||
#endif
|
||||
CHECK( leave.GetCount() == 0 );
|
||||
}
|
||||
}
|
||||
|
||||
#endif // wxUSE_UIACTIONSIMULATOR
|
||||
@@ -224,6 +224,7 @@ TEST_GUI_OBJECTS = \
|
||||
$(OBJS)\test_gui_windowtest.o \
|
||||
$(OBJS)\test_gui_dialogtest.o \
|
||||
$(OBJS)\test_gui_clone.o \
|
||||
$(OBJS)\test_gui_enterleave.o \
|
||||
$(OBJS)\test_gui_evtlooptest.o \
|
||||
$(OBJS)\test_gui_propagation.o \
|
||||
$(OBJS)\test_gui_keyboard.o \
|
||||
@@ -1109,6 +1110,9 @@ $(OBJS)\test_gui_dialogtest.o: ./controls/dialogtest.cpp
|
||||
$(OBJS)\test_gui_clone.o: ./events/clone.cpp
|
||||
$(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $<
|
||||
|
||||
$(OBJS)\test_gui_enterleave.o: ./events/enterleave.cpp
|
||||
$(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $<
|
||||
|
||||
$(OBJS)\test_gui_evtlooptest.o: ./events/evtlooptest.cpp
|
||||
$(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $<
|
||||
|
||||
|
||||
@@ -239,6 +239,7 @@ TEST_GUI_OBJECTS = \
|
||||
$(OBJS)\test_gui_windowtest.obj \
|
||||
$(OBJS)\test_gui_dialogtest.obj \
|
||||
$(OBJS)\test_gui_clone.obj \
|
||||
$(OBJS)\test_gui_enterleave.obj \
|
||||
$(OBJS)\test_gui_evtlooptest.obj \
|
||||
$(OBJS)\test_gui_propagation.obj \
|
||||
$(OBJS)\test_gui_keyboard.obj \
|
||||
@@ -1410,6 +1411,9 @@ $(OBJS)\test_gui_dialogtest.obj: .\controls\dialogtest.cpp
|
||||
$(OBJS)\test_gui_clone.obj: .\events\clone.cpp
|
||||
$(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\events\clone.cpp
|
||||
|
||||
$(OBJS)\test_gui_enterleave.obj: .\events\enterleave.cpp
|
||||
$(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\events\enterleave.cpp
|
||||
|
||||
$(OBJS)\test_gui_evtlooptest.obj: .\events\evtlooptest.cpp
|
||||
$(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\events\evtlooptest.cpp
|
||||
|
||||
|
||||
@@ -261,6 +261,7 @@
|
||||
controls/windowtest.cpp
|
||||
controls/dialogtest.cpp
|
||||
events/clone.cpp
|
||||
events/enterleave.cpp
|
||||
<!--
|
||||
Duplicate this file here to test GUI event loops too.
|
||||
-->
|
||||
|
||||
Reference in New Issue
Block a user