diff --git a/roles/quickshell/files/bar/NetworkStatus.qml b/roles/quickshell/files/bar/NetworkStatus.qml index 8133401..f09498c 100644 --- a/roles/quickshell/files/bar/NetworkStatus.qml +++ b/roles/quickshell/files/bar/NetworkStatus.qml @@ -11,6 +11,10 @@ Item { property string connType: "none" // "wifi", "ethernet", "none" property string connName: "" + // accumulate results here, flush to displayed props on exit + property string _pendingType: "none" + property string _pendingName: "" + // \uf1eb = FA wifi, \uf0e8 = FA sitemap (wired proxy), \uf127 = FA chain-broken readonly property string netIcon: connType === "wifi" ? "\uf1eb" : @@ -25,9 +29,9 @@ Item { const state = line.substring(idx1 + 1, idx2) const conn = line.substring(idx2 + 1).trim() if (state === "connected" && (type === "wifi" || type === "ethernet")) { - if (root.connType === "none" || type === "wifi") { - root.connType = type - root.connName = conn + if (root._pendingType === "none" || type === "wifi") { + root._pendingType = type + root._pendingName = conn } } } @@ -38,8 +42,8 @@ Item { repeat: true triggeredOnStart: true onTriggered: { - root.connType = "none" - root.connName = "" + root._pendingType = "none" + root._pendingName = "" netProc.running = true } } @@ -51,7 +55,11 @@ Item { splitMarker: "\n" onRead: data => root.parseLine(data) } - onExited: running = false + onExited: { + root.connType = root._pendingType + root.connName = root._pendingName + running = false + } } Row { diff --git a/roles/quickshell/files/bar/SysTray.qml b/roles/quickshell/files/bar/SysTray.qml index fb71585..01686ca 100644 --- a/roles/quickshell/files/bar/SysTray.qml +++ b/roles/quickshell/files/bar/SysTray.qml @@ -33,31 +33,11 @@ Item { Behavior on color { ColorAnimation { duration: 80 } } } - IconImage { - id: iconImg - anchors.centerIn: parent - implicitSize: 16 - visible: status === Image.Ready - source: { - const icon = trayDelegate.modelData.icon - if (!icon || icon === "") return "" - if (icon.startsWith("/") || icon.startsWith("file://") || icon.startsWith("image://")) return icon - const path = Quickshell.iconPath(icon, "") - if (path !== "") return "file://" + path - return "image://icon/" + icon - } - mipmap: true - } - - // Letter fallback when icon fails to load - Text { - anchors.centerIn: parent - visible: iconImg.status !== Image.Ready - text: (trayDelegate.modelData.title ?? trayDelegate.modelData.id ?? "?").charAt(0).toUpperCase() - color: Theme.textDim - font.pixelSize: 11 - font.bold: true - } + TrayIcon { + anchors.centerIn: parent + icon: trayDelegate.modelData.icon + size: 16 + } MouseArea { id: hoverArea diff --git a/roles/quickshell/files/bar/WorkspaceButton.qml b/roles/quickshell/files/bar/WorkspaceButton.qml index e4ec3e9..49c21a6 100644 --- a/roles/quickshell/files/bar/WorkspaceButton.qml +++ b/roles/quickshell/files/bar/WorkspaceButton.qml @@ -1,6 +1,6 @@ import Quickshell import Quickshell.Hyprland -import Quickshell.Widgets +// import Quickshell.Widgets import QtQuick import "../components" @@ -27,17 +27,29 @@ Rectangle { delegate: Item { id: iconItem required property var modelData - property string appClass: modelData.lastIpcObject["class"] ?? "" - property var entry: appClass !== "" ? DesktopEntries.heuristicLookup(appClass) : null - width: 16 - height: 16 + implicitWidth: 16 + implicitHeight: 16 - IconImage { + property string appClass: "" + + Component.onCompleted: { + var cls = modelData?.lastIpcObject?.["class"] ?? "" + if (cls !== "") { + appClass = cls + } else if (modelData) { + modelData.lastIpcObjectChanged.connect(function() { + var c = iconItem.modelData?.lastIpcObject?.["class"] ?? "" + if (c !== "") iconItem.appClass = c + }) + Qt.callLater(Hyprland.refreshToplevels) + } + } + + TrayIcon { anchors.fill: parent - source: iconItem.entry && iconItem.entry.icon !== "" - ? "image://icon/" + iconItem.entry.icon - : "" + icon: iconItem.appClass + size: 16 } } } diff --git a/roles/quickshell/files/bar/Workspaces.qml b/roles/quickshell/files/bar/Workspaces.qml index fba3c53..a567914 100644 --- a/roles/quickshell/files/bar/Workspaces.qml +++ b/roles/quickshell/files/bar/Workspaces.qml @@ -1,3 +1,5 @@ +pragma ComponentBehavior: Bound + import Quickshell.Hyprland import QtQuick diff --git a/roles/quickshell/files/components/TrayIcon.qml b/roles/quickshell/files/components/TrayIcon.qml new file mode 100644 index 0000000..eb81318 --- /dev/null +++ b/roles/quickshell/files/components/TrayIcon.qml @@ -0,0 +1,103 @@ +// import QtQuick +// import Quickshell.Widgets +// +// Item { +// id: root +// +// property string icon: "" +// property int size: 16 +// +// implicitWidth: size +// implicitHeight: size +// +// readonly property url resolvedSource: { +// if (!icon) return "" +// if (icon.startsWith("/") || icon.startsWith("file://") || icon.startsWith("image://")) +// return icon +// if (icon.includes("?path=")) { +// const [name, path] = icon.split("?path=") +// const baseName = name.slice(name.lastIndexOf("/") + 1) +// return `file://${path}/${baseName}` +// } +// return `image://icon/${icon}` +// } +// +// readonly property bool isIconTheme: resolvedSource.toString().startsWith("image://icon/") +// readonly property bool hasSource: resolvedSource.toString() !== "" +// +// IconImage { +// anchors.fill: parent +// source: root.resolvedSource +// visible: root.hasSource && root.isIconTheme +// asynchronous: true +// } +// +// Image { +// anchors.fill: parent +// source: root.hasSource && !root.isIconTheme ? root.resolvedSource : "" +// visible: root.hasSource && !root.isIconTheme +// asynchronous: true +// fillMode: Image.PreserveAspectFit +// } +// } + + + +// pragma ComponentBehavior: Bound + +import QtQuick +// import Quickshell +import Quickshell.Widgets + +Item { + id: root + + property string icon: "" + property int size: 16 + + implicitWidth: size + implicitHeight: size + + readonly property url resolvedSource: { + if (!icon) return "" + if (icon.startsWith("/") || icon.startsWith("file://") || icon.startsWith("image://")) + return icon + if (icon.includes("?path=")) { + const [name, path] = icon.split("?path=") + const baseName = name.slice(name.lastIndexOf("/") + 1) + return `file://${path}/${baseName}` + } + // Use Quickshell's icon image provider instead of iconPath() + return `image://icon/${icon}` + } + + Loader { + anchors.fill: parent + // asynchronous: true + sourceComponent: root.resolvedSource && root.resolvedSource.toString() !== "" + ? root.resolvedSource.toString().startsWith("image://icon/") + ? iconComponent + : imageComponent + : null + } + + Component { + id: iconComponent + IconImage { + source: root.resolvedSource + asynchronous: true + // mipmap: true + } + } + + Component { + id: imageComponent + Image { + source: root.resolvedSource + asynchronous: true + // mipmap: true + fillMode: Image.PreserveAspectFit + } + } +} + diff --git a/roles/quickshell/files/components/qmldir b/roles/quickshell/files/components/qmldir index 0400b7e..d634da4 100644 --- a/roles/quickshell/files/components/qmldir +++ b/roles/quickshell/files/components/qmldir @@ -1,2 +1,3 @@ singleton Theme 1.0 Theme.qml Enclosure 1.0 Enclosure.qml +TrayIcon 1.0 TrayIcon.qml