Frameless Qt + WinAPI 最大化窗口大小大于 availableGeometry()

问题描述 投票:0回答:1

我正在努力解决以下问题。我的窗口左上角在最大化时具有负坐标(x 和 y 均为 -9),并且它的大小实际上大于可用区域。 我正在使用 WinAPI 来获取一些特定于平台的功能,例如 Aero snap,因此理解这个问题发生的确切原因变得很复杂。 我的窗口设置了以下样式和属性:

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 
,我就会松开空气动力按扣。 我想也许我可以在
nativeEvent
中做一些最大混合尺寸的事情,所以我尝试了以下方法:

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; //this removes the native border and title
    }
    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 px 的空白间隙。所以我无法通过改变
MINMAXINFO
来获得正确的尺寸,因为真正正确的尺寸实际上应该与上面的代码相同。最初的 ptMaxTrackSize 非常奇怪:
mmi
中的 3462、1462 vs
availableGeometry
中的 3440、1390)。

我可以做类似的事情,但我想保持 QWindow::Maximized 状态,当我调用

setGeometry
时会重置该状态。

bool FramelessWindow::eventFilter(QObject* watched, QEvent* event)
{
    if (event->type() == QEvent::WindowStateChange && QWindow::visibility() == QWindow::Maximized)
    {
        setGeometry(screen()->availableGeometry()); //the window is no longer maximized
        return true;
    }

    return QQuickWindow::eventFilter(watched, event);
}

由于我无法在

QWindow::Maximized
方法中使用
setGeometry
,这种方法相当于实现我自己的最大化,归一化等,这是不可取的,也不会影响航空最大化。

我还尝试在最大化状态下添加边距,但是创建布局会在调整大小时导致难看的闪烁,因为我的窗口实例是在 QML 中创建的。我无法使用

setLayout
并尝试在 C++ 代码中添加边距,因为我的类继承了
QQuickWindow

我错过了什么吗?

更新: 现在我发现这个问题主要与WinAPI +无框有关,而不是与Qt有关,所以不仅仅是Qt用户有“窗口最大化时超出屏幕”的情况。 它有点帮助,至少我现在能够测试已经存在的 WinAPI 解决方案,但问题是它们不能按预期工作。

我尝试在处理

NCCALCSIZE_PARAMS
消息时修改
WM_NCCALCSIZE

这是我得到的

rgrc[0]

我根据melak47的代码尝试过的

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;
    }
...
}

我的右上角变得更糟:

c++ qt winapi
1个回答
0
投票

遗憾的是,我未能找到任何可行的 WinAPI 解决方案,所以我选择了 Qt 方式。这种方法并不像我期望的 WinAPI 方法那样简洁,但它确实有效。 这是仅描述必要部分的代码片段。

.hpp

class FramelessWindow : public QQuickWindow
{
    Q_OBJECT
    QML_ELEMENT
    Q_PROPERTY(bool isMaximized READ isMaximized NOTIFY isMaximizedChanged)

signals:
    void isMaximizedChanged();
public:
    FramelessWindow() noexcept;
    Q_INVOKABLE void showNormal() noexcept;
    Q_INVOKABLE void showMaximized() noexcept;
    bool isMaximized() const noexcept;

private:
    bool eventFilter(QObject* watched, QEvent* event) override;
    bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;

    QRect restoredGeometry_;
    bool isMaximized_;
};

.cpp

FramelessWindow::FramelessWindow() noexcept :
    isMaximized_ { false }
{
    setFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMaximizeButtonHint);
    installEventFilter(this);
    SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
}

void FramelessWindow::showNormal() noexcept
{
    setGeometry(restoredGeometry_);
    isMaximized_ = false;
    emit isMaximizedChanged();
}

void FramelessWindow::showMaximized() noexcept
{
    restoredGeometry_ = geometry();
    setGeometry(screen()->availableGeometry());
    isMaximized_ = true;
    emit isMaximizedChanged();
}

bool FramelessWindow::isMaximized() const noexcept
{
    return isMaximized_;
}

bool FramelessWindow::eventFilter(QObject* watched, QEvent* event)
{
    QPoint cursorPos = QCursor::pos();
    qreal dpr = devicePixelRatio();
    QRect draggingArea = geometry();
    draggingArea.setHeight(32 * dpr);
    draggingArea.setY(draggingArea.y() + dpr * ResizeBorderWidth);

    if (draggingArea.contains(cursorPos))
    {
        if (event->type() == QEvent::MouseButtonPress)
        {
            if (isMaximized_)
            {
                restoredGeometry_.moveTo({ QCursor::pos().x() - restoredGeometry_.width() / 2, QCursor::pos().y() - 10 });
                showNormal();
            }
            startSystemMove();
            return true;
        }
        else if (isResizable_ && event->type() == QEvent::MouseButtonDblClick)
        {
            if (draggingArea.contains(cursorPos))
            {
                if (isMaximized_)
                {
                    showNormal();
                }
                else
                {
                    showMaximized();
                }
                return true;
            }
        }
        else if (event->type() == QEvent::WindowStateChange && QWindow::visibility() == QWindow::Maximized)
        {
            setGeometry(screen()->availableGeometry());
            isMaximized_ = true;
            emit isMaximizedChanged();
            return true;
        }
    }

    return QQuickWindow::eventFilter(watched, event);
}

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 = 0;
        return true;
    }

    return QQuickWindow::nativeEvent(eventType, message, result);
}
© www.soinside.com 2019 - 2024. All rights reserved.