diff --git a/frame/popupwindow.cpp b/frame/popupwindow.cpp index 95e911efc..a7425f9b9 100644 --- a/frame/popupwindow.cpp +++ b/frame/popupwindow.cpp @@ -4,6 +4,8 @@ #include "popupwindow.h" +#include + DS_BEGIN_NAMESPACE PopupWindow::PopupWindow(QWindow *parent) : QQuickApplicationWindow(parent) @@ -22,6 +24,12 @@ PopupWindow::PopupWindow(QWindow *parent) connect(this, &QWindow::screenChanged, this, setMaximumSize); setMaximumSize(); + + connect(this, &QWindow::visibleChanged, this, [this]() { + if (!isVisible()) { + setX11GrabFocusTransition(false); + } + }); } void PopupWindow::mouseReleaseEvent(QMouseEvent *event) @@ -49,4 +57,42 @@ void PopupWindow::mouseMoveEvent(QMouseEvent *event) return QQuickApplicationWindow::mouseMoveEvent(event); } +void PopupWindow::setX11GrabFocusTransition(bool transition) +{ + if (m_x11GrabFocusTransition == transition) { + return; + } + + m_x11GrabFocusTransition = transition; + Q_EMIT x11GrabFocusTransitionChanged(); +} + +bool PopupWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) +{ + if (eventType == QByteArrayLiteral("xcb_generic_event_t")) { + auto *event = static_cast(message); + if (!event) { + return QQuickApplicationWindow::nativeEvent(eventType, message, result); + } + + const uint8_t responseType = event->response_type & ~0x80; + if (responseType == XCB_FOCUS_IN || responseType == XCB_FOCUS_OUT) { + auto *focusEvent = reinterpret_cast(event); + if (focusEvent->event == static_cast(winId())) { + if (responseType == XCB_FOCUS_OUT && focusEvent->mode == XCB_NOTIFY_MODE_GRAB) { + setX11GrabFocusTransition(true); + Q_EMIT x11FocusOutByGrab(); + } else if (responseType == XCB_FOCUS_IN && focusEvent->mode == XCB_NOTIFY_MODE_UNGRAB) { + Q_EMIT x11FocusInByUngrab(); + setX11GrabFocusTransition(false); + } else if (responseType == XCB_FOCUS_IN && focusEvent->mode != XCB_NOTIFY_MODE_GRAB) { + setX11GrabFocusTransition(false); + } + } + } + } + + return QQuickApplicationWindow::nativeEvent(eventType, message, result); +} + DS_END_NAMESPACE diff --git a/frame/popupwindow.h b/frame/popupwindow.h index cf68dbe9a..5c9925446 100644 --- a/frame/popupwindow.h +++ b/frame/popupwindow.h @@ -12,18 +12,28 @@ class PopupWindow : public QQuickApplicationWindow { Q_OBJECT Q_PROPERTY(QWindow *transientParent READ transientParent WRITE setTransientParent NOTIFY transientParentChanged) + Q_PROPERTY(bool x11GrabFocusTransition READ x11GrabFocusTransition NOTIFY x11GrabFocusTransitionChanged) QML_NAMED_ELEMENT(PopupWindow) public: PopupWindow(QWindow *parent = nullptr); + bool x11GrabFocusTransition() const { return m_x11GrabFocusTransition; } protected: void mouseReleaseEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; + bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override; + +signals: + void x11GrabFocusTransitionChanged(); + void x11FocusOutByGrab(); + void x11FocusInByUngrab(); private: + void setX11GrabFocusTransition(bool transition); bool m_dragging; bool m_pressing; + bool m_x11GrabFocusTransition = false; }; DS_END_NAMESPACE diff --git a/frame/qml/PanelPopup.qml b/frame/qml/PanelPopup.qml index 8987df717..b01c69b82 100644 --- a/frame/qml/PanelPopup.qml +++ b/frame/qml/PanelPopup.qml @@ -15,6 +15,8 @@ Item { property int popupX: 0 property int popupY: 0 property bool readyBinding: false + property bool grabInactivePending: false + property int grabInactiveTimeout: 200 // WM_NAME, used for kwin. property string windowTitle: "dde-shell/panelpopup" width: popup.childrenRect.width @@ -84,8 +86,24 @@ Item { popupWindow.requestActivate() } } + Timer { + id: grabInactiveTimer + interval: control.grabInactiveTimeout + repeat: false + onTriggered: { + control.grabInactivePending = false + if (!popupWindow || !readyBinding || popupWindow.currentItem !== control || !popup.visible) { + return + } + if (!popupWindow.active) { + control.close() + } + } + } function close() { + grabInactivePending = false + grabInactiveTimer.stop() if (!popupWindow) return @@ -103,11 +121,55 @@ Item { { if (!popupWindow) return + if (popupWindow.currentItem !== control || !popup.visible) { + control.grabInactivePending = false + grabInactiveTimer.stop() + return + } + if (popupWindow.active) { + control.grabInactivePending = false + grabInactiveTimer.stop() + return + } + if (control.grabInactivePending || popupWindow.x11GrabFocusTransition) { + return + } // TODO why activeChanged is not emit. - if (popupWindow && !popupWindow.active) { + if (!popupWindow.active) { control.close() } } + + function onX11FocusOutByGrab() + { + if (!popupWindow || !readyBinding || !popup.visible || popupWindow.currentItem !== control) { + return + } + control.grabInactivePending = true + grabInactiveTimer.start() + } + + function onX11FocusInByUngrab() + { + if (!popupWindow || popupWindow.currentItem !== control || !control.grabInactivePending) { + return + } + control.grabInactivePending = false + grabInactiveTimer.stop() + + Qt.callLater(function() { + if (!popupWindow + || !readyBinding + || popupWindow.currentItem !== control + || !popup.visible + || control.grabInactivePending) { + return + } + if (!popupWindow.active) { + control.close() + } + }) + } } Item {