From 7eea971ca4233cb4bea4d597bc091e8b954b0bc5 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Fri, 28 Feb 2025 09:09:40 +0100 Subject: [PATCH] wxQt: Fix Qt objects destruction Fix crashes start to appear when porting to Qt version 6.x --- include/wx/qt/private/winevent.h | 91 ++++++++++++++------------------ include/wx/qt/stattext.h | 2 + src/qt/anybutton.cpp | 23 ++++---- src/qt/stattext.cpp | 18 +++++++ src/qt/window.cpp | 23 ++------ 5 files changed, 79 insertions(+), 78 deletions(-) diff --git a/include/wx/qt/private/winevent.h b/include/wx/qt/private/winevent.h index 564816f04d..90be456432 100644 --- a/include/wx/qt/private/winevent.h +++ b/include/wx/qt/private/winevent.h @@ -83,17 +83,9 @@ public: // Set immediately as it is used to check if wxWindow is alive wxWindow::QtStoreWindowPointer( this, handler ); - // Handle QWidget destruction signal AFTER it gets deleted - QObject::connect( this, &QObject::destroyed, this, - &wxQtEventSignalHandler::HandleDestroyedSignal ); - Widget::setMouseTracking(true); } - void HandleDestroyedSignal() - { - } - virtual Handler *GetHandler() const override { // Only process the signal / event if the wxWindow is not destroyed @@ -383,42 +375,44 @@ protected: bool touchEvent(QTouchEvent *touch) { - wxWindow *win = wxWindow::QtRetrieveWindowPointer(this); bool handled = false; - for (const QTouchEvent::TouchPoint& tp : touch->touchPoints()) + if ( wxWindow *win = wxWindow::QtRetrieveWindowPointer(this) ) { - wxEventType evtype = wxEVT_NULL; - - switch (tp.state()) + for (const QTouchEvent::TouchPoint& tp : touch->touchPoints()) { - case Qt::TouchPointPressed: - evtype = wxEVT_TOUCH_BEGIN; - break; + wxEventType evtype = wxEVT_NULL; - case Qt::TouchPointMoved: - evtype = wxEVT_TOUCH_MOVE; - break; - case Qt::TouchPointReleased: - evtype = wxEVT_TOUCH_END; - break; + switch (tp.state()) + { + case Qt::TouchPointPressed: + evtype = wxEVT_TOUCH_BEGIN; + break; - default: - continue; + case Qt::TouchPointMoved: + evtype = wxEVT_TOUCH_MOVE; + break; + case Qt::TouchPointReleased: + evtype = wxEVT_TOUCH_END; + break; + + default: + continue; + } + + wxMultiTouchEvent evt(win->GetId(), evtype); + + // Use screen position as the event might originate from a different + // Qt window than this one. + wxPoint2DDouble pt = wxQtConvertPointF(tp.screenPos().toPoint()); + wxPoint ref = pt.GetFloor(); + + evt.SetPosition(win->ScreenToClient(ref) + (pt - ref)); + evt.SetSequenceId(wxTouchSequenceId(wxUIntToPtr((unsigned)tp.id()))); + // Qt doesn't provide the primary point flag + + handled |= win->ProcessWindowEvent(evt); } - - wxMultiTouchEvent evt(win->GetId(), evtype); - - // Use screen position as the event might originate from a different - // Qt window than this one. - wxPoint2DDouble pt = wxQtConvertPointF(tp.screenPos().toPoint()); - wxPoint ref = pt.GetFloor(); - - evt.SetPosition(win->ScreenToClient(ref) + (pt - ref)); - evt.SetSequenceId(wxTouchSequenceId(wxUIntToPtr((unsigned)tp.id()))); - // Qt doesn't provide the primary point flag - - handled |= win->ProcessWindowEvent(evt); } return handled; @@ -448,11 +442,9 @@ protected: void tapandholdTriggered(QTapAndHoldGesture *gesture, QEvent *event) { - wxWindow *win = wxWindow::QtRetrieveWindowPointer( this ); - - if (gesture->state() == Qt::GestureFinished) + if ( wxWindow *win = wxWindow::QtRetrieveWindowPointer( this ) ) { - if ( win ) + if (gesture->state() == Qt::GestureFinished) { wxLongPressEvent ev(win->GetId()); ev.SetPosition( wxQtConvertPoint( gesture->position().toPoint() ) ); @@ -461,15 +453,14 @@ protected: win->ProcessWindowEvent( ev ); event->accept(); } - - } - else if (gesture->state() == Qt::GestureStarted) - { - event->accept(); - } - else - { - event->accept(); + else if (gesture->state() == Qt::GestureStarted) + { + event->accept(); + } + else + { + event->accept(); + } } } diff --git a/include/wx/qt/stattext.h b/include/wx/qt/stattext.h index 9bd3b8ec4e..eab30a2e03 100644 --- a/include/wx/qt/stattext.h +++ b/include/wx/qt/stattext.h @@ -23,6 +23,8 @@ public: long style = 0, const wxString &name = wxASCII_STR(wxStaticTextNameStr) ); + ~wxStaticText(); + bool Create(wxWindow *parent, wxWindowID id, const wxString &label, diff --git a/src/qt/anybutton.cpp b/src/qt/anybutton.cpp index fc3b3bada3..6b9166f329 100644 --- a/src/qt/anybutton.cpp +++ b/src/qt/anybutton.cpp @@ -61,17 +61,20 @@ void wxQtPushButton::action() bool wxQtPushButton::event(QEvent* e) { - switch ( e->type() ) + if ( GetHandler() ) { - case QEvent::EnabledChange: - case QEvent::Enter: - case QEvent::Leave: - case QEvent::FocusIn: - case QEvent::FocusOut: - GetHandler()->QtUpdateState(); - break; - default: - break; + switch ( e->type() ) + { + case QEvent::EnabledChange: + case QEvent::Enter: + case QEvent::Leave: + case QEvent::FocusIn: + case QEvent::FocusOut: + GetHandler()->QtUpdateState(); + break; + default: + break; + } } return QPushButton::event(e); diff --git a/src/qt/stattext.cpp b/src/qt/stattext.cpp index ce70094582..3ecc107174 100644 --- a/src/qt/stattext.cpp +++ b/src/qt/stattext.cpp @@ -35,6 +35,24 @@ wxStaticText::wxStaticText(wxWindow *parent, Create( parent, id, label, pos, size, style, name ); } +wxStaticText::~wxStaticText() +{ + // Dissociate the buddy before QLabel get destroyed to avoid this assertion: + // + // ASSERT failure in QLabel: "Called object is not of the correct type (class + // destructor may have already run)", file... + // + // Explanation: + // ------------ + // When setBuddy() is called to set the buddy (see Create() below), Qt (internally) + // connects the QLabel to the QObject::destroyed() signal to be notified of the + // buddy's destruction and to dissociate it. Since the QLabel and its buddy are + // the same object, setBuddy() will be called on an already destroyed object, producing + // the aforementioned assertion message. + + GetQLabel()->setBuddy( nullptr ); +} + bool wxStaticText::Create(wxWindow *parent, wxWindowID id, const wxString &label, diff --git a/src/qt/window.cpp b/src/qt/window.cpp index f011d947f6..47a71069ae 100644 --- a/src/qt/window.cpp +++ b/src/qt/window.cpp @@ -350,36 +350,23 @@ wxWindowQt::~wxWindowQt() { if ( !m_qtWindow ) { - wxLogTrace(TRACE_QT_WINDOW, wxT("wxWindow::~wxWindow %s m_qtWindow is null"), GetName()); + // Pseudo windows don't have a valid m_qtWindow, so just return. return; } - // Delete only if the qt widget was created or assigned to this base class - wxLogTrace(TRACE_QT_WINDOW, wxT("wxWindow::~wxWindow %s m_qtWindow=%p"), GetName(), m_qtWindow); - - if ( !IsBeingDeleted() ) - { - SendDestroyEvent(); - } - - // Avoid processing pending events which quite often would lead to crashes after this. - QCoreApplication::removePostedEvents(m_qtWindow); - - // Block signals because the handlers access members of a derived class. - m_qtWindow->blockSignals(true); - if ( s_capturedWindow == this ) s_capturedWindow = nullptr; - DestroyChildren(); // This also destroys scrollbars + SendDestroyEvent(); - if (m_qtWindow) - QtStoreWindowPointer( GetHandle(), nullptr ); + QtStoreWindowPointer( GetHandle(), nullptr ); #if wxUSE_DRAG_AND_DROP SetDropTarget(nullptr); #endif + DestroyChildren(); // This also destroys scrollbars + delete m_qtWindow; }