nova-shell/shell/modules/TrayModule.qml

116 lines
4.3 KiB
QML

import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.SystemTray
import "." as M
import "../services" as S
M.BarModule {
id: root
spacing: S.Theme.moduleSpacing + 2
cursorShape: Qt.ArrowCursor
active: S.Modules.tray.enable && _trayRepeater.count > 0
property var _activeMenu: null
// --- debug logging (remove once tray is confirmed working) ---
onActiveChanged: console.log("[TrayModule] active:", active, "count:", _trayRepeater.count)
onVisibleChanged: console.log("[TrayModule] visible:", visible)
Repeater {
id: _trayRepeater
model: SystemTray.items
delegate: Item {
id: iconItem
required property SystemTrayItem modelData
readonly property bool _needsAttention: modelData.status === 2
property bool _hovered: false
property real _pulseOpacity: 1
implicitWidth: S.Theme.fontSize + 4
implicitHeight: S.Theme.fontSize + 4
M.PulseAnimation on _pulseOpacity {
running: iconItem._needsAttention
minOpacity: 0.3
}
Item {
anchors.fill: parent
opacity: iconItem._pulseOpacity
layer.enabled: iconItem._needsAttention || iconItem._hovered
layer.effect: MultiEffect {
shadowEnabled: true
shadowColor: iconItem._needsAttention ? S.Theme.base08 : S.Theme.base05
shadowBlur: iconItem._needsAttention ? 0.8 : 0.5
shadowVerticalOffset: 0
shadowHorizontalOffset: 0
}
M.ThemedIcon {
anchors.fill: parent
source: iconItem.modelData.icon
tint: iconItem._needsAttention ? S.Theme.base08 : root.accentColor
}
}
HoverHandler {
onHoveredChanged: {
iconItem._hovered = hovered;
const tip = [iconItem.modelData.tooltipTitle, iconItem.modelData.tooltipDescription].filter(s => s).join("\n") || iconItem.modelData.title;
if (hovered && tip) {
M.TooltipState.text = tip;
M.TooltipState.itemX = iconItem.mapToGlobal(iconItem.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
M.TooltipState.screen = QsWindow.window?.screen ?? null;
M.TooltipState.accentColor = root.accentColor;
M.TooltipState.visible = true;
} else if (!hovered) {
M.TooltipState.visible = false;
}
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
iconItem.modelData.activate();
} else if (mouse.button === Qt.RightButton) {
if (iconItem.modelData.menu) {
if (root._activeMenu && root._activeMenu !== menuLoader)
root._activeMenu.active = false;
menuLoader.active = true;
M.TooltipState.visible = false;
root._activeMenu = menuLoader;
} else {
iconItem.modelData.secondaryActivate();
}
}
}
}
// Per-icon context menu window, created on demand
LazyLoader {
id: menuLoader
active: false
M.TrayMenu {
accentColor: root.accentColor
handle: iconItem.modelData.menu
screen: QsWindow.window?.screen ?? null
anchorX: iconItem.mapToGlobal(iconItem.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0)
onDismissed: {
menuLoader.active = false;
root._activeMenu = null;
}
}
}
}
}
}