Skip to content

Instantly share code, notes, and snippets.

@tylerjl
Created October 25, 2025 20:12
Show Gist options
  • Select an option

  • Save tylerjl/5bc79f1ee0e4f66abed29b66f4f5f3cc to your computer and use it in GitHub Desktop.

Select an option

Save tylerjl/5bc79f1ee0e4f66abed29b66f4f5f3cc to your computer and use it in GitHub Desktop.
Quickshell launcher example
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.LocalStorage
import Quickshell
import Quickshell.Widgets
Window {
id: launcher
title: "qs-launcher"
visible: true
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
color: Qt.rgba(0, 0, 0, 0.8)
width: 560
height: 360
property string query: ""
function launchSelected() {
if (list.currentItem && list.currentItem.modelData) {
list.currentItem.modelData.execute();
Qt.quit();
}
}
ColumnLayout {
anchors.fill: parent
spacing: 8
RowLayout {
IconImage {
Layout.leftMargin: 10
source: Quickshell.iconPath("nix-snowflake", true)
Layout.preferredWidth: 25
Layout.preferredHeight: 25
}
TextField {
id: input
Layout.fillWidth: true
placeholderText: "Run…"
font.pixelSize: 18
color: "white"
focus: true
padding: 15
onTextChanged: {
launcher.query = text;
// reset selection to first item of the filtered list
list.currentIndex = filtered.values.length > 0 ? 0 : -1;
}
background: Rectangle {
border.width: 0
color: "transparent"
}
// Quit
Keys.onEscapePressed: Qt.quit()
Keys.onPressed: event => {
const ctrl = event.modifiers & Qt.ControlModifier;
if (event.key == Qt.Key_Up || event.key == Qt.Key_P && ctrl) {
event.accepted = true;
if (list.currentIndex > 0)
list.currentIndex--;
} else if (event.key == Qt.Key_Down || event.key == Qt.Key_N && ctrl) {
event.accepted = true;
if (list.currentIndex < list.count - 1)
list.currentIndex++;
} else if ([Qt.Key_Return, Qt.Key_Enter].includes(event.key)) {
event.accepted = true;
launcher.launchSelected();
} else if (event.key == Qt.Key_C && ctrl) {
event.accepted = true;
Qt.quit();
}
}
}
}
// Filtered model: only items matching the query
ScriptModel {
id: filtered
values: {
const allEntries = [...DesktopEntries.applications.values];
const q = launcher.query.trim();
if (q === "") {
return allEntries;
} else {
return allEntries.filter(d => d.name && d.name.toLowerCase().includes(q));
}
}
}
ListView {
id: list
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
model: filtered.values
currentIndex: filtered.values.length > 0 ? 0 : -1
keyNavigationWraps: true
preferredHighlightBegin: 0
preferredHighlightEnd: height
highlightRangeMode: ListView.ApplyRange
highlightMoveDuration: 80
highlight: Rectangle {
radius: 4
opacity: 0.45
color: input.palette.highlight
}
delegate: Item {
id: entry
required property var modelData
required property int index
width: ListView.view.width
height: 36
MouseArea {
anchors.fill: parent
onClicked: list.currentIndex = entry.index
onDoubleClicked: launcher.launchSelected()
}
Row {
anchors.fill: parent
anchors.margins: 8
spacing: 10
IconImage {
source: Quickshell.iconPath(modelData.icon, true)
width: 23
height: 23
}
Text {
id: label
color: "white"
text: modelData.name
font.pointSize: 13
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
}
}
// Enter also works while ListView has focus
Keys.onReturnPressed: launcher.launchSelected()
}
}
}
@kawsaramin101

Copy link
Copy Markdown

Hey, I have made some improvements to how apps are sorted and searched:

  • Apps are now sorted alphabetically instead of randomly
  • The search feature now looks beyond just the app name. It also searches the app’s comment (description), keywords, and categories from the .desktop file
ScriptModel {
    id: filtered
    values: {
        const allEntries = [...DesktopEntries.applications.values]
            .filter(d => d.name)
            .sort((a, b) => a.name.localeCompare(b.name))
        const q = launcher.query.trim()
        if (q === "") return allEntries
           return allEntries.filter(d => {
            const name       = (d.name       || "").toLowerCase()
            const comment    = (d.comment    || "").toLowerCase()
            const keywords   = (d.keywords   || []).join(" ").toLowerCase()
            const categories = (d.categories || []).join(" ").toLowerCase()
            return name.includes(q) || comment.includes(q) || keywords.includes(q) || categories.includes(q)
        })
    
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment