You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:17:07