英文:
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.
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);
//...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论