quickshell and hyprland additions

This commit is contained in:
2026-03-15 13:56:00 +02:00
parent c9c27d1554
commit 1ad06b82a6
509 changed files with 68371 additions and 19 deletions

View File

@@ -0,0 +1,168 @@
pragma Singleton
import QtQuick
import Quickshell
import qs.modules.functions
import qs.config
/*
This registry is only used to get app details for wm classes.
*/
Singleton {
id: registry
property var apps: []
property var classToIcon: ({})
property var desktopIdToIcon: ({})
property var nameToIcon: ({})
signal ready()
function iconForDesktopIcon(icon) {
if (!icon) return ""
// If it's already a URL, keep it
if (icon.startsWith("file://") || icon.startsWith("qrc:/"))
return icon
// Absolute filesystem path → convert to file URL
if (icon.startsWith("/"))
return "file://" + icon
// Otherwise treat as theme icon name
return Quickshell.iconPath(icon)
}
// Try very aggressive matching so the running app always gets the same icon as launcher
function iconForClass(id) {
if (!id) return ""
const lower = id.toLowerCase()
// direct hits first
if (classToIcon[lower])
return iconForDesktopIcon(classToIcon[lower])
if (desktopIdToIcon[lower])
return iconForDesktopIcon(desktopIdToIcon[lower])
if (nameToIcon[lower])
return iconForDesktopIcon(nameToIcon[lower])
// fuzzy contains match against wmClass map
for (let key in classToIcon) {
if (lower.includes(key) || key.includes(lower))
return iconForDesktopIcon(classToIcon[key])
}
// fuzzy against desktop ids
for (let key in desktopIdToIcon) {
if (lower.includes(key) || key.includes(lower))
return iconForDesktopIcon(desktopIdToIcon[key])
}
// fuzzy against names
for (let key in nameToIcon) {
if (lower.includes(key) || key.includes(lower))
return iconForDesktopIcon(nameToIcon[key])
}
// final fallback to theme resolution
const resolved = FileUtils.resolveIcon(id)
return iconForDesktopIcon(resolved)
}
// Extra helper: resolve icon using any metadata we might have (Hyprland, Niri, etc.)
function iconForAppMeta(meta) {
if (!meta) return Quickshell.iconPath("application-x-executable")
const candidates = [
meta.appId,
meta.class,
meta.initialClass,
meta.desktopId,
meta.title,
meta.name
]
for (let c of candidates) {
const icon = iconForClass(c)
if (icon !== "")
return icon
}
// fallback: try compositor provided icon name
if (meta.icon)
return iconForDesktopIcon(meta.icon)
// hard fallback icons (guaranteed to exist in most themes)
const fallbacks = [
"application-x-executable",
"application-default-icon",
"window"
]
for (let f of fallbacks) {
const resolved = Quickshell.iconPath(f)
if (resolved)
return resolved
}
return ""
}
function registerApp(displayName, comment, icon, exec, wmClass, desktopId) {
const entry = {
name: displayName,
comment: comment,
icon: icon,
exec: exec,
wmClass: wmClass,
desktopId: desktopId
}
apps.push(entry)
if (wmClass)
classToIcon[wmClass.toLowerCase()] = icon
if (desktopId)
desktopIdToIcon[desktopId.toLowerCase()] = icon
if (displayName)
nameToIcon[displayName.toLowerCase()] = icon
// Hard aliases for apps with messy WM_CLASS values
if (displayName.toLowerCase().includes("visual studio code") ||
icon.toLowerCase().includes("code")) {
classToIcon["code"] = icon
classToIcon["code-oss"] = icon
classToIcon["code-url-handler"] = icon
desktopIdToIcon["code.desktop"] = icon
desktopIdToIcon["code-oss.desktop"] = icon
}
}
function buildRegistry() {
const entries = DesktopEntries.applications.values
for (let entry of entries) {
if (entry.noDisplay)
continue
registry.registerApp(
entry.name || "",
entry.comment || "",
entry.icon || "",
entry.execString || "",
entry.startupWMClass || "",
entry.id || ""
)
}
registry.ready()
}
Component.onCompleted: buildRegistry()
}