Added favorites section in launcher and now the filter sorts programs alphabetically

This commit is contained in:
SomeElse
2026-06-04 02:20:00 +00:00
parent c28f00caf7
commit c97bcaf1f3
6 changed files with 469 additions and 7 deletions

View File

@@ -33,14 +33,28 @@ PanelWindow {
property var allItems: []
property bool loading: false
property int lastCacheUpdate: 0
property var favoriteIds: []
readonly property string favoritesPath: Quickshell.env("HOME") + "/.config/quickshell/launcher/favorites.txt"
readonly property int defaultIndex: Cfg.Config.launcherSelectFirst ? 0 : -1
readonly property var filteredItems: {
const q = searchInput.text.toLowerCase()
if (!q) return allItems.slice(0, 80)
return allItems.filter(i => i.name.toLowerCase().includes(q))
let filtered
if (!q) {
filtered = allItems.slice(0, 80)
} else {
filtered = allItems.filter(i => i.name.toLowerCase().startsWith(q))
}
const favs = filtered.filter(i => root.favoriteIds.includes(i.id))
const rest = filtered.filter(i => !root.favoriteIds.includes(i.id))
const result = []
if (favs.length > 0)
result.push(...favs.map(i => Object.assign({}, i, { section: "Favorites" })))
if (rest.length > 0)
result.push(...rest.map(i => Object.assign({}, i, { section: "All Applications" })))
return result
}
function toggle() {
@@ -60,6 +74,7 @@ PanelWindow {
searchInput.text = ""
resultsList.currentIndex = root.defaultIndex
loadItems()
loadFavorites()
Qt.callLater(() => searchInput.forceActiveFocus())
}
@@ -132,6 +147,34 @@ PanelWindow {
return args
}
function loadFavorites() {
favoritesReadProc.command = ["sh", "-c", "cat " + shellQuote(favoritesPath) + " 2>/dev/null || true"]
favoritesReadProc.running = true
}
function saveFavorites() {
if (favoriteIds.length === 0) {
favoritesWriteProc.command = ["sh", "-c", ": > " + shellQuote(favoritesPath)]
} else {
const quoted = favoriteIds.map(id => shellQuote(id)).join(" ")
favoritesWriteProc.command = ["sh", "-c",
"printf '%s\\n' " + quoted + " > " + shellQuote(favoritesPath)]
}
favoritesWriteProc.running = true
}
function toggleFavorite(itemId) {
const arr = favoriteIds.slice()
const idx = arr.indexOf(itemId)
if (idx >= 0) {
arr.splice(idx, 1)
} else {
arr.push(itemId)
}
favoriteIds = arr
saveFavorites()
}
function launch(item) {
if (!item || !item.exec) return
@@ -194,6 +237,22 @@ PanelWindow {
Process { id: launchProc }
Process {
id: favoritesReadProc
property string rawData: ""
stdout: SplitParser { onRead: data => favoritesReadProc.rawData += data + "\n" }
onRunningChanged: {
if (!running) {
if (rawData.trim()) {
root.favoriteIds = rawData.trim().split("\n").filter(l => l.length > 0)
}
rawData = ""
}
}
}
Process { id: favoritesWriteProc }
MouseArea {
anchors.fill: parent
@@ -312,6 +371,21 @@ PanelWindow {
currentIndex: root.defaultIndex
spacing: 2
section.property: "section"
section.criteria: ViewSection.FullString
section.delegate: Rectangle {
width: resultsList.width
height: 28
color: "transparent"
Text {
anchors { left: parent.left; leftMargin: 12; verticalCenter: parent.verticalCenter }
text: section
color: t.launcherDim
font.pixelSize: 11
font.weight: Font.Bold
}
}
onModelChanged: currentIndex = root.defaultIndex
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded }
@@ -366,6 +440,18 @@ PanelWindow {
Layout.fillWidth: true
elide: Text.ElideRight
}
Item {
width: 22; height: 22
Layout.alignment: Qt.AlignVCenter
Text {
anchors.centerIn: parent
text: root.favoriteIds.includes(modelData.id) ? "★" : "☆"
color: root.favoriteIds.includes(modelData.id) ? t.launcherAccent : t.launcherDim
font.pixelSize: 14
}
}
}
MouseArea {
@@ -373,7 +459,13 @@ PanelWindow {
hoverEnabled: true
onEntered: parent.hovered = true
onExited: parent.hovered = false
onClicked: root.launch(modelData)
onClicked: mouse => {
if (mouse.x >= width - 46) {
root.toggleFavorite(modelData.id)
} else {
root.launch(modelData)
}
}
}
}
}