Qt无边框窗口结合WinAPI后最大化尺寸超出availableGeometry()
Qt无边框窗口结合WinAPI实现Aero Snap时最大化窗口位置和尺寸异常问题
使用Qt无边框窗口结合WinAPI实现Aero Snap等特性时,窗口最大化后左上角坐标为负值(x和y均为-9),且尺寸超出屏幕可用区域。
当前窗口样式与属性设置
setFlags(Qt::Window | Qt::FramelessWindowHint); SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN);
WS_CAPTION等价于WS_BORDER | WS_DLGFRAME:
- 若移除
WS_DLGFRAME,最大化尺寸恢复正常,但原生最大化动画消失,且调整窗口大小时因WS_SIZEBOX(与WS_THICKFRAME等价)会出现白色边框闪烁; - 若移除
WS_SIZEBOX则会丢失Aero Snap功能。
尝试过的解决方案及问题
1. 在nativeEvent中拦截WM_GETMINMAXINFO调整尺寸
bool FramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) { if (auto* msg = static_cast<MSG*>(message); msg->message == WM_NCCALCSIZE) { NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam); if (params.rgrc[0].top != 0) { --params.rgrc[0].top; } *result = WVR_REDRAW; return true; //移除原生边框和标题栏 } else if (msg->message == WM_GETMINMAXINFO) { if (IsMaximized((HWND)winId())) { MINMAXINFO* mmi = (MINMAXINFO*)msg->lParam; auto g = screen()->availableGeometry(); mmi->ptMaxTrackSize.x = g.width() * devicePixelRatio(); mmi->ptMaxTrackSize.y = g.height() * devicePixelRatio(); *result = 0; return true; } } return QQuickWindow::nativeEvent(eventType, message, result); }
- 修改
MINMAXINFO无效果,除非设置mmi->ptMaxTrackSize.x = g.width() * devicePixelRatio() - 1;,此时尺寸接近正常,但窗口右侧与屏幕间会出现1像素间隙; - 初始的
ptMaxTrackSize值异常:mmi中为3462,1462,而availableGeometry中为3440,1390。
2. 窗口状态变化时调用setGeometry
bool FramelessWindow::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::WindowStateChange && QWindow::visibility() == QWindow::Maximized) { setGeometry(screen()->availableGeometry()); //窗口不再处于最大化状态 return true; } return QQuickWindow::eventFilter(watched, event); }
- 该方式会导致窗口失去
QWindow::Maximized状态,需要自行实现最大化、还原等逻辑; - 对Aero Snap触发的最大化无效。
3. 尝试添加边距
- 窗口实例在QML中创建,调整布局会导致缩放时闪烁;
- 类继承自
QQuickWindow,无法在C++中使用setLayout添加边距。
更新尝试:参考WinAPI方案修改WM_NCCALCSIZE
发现该问题并非Qt独有,是WinAPI无边框窗口的常见问题。参考现有实现编写adjustMaximized函数:
void adjustMaximized(HWND hwnd, RECT& rect) { auto monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL); if (!monitor) { return; } MONITORINFO monitor_info{}; monitor_info.cbSize = sizeof(monitor_info); if (!::GetMonitorInfoW(monitor, &monitor_info)) { return; } rect = monitor_info.rcWork; } bool FramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) { if (auto* msg = static_cast<MSG*>(message); msg->message == WM_NCCALCSIZE) { NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam); if (params.rgrc[0].top != 0) { --params.rgrc[0].top; } if (IsMaximized(hwnd_)) { adjustMaximized(hwnd_, params.rgrc[0]); } return true; } // 其他消息处理... }
结果更糟,窗口右上角位置异常。
请问我遗漏了什么关键点?
内容的提问来源于stack exchange,提问作者Bibasmall
相关产品推荐
相关产品推荐

