129 lines
4.0 KiB
QML
129 lines
4.0 KiB
QML
pragma Singleton
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
|
|
Item {
|
|
id: root
|
|
|
|
// All parsed desktop apps
|
|
property var allApps: []
|
|
|
|
// Category list — deduplicated by display label ("All" always first)
|
|
readonly property var categories: {
|
|
let cats = []
|
|
let seen = {}
|
|
for (let i = 0; i < allApps.length; i++) {
|
|
let appCats = allApps[i].categories || []
|
|
for (let j = 0; j < appCats.length; j++) {
|
|
let rawKey = appCats[j].trim()
|
|
let displayName = knownCategories[rawKey]
|
|
if (displayName && !seen[displayName]) {
|
|
seen[displayName] = true
|
|
cats.push(displayName)
|
|
}
|
|
}
|
|
}
|
|
cats.sort((a, b) => a.localeCompare(b))
|
|
return ["All"].concat(cats)
|
|
}
|
|
|
|
// Human-readable labels for XDG category keys
|
|
readonly property var knownCategories: ({
|
|
"AudioVideo": "Sound & Video",
|
|
"Audio": "Sound & Video",
|
|
"Video": "Sound & Video",
|
|
"Development": "Development",
|
|
"Education": "Education",
|
|
"Game": "Games",
|
|
"Games": "Games",
|
|
"Graphics": "Graphics",
|
|
"Network": "Internet",
|
|
"Internet": "Internet",
|
|
"Office": "Office",
|
|
"Science": "Science",
|
|
"Settings": "Settings",
|
|
"System": "System",
|
|
"Utility": "Utilities",
|
|
"Utilities": "Utilities"
|
|
})
|
|
|
|
property string activeCategory: "All"
|
|
property string searchQuery: ""
|
|
|
|
// Filtered app list
|
|
readonly property var filteredApps: {
|
|
let q = searchQuery.toLowerCase().trim()
|
|
let result = []
|
|
for (let i = 0; i < allApps.length; i++) {
|
|
let app = allApps[i]
|
|
|
|
// Category filter
|
|
if (activeCategory !== "All") {
|
|
let inCat = false
|
|
let appCats = app.categories || []
|
|
for (let j = 0; j < appCats.length; j++) {
|
|
let rawKey = appCats[j].trim()
|
|
let displayName = knownCategories[rawKey] || rawKey
|
|
if (displayName === activeCategory || rawKey === activeCategory) {
|
|
inCat = true
|
|
break
|
|
}
|
|
}
|
|
if (!inCat) continue
|
|
}
|
|
|
|
// Search filter
|
|
if (q && !app.name.toLowerCase().includes(q)) continue
|
|
|
|
result.push(app)
|
|
}
|
|
// Sort alphabetically
|
|
result.sort((a, b) => a.name.localeCompare(b.name))
|
|
return result
|
|
}
|
|
|
|
function launch(app) {
|
|
if (!app || !app.exec) return
|
|
// Strip desktop-file field codes (%u %U %f %F etc.)
|
|
let cmd = app.exec.replace(/%[a-zA-Z]/g, "").trim()
|
|
TaskService.launchApp(cmd)
|
|
}
|
|
|
|
function reset() {
|
|
activeCategory = "All"
|
|
searchQuery = ""
|
|
}
|
|
|
|
// Fallback icon path from active system icon theme
|
|
property string fallbackIconPath: ""
|
|
|
|
Process { id: launchProc }
|
|
|
|
// Parse desktop files on startup and monitor real-time directory changes
|
|
Process {
|
|
id: parseProc
|
|
command: ["python3", "-u", Quickshell.env("HOME") + "/.config/quickshell/services/python/app_launcher_service.py"]
|
|
running: true
|
|
stdout: SplitParser {
|
|
onRead: data => {
|
|
try {
|
|
let parsed = JSON.parse(data.trim())
|
|
if (parsed && typeof parsed === "object") {
|
|
if (parsed.fallback) root.fallbackIconPath = parsed.fallback
|
|
if (parsed.apps && Array.isArray(parsed.apps)) root.allApps = parsed.apps
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
function reload() {
|
|
if (parseProc.running) {
|
|
parseProc.write("reload\n")
|
|
} else {
|
|
parseProc.running = true
|
|
}
|
|
}
|
|
}
|