From 27dc5f12b1df2de590746306c52903af2607e017 Mon Sep 17 00:00:00 2001 From: renbin Date: Wed, 3 Jan 2024 11:40:40 +0800 Subject: [PATCH] fix: Crash when show/close setting dialog. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 设置弹窗在构建时设置了父控件,而父控件(终端窗口)销毁 时同时销毁设置弹窗.导致关闭后在其它终端窗口打开设置 弹窗时,访问已销毁的弹窗对象,程序崩溃. 调整设置弹窗生命周期由 Service 维护,不过仍需要设置 父窗口以实现正常的显示坐标计算. Log: 修复在多个窗口上切换显示设置弹窗崩溃的问题. Bug: https://pms.uniontech.com/bug-view-28636.html Influence: SettingDialog --- src/main/service.cpp | 27 ++++++++++++++++++++++++++- src/main/service.h | 8 +++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/service.cpp b/src/main/service.cpp index 222f222ad..f430e13c7 100644 --- a/src/main/service.cpp +++ b/src/main/service.cpp @@ -76,6 +76,11 @@ void Service::releaseInstance() void Service::initSetting(MainWindow *pOwner) { if (nullptr != m_settingDialog) { + // 当前处于弹出状态,不调整焦点位置 + if (m_settingDialog->isVisible()) { + return; + } + //1050e版本:二次打开设置窗口,焦点在【关闭按钮】上(bug#104810) DTitlebar *titleBar = Utils::findWidgetByAccessibleName(m_settingDialog, "DSettingTitleBar"); QScrollArea *scrollArea = Utils::findWidgetByAccessibleName(m_settingDialog, "ContentScrollArea"); @@ -89,9 +94,12 @@ void Service::initSetting(MainWindow *pOwner) } QDateTime startTime = QDateTime::currentDateTime(); + // Warning: 此处虽然设置父控件,但是生命周期并不交由 pOwner 维护,因为终端存在多个同一层级的主窗体, + // 每次调用 showSettingDialog() 时都会重新设置父窗口 setParent(pOwner, Flags) + // 当前 pOwner 指向的主窗口销毁时,会优先调用 resetSettingOwner() 重设弹窗所有者,不会销毁 m_settingDialog 。 + // m_settingDialog 弹窗在 Service::~Service() 析构时判断销毁。 m_settingDialog = new DSettingsDialog(pOwner); m_settingDialog->setObjectName("SettingDialog"); - // 关闭后将指针置空,下次重新new connect(m_settingDialog, &DSettingsDialog::finished, this, &Service::slotSettingsDialogFinished); // 关闭时delete m_settingDialog->widgetFactory()->registerWidget("fontcombobox", Settings::createFontComBoBoxHandle); @@ -294,6 +302,7 @@ void Service::showSettingDialog(MainWindow *pOwner) initSetting(pOwner); //保存设置框的有拥者 m_settingOwner = pOwner; + if (nullptr != m_settingDialog) { //雷神需要让窗口置顶,可是普通窗口不要 if (m_settingOwner == WindowsManager::instance()->getQuakeWindow()) { @@ -311,6 +320,12 @@ void Service::showSettingDialog(MainWindow *pOwner) // 重新加载shell配置数据 Settings::instance()->reloadShellOptions(); + + // 重设当前弹窗的父窗口,在 DAbstractDialog / QDialog 的 show() / showEvent() 处理中, + // 均含有关联父窗口的坐标计算,若不设置父控件,需单独计算显示坐标。 + if (pOwner != m_settingDialog->parentWidget()) { + m_settingDialog->setParent(pOwner, m_settingDialog->windowFlags()); + } m_settingDialog->show(); } else { qCWarning(mainprocess) << "No setting dialog."; @@ -399,6 +414,16 @@ void Service::slotSettingShortcutConflictDialogFinished() m_settingShortcutConflictDialog = nullptr; } +void Service::resetSettingOwner() +{ + m_settingOwner = nullptr; + + // m_settingDialog 生命周期不由父控件维护,在 Service 析构时销毁 + if (m_settingDialog) { + m_settingDialog->setParent(nullptr); + } +} + bool Service::isCountEnable() { return WindowsManager::instance()->widgetCount() < MAXWIDGETCOUNT; diff --git a/src/main/service.h b/src/main/service.h index 7b6851677..1b757f326 100644 --- a/src/main/service.h +++ b/src/main/service.h @@ -90,12 +90,10 @@ class Service : public QObject } /** - * @brief 重置设置框的所有者 + * @brief 重置设置框的所有者,setParent(nullptr) + * @warning 父控件(终端窗口)关闭销毁前必须调用 */ - inline void resetSettingOwner() - { - m_settingOwner = nullptr; - } + void resetSettingOwner(); /** * @brief i从term数量的角度判断是否允许继续创建