Hide convergence chrome in Overview

Track KWin Overview through the activeEffects DBus property and expose the state to Folio QML. Hide the workspace frame, dock overlay, and in-containment favourites scrim while Overview is active so the effect does not show Shift shell chrome in its desktop view.

Set the state before invoking the Overview shortcut from Folio so the chrome is already gone when KWin starts composing the effect. The convergence dock invariant now guards both the hide rules and this ordering.
This commit is contained in:
Marco Allegretti 2026-05-23 15:33:23 +02:00
parent 02170250f1
commit a3e6182086
5 changed files with 142 additions and 4 deletions

View file

@ -9,14 +9,48 @@
#include <KWindowSystem>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDBusReply>
#include <QDBusVariant>
#include <QDebug>
#include <QQmlEngine>
#include <QQmlExtensionPlugin>
#include <QQuickItem>
#include <QTimer>
K_PLUGIN_CLASS_WITH_JSON(HomeScreen, "metadata.json")
namespace
{
const QString s_kwinService = QStringLiteral("org.kde.KWin");
const QString s_kwinEffectsPath = QStringLiteral("/Effects");
const QString s_kwinEffectsInterface = QStringLiteral("org.kde.kwin.Effects");
const QString s_dbusPropertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties");
QStringList effectListFromVariant(const QVariant &value)
{
QVariant effectValue = value;
if (effectValue.canConvert<QDBusVariant>()) {
effectValue = effectValue.value<QDBusVariant>().variant();
}
if (effectValue.canConvert<QStringList>()) {
return effectValue.toStringList();
}
QStringList effects;
const QVariantList effectList = effectValue.toList();
effects.reserve(effectList.size());
for (const QVariant &effect : effectList) {
effects.append(effect.toString());
}
return effects;
}
}
HomeScreen::HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
: Plasma::Containment{parent, data, args}
, m_folioSettings{new FolioSettings{this}}
@ -34,6 +68,20 @@ HomeScreen::HomeScreen(QObject *parent, const KPluginMetaData &data, const QVari
connect(KWindowSystem::self(), &KWindowSystem::showingDesktopChanged, this, &HomeScreen::showingDesktopChanged);
updateOverviewActive();
QDBusConnection::sessionBus().connect(s_kwinService,
s_kwinEffectsPath,
s_dbusPropertiesInterface,
QStringLiteral("PropertiesChanged"),
this,
SLOT(onOverviewEffectsChanged(QString, QVariantMap, QStringList)));
auto overviewRefreshTimer = new QTimer(this);
overviewRefreshTimer->setInterval(250);
overviewRefreshTimer->setTimerType(Qt::CoarseTimer);
connect(overviewRefreshTimer, &QTimer::timeout, this, &HomeScreen::updateOverviewActive);
overviewRefreshTimer->start();
connect(this, &Plasma::Containment::appletAdded, this, &HomeScreen::onAppletAdded);
connect(this, &Plasma::Containment::appletAboutToBeRemoved, this, &HomeScreen::onAppletAboutToBeRemoved);
}
@ -91,8 +139,58 @@ PageListModel *HomeScreen::pageListModel()
return m_pageListModel;
}
void HomeScreen::triggerOverview() const
bool HomeScreen::overviewActive() const
{
return m_overviewActive;
}
void HomeScreen::setOverviewActive(bool overviewActive)
{
if (m_overviewActive == overviewActive) {
return;
}
m_overviewActive = overviewActive;
Q_EMIT overviewActiveChanged();
}
void HomeScreen::updateOverviewActive()
{
QDBusInterface propIface(s_kwinService, s_kwinEffectsPath, s_dbusPropertiesInterface, QDBusConnection::sessionBus());
if (!propIface.isValid()) {
setOverviewActive(false);
return;
}
QDBusReply<QDBusVariant> activeEffectsReply = propIface.call(QStringLiteral("Get"), s_kwinEffectsInterface, QStringLiteral("activeEffects"));
if (!activeEffectsReply.isValid()) {
setOverviewActive(false);
return;
}
setOverviewActive(effectListFromVariant(activeEffectsReply.value().variant()).contains(QStringLiteral("overview")));
}
void HomeScreen::onOverviewEffectsChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated)
{
if (interface != s_kwinEffectsInterface) {
return;
}
if (changed.contains(QStringLiteral("activeEffects"))) {
setOverviewActive(effectListFromVariant(changed.value(QStringLiteral("activeEffects"))).contains(QStringLiteral("overview")));
return;
}
if (invalidated.contains(QStringLiteral("activeEffects"))) {
updateOverviewActive();
}
}
void HomeScreen::triggerOverview()
{
setOverviewActive(true);
QDBusMessage message = QDBusMessage::createMethodCall("org.kde.kglobalaccel", "/component/kwin", "org.kde.kglobalaccel.Component", "invokeShortcut");
message.setArguments({QStringLiteral("Overview")});
QDBusConnection::sessionBus().send(message);

View file

@ -43,6 +43,7 @@ class HomeScreen : public Plasma::Containment
Q_PROPERTY(ApplicationListSearchModel *ApplicationListSearchModel READ applicationListSearchModel CONSTANT)
Q_PROPERTY(FavouritesModel *FavouritesModel READ favouritesModel CONSTANT)
Q_PROPERTY(PageListModel *PageListModel READ pageListModel CONSTANT)
Q_PROPERTY(bool overviewActive READ overviewActive NOTIFY overviewActiveChanged)
public:
HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args);
@ -50,7 +51,7 @@ public:
void configChanged() override;
Q_INVOKABLE void triggerOverview() const;
Q_INVOKABLE void triggerOverview();
Q_INVOKABLE void triggerMinimizeAll() const;
Q_INVOKABLE void activateVirtualDesktop(const QVariant &desktop) const;
Q_INVOKABLE void createVirtualDesktop() const;
@ -64,15 +65,21 @@ public:
ApplicationListSearchModel *applicationListSearchModel();
FavouritesModel *favouritesModel();
PageListModel *pageListModel();
bool overviewActive() const;
Q_SIGNALS:
void overviewActiveChanged();
void showingDesktopChanged(bool showingDesktop);
private Q_SLOTS:
void onOverviewEffectsChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated);
void onAppletAdded(Plasma::Applet *applet, const QRectF &geometryHint);
void onAppletAboutToBeRemoved(Plasma::Applet *applet);
private:
void setOverviewActive(bool overviewActive);
void updateOverviewActive();
FolioSettings *m_folioSettings{nullptr};
HomeScreenState *m_homeScreenState{nullptr};
WidgetsManager *m_widgetsManager{nullptr};
@ -80,4 +87,5 @@ private:
ApplicationListSearchModel *m_applicationListSearchModel{nullptr};
FavouritesModel *m_favouritesModel{nullptr};
PageListModel *m_pageListModel{nullptr};
bool m_overviewActive{false};
};

View file

@ -358,6 +358,7 @@ Item {
// don't show in settings mode
opacity: 1 - folio.HomeScreenState.settingsOpenProgress
visible: folio.FolioSettings.showFavouritesBarBackground
&& !ShellSettings.Settings.convergenceModeEnabled
anchors.top: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom ? favouritesBar.top : parent.top
anchors.bottom: parent.bottom

View file

@ -278,7 +278,9 @@ ContainmentItem {
// task panel containment; this window only provides the visible dock.
Window {
id: dockOverlay
readonly property bool active: ShellSettings.Settings.convergenceModeEnabled && !ShellSettings.Settings.gamingModeEnabled
readonly property bool active: ShellSettings.Settings.convergenceModeEnabled
&& !ShellSettings.Settings.gamingModeEnabled
&& !folio.overviewActive
visible: active
opacity: active ? 1 : 0
@ -882,7 +884,9 @@ ContainmentItem {
Item {
id: workspaceFrame
anchors.fill: parent
visible: ShellSettings.Settings.convergenceModeEnabled && !ShellSettings.Settings.gamingModeEnabled
visible: ShellSettings.Settings.convergenceModeEnabled
&& !ShellSettings.Settings.gamingModeEnabled
&& !folio.overviewActive
z: -1
readonly property real frameThickness: MobileShell.Constants.convergenceWorkspaceFrameThickness

View file

@ -11,6 +11,8 @@ panel="$repo_root/containments/panel/qml/main.qml"
taskpanel="$repo_root/containments/taskpanel/qml/main.qml"
folio_main="$repo_root/containments/homescreens/folio/qml/main.qml"
folio_home="$repo_root/containments/homescreens/folio/qml/FolioHomeScreen.qml"
folio_backend="$repo_root/containments/homescreens/folio/homescreen.h"
folio_backend_cpp="$repo_root/containments/homescreens/folio/homescreen.cpp"
action_content="$repo_root/components/mobileshell/qml/actiondrawer/private/ContentContainer.qml"
action_landscape="$repo_root/components/mobileshell/qml/actiondrawer/private/LandscapeContentContainer.qml"
quick_settings="$repo_root/components/mobileshell/qml/actiondrawer/private/QuickSettings.qml"
@ -64,6 +66,31 @@ require_line "$folio_main" "- MobileShell.Constants.convergenceWorkspaceFrameThi
require_line "$folio_main" "readonly property real popupHeight: Math.max(0, popupBottomY - popupTopY)"
require_line "$folio_main" "? popupTopY + animationY"
require_line "$folio_home" "height: ShellSettings.Settings.convergenceModeEnabled ? MobileShell.Constants.convergenceDockHeight : Kirigami.Units.gridUnit * 6"
require_line "$folio_home" "&& !ShellSettings.Settings.convergenceModeEnabled"
require_line "$folio_backend" "Q_PROPERTY(bool overviewActive READ overviewActive NOTIFY overviewActiveChanged)"
overview_hide_guards="$(grep -F "&& !folio.overviewActive" "$folio_main" | wc -l)"
if [[ "$overview_hide_guards" -ne 2 ]]; then
echo "Expected the Folio convergence workspace frame and dock overlay to hide during KWin Overview; found $overview_hide_guards overview guards" >&2
exit 1
fi
if ! awk '
/void HomeScreen::triggerOverview\(\)/ { in_trigger = 1; next }
in_trigger && /setOverviewActive\(true\);/ { saw_pre_hide = 1; next }
in_trigger && /createMethodCall/ {
if (saw_pre_hide) {
found = 1
exit 0
}
exit 1
}
in_trigger && /^}/ { exit 1 }
END { if (!found) exit 1 }
' "$folio_backend_cpp"; then
echo "Expected Folio to hide convergence chrome before invoking KWin Overview" >&2
exit 1
fi
require_line "$action_content" "readonly property real convergenceSurfaceTopInset: MobileShell.Constants.topPanelHeight"
require_line "$action_content" "readonly property real convergenceSurfaceBottomInset: MobileShell.Constants.convergenceDockHeight + convergenceFrameThickness"