Unsolved Make QML subwindow behave like a context menu
-
I have a QML window with a custom dropdown menu implemented as a separate window:
import QtQuick 2.2 import QtQuick.Window 2.1 Window { id: mainWindow width: 200 height: 200 MouseArea { anchors.fill: parent hoverEnabled: true Text { text: "click here" } onClicked: { if(dropdown.visible) { dropdown.close(); } else { dropdown.x = mainWindow.x + 50; dropdown.y = mainWindow.y + 50; dropdown.visible = true; dropdown.requestActivate(); } } } Window { id: dropdown height: 200 width: 200 flags: Qt.Popup color: 'green' visible: false Text { text: "I should disappear when you click anywhere outside me" wrapMode: Text.WrapAnywhere anchors.left: parent.left anchors.right: parent.right anchors.margins: 5 color: "white" } onActiveChanged: { if (!active) { dropdown.close(); } } } }
I want the
dropdown
window to behave like a native context menu, this means it should be closed when the user clicks ANYWHERE outside the menu. It already works when you click on the desktop or switch to a different application (onActiveChanged), but it doesn't recognize when you click on the title bar ofmainWindow
, which it should (try right-clicking somewhere in your browser and then click the title bar, the menu will disappear).
I also tried settingmodality: Qt.ApplicationModal
on thedropdown
menu, which didn't work either. -
Using Qt Widgets, it works as expected:
#include <QWindow> #include <QApplication> #include <QMainWindow> int main(int argc, char **argv) { QApplication app(argc, argv); QMainWindow qWin; qWin.setFixedSize(640, 400); qWin.show(); QWidget qPopup(&qWin, Qt::Popup); qPopup.setFixedSize(320, 200); qPopup.show(); return app.exec(); }
Why does a Qt::Popup window in QML behave differently than a Qt::Popup window in Qt Widgets? Is this intentional or a bug?
As a workaround, I guess I will use the Qt Widgets approach to display a QQuickWidget with my custom QML menu, unless someone can tell me how to do this in pure QML?
-
First off, nice example. It's always nice to get something that actually works easily to test with. :)
Second, I did some playing and research and found out that when clicking the titlebar the active window is not changed. That is why your window isn't closing. It never gets notified it is inactive because it is not considered inactive when clicking the title bar.
Here is a bug with the same issue that was closed as a non issues because popup windows are not considered active:
https://bugreports.qt.io/browse/QTBUG-37010As for a solution, you are probably going to need to use C++ or at least a mouse handler in your C++ that reports the click on the title bar.
Also, it literally only happens with the title bar for me. Anywhere else I click either in the application window or outside of it causes the window to close. So you could just live with the fact it doesn't close if someone clicks the title bar only.
There may be some QML thing you can do to catch that titlebar click but I don't know it. You could use a mouse exit even when the mouse leaves the window it closes, but that isn't like a normal context menu either. Maybe some sort of mouse click handler that checks the mouse cursor position and if it is outside the popup, then close it.
-
Thanks for the answer. I filed a new bug about this issue yesterday: https://bugreports.qt.io/browse/QTBUG-69777
There seems to be a lot of similar known bugs.As a workaround, I decided to use a Qt Widgets window for the popup containing a QQuickWidget which displays my QML content.
Something seems to go wrong in reporting the Qt::Popup flag correctly to the window manager. X11 usually displays (correctly implemented) popups together with a transparent overlay which does not even let mouseover events through to the underlying windows until the popup is closed. This does not happen with a QML popup, which indicates that Qt Quick is not producing a "real" popup but some kind of normal frameless window.
Also, I think it is not possible to listen for mouse events on the title bar, or at least that's not the way Qt registers the "click outside" the window. The click outside should be detected by the window manager which then reports to the Qt application that the popup window got "out of focus" or something similar. I'm not sure how this works internally, though.
EDIT
I read about the concept of mouse grabbing. Qt Widgets popups call grabMouse(), this will cause the window manager to forward all mouse events to the widget, even if they are issued outside of the widget, until the mouse is ungrabbed again when the popup is closed.
There is also a method QQuickItem::grabMouse(). Maybe this function should be called, but isn't for Popup windows?