diff --git a/CMakeLists.txt b/CMakeLists.txt index f9f8867..1cd1d55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,8 +10,8 @@ set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(QT Core Widgets Concurrent Quick QuickControls2 X11Extras DBus Svg LinguistTools) -find_package(Qt5 REQUIRED ${QT}) +find_package(Qt5 CONFIG REQUIRED Widgets DBus X11Extras Concurrent Svg LinguistTools QuickControls2) + find_package(KF5WindowSystem REQUIRED) find_package(dbusmenu-qt5 REQUIRED) find_package(MeuiKit REQUIRED) @@ -33,7 +33,7 @@ set(SRCS src/volumemanager.cpp src/utils.cpp src/xwindowinterface.cpp - src/toplevelmenu.cpp + src/popupwindow.cpp src/appearance.cpp src/fakewindow.cpp @@ -68,7 +68,7 @@ qt5_add_dbus_adaptor(SRCS src/systemtray/org.kde.StatusNotifierWatcher.xml src/systemtray/statusnotifierwatcher.h StatusNotifierWatcher) add_executable(${PROJECT_NAME} ${SRCS} ${DBUS_SRCS} ${RESOURCES}) -target_link_libraries(${PROJECT_NAME} +target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Widgets Qt5::Quick diff --git a/qml/AppItem.qml b/qml/AppItem.qml index a86bddd..0324a51 100644 --- a/qml/AppItem.qml +++ b/qml/AppItem.qml @@ -23,7 +23,7 @@ DockItem { onPositionChanged: updateGeometry() onPressed: updateGeometry() onClicked: appModel.clicked(model.appId) - onRightClicked: if (model.appId !== "cutefish-launcher") contextMenu.open() + onRightClicked: if (model.appId !== "cutefish-launcher") contextMenu.show() dropArea.onEntered: { if (drag.source) @@ -37,7 +37,7 @@ DockItem { updateGeometry() } - TopLevelMenu { + DockMenu { id: contextMenu MenuItem { diff --git a/qml/DockMenu.qml b/qml/DockMenu.qml new file mode 100644 index 0000000..c1c594d --- /dev/null +++ b/qml/DockMenu.qml @@ -0,0 +1,43 @@ +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import Cutefish.Dock 1.0 +import MeuiKit 1.0 as Meui + +PopupWindow { + id: control + + default property alias content : _mainLayout.data + + Rectangle { + id: _background + anchors.fill: parent + opacity: 0.6 + color: Meui.Theme.backgroundColor + radius: Meui.Theme.mediumRadius + + Meui.WindowShadow { + view: control + geometry: Qt.rect(control.x, control.y, control.width, control.height) + radius: _background.radius + } + + Meui.WindowBlur { + view: control + geometry: Qt.rect(control.x, control.y, control.width, control.height) + windowRadius: _background.radius + enabled: true + } + } + + ColumnLayout { + id: _mainLayout + anchors.fill: parent + anchors.topMargin: 8 + anchors.bottomMargin: 8 + } + + function open() { + control.show() + } +} diff --git a/qml/TopLevelMenu.qml b/qml/TopLevelMenu.qml deleted file mode 100644 index 777faad..0000000 --- a/qml/TopLevelMenu.qml +++ /dev/null @@ -1,32 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Window 2.12 -import QtQuick.Layouts 1.12 - -Window { - id: control - visible: false - - width: _mainLayout.implicitWidth - height: _mainLayout.implicitHeight - - flags: Qt.Popup - - onActiveChanged: { - if (!active) - control.close() - } - - default property alias content: _mainLayout.data - - function open() { - control.visible = true - control.x = 500 - control.y = 500 - } - - ColumnLayout { - id: _mainLayout - anchors.fill: parent - } -} diff --git a/resources.qrc b/resources.qrc index a68d34f..31dbcec 100644 --- a/resources.qrc +++ b/resources.qrc @@ -102,6 +102,6 @@ svg/light/dark-mode.svg svg/dark/dark-mode.svg svg/dark/bluetooth-symbolic.svg - qml/TopLevelMenu.qml + qml/DockMenu.qml diff --git a/src/main.cpp b/src/main.cpp index 63e9c1c..24d8ef1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,6 +33,7 @@ #include "systemtray/systemtraymodel.h" #include "appearance.h" #include "iconitem.h" +#include "popupwindow.h" int main(int argc, char *argv[]) { @@ -47,6 +48,7 @@ int main(int argc, char *argv[]) qmlRegisterType("Cutefish.Dock", 1, 0, "SystemTrayModel"); qmlRegisterType("Cutefish.Dock", 1, 0, "Appearance"); qmlRegisterType("Cutefish.Dock", 1, 0, "IconItem"); + qmlRegisterType("Cutefish.Dock", 1, 0, "PopupWindow"); QString qmFilePath = QString("%1/%2.qm").arg("/usr/share/cutefish-dock/translations/").arg(QLocale::system().name()); if (QFile::exists(qmFilePath)) { diff --git a/src/popupwindow.cpp b/src/popupwindow.cpp new file mode 100644 index 0000000..d1aeba8 --- /dev/null +++ b/src/popupwindow.cpp @@ -0,0 +1,129 @@ +#include "popupwindow.h" +#include +#include +#include +#include + +PopupWindow::PopupWindow(QQuickWindow *parent) + : QQuickWindow(parent) + , m_parentItem(0) + , m_contentItem(0) + , m_mouseMoved(false) + , m_dismissed(false) +{ + setFlags(Qt::Popup); + setColor(Qt::transparent); + connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), + this, SLOT(applicationStateChanged(Qt::ApplicationState))); +} + +void PopupWindow::applicationStateChanged(Qt::ApplicationState state) +{ + if (state != Qt::ApplicationActive) + dismissPopup(); +} + +void PopupWindow::show() +{ + QPoint pos = QCursor::pos(); + int w = m_contentItem->implicitWidth(); + int h = m_contentItem->implicitHeight() + 16; + int posx = pos.x(); + int posy = pos.y(); + + QWindow *pw = transientParent(); + if (!pw && parentItem()) + pw = parentItem()->window(); + if (!pw) + pw = this; + + QRect g = pw->screen()->availableGeometry(); + + if (posx + w > g.right()) { + if (qobject_cast(transientParent())) { + // reposition submenu window on the parent menu's left side + int submenuOverlap = pw->x() + pw->width() - posx; + posx -= pw->width() + w - 2 * submenuOverlap; + } else { + posx = g.right() - w; + } + } else { + posx = qMax(posx, g.left()); + } + + posy = qBound(g.top(), posy, g.bottom() - h); + + setGeometry(posx, posy, w, h); + + QQuickWindow::show(); + setMouseGrabEnabled(true); + setKeyboardGrabEnabled(true); +} + +void PopupWindow::setParentItem(QQuickItem *item) +{ + m_parentItem = item; + if (m_parentItem) + setTransientParent(m_parentItem->window()); +} + +void PopupWindow::setPopupContentItem(QQuickItem *contentItem) +{ + if (!contentItem) + return; + + contentItem->setParentItem(this->contentItem()); + m_contentItem = contentItem; +} + +void PopupWindow::dismissPopup() +{ + m_dismissed = true; + emit popupDismissed(); + hide(); +} + +void PopupWindow::mouseMoveEvent(QMouseEvent *e) +{ + QQuickWindow::mouseMoveEvent(e); +} + +void PopupWindow::mousePressEvent(QMouseEvent *e) +{ + QRect rect = QRect(QPoint(), size()); + if (rect.contains(e->pos())) { + QQuickWindow::mousePressEvent(e); + } else { + dismissPopup(); + } +} + +void PopupWindow::mouseReleaseEvent(QMouseEvent *e) +{ + QQuickWindow::mouseReleaseEvent(e); + dismissPopup(); +} + +bool PopupWindow::event(QEvent *event) +{ + //QTBUG-45079 + //This is a workaround for popup menu not being closed when using touch input. + //Currently mouse synthesized events are not created for touch events which are + //outside the qquickwindow. + + if (event->type() == QEvent::TouchBegin && !qobject_cast(transientParent())) { + QRect rect = QRect(QPoint(), size()); + QTouchEvent *touch = static_cast(event); + QTouchEvent::TouchPoint point = touch->touchPoints().first(); + if ((point.state() == Qt::TouchPointPressed) && !rect.contains(point.pos().toPoint())) { + //first default handling + bool result = QQuickWindow::event(event); + //now specific broken case + if (!m_dismissed) + dismissPopup(); + return result; + } + } + + return QQuickWindow::event(event); +} diff --git a/src/popupwindow.h b/src/popupwindow.h new file mode 100644 index 0000000..e18658d --- /dev/null +++ b/src/popupwindow.h @@ -0,0 +1,46 @@ +#ifndef TOPLEVELMENU_H +#define TOPLEVELMENU_H + +#include + +class PopupWindow : public QQuickWindow +{ + Q_OBJECT + Q_PROPERTY(QQuickItem *popupContentItem READ popupContentItem WRITE setPopupContentItem) + Q_CLASSINFO("DefaultProperty", "popupContentItem") + Q_PROPERTY(QQuickItem *parentItem READ parentItem WRITE setParentItem) + +public: + PopupWindow(QQuickWindow *parent = nullptr); + + QQuickItem *popupContentItem() const { return m_contentItem; } + void setPopupContentItem(QQuickItem *popupContentItem); + + QQuickItem *parentItem() const { return m_parentItem; } + virtual void setParentItem(QQuickItem *); + +public slots: + void show(); + void dismissPopup(); + +signals: + void popupDismissed(); + void geometryChanged(); + +protected: + void mousePressEvent(QMouseEvent *) override; + void mouseReleaseEvent(QMouseEvent *) override; + void mouseMoveEvent(QMouseEvent *) override; + bool event(QEvent *) override; + +protected slots: + void applicationStateChanged(Qt::ApplicationState state); + +private: + QQuickItem *m_parentItem; + QPointer m_contentItem; + bool m_mouseMoved; + bool m_dismissed; +}; + +#endif // POPUPWINDOW_H diff --git a/src/toplevelmenu.cpp b/src/toplevelmenu.cpp deleted file mode 100644 index 8486119..0000000 --- a/src/toplevelmenu.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "toplevelmenu.h" - -TopLevelMenu::TopLevelMenu(QQuickView *parent) - : QQuickView(parent) -{ - setFlags(Qt::Popup); -} diff --git a/src/toplevelmenu.h b/src/toplevelmenu.h deleted file mode 100644 index c7e5fa4..0000000 --- a/src/toplevelmenu.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TOPLEVELMENU_H -#define TOPLEVELMENU_H - -#include - -class TopLevelMenu : public QQuickView -{ - Q_OBJECT - -public: - explicit TopLevelMenu(QQuickView *parent = nullptr); - -signals: - -}; - -#endif // TOPLEVELMENU_H