From b00a68a8e19370e106cd76258a3c1825f43613ee Mon Sep 17 00:00:00 2001 From: Jan-Marek Glogowski Date: Sun, 31 Oct 2021 02:33:46 +0200 Subject: tdf#145363 Qt reparent modal dialogs on show Simply said, one can't have two modal dialogs open at the same parent in Qt. All modal windows must open as a stack, each parented to the previous one. This is kind of logical. Unexpectedly Qt totally breaks, if you open two modal dialogs on the same window. This happens, because the existing paragraph style dialog and the "sub" "list style" dialog are both children of their Writer window. I'm not sure the additionally introduced QWidget-based parent handling is strictly needed. It seems Ok. So for every visibility and modality change, we reparent the Qt widget, either on top of the modal stack or restore the original LO-based parent. The LO hierachy is never changed! Change-Id: Id209c9aa67774440089dc50a6648ac293950087a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124500 Tested-by: Jenkins Reviewed-by: Jan-Marek Glogowski --- vcl/inc/qt5/QtFrame.hxx | 1 + vcl/qt5/QtFrame.cxx | 80 +++++++++++++++++++++++++++++++++++++++++++------ vcl/qt5/QtWidget.cxx | 6 +++- 3 files changed, 77 insertions(+), 10 deletions(-) diff --git a/vcl/inc/qt5/QtFrame.hxx b/vcl/inc/qt5/QtFrame.hxx index 23c92d7e1da3..a338b106eaeb 100644 --- a/vcl/inc/qt5/QtFrame.hxx +++ b/vcl/inc/qt5/QtFrame.hxx @@ -132,6 +132,7 @@ class VCLPLUG_QT_PUBLIC QtFrame : public QObject, public SalFrame void SetWindowStateImpl(Qt::WindowStates eState); void fixICCCMwindowGroup(); + void modalReparent(bool bVisible); public: QtFrame(QtFrame* pParent, SalFrameStyleFlags nSalFrameStyle, bool bUseCairo); diff --git a/vcl/qt5/QtFrame.cxx b/vcl/qt5/QtFrame.cxx index 73bdad874552..1ae4016d589d 100644 --- a/vcl/qt5/QtFrame.cxx +++ b/vcl/qt5/QtFrame.cxx @@ -417,24 +417,71 @@ void QtFrame::DrawMenuBar() { /* not needed */} void QtFrame::SetExtendedFrameStyle(SalExtStyle /*nExtStyle*/) { /* not needed */} +void QtFrame::modalReparent(bool bVisible) +{ +#ifndef NDEBUG + auto* pSalInst(static_cast(GetSalData()->m_pInstance)); + assert(pSalInst); + assert(pSalInst->IsMainThread()); + assert(!asChild()->isVisible()); + assert(asChild()->isModal()); +#endif + + if (!bVisible) + { + m_pQWidget->setParent(m_pParent ? m_pParent->asChild() : nullptr, + m_pQWidget->windowFlags()); + return; + } + + if (!QGuiApplication::modalWindow()) + return; + + QtInstance* pInst = static_cast(GetSalData()->m_pInstance); + for (auto* pFrame : pInst->getFrames()) + { + QWidget* pQWidget = static_cast(pFrame)->asChild(); + if (pQWidget->windowHandle() == QGuiApplication::modalWindow()) + { + m_pQWidget->setParent(pQWidget, m_pQWidget->windowFlags()); + break; + } + } +} + void QtFrame::Show(bool bVisible, bool bNoActivate) { assert(m_pQWidget); if (bVisible == asChild()->isVisible()) return; + auto* pSalInst(static_cast(GetSalData()->m_pInstance)); + assert(pSalInst); + + if (!bVisible) // hide + { + pSalInst->RunInMainThread([this]() { + asChild()->hide(); + if (m_pQWidget->isModal()) + modalReparent(false); + }); + return; + } + + // show SetDefaultSize(); SetDefaultPos(); - auto* pSalInst(static_cast(GetSalData()->m_pInstance)); - assert(pSalInst); - pSalInst->RunInMainThread([this, bVisible, bNoActivate]() { - asChild()->setVisible(bVisible); - asChild()->raise(); + pSalInst->RunInMainThread([this, bNoActivate]() { + QWidget* const pChild = asChild(); + if (m_pQWidget->isModal()) + modalReparent(true); + pChild->show(); + pChild->raise(); if (!bNoActivate && !isPopup()) { - asChild()->activateWindow(); - asChild()->setFocus(); + pChild->activateWindow(); + pChild->setFocus(); } }); } @@ -610,7 +657,7 @@ SalFrame* QtFrame::GetParent() const { return m_pParent; } void QtFrame::SetModal(bool bModal) { - if (!isWindow()) + if (!isWindow() || asChild()->isModal() == bModal) return; auto* pSalInst(static_cast(GetSalData()->m_pInstance)); @@ -622,12 +669,20 @@ void QtFrame::SetModal(bool bModal) // modality change is only effective if the window is hidden if (bWasVisible) + { pChild->hide(); + if (!bModal) + modalReparent(false); + } pChild->setWindowModality(bModal ? Qt::WindowModal : Qt::NonModal); if (bWasVisible) + { + if (bModal) + modalReparent(true); pChild->show(); + } }); } @@ -1180,7 +1235,14 @@ void QtFrame::SimulateKeyPress(sal_uInt16 nKeyCode) SAL_WARN("vcl.qt", "missing simulate keypress " << nKeyCode); } -void QtFrame::SetParent(SalFrame* pNewParent) { m_pParent = static_cast(pNewParent); } +void QtFrame::SetParent(SalFrame* pNewParent) +{ + if (m_pParent == pNewParent) + return; + m_pParent = static_cast(pNewParent); + if (!m_pTopLevel) + m_pQWidget->setParent(m_pParent->asChild(), m_pQWidget->windowFlags()); +} void QtFrame::SetPluginParent(SystemParentData* /*pNewParent*/) { diff --git a/vcl/qt5/QtWidget.cxx b/vcl/qt5/QtWidget.cxx index 44952f9318b0..85bcd814b03a 100644 --- a/vcl/qt5/QtWidget.cxx +++ b/vcl/qt5/QtWidget.cxx @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -616,7 +617,10 @@ void QtWidget::focusOutEvent(QFocusEvent*) } QtWidget::QtWidget(QtFrame& rFrame, Qt::WindowFlags f) - : QWidget(Q_NULLPTR, f) + : QWidget(!rFrame.GetTopLevelWindow() && rFrame.GetParent() + ? static_cast(rFrame.GetParent())->asChild() + : Q_NULLPTR, + f) , m_rFrame(rFrame) , m_bNonEmptyIMPreeditSeen(false) , m_nDeltaX(0) -- cgit