Qt子本机窗口阻止本机窗口父级的本机事件消息

huangapple go评论65阅读模式
英文:

Qt child native window blocks native event messages for a native window parent

问题

我有一个顶级无边框窗口,我希望我的应用程序具有Aero Snap Windows功能。所以我需要混合使用Qt和WinAPI。

我使用了这里的方法:https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window,它基于覆盖nativeEvent。所以我的无边框窗口是基于QMainWindow的,我重写了nativeEvent

bool CFramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) {
    MSG* msg = static_cast<MSG*>(message);
    auto tmp = msg->hwnd;
    //msg->hwnd = (HWND)this->m_winId;

    switch (msg->message)
    {
    case WM_NCCALCSIZE:
        // 一些代码
        return true;
    case WM_NCHITTEST:
        // 一些代码
        return true;
    case WM_GETMINMAXINFO:
        // 一些代码
        return false;
    default:
        return false;
    }
}

如果我的无边框包装器的中央小部件不是本地的,它就能正常工作。如果它是本地的(我需要它是本地的),则顶级窗口的nativeEvent的重新实现将无法获得所需的消息,因此小部件变得不可调整大小和不可移动。

如果我重写了中央小部件的nativeEvent,我会发现当我点击子部件时会收到正确的消息,但当我点击无边框父窗口时什么都不会发生。

我发现可以使用installNativeEventFilter,然后直接在nativeEventFilter()中调用CFramelessWindow::nativeEvent,以便将所需的消息传递到正确的位置,但结果看起来明显出现了问题。看起来我只调整了无边框包装器的子部件,而不是整个主窗口。

然后我发现,每个子元素都有自己的窗口句柄(HWND),例如,如果我点击菜单栏,它的HWND与主窗口的不同。我尝试使用msg->hwnd = (HWND)this->m_winId;,它确实修复了无边框功能和Aero Snap,但它破坏了应用程序的所有其他事件。

我真的陷入了困境。我做错了什么?

提前感谢。以防万一,我为术语使用不当道歉。

英文:

I have a top-level frameless window, and I want my application to have the Aero Snap Windows feature. So I needed to mix Qt and WinAPI.
I used the approach from here
https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window,
which is based on overriding nativeEvent. So my frameless window is based on QMainWindow and I override nativeEvent.

bool CFramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) {
MSG* msg = static_cast<MSG*>(message);
auto tmp = msg->hwnd;
//msg->hwnd = (HWND)this->m_winId;

switch (msg->message)
{
case WM_NCCALCSIZE:
{
    NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
    if (params.rgrc[0].top != 0)
        params.rgrc[0].top -= 1;

    return true;
}
case WM_NCHITTEST:
{
    *result = 0;

    const LONG border_width = m_borderWidth;
    RECT winrect;
    GetWindowRect(HWND(winId()), &winrect);

    long x = GET_X_LPARAM(msg->lParam);
    long y = GET_Y_LPARAM(msg->lParam);

    if (m_bResizeable)
    {

        bool resizeWidth = minimumWidth() != maximumWidth();
        bool resizeHeight = minimumHeight() != maximumHeight();

        if (resizeWidth)
        {
            //left border
            if (x >= winrect.left && x < winrect.left + border_width)
            {
                *result = HTLEFT;
            }
            //right border
            if (x < winrect.right && x >= winrect.right - border_width)
            {
                *result = HTRIGHT;
            }
        }
        if (resizeHeight)
        {
            //bottom border
            if (y < winrect.bottom && y >= winrect.bottom - border_width)
            {
                *result = HTBOTTOM;
            }
            //top border
            if (y >= winrect.top && y < winrect.top + border_width)
            {
                *result = HTTOP;
            }
        }
        if (resizeWidth && resizeHeight)
        {
            //bottom left corner
            if (x >= winrect.left && x < winrect.left + border_width &&
                y < winrect.bottom && y >= winrect.bottom - border_width)
            {
                *result = HTBOTTOMLEFT;
            }
            //bottom right corner
            if (x < winrect.right && x >= winrect.right - border_width &&
                y < winrect.bottom && y >= winrect.bottom - border_width)
            {
                *result = HTBOTTOMRIGHT;
            }
            //top left corner
            if (x >= winrect.left && x < winrect.left + border_width &&
                y >= winrect.top && y < winrect.top + border_width)
            {
                *result = HTTOPLEFT;
            }
            //top right corner
            if (x < winrect.right && x >= winrect.right - border_width &&
                y >= winrect.top && y < winrect.top + border_width)
            {
                *result = HTTOPRIGHT;
            }
        }
    }
    if (*result) return true;

    //*result still equals 0, that means the cursor locate OUTSIDE the frame area
    //but it may locate in titlebar area
    if (!m_titlebar) 
        return false;

    //support highdpi
    double dpr = this->devicePixelRatioF();
    QPoint pos = m_titlebar->mapFromGlobal(QPoint(x / dpr, y / dpr));

    if (!m_titlebar->rect().contains(pos)) return false;
    QWidget* child = m_titlebar->childAt(pos);
    if (!child)
    {
        *result = HTCAPTION;
        return true;
    }
    else 
    {
        if (m_whiteList.contains(child))
        {
            *result = HTCAPTION;
            return true;
        }
    }
    return false;
} 
case WM_GETMINMAXINFO:
{
    if (::IsZoomed(msg->hwnd)) 
    {
        RECT frame = { 0, 0, 0, 0 };
        AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);

        //record frame area data
        double dpr = this->devicePixelRatioF();

        m_frames.setLeft(abs(frame.left) / dpr + 0.5);
        m_frames.setTop(abs(frame.bottom) / dpr + 0.5);
        m_frames.setRight(abs(frame.right) / dpr + 0.5);
        m_frames.setBottom(abs(frame.bottom) / dpr + 0.5);

        QMainWindow::setContentsMargins(m_frames.left() + m_margins.left(), \
            m_frames.top() + m_margins.top(), \
            m_frames.right() + m_margins.right(), \
            m_frames.bottom() + m_margins.bottom());
        m_bJustMaximized = true;
    }
    else 
    {
        if (m_bJustMaximized)
        {
            QMainWindow::setContentsMargins(m_margins);
            m_frames = QMargins();
            m_bJustMaximized = false;
        }
    }
    return false;
}
default:
    return false;
}

}

It works like a charm, if the central widget of my frameless wrapper isn't native as well. If it is (I need it to be native), the reimplementation of nativeEvent of the top-level window doesn't get the required messages, so the widget becomes unresizable and unmovable.
If I override nativeEvent for the central widget, I see that I get the correct message when I hit the child, but I get nothing when I hit the frameless parent window.
I found that I could installNativeEventFilter and then call CFramelessWindow::nativeEvent directly inside nativeEventFilter(), so that I would actually pass the required message into the right place, but the result looks definetely broken. It looks like I resize only the child of the frameless wrapper, not the whole main window.
Qt子本机窗口阻止本机窗口父级的本机事件消息

Then I found, that every child element got it's own window descriptor (HWND), so, for example, if I hit the menubar, it has a different HWND than the main window has. I tried to use msg->hwnd = (HWND)this->m_winId;, and it indeed fixes the frameless features and aerosnap also, but it breakes all other events of the application.

I'm really stuck. What am I doing wrong?

Thanks in advance. Just in case, I apologize for the incorrect terminology.

答案1

得分: 2

以下是已翻译的内容:

这是解决了窗口无框子级描述符问题的方法:

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    a.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
    //...
}
英文:

This is what solved my problem with descriptors of any child of the frameless window:

int main(int argc, char *argv[]) {
QApplication a(argc, argv);
a.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
//...

}

huangapple
  • 本文由 发表于 2023年2月27日 01:29:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/75573802.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定