Skip to content

Commit 1c27de9

Browse files
committed
more tray clean up
1 parent 8083837 commit 1c27de9

10 files changed

Lines changed: 1060 additions & 145 deletions

File tree

dot_files/quickshell/defaults/config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"showBattery": true,
1616
"showNetwork": true,
1717
"showTray": true,
18-
"showClock": true
18+
"showClock": true,
19+
"use24Hour": true
1920
},
2021
"notifications": {
2122
"timeout": 5000,

dot_files/quickshell/modules/bar/StatusBar.qml

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import QtQuick
22
import QtQuick.Layouts
33
import Quickshell
44
import Quickshell.Wayland
5+
import Quickshell.Services.SystemTray
56

67
import "../common" as Common
78
import "../../services" as Services
@@ -151,6 +152,119 @@ PanelWindow {
151152
visible: root.isRightmost
152153
spacing: 2
153154

155+
// System tray icons
156+
Repeater {
157+
model: SystemTray.items
158+
159+
delegate: MouseArea {
160+
id: trayItemArea
161+
required property var modelData
162+
163+
Layout.preferredHeight: 28
164+
Layout.preferredWidth: 28
165+
hoverEnabled: true
166+
cursorShape: Qt.PointingHandCursor
167+
168+
// Check if icon has unsupported custom path (e.g. "icon_name?path=/some/path")
169+
property bool hasCustomPath: modelData.icon && modelData.icon.includes("?path=")
170+
171+
// Resolve icon source - handle paths, icon names, skip unsupported custom paths
172+
property string iconSource: {
173+
const icon = modelData.icon
174+
if (!icon || icon === "") return ""
175+
// Skip icons with custom paths - Quickshell doesn't support them
176+
if (icon.includes("?path=")) return ""
177+
// Already a full path or URL
178+
if (icon.startsWith("/")) return "file://" + icon
179+
if (icon.startsWith("file://") || icon.startsWith("image://")) return icon
180+
// Icon name - try Qt icon provider
181+
return "image://icon/" + icon
182+
}
183+
184+
// Datacube fallback lookup using app title
185+
property string datacubeIcon: Services.IconResolver.getIcon(modelData.title)
186+
187+
Rectangle {
188+
anchors.fill: parent
189+
radius: Common.Appearance.rounding.small
190+
color: trayItemArea.containsMouse
191+
? Common.Appearance.m3colors.surfaceVariant
192+
: "transparent"
193+
194+
Behavior on color {
195+
ColorAnimation { duration: 150 }
196+
}
197+
}
198+
199+
// Track if primary icon failed (Error, Null status, or has unsupported custom path)
200+
property bool primaryFailed: trayItemArea.hasCustomPath || primaryTrayIcon.status === Image.Error || primaryTrayIcon.status === Image.Null || trayItemArea.iconSource === ""
201+
202+
// Primary icon from tray item
203+
Image {
204+
id: primaryTrayIcon
205+
anchors.centerIn: parent
206+
width: Common.Appearance.sizes.iconMedium
207+
height: Common.Appearance.sizes.iconMedium
208+
sourceSize: Qt.size(Common.Appearance.sizes.iconMedium, Common.Appearance.sizes.iconMedium)
209+
source: trayItemArea.iconSource
210+
smooth: true
211+
visible: status === Image.Ready
212+
}
213+
214+
// Datacube fallback icon
215+
Image {
216+
id: fallbackTrayIcon
217+
anchors.centerIn: parent
218+
width: Common.Appearance.sizes.iconMedium
219+
height: Common.Appearance.sizes.iconMedium
220+
sourceSize: Qt.size(Common.Appearance.sizes.iconMedium, Common.Appearance.sizes.iconMedium)
221+
source: trayItemArea.primaryFailed ? trayItemArea.datacubeIcon : ""
222+
smooth: true
223+
visible: trayItemArea.primaryFailed && status === Image.Ready
224+
}
225+
226+
// Last resort: letter icon
227+
Rectangle {
228+
anchors.centerIn: parent
229+
width: Common.Appearance.sizes.iconMedium
230+
height: Common.Appearance.sizes.iconMedium
231+
radius: Common.Appearance.rounding.small
232+
color: Common.Appearance.m3colors.primaryContainer
233+
visible: trayItemArea.primaryFailed && fallbackTrayIcon.status !== Image.Ready
234+
235+
Text {
236+
anchors.centerIn: parent
237+
text: trayItemArea.modelData.title ? trayItemArea.modelData.title.charAt(0).toUpperCase() : "?"
238+
font.pixelSize: 10
239+
font.bold: true
240+
color: Common.Appearance.m3colors.onPrimaryContainer
241+
}
242+
}
243+
244+
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
245+
246+
onClicked: (mouse) => {
247+
if (mouse.button === Qt.RightButton || (trayItemArea.modelData.onlyMenu && trayItemArea.modelData.hasMenu)) {
248+
// Right click or menu-only item: show menu
249+
if (trayItemArea.modelData.hasMenu) {
250+
// Map coordinates to window
251+
const pos = trayItemArea.mapToItem(null, 0, trayItemArea.height)
252+
trayItemArea.modelData.display(root, pos.x, pos.y)
253+
}
254+
} else if (mouse.button === Qt.MiddleButton) {
255+
trayItemArea.modelData.secondaryActivate()
256+
} else {
257+
// Left click: activate
258+
trayItemArea.modelData.activate()
259+
}
260+
}
261+
262+
onWheel: (wheel) => {
263+
trayItemArea.modelData.scroll(wheel.angleDelta.y, false)
264+
}
265+
}
266+
}
267+
154268
// Weather (if enabled)
155269
BarButton {
156270
visible: Common.Config.showWeather && Services.Weather.ready
@@ -167,9 +281,9 @@ PanelWindow {
167281
tooltip: "Camera in use"
168282
}
169283

170-
// Network - only show if wifi available (for wifi) or ethernet connected
171-
BarIndicator {
172-
visible: Common.Config.showNetwork && (Services.Network.wifiAvailable || (Services.Network.connected && Services.Network.type === "ethernet"))
284+
// Network
285+
BarButton {
286+
visible: Common.Config.showNetwork
173287
icon: {
174288
if (!Services.Network.connected) {
175289
return Services.Network.wifiAvailable ? Common.Icons.icons.wifiOff : Common.Icons.icons.ethernetOff
@@ -182,6 +296,7 @@ PanelWindow {
182296
tooltip: Services.Network.connected
183297
? Services.Network.name
184298
: "Disconnected"
299+
onClicked: Root.GlobalStates.toggleSidebarRight(root.targetScreen, "network")
185300
}
186301

187302
// Audio output

dot_files/quickshell/modules/common/Config.qml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Singleton {
3232
property bool showNetwork: true
3333
property bool showTray: true
3434
property bool showClock: true
35+
property bool use24Hour: true
3536

3637
// Notification settings
3738
property int notificationTimeout: 5000
@@ -127,6 +128,7 @@ Singleton {
127128
showNetwork = config.bar.showNetwork ?? showNetwork
128129
showTray = config.bar.showTray ?? showTray
129130
showClock = config.bar.showClock ?? showClock
131+
use24Hour = config.bar.use24Hour ?? use24Hour
130132
}
131133

132134
// Notifications
@@ -190,7 +192,8 @@ Singleton {
190192
showBattery: showBattery,
191193
showNetwork: showNetwork,
192194
showTray: showTray,
193-
showClock: showClock
195+
showClock: showClock,
196+
use24Hour: use24Hour
194197
},
195198
notifications: {
196199
timeout: notificationTimeout,

dot_files/quickshell/modules/notifications/NotificationPopup.qml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,17 +113,24 @@ PanelWindow {
113113
onTriggered: dismissed()
114114
}
115115

116-
// Pause timer on hover
116+
// Click to invoke default action, hover to pause timer
117117
MouseArea {
118118
anchors.fill: parent
119119
hoverEnabled: true
120+
cursorShape: notification.actions && notification.actions.length === 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
120121
onContainsMouseChanged: {
121122
if (containsMouse) {
122123
dismissTimer.stop()
123124
} else {
124125
dismissTimer.restart()
125126
}
126127
}
128+
onClicked: {
129+
// If there's exactly one action, clicking the notification invokes it
130+
if (notification.actions && notification.actions.length === 1) {
131+
actionInvoked(notification.actions[0].identifier || "")
132+
}
133+
}
127134
}
128135

129136
ColumnLayout {
@@ -284,9 +291,9 @@ PanelWindow {
284291
}
285292
}
286293

287-
// Actions row
294+
// Actions row - only show if multiple actions (single action is triggered by clicking notification)
288295
RowLayout {
289-
visible: notification.actions && notification.actions.length > 0
296+
visible: notification.actions && notification.actions.length > 1
290297
Layout.fillWidth: true
291298
spacing: Common.Appearance.spacing.small
292299

0 commit comments

Comments
 (0)