From 8a71bd72f53e3629fb1d23d7c1b153207ba44789 Mon Sep 17 00:00:00 2001 From: littleblack111 Date: Fri, 18 Sep 2026 15:10:16 +0800 Subject: feat: system tray --- bar/TimeDate.qml | 66 +++++++++++++++++++++++++---- bar/Tray.qml | 87 +++++++++++++++++++++++++++++++++++++-- bar/components/SimpleResource.qml | 8 ++-- components/IToolTip.qml | 23 +++++++++++ config/General.qml | 3 ++ 5 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 components/IToolTip.qml diff --git a/bar/TimeDate.qml b/bar/TimeDate.qml index adbda83..0d9fe28 100644 --- a/bar/TimeDate.qml +++ b/bar/TimeDate.qml @@ -8,11 +8,12 @@ import qs.config Item { id: root - property bool isAlt: false - property bool isCollapsed: false + property bool alt: false + property bool collapsed: true implicitWidth: container.implicitWidth implicitHeight: container.implicitHeight - General.rectMargin + IRect { id: container anchors { @@ -32,7 +33,7 @@ Item { anchors.centerIn: parent spacing: Bar.resourceIconTextSpacing Loader { - sourceComponent: !root.isAlt ? main : alt + sourceComponent: !root.alt ? main : alt readonly property Component main: Component { RowLayout { id: root @@ -89,6 +90,7 @@ Item { } } } + readonly property Component alt: Component { RowLayout { Item { @@ -121,13 +123,63 @@ Item { } } } + + Item { + implicitWidth: root.collapsed ? 0 : tray.width + implicitHeight: tray.height + visible: !root.collapsed || tray.active + + Tray { + id: tray + anchors.verticalCenter: parent.verticalCenter + height: container.height - General.rectMargin * 2 + active: !root.collapsed + + SequentialAnimation { + running: true + NumberAnimation { + target: parent + property: "opacity" + from: 0 + to: 1 + duration: General.animationDuration / 2 + easing.type: Easing.InOutQuad + } + } + + HoverHandler { + id: trayHover + } + } + + Behavior on implicitWidth { + NumberAnimation { + duration: General.animationDuration / 4 + easing.type: Easing.InOutQuad + } + } + + // no idea why when collapsing the width animation won't trigger + Behavior on visible { + NumberAnimation { + duration: General.animationDuration / 4 + easing.type: Easing.InOutQuad + } + } + } } } - MouseArea { - anchors.fill: parent + + TapHandler { + enabled: !trayHover.hovered + onTapped: root.alt = !root.alt + } + + HoverHandler { cursorShape: Qt.PointingHandCursor - onClicked: { - root.isAlt = !root.isAlt; + + onHoveredChanged: { + root.collapsed = !hovered; } } } diff --git a/bar/Tray.qml b/bar/Tray.qml index 1148ac3..1011e01 100644 --- a/bar/Tray.qml +++ b/bar/Tray.qml @@ -1,10 +1,89 @@ import QtQuick +import QtQuick.Controls import QtQuick.Layouts -import Quickshell.Hyprland import Quickshell.Services.SystemTray -import Quickshell.Wayland import Quickshell.Widgets import Qt5Compat.GraphicalEffects -import Quickshell -Item {} +import qs.components +import qs.config + +Item { + id: root + + property bool active + property bool hasImportant: false + + implicitWidth: layout.implicitWidth + implicitHeight: layout.implicitHeight + + visible: active || hasImportant + + RowLayout { + id: layout + anchors.fill: parent + + Repeater { + Layout.fillHeight: true + model: SystemTray.items + + delegate: IRect { + required property SystemTrayItem modelData + + color: "transparent" + + Layout.fillHeight: true + implicitWidth: childrenRect.width + implicitHeight: childrenRect.height + + IconImage { + source: parent.modelData.icon + implicitSize: parent.height + } + + IToolTip { + active: (modelData.tooltipTitle || modelData.tooltipDescription) + text: (modelData.tooltipTitle || '') + (modelData.tooltipTitle && modelData.tooltipDescription ? '\n' : '') + (modelData.tooltipDescription || '') + } + + function syncImportance() { + if (modelData?.status === Status.Active || modelData?.status === Status.NeedsAttention) + root.hasImportant = true; + else if (SystemTray.items.values.every(item => item.status === Status.Passive)) + root.hasImportant = false; + } + + Component.onCompleted: { + syncImportance(); + } + + Connections { + target: modelData + + function onStatusChanged() { + syncImportance(); + } + } + + TapHandler { + acceptedButtons: Qt.LeftButton + onTapped: parent.modelData.activate() + } + TapHandler { + acceptedButtons: Qt.MiddleButton + onTapped: parent.modelData.secondaryActivate() + } + TapHandler { + acceptedButtons: Qt.RightButton + onTapped: (eventPoint, button) => parent.modelData.display(barWindow, eventPoint.scenePosition.x, eventPoint.scenePosition.y) + } + WheelHandler { + onWheel: event => parent.modelData.scroll(event.angleDelta.y) + } + HoverHandler { + id: hover + } + } + } + } +} diff --git a/bar/components/SimpleResource.qml b/bar/components/SimpleResource.qml index 6159f62..54564c3 100644 --- a/bar/components/SimpleResource.qml +++ b/bar/components/SimpleResource.qml @@ -25,7 +25,7 @@ Item { property color thresholdColorL2: Colors.yellow property color thresholdColorL3: Colors.red - property bool isAlt: false + property bool alt: false property color color: value < thresholdL1 ? Colors.accentAlt : value >= thresholdL3 ? thresholdColorL3 : value >= thresholdL2 ? thresholdColorL2 : thresholdColorL1 @@ -53,7 +53,7 @@ Item { handle: Item from: 0 to: 100 - value: !root.isAlt ? root.value : root.altValue + value: !root.alt ? root.value : root.altValue background: IRect { anchors.fill: parent color: "transparent" @@ -81,7 +81,7 @@ Item { centerIn: parent } iconSize: Style.font.size.small - text: !root.isAlt ? root.icon : root.altIcon + text: !root.alt ? root.icon : root.altIcon color: root.value < root.thresholdL1 ? Colors.foreground2 : root.value >= root.thresholdL3 ? root.thresholdColorL3 : root.value >= root.thresholdL2 ? root.thresholdColorL2 : root.thresholdColorL1 } } @@ -94,7 +94,7 @@ Item { anchors.fill: parent cursorShape: altValue != value ? Qt.PointingHandCursor : Qt.ArrowCursor onClicked: { - root.isAlt = !root.isAlt; + root.alt = !root.alt; } } } diff --git a/components/IToolTip.qml b/components/IToolTip.qml new file mode 100644 index 0000000..bea05b8 --- /dev/null +++ b/components/IToolTip.qml @@ -0,0 +1,23 @@ +import QtQuick +import QtQuick.Controls +import Qt5Compat.GraphicalEffects + +import qs.components +import qs.config + +ToolTip { + id: root + + property bool active + + visible: hover.hovered && active + delay: General.toolTipDelay + + contentItem: IText { + text: root.text + } + + background: Rectangle { + color: Qt.rgba(Colors.background3.r, Colors.background3.g, Colors.background3.b, General.toolTipBackgroundOpacity) + } +} diff --git a/config/General.qml b/config/General.qml index 77bc177..3e431b7 100644 --- a/config/General.qml +++ b/config/General.qml @@ -29,4 +29,7 @@ Singleton { readonly property int defaultSearchAlgorithm: Searchable.SearchAlgorithm.Include readonly property int maxClipPreviewChar: 100 + + readonly property int toolTipDelay: 500 + readonly property double toolTipBackgroundOpacity: 0.75 } -- cgit v1.2.3