pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import Quickshell.Services.SystemTray import qs.Components import ZShell.Config import qs.Modules.SysTray.Widgets import qs.Modules import qs.Services GridLayout { id: root required property bool horizontal readonly property alias items: repeater required property GridLayout loader required property Wrapper popouts readonly property real shortSize: Config.bar.height + Tokens.padding.smallest * 2 readonly property real size: horizontal ? sysTray.implicitWidth + sysTrayMod.implicitWidth + Tokens.padding.small : sysTray.implicitHeight + sysTrayMod.implicitHeight + Tokens.padding.small function closestRowChild(row, x) { let child = row.childAt(x, row.height / 2); if (child) return child; let closest = null; let closestDistance = Infinity; for (let i = 0; i < row.children.length; ++i) { let c = row.children[i]; if (!c.visible || c.width <= 0) continue; let centerX = c.x + c.width / 2; let dist = Math.abs(x - centerX); if (dist < closestDistance) { closestDistance = dist; closest = c; } } return closest; } function getHoveredSubItem(localX, localY) { let modPos = mapToItem(sysTrayMod, localX, localY); if (sysTrayMod.contains(Qt.point(modPos.x, modPos.y))) { let modRowPos = sysTrayMod.mapToItem(sysIcons, modPos.x, modPos.y); let child = closestRowChild(sysIcons, modRowPos.x); if (child) { if (child.objectName === "audio" && Config.bar.popouts.audio) return { id: "audio", item: child }; if (child.objectName === "microphone" && Config.bar.popouts.audio) return { id: "audio", item: child }; if (child.objectName === "upower" && Config.bar.popouts.upower) return { id: "upower", item: child }; } } let trayPos = mapToItem(sysTray, localX, localY); if (Config.bar.tray.showOnHover && sysTray.contains(Qt.point(trayPos.x, trayPos.y))) { let trayRowPos = sysTray.mapToItem(sysRow, trayPos.x, trayPos.y); let child = sysRow.childAt(trayRowPos.x, trayRowPos.y); if (child && child.hasOwnProperty("ind")) { return { id: `traymenu${child.ind}`, item: child }; } } return null; } columnSpacing: Tokens.padding.small columns: horizontal ? -1 : 1 height: horizontal ? shortSize : size rowSpacing: Tokens.padding.small width: horizontal ? size : shortSize CustomClippingRect { id: sysTray Layout.fillHeight: true bottomRightRadius: Tokens.rounding.smallest / 2 color: Colors.tPalette.m3surfaceContainer implicitWidth: sysRow.width + Tokens.padding.small * 2 radius: Tokens.rounding.full topRightRadius: Tokens.rounding.smallest / 2 GridLayout { id: sysRow anchors.bottom: root.horizontal ? undefined : parent.bottom anchors.centerIn: parent anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined anchors.left: root.horizontal ? undefined : parent.left anchors.right: root.horizontal ? undefined : parent.right anchors.verticalCenter: root.horizontal ? parent.verticalCenter : undefined columnSpacing: Tokens.spacing.small / 2 columns: root.horizontal ? -1 : 1 rowSpacing: Tokens.spacing.small / 2 Repeater { id: repeater model: SystemTray.items TrayItem { id: trayItem required property int index required property SystemTrayItem modelData implicitHeight: 34 implicitWidth: 34 ind: index item: modelData loader: root.loader popouts: root.popouts } } } } CustomClippingRect { id: sysTrayMod Layout.fillHeight: root.horizontal Layout.fillWidth: !root.horizontal bottomLeftRadius: Tokens.rounding.smallest / 2 color: Colors.tPalette.m3surfaceContainer implicitHeight: root.horizontal ? sysIcons.implicitHeight : sysIcons.implicitHeight + Tokens.padding.smaller + Tokens.padding.normal implicitWidth: root.horizontal ? sysIcons.implicitWidth + Tokens.padding.smaller + Tokens.padding.normal : sysIcons.implicitWidth radius: Tokens.rounding.full topLeftRadius: Tokens.rounding.smallest / 2 StatusIcons { id: sysIcons anchors.fill: parent anchors.leftMargin: Tokens.padding.smaller anchors.rightMargin: Tokens.padding.normal horizontal: root.horizontal } } }