pragma ComponentBehavior: Bound import QtQuick import Quickshell import ZShell.Config import qs.Components import qs.Services import qs.Modules.Launcher.Items import qs.Modules.Launcher.Services CustomListView { id: root readonly property string displayState: stateForText(displayText) property string displayText readonly property string requestedState: stateForText(search.text) required property SearchBar search required property PersistentProperties visibilities function resultsForText(text: string): var { switch (stateForText(text)) { case "actions": return Actions.query(text); case "calc": return [0]; case "variant": return SchemeVariants.query(text); case "files": return Files.query(text); default: return Apps.search(text); } } function stateForText(text: string): string { const prefix = Config.launcher.actionPrefix; if (text.startsWith(prefix)) { for (const action of ["calc", "scheme", "variant", "files"]) if (text.startsWith(`${prefix}${action} `)) return action; return "actions"; } return "apps"; } function syncDisplayText(): void { if (visibilities.launcher && requestedState === displayState) displayText = search.text; } highlightFollowsCurrentItem: false highlightRangeMode: ListView.ApplyRange implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing orientation: Qt.Vertical preferredHighlightBegin: 0 preferredHighlightEnd: height spacing: Tokens.spacing.small state: visibilities.launcher ? requestedState : displayState verticalLayoutDirection: ListView.BottomToTop CustomScrollBar.vertical: CustomScrollBar { flickable: root } add: Transition { enabled: !root.state Anim { from: 0 property: "opacity" to: 1 type: Anim.DefaultEffects } } addDisplaced: Transition { Anim { property: "y" type: Anim.StandardSmall } Anim { property: "opacity" to: 1 type: Anim.DefaultEffects } } displaced: Transition { Anim { property: "y" } Anim { property: "opacity" to: 1 type: Anim.DefaultEffects } } highlight: CustomRect { color: Colors.palette.m3onSurface implicitHeight: root.currentItem?.implicitHeight ?? 0 implicitWidth: root.width opacity: 0.08 radius: Tokens.rounding.medium y: root.currentItem?.y ?? 0 Behavior on y { Anim { duration: Tokens.anim.durations.small easing: Tokens.anim.expressiveDefaultEffects } } } model: ScriptModel { values: root.resultsForText(root.displayText) onValuesChanged: root.currentIndex = 0 } move: Transition { Anim { property: "y" } Anim { property: "opacity" to: 1 type: Anim.DefaultEffects } } remove: Transition { enabled: !root.state Anim { from: 1 property: "opacity" to: 0 type: Anim.DefaultEffects } } states: [ State { name: "apps" PropertyChanges { root.delegate: appItem } }, State { name: "actions" PropertyChanges { root.delegate: actionItem } }, State { name: "calc" PropertyChanges { root.delegate: calcItem } }, State { name: "variant" PropertyChanges { root.delegate: variantItem } }, State { name: "files" PropertyChanges { root.delegate: filesItem } } ] transitions: Transition { SequentialAnimation { ParallelAnimation { Anim { duration: Tokens.anim.durations.small easing: Tokens.anim.standardAccel from: 1 property: "opacity" target: root to: 0 } Anim { duration: Tokens.anim.durations.small easing: Tokens.anim.standardAccel from: 1 property: "scale" target: root to: 0.9 } } PropertyAction { property: "delegate" target: root value: null } ScriptAction { script: root.displayText = root.search.text } PropertyAction { property: "delegate" target: root } ParallelAnimation { Anim { duration: Tokens.anim.durations.small easing: Tokens.anim.standardDecel from: 0 property: "opacity" target: root to: 1 } Anim { duration: Tokens.anim.durations.small easing: Tokens.anim.standardDecel from: 0.9 property: "scale" target: root to: 1 } } PropertyAction { property: "enabled" targets: [root.add, root.remove] value: true } } } Component.onCompleted: displayText = search.text Component { id: appItem AppItem { visibilities: root.visibilities } } Component { id: actionItem ActionItem { list: root } } Component { id: calcItem CalcItem { list: root } } Component { id: variantItem VariantItem { list: root } } Component { id: filesItem FilesItem { list: root } } Connections { function onTextChanged() { root.syncDisplayText(); } target: root.search } Connections { function onLauncherChanged() { root.syncDisplayText(); } target: root.visibilities } }