fix(nmNum): 修复结果窗口最小化预览及关闭重开状态

feature/Fix-TimeDependentSkin-20261029
lvjunjie 2 weeks ago
parent 10d3ce4116
commit cbc3e02b27

@ -1,4 +1,4 @@
#ifndef NM_DIALOG_PREVIEW_GUARD_H
#ifndef NM_DIALOG_PREVIEW_GUARD_H
#define NM_DIALOG_PREVIEW_GUARD_H
#include <QEvent>
@ -20,6 +20,26 @@ public:
window->installEventFilter(this);
}
// 已隐藏的窗口可立即清理,供结果窗在恢复尺寸前解除原生最小化状态。
void resetHiddenWindow()
{
cancelPendingReset();
if(m_window.isNull() || m_window->isVisible() || !m_window->isWindow() ||
!m_window->internalWinId()) return;
m_rebuilding = true;
// 重建原生窗口会触发事件,期间本对象可能被释放,用弱指针检查是否仍然存活。
QPointer<nmDialogPreviewGuard> alive(this);
const QRect geometry = m_window->geometry();
// 在当前界面库的系统后端,即使父对象和标志不变,双参数调用也会重建原生窗口。
// 子控件的原生父窗口由界面库同步调整,不能绕过界面库直接销毁系统句柄。
m_window->setParent(m_window->parentWidget(), m_window->windowFlags());
if(alive.isNull()) return;
// 恢复原有位置和尺寸,确保下次打开时窗口布局不因句柄重建而改变。
if(!m_window.isNull()) m_window->setGeometry(geometry);
if(!alive.isNull()) m_rebuilding = false;
}
protected:
// 只监听目标窗口的显式隐藏和重新显示;重建过程产生的事件不重复处理。
bool eventFilter(QObject* object, QEvent* event)
@ -52,21 +72,7 @@ protected:
QObject::timerEvent(event);
return;
}
cancelPendingReset();
if(m_window.isNull() || m_window->isVisible() || !m_window->isWindow() ||
!m_window->internalWinId()) return;
m_rebuilding = true;
// 重建原生窗口会触发事件,期间本对象可能被释放,用弱指针检查是否仍然存活。
QPointer<nmDialogPreviewGuard> alive(this);
const QRect geometry = m_window->geometry();
// 在当前界面库的系统后端,即使父对象和标志不变,双参数调用也会重建原生窗口。
// 子控件的原生父窗口由界面库同步调整,不能绕过界面库直接销毁系统句柄。
m_window->setParent(m_window->parentWidget(), m_window->windowFlags());
if(alive.isNull()) return;
// 恢复原有位置和尺寸,确保下次打开时窗口布局不因句柄重建而改变。
if(!m_window.isNull()) m_window->setGeometry(geometry);
if(!alive.isNull()) m_rebuilding = false;
resetHiddenWindow();
}
private:

@ -107,8 +107,100 @@
#include <QPointer>
#include <QtCore/qnumeric.h>
#ifdef Q_OS_WIN
#include <qt_windows.h>
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "gdi32.lib")
#endif
namespace
{
// 结果窗保留原生最小化小框,仅替换最小化期间的系统预览。
class nmResultDialog : public QDialog
{
public:
explicit nmResultDialog(QWidget* parent, Qt::WindowFlags flags)
: QDialog(parent, flags), m_previewGuard(new nmDialogPreviewGuard(this)),
m_nativeMinimized(false)
{
}
void hideAndRestoreGeometry()
{
// 先隐藏再恢复,避免关闭小框时在原位置短暂显示大窗。
if(!isMinimized()) return;
const QRect geometryBeforeMinimize = m_geometryBeforeMinimize;
const Qt::WindowStates restoredState = windowState() & ~Qt::WindowMinimized;
hide();
setWindowState(restoredState);
// 复用预览清理来重建隐藏句柄,否则 Qt 4 调整原生最小化窗口尺寸时会重新显示它。
m_previewGuard->resetHiddenWindow();
if(!(restoredState & Qt::WindowMaximized) && geometryBeforeMinimize.isValid())
setGeometry(geometryBeforeMinimize);
setWindowState(restoredState);
m_nativeMinimized = false;
}
#ifdef Q_OS_WIN
protected:
bool winEvent(MSG* message, long* result) override
{
// 主窗口恢复时仅显示原来的小框,避免系统默认处理将结果窗展开。
if(message->message == WM_SHOWWINDOW && message->wParam &&
message->lParam == SW_PARENTOPENING && IsIconic(message->hwnd)) {
ShowWindow(message->hwnd, SW_SHOWMINNOACTIVE);
*result = 0;
return true;
}
if(message->message == WM_SIZE) {
// 恢复或最大化后交还实时预览;窗口句柄重建后也由尺寸消息重新设置。
const BOOL minimized = message->wParam == SIZE_MINIMIZED;
// Qt 此时尚未处理最小化尺寸消息,仍保留大窗的客户区位置和尺寸。
if(minimized && !m_nativeMinimized) m_geometryBeforeMinimize = geometry();
m_nativeMinimized = minimized;
DwmSetWindowAttribute(message->hwnd, DWMWA_FORCE_ICONIC_REPRESENTATION,
&minimized, sizeof(minimized));
DwmSetWindowAttribute(message->hwnd, DWMWA_HAS_ICONIC_BITMAP,
&minimized, sizeof(minimized));
DwmInvalidateIconicBitmaps(message->hwnd);
}
if((message->message == WM_DWMSENDICONICTHUMBNAIL ||
message->message == WM_DWMSENDICONICLIVEPREVIEWBITMAP) &&
IsIconic(message->hwnd)) {
// 透明位图只作用于任务栏预览,不隐藏结果窗或替换其原生标题栏。
BITMAPINFO info = {0};
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = 1;
info.bmiHeader.biHeight = -1;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 32;
void* pixels = NULL;
HBITMAP bitmap = CreateDIBSection(NULL, &info, DIB_RGB_COLORS, &pixels, NULL, 0);
if(bitmap) {
*static_cast<DWORD*>(pixels) = 0;
const HRESULT status = message->message == WM_DWMSENDICONICTHUMBNAIL
? DwmSetIconicThumbnail(message->hwnd, bitmap, 0)
: DwmSetIconicLivePreviewBitmap(message->hwnd, bitmap, NULL, 0);
DeleteObject(bitmap);
if(SUCCEEDED(status)) {
*result = 0;
return true;
}
}
}
return QDialog::winEvent(message, result);
}
#endif
private:
nmDialogPreviewGuard* m_previewGuard;
QRect m_geometryBeforeMinimize;
bool m_nativeMinimized; // 主窗口恢复时可能重复通知最小化,不重复记录小框尺寸。
};
// 保留原来求解器单例的防重入语义:全局同一时间只允许一个DLL求解任务运行。
// QPointer会在QObject销毁后自动变空,避免保留已经释放的线程指针。
QPointer<nmCalculationDllPebiSolverTask> s_pRunningSolverTask;
@ -135,9 +227,8 @@ void clearResultCurveDataTables(iGuiPlot* plot)
void resetResultDialog(QDialog* window)
{
if(!window) return;
// 先恢复最小化状态再隐藏,避免隐藏后调整尺寸使系统重新显示空白窗口。
if(window->isMinimized())
window->setWindowState(window->windowState() & ~Qt::WindowMinimized);
// 最小化结果先在隐藏状态下恢复尺寸,再清理旧曲线。
static_cast<nmResultDialog*>(window)->hideAndRestoreGeometry();
window->hide();
foreach(iGuiPlot* plot, window->findChildren<iGuiPlot*>()) clearResultCurveDataTables(plot);
foreach(nmWxSensitivityShow* dialog, window->findChildren<nmWxSensitivityShow*>())
@ -1978,12 +2069,11 @@ void nmSubWndMain::onForecastDataChanged()
QDialog* nmSubWndMain::createResultDialog(const QString& name, const QString& title)
{
// 关闭只隐藏,窗口及绘图控件由所属分析统一销毁。
QDialog* dialog = new QDialog(m_pResultRefreshFitting.data(),
QDialog* dialog = new nmResultDialog(m_pResultRefreshFitting.data(),
Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);
dialog->setObjectName(name);
dialog->setWindowTitle(title);
dialog->setAttribute(Qt::WA_DeleteOnClose, false);
new nmDialogPreviewGuard(dialog);
dialog->resize(900, 650);
QVBoxLayout* layout = new QVBoxLayout(dialog);
layout->setContentsMargins(0, 0, 0, 0);
@ -2154,10 +2244,10 @@ void nmSubWndMain::onReadonlyCurveDoubleClicked(
bool nmSubWndMain::eventFilter(QObject* watched, QEvent* event)
{
// 最小化时先恢复窗口状态,再交给默认关闭流程隐藏,避免复用时丢失正常尺寸。
// 最小化结果在隐藏后恢复尺寸,关闭过程中不显示大窗。
QDialog* dialog = qobject_cast<QDialog*>(watched);
if(dialog && event->type() == QEvent::Close && dialog->isMinimized())
dialog->setWindowState(dialog->windowState() & ~Qt::WindowMinimized);
if(dialog && event->type() == QEvent::Close)
static_cast<nmResultDialog*>(dialog)->hideAndRestoreGeometry();
// 关闭结果对话框时同步关闭其曲线数据表、Show 和统计设置窗口。
if((event->type() == QEvent::Close || event->type() == QEvent::Hide) &&
qobject_cast<QDialog*>(watched)) {

Loading…
Cancel
Save