You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
92 lines
3.8 KiB
C++
92 lines
3.8 KiB
C++
#ifndef NM_DIALOG_PREVIEW_GUARD_H
|
|
#define NM_DIALOG_PREVIEW_GUARD_H
|
|
|
|
#include <QEvent>
|
|
#include <QPointer>
|
|
#include <QTimerEvent>
|
|
#include <QWidget>
|
|
|
|
// 修复对话框隐藏后仍残留在系统任务栏预览中的问题。
|
|
// 旧版界面库即使设置了系统隐藏属性,也可能继续复用上一次的窗口预览图像。
|
|
// 等隐藏事件处理完成后,通过界面库重建原生窗口,释放旧句柄对应的预览。
|
|
// 对话框对象、子控件、用户输入和计算任务的归属保持不变。
|
|
class nmDialogPreviewGuard : public QObject
|
|
{
|
|
public:
|
|
// 由被保护窗口持有本对象,窗口销毁时自动释放;调用时窗口必须有效。
|
|
explicit nmDialogPreviewGuard(QWidget* window)
|
|
: QObject(window), m_window(window), m_timerId(0), m_rebuilding(false)
|
|
{
|
|
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)
|
|
{
|
|
#ifdef Q_OS_WIN
|
|
if(object != m_window || m_rebuilding)
|
|
return QObject::eventFilter(object, event);
|
|
switch(event->type()) {
|
|
case QEvent::Hide:
|
|
// 最小化引起的隐藏来自系统,必须保留正常预览,不能重建其原生窗口。
|
|
// 使用零间隔定时器延后处理,避免在隐藏事件内部直接重建窗口。
|
|
if(!event->spontaneous() && !m_timerId && m_window->isWindow() &&
|
|
m_window->internalWinId()) m_timerId = startTimer(0);
|
|
break;
|
|
case QEvent::Show:
|
|
// 窗口可能在延后处理前就被重新打开,此时取消清理,避免影响可见窗口。
|
|
cancelPendingReset();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
return QObject::eventFilter(object, event);
|
|
}
|
|
|
|
// 隐藏完成后执行一次清理;再次检查窗口状态,避免处理已销毁或重新显示的窗口。
|
|
void timerEvent(QTimerEvent* event)
|
|
{
|
|
if(event->timerId() != m_timerId) {
|
|
QObject::timerEvent(event);
|
|
return;
|
|
}
|
|
resetHiddenWindow();
|
|
}
|
|
|
|
private:
|
|
// 清除尚未执行的重建请求,使定时处理保持为单次操作。
|
|
void cancelPendingReset()
|
|
{
|
|
if(m_timerId) killTimer(m_timerId);
|
|
m_timerId = 0;
|
|
}
|
|
|
|
QPointer<QWidget> m_window; // 被保护窗口的弱引用,窗口销毁后自动置空。
|
|
int m_timerId; // 等待执行的定时器编号,为零表示没有待处理请求。
|
|
bool m_rebuilding; // 重建期间屏蔽重复事件,避免递归触发清理。
|
|
};
|
|
|
|
#endif
|