import QtQuick import QtQuick.Effects import QtQuick.Layouts import QtQuick.Controls import Quickshell import Quickshell.Services.SystemTray import Quickshell.Widgets import qs.Commons import qs.Modules.Bar.Extras import qs.Services import qs.Widgets Rectangle { id: root property ShellScreen screen property real scaling: screen ? ScalingService.scale(screen) : 1.0 readonly property real itemSize: 24 * scaling visible: SystemTray.items.values.length > 0 implicitWidth: tray.width + Style.marginM * scaling * 2 implicitHeight: Math.round(Style.capsuleHeight * scaling) radius: Math.round(Style.radiusM * scaling) color: Color.mSurfaceVariant Layout.alignment: Qt.AlignVCenter Row { id: tray anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter spacing: Style.marginS * scaling Repeater { id: repeater model: SystemTray.items delegate: Item { width: itemSize height: itemSize visible: modelData IconImage { id: trayIcon anchors.centerIn: parent width: Style.marginL * scaling height: Style.marginL * scaling smooth: false asynchronous: true backer.fillMode: Image.PreserveAspectFit source: { let icon = modelData?.icon || "" if (!icon) { return "" } // Process icon path if (icon.includes("?path=")) { // Seems qmlfmt does not support the following ES6 syntax: const[name, path] = icon.split const chunks = icon.split("?path=") const name = chunks[0] const path = chunks[1] const fileName = name.substring(name.lastIndexOf("/") + 1) return `file://${path}/${fileName}` } return icon } opacity: status === Image.Ready ? 1 : 0 } MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton onClicked: mouse => { if (!modelData) { return } if (mouse.button === Qt.LeftButton) { // Close any open menu first var trayMenuPanel = PanelService.getPanel("trayMenu") if (trayMenuPanel && trayMenuPanel.active) { trayMenuPanel.close() } if (!modelData.onlyMenu) { modelData.activate() } } else if (mouse.button === Qt.MiddleButton) { // Close any open menu first var trayMenuPanel = PanelService.getPanel("trayMenu") if (trayMenuPanel && trayMenuPanel.active) { trayMenuPanel.close() } modelData.secondaryActivate && modelData.secondaryActivate() } else if (mouse.button === Qt.RightButton) { trayTooltip.hide() // Don't open menu if screen is invalid if (!screen || !screen.name) { Logger.warn("Tray", "Cannot open tray menu: invalid screen object") return } if (modelData.hasMenu && modelData.menu) { // Get the tray menu panel from PanelService var trayMenuPanel = PanelService.getPanel("trayMenu") if (trayMenuPanel) { trayMenuPanel.menu = modelData.menu trayMenuPanel.toggle(screen, this) } else { Logger.warn("Tray", "Tray menu panel not found") } } else { Logger.log("Tray", "No menu available for", modelData.id) } } } onEntered: trayTooltip.show() onExited: trayTooltip.hide() } NTooltip { id: trayTooltip target: trayIcon text: modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item" positionAbove: Settings.data.bar.position === "bottom" } } } } }