diff --git a/applets/appmenu/package/contents/ui/main.qml b/applets/appmenu/package/contents/ui/main.qml --- a/applets/appmenu/package/contents/ui/main.qml +++ b/applets/appmenu/package/contents/ui/main.qml @@ -104,7 +104,7 @@ Repeater { id: buttonRepeater - model: appMenuModel + model: !appMenuModel.menuHidden ? appMenuModel : null PlasmaComponents.ToolButton { readonly property int buttonIndex: index @@ -148,6 +148,7 @@ AppMenuPrivate.AppMenuModel { id: appMenuModel + screenGeometry: plasmoid.screenGeometry onRequestActivateIndex: plasmoid.nativeInterface.requestActivateIndex(index) Component.onCompleted: { plasmoid.nativeInterface.model = appMenuModel diff --git a/applets/appmenu/plugin/appmenumodel.h b/applets/appmenu/plugin/appmenumodel.h --- a/applets/appmenu/plugin/appmenumodel.h +++ b/applets/appmenu/plugin/appmenumodel.h @@ -27,6 +27,7 @@ #include #include #include +#include class QMenu; class QAction; @@ -39,6 +40,9 @@ Q_OBJECT Q_PROPERTY(bool menuAvailable READ menuAvailable WRITE setMenuAvailable NOTIFY menuAvailableChanged) + Q_PROPERTY(bool menuHidden READ menuHidden NOTIFY menuHiddenChanged) + + Q_PROPERTY(QRect screenGeometry READ screenGeometry WRITE setScreenGeometry NOTIFY screenGeometryChanged) public: explicit AppMenuModel(QObject *parent = nullptr); @@ -58,25 +62,40 @@ bool menuAvailable() const; void setMenuAvailable(bool set); + bool menuHidden() const; + + QRect screenGeometry() const; + void setScreenGeometry(QRect geometry); + signals: void requestActivateIndex(int index); protected: bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override; private Q_SLOTS: void onActiveWindowChanged(WId id); + void onWindowChanged(WId id); + void setMenuHidden(bool hide); void update(); signals: void menuAvailableChanged(); + void menuHiddenChanged(); void modelNeedsUpdate(); + void screenGeometryChanged(); private: bool m_menuAvailable; + bool m_menuHidden = false; bool m_updatePending = false; + QRect m_screenGeometry; + + //! current active window used WId m_currentWindowId = 0; + //! window that its menu initialization may be delayed + WId m_delayedMenuWindowId = 0; QPointer m_menu; diff --git a/applets/appmenu/plugin/appmenumodel.cpp b/applets/appmenu/plugin/appmenumodel.cpp --- a/applets/appmenu/plugin/appmenumodel.cpp +++ b/applets/appmenu/plugin/appmenumodel.cpp @@ -69,12 +69,22 @@ m_serviceWatcher(new QDBusServiceWatcher(this)) { connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AppMenuModel::onActiveWindowChanged); + connect(KWindowSystem::self() + , static_cast(&KWindowSystem::windowChanged) + , this + , &AppMenuModel::onWindowChanged); + connect(this, &AppMenuModel::modelNeedsUpdate, this, [this] { if (!m_updatePending) { m_updatePending = true; QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); } }); + + connect(this, &AppMenuModel::screenGeometryChanged, this, [this] { + onWindowChanged(m_currentWindowId); + }); + onActiveWindowChanged(KWindowSystem::activeWindow()); m_serviceWatcher->setConnection(QDBusConnection::sessionBus()); @@ -100,10 +110,39 @@ { if (m_menuAvailable != set) { m_menuAvailable = set; + setMenuHidden(false); emit menuAvailableChanged(); } } +bool AppMenuModel::menuHidden() const +{ + return m_menuHidden; +} + +void AppMenuModel::setMenuHidden(bool hide) +{ + if (m_menuHidden != hide) { + m_menuHidden = hide; + emit menuHiddenChanged(); + } +} + +QRect AppMenuModel::screenGeometry() const +{ + return m_screenGeometry; +} + +void AppMenuModel::setScreenGeometry(QRect geometry) +{ + if (m_screenGeometry == geometry) { + return; + } + + m_screenGeometry = geometry; + emit screenGeometryChanged(); +} + int AppMenuModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); @@ -187,23 +226,27 @@ return; } + m_currentWindowId = id; + WId transientId = info.transientFor(); // lok at transient windows first while (transientId) { if (updateMenuFromWindowIfHasMenu(transientId)) { + setMenuHidden(false); return; } transientId = KWindowInfo(transientId, nullptr, NET::WM2TransientFor).transientFor(); } if (updateMenuFromWindowIfHasMenu(id)) { + setMenuHidden(false); return; } // monitor whether an app menu becomes available later // this can happen when an app starts, shows its window, and only later announces global menu (e.g. Firefox) qApp->installNativeEventFilter(this); - m_currentWindowId = id; + m_delayedMenuWindowId = id; //no menu found, set it to unavailable setMenuAvailable(false); @@ -213,6 +256,15 @@ } +void AppMenuModel::onWindowChanged(WId id) +{ + if (m_currentWindowId == id) { + KWindowInfo info(id, NET::WMState | NET::WMGeometry); + const bool contained = m_screenGeometry.isNull() || m_screenGeometry.contains(info.geometry().center()); + + setMenuHidden(info.isMinimized() || !contained); + } +} QHash AppMenuModel::roleNames() const { @@ -321,7 +373,7 @@ const uint8_t type = e->response_type & ~0x80; if (type == XCB_PROPERTY_NOTIFY) { auto *event = reinterpret_cast(e); - if (event->window == m_currentWindowId) { + if (event->window == m_delayedMenuWindowId) { auto serviceNameAtom = s_atoms.value(s_x11AppMenuServiceNamePropertyName); auto objectPathAtom = s_atoms.value(s_x11AppMenuObjectPathPropertyName);