Is there a way to find out which widget consumed an event?
-
@Violet-Giraffe said in Is there a way to find out which widget consumed an event?:
this widget calls winId() to become a native window
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.my custom widget cannot get focus by clicking the mouse
Have you overridden
QWidget::event()
?
If so, does the overridereturn QWidget::event(event)
when a mouse event is delivered (and hopefully remains unconsumed)?
What doesqDebug() << myCustomWidget->testAttribute(Qt::WA_TransparentForMouseEvents)
say?@Axel-Spoerl
You're right, it must be related to the pointer events, but in a way that I cannot yet figure out. The thing is that thisQWidget
which doesn't get focus is acting as a host to a native widget with its own window procedure. That procedure wants to processWM_POINTERDOWN
and does get it! Despite the host widget not getting focus. And the same procedure also wants to processWM_KEYDOWN
and doesn't get it. The host widget doesn't get key events, too, unless manually focused by Tab. -
@Axel-Spoerl
You're right, it must be related to the pointer events, but in a way that I cannot yet figure out. The thing is that thisQWidget
which doesn't get focus is acting as a host to a native widget with its own window procedure. That procedure wants to processWM_POINTERDOWN
and does get it! Despite the host widget not getting focus. And the same procedure also wants to processWM_KEYDOWN
and doesn't get it. The host widget doesn't get key events, too, unless manually focused by Tab.@Violet-Giraffe
...then all the fellow widgets are proven innocent and the issue is in that custom widget implementation. Please share the code or mark this post solved. -
@Violet-Giraffe
...then all the fellow widgets are proven innocent and the issue is in that custom widget implementation. Please share the code or mark this post solved.Yes, you're right that Qt is not to blame, but could you please help me figure out how to solve the problem? The host widget looks like this:
template <class WindowT> class WindowContainer : public QWidget, public WindowT { public: WindowContainer(QWidget * parent) : QWidget{parent} { winId(); /// ensure native window is created setFocusPolicy(Qt::StrongFocus); } protected: bool nativeEvent(const QByteArray & eventType, void * message, long * result) override { const auto msg = static_cast<MSG *>(message); if (msg->message == WM_SHOWWINDOW) { if (msg->wParam == TRUE) { RECT rc; ::GetClientRect(msg->hwnd, &rc); ::SetWindowPos(WindowT::hWnd, HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOZORDER); } else { ::SetWindowPos(WindowT::hWnd, HWND_TOP, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOZORDER); } } return QWidget::nativeEvent(eventType, message, result); } };
If I remove the
WM_SHOWWINDOW
handling from thenativeEvent()
, then the host widget becomes focusable and gets key events. But the native window (WindowT::hWnd
) isn't displayed. Do you understand what exactly is happening?Specifically, I do not understand why the native windows gets
WM_POINTERDOWN
but doesn't getWM_KEYDOWN
.It seems that I need to manually set focus to the host widget upon receiving
WM_POINTERDOWN
, but why? That feels like a hack, not a solution. -
Yes, you're right that Qt is not to blame, but could you please help me figure out how to solve the problem? The host widget looks like this:
template <class WindowT> class WindowContainer : public QWidget, public WindowT { public: WindowContainer(QWidget * parent) : QWidget{parent} { winId(); /// ensure native window is created setFocusPolicy(Qt::StrongFocus); } protected: bool nativeEvent(const QByteArray & eventType, void * message, long * result) override { const auto msg = static_cast<MSG *>(message); if (msg->message == WM_SHOWWINDOW) { if (msg->wParam == TRUE) { RECT rc; ::GetClientRect(msg->hwnd, &rc); ::SetWindowPos(WindowT::hWnd, HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOZORDER); } else { ::SetWindowPos(WindowT::hWnd, HWND_TOP, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOZORDER); } } return QWidget::nativeEvent(eventType, message, result); } };
If I remove the
WM_SHOWWINDOW
handling from thenativeEvent()
, then the host widget becomes focusable and gets key events. But the native window (WindowT::hWnd
) isn't displayed. Do you understand what exactly is happening?Specifically, I do not understand why the native windows gets
WM_POINTERDOWN
but doesn't getWM_KEYDOWN
.It seems that I need to manually set focus to the host widget upon receiving
WM_POINTERDOWN
, but why? That feels like a hack, not a solution.P. S. This hack works to fix focus, and the host widget starts getting
keyPressEvent()
. But itsnativeEvent()
is not getting anything when keys are pressed. And neither does the underlying native window.
??? -
P. S. This hack works to fix focus, and the host widget starts getting
keyPressEvent()
. But itsnativeEvent()
is not getting anything when keys are pressed. And neither does the underlying native window.
???@Violet-Giraffe
Oh, didn't know that the code intercepts native events.The native WM doesn't know about Qt's focus chain. Therefore, WM_POINTERDOWN is easier delivered to the right window because it can be derived from the pointer position. WM_KEYDOWN can get delivered to the wrong window, when the native WM doesn't know about the right focus window. Even though MS-Windows is pretty synchronous and reliable, there are chances that event orders get mixed up and the native WM just makes a wrong assumption about where to deliver key events. Eventually, the wrong
nativeEvent
handler is called.A potential (and fairly solid) workaround is to override
event
in the custom widget, intercept thefocusAboutToChange
andfocusIn
events, to forcefully propagate focus to the native window. Since that stuff heavily depends on the screen layout, I can't promise that it works. But if it works, it can reliably fix the problem. Code could look as follows:bool customWidget::event(QEvent* event) { switch (event->type()) { case QEvent::FocusIn: case QEvent::FocusAboutToChange: if (hasFocus() && ::GetFocus() != reinterpret_cast<HWND>(winId())) { QWidget* nativeParent = this; while (nativeParent) { if (nativeParent->isWindow()) break; nativeParent = nativeParent->nativeParentWidget(); } if (nativeParent && nativeParent != this && ::GetFocus() == reinterpret_cast<HWND>(nativeParent->winId())) { ::SetFocus(reinterpret_cast<HWND>(winId())); } break; } default: break; } return QWidget::event(event); }
-
@Violet-Giraffe
Oh, didn't know that the code intercepts native events.The native WM doesn't know about Qt's focus chain. Therefore, WM_POINTERDOWN is easier delivered to the right window because it can be derived from the pointer position. WM_KEYDOWN can get delivered to the wrong window, when the native WM doesn't know about the right focus window. Even though MS-Windows is pretty synchronous and reliable, there are chances that event orders get mixed up and the native WM just makes a wrong assumption about where to deliver key events. Eventually, the wrong
nativeEvent
handler is called.A potential (and fairly solid) workaround is to override
event
in the custom widget, intercept thefocusAboutToChange
andfocusIn
events, to forcefully propagate focus to the native window. Since that stuff heavily depends on the screen layout, I can't promise that it works. But if it works, it can reliably fix the problem. Code could look as follows:bool customWidget::event(QEvent* event) { switch (event->type()) { case QEvent::FocusIn: case QEvent::FocusAboutToChange: if (hasFocus() && ::GetFocus() != reinterpret_cast<HWND>(winId())) { QWidget* nativeParent = this; while (nativeParent) { if (nativeParent->isWindow()) break; nativeParent = nativeParent->nativeParentWidget(); } if (nativeParent && nativeParent != this && ::GetFocus() == reinterpret_cast<HWND>(nativeParent->winId())) { ::SetFocus(reinterpret_cast<HWND>(winId())); } break; } default: break; } return QWidget::event(event); }
@Axel-Spoerl, thank you!
So basically, there is no way to figure out why the native window doesn't want to receive focus? And no way to figure out why theQWidget
host doesn't want to receive focus on mouse click either? These are probably two different issues, because solving the latter doesn't fix the former. -
@Axel-Spoerl, thank you!
So basically, there is no way to figure out why the native window doesn't want to receive focus? And no way to figure out why theQWidget
host doesn't want to receive focus on mouse click either? These are probably two different issues, because solving the latter doesn't fix the former.@Violet-Giraffe
What I love (under more) about C++: Where there is smoke, there is fire.
If the research takes a little longer, the answer is probably 42 ;-)But to isolate the problem, we'd need to see and understand the full picture, rather than a few snippets of code.
What is the purpose of the custom widget, how is it implemented and how does it interact with the other widgets?
Are eventFilters and event overrides in place? Are any of the parent widgets heavy consumers of mouse and key events?
What's the reason and the purpose for descending into the rabbit hole of native events? What's happening there, that cannot be seen or done from the Qt event loop? Just the simple coincidence that (a) native events are intercepted and (b) a custom widget doesn't get certain events delivered, would make me suspicious. And please forgive my bluntness: Disclosing the native adventure only after eliminating other paths, is even more food for doubt. -
-
@Violet-Giraffe said in Is there a way to find out which widget consumed an event?:
this widget calls winId() to become a native window
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.my custom widget cannot get focus by clicking the mouse
Have you overridden
QWidget::event()
?
If so, does the overridereturn QWidget::event(event)
when a mouse event is delivered (and hopefully remains unconsumed)?
What doesqDebug() << myCustomWidget->testAttribute(Qt::WA_TransparentForMouseEvents)
say?Hi,
@Axel-Spoerl said in Is there a way to find out which widget consumed an event?:
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.Aren't you contradicting point 4 of Native Widgets VS Alien Widgets in QWidget's documentation ?
-
Hi,
@Axel-Spoerl said in Is there a way to find out which widget consumed an event?:
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.Aren't you contradicting point 4 of Native Widgets VS Alien Widgets in QWidget's documentation ?
@SGaist
You are actually right. I was referring to the properties of a QWidget, not to what happens on the WM side. At the point when I wrote the comment, I was unaware that there is native stuff involved. But the extended context shows my statement was imprecise. Thanks for bringing that up. -
Hi,
@Axel-Spoerl said in Is there a way to find out which widget consumed an event?:
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.Aren't you contradicting point 4 of Native Widgets VS Alien Widgets in QWidget's documentation ?
@SGaist said in Is there a way to find out which widget consumed an event?:
Aren't you contradicting point 4 of Native Widgets VS Alien Widgets in QWidget's documentation ?
Thanks for the clarification, that's exactly what I meant. And after Axel's response I started thinking maybe that is no longer the case in new versions of Qt or something, so it's great we cleared that up