Add system tray to status bar in convergence mode

Reintroduce StatusNotifierModel-based system tray icons, gated on
convergenceModeEnabled. Desktop apps (KDE Connect, nm-applet, etc.)
expose tray icons that were removed in 1914e9be as unusable on
phone screens.

The status bar wrapper is raised above the ActionDrawerOpenSurface
so tray icon MouseAreas receive click events while non-interactive
areas still fall through to the swipe handler.

TaskWidget.qml supports left-click (Activate), right-click
(ContextMenu), hover tooltip, and hides Passive/ApplicationStatus
items. The lockscreen SIGABRT guard (disableSystemTray) is restored
on the action drawer's StatusBar instance.
This commit is contained in:
Marco Allegretti 2026-04-08 20:12:16 +02:00
parent a0dceaedc1
commit e12e6b3a66
5 changed files with 99 additions and 0 deletions

View file

@ -102,6 +102,7 @@ ecm_target_qml_sources(mobileshellplugin SOURCES
qml/statusbar/indicators/VolumeIndicator.qml qml/statusbar/indicators/VolumeIndicator.qml
qml/statusbar/ClockText.qml qml/statusbar/ClockText.qml
qml/statusbar/StatusBar.qml qml/statusbar/StatusBar.qml
qml/statusbar/TaskWidget.qml
qml/widgets/krunner/KRunnerScreen.qml qml/widgets/krunner/KRunnerScreen.qml
qml/widgets/mediacontrols/BlurredBackground.qml qml/widgets/mediacontrols/BlurredBackground.qml

View file

@ -269,6 +269,9 @@ Item {
showDropShadow: false showDropShadow: false
showTime: root.actionDrawer.mode == MobileShell.ActionDrawer.Portrait showTime: root.actionDrawer.mode == MobileShell.ActionDrawer.Portrait
// Disable system tray on lockscreen to prevent SIGABRT
disableSystemTray: root.actionDrawer.restrictedPermissions
opacity: brightnessPressedValue opacity: brightnessPressedValue
} }

View file

@ -46,6 +46,11 @@ Item {
*/ */
property bool showTime: true property bool showTime: true
/**
* Whether to disable the system tray (e.g. on the lockscreen to prevent SIGABRT).
*/
property bool disableSystemTray: false
readonly property real textPixelSize: Math.round(11 * ShellSettings.Settings.statusBarScaleFactor) readonly property real textPixelSize: Math.round(11 * ShellSettings.Settings.statusBarScaleFactor)
readonly property real smallerTextPixelSize: Math.round(9 * ShellSettings.Settings.statusBarScaleFactor) readonly property real smallerTextPixelSize: Math.round(9 * ShellSettings.Settings.statusBarScaleFactor)
readonly property real elementSpacing: Math.round(Kirigami.Units.smallSpacing * 1.5) readonly property real elementSpacing: Math.round(Kirigami.Units.smallSpacing * 1.5)
@ -153,6 +158,21 @@ Item {
implicitHeight: mainRow.rowHeight implicitHeight: mainRow.rowHeight
Layout.preferredWidth: height Layout.preferredWidth: height
} }
// System tray icons (convergence mode only)
Loader {
id: statusNotifierSourceLoader
active: ShellSettings.Settings.convergenceModeEnabled && !root.disableSystemTray
sourceComponent: SystemTray.StatusNotifierModel {}
}
Repeater {
id: statusNotifierRepeater
model: statusNotifierSourceLoader.item
delegate: TaskWidget {
Layout.leftMargin: root.elementSpacing
}
}
} }
} }

View file

@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
Item {
id: taskIcon
width: parent.height
height: width
// Hide ApplicationStatus and Passive items
opacity: (model.category !== "ApplicationStatus" && model.status !== "Passive") ? 1 : 0
onOpacityChanged: visible = opacity
Behavior on opacity {
NumberAnimation {
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
}
}
Kirigami.Icon {
id: icon
source: model.iconName ? model.iconName : (model.icon ? model.icon : "")
width: Math.min(parent.width, parent.height)
height: width
anchors.centerIn: parent
}
Controls.ToolTip.text: model.toolTipTitle ? model.toolTipTitle : (model.title ? model.title : "")
Controls.ToolTip.visible: mouseArea.containsMouse && Controls.ToolTip.text !== ""
Controls.ToolTip.delay: Kirigami.Units.toolTipDelay
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
if (!model.service) {
return;
}
var operationName = mouse.button === Qt.RightButton ? "ContextMenu" : "Activate";
var operation = model.service.operationDescription(operationName);
operation.x = taskIcon.mapToGlobal(0, 0).x;
operation.y = taskIcon.mapToGlobal(0, taskIcon.height).y;
model.service.startOperationCall(operation);
}
}
}

View file

@ -64,8 +64,12 @@ Item {
} }
// Status bar component // Status bar component
// z: 1 so system tray icon MouseAreas inside StatusBar receive events
// before ActionDrawerOpenSurface (z: default). Non-interactive areas
// pass through to the swipe surface below.
StatusBarWrapper { StatusBarWrapper {
id: statusBarWrapper id: statusBarWrapper
z: 1
anchors.fill: parent anchors.fill: parent
statusPanelHeight: MobileShell.Constants.topPanelHeight statusPanelHeight: MobileShell.Constants.topPanelHeight
@ -116,6 +120,20 @@ Item {
} }
} }
// Keyboard shortcut to toggle action drawer (convergence mode)
Shortcut {
sequence: "Meta+A"
enabled: ShellSettings.Settings.convergenceModeEnabled
context: Qt.ApplicationShortcut
onActivated: {
if (drawer.actionDrawer.intendedToBeVisible) {
drawer.actionDrawer.close();
} else {
drawer.actionDrawer.open();
}
}
}
// Swiping area for swipe-down drawer // Swiping area for swipe-down drawer
MobileShell.ActionDrawerOpenSurface { MobileShell.ActionDrawerOpenSurface {
id: swipeArea id: swipeArea