pragma ComponentBehavior: Bound import "services" import qs.services import qs.config import qs.custom import Quickshell import QtQuick Item { id: root required property PersistentProperties uiState required property var wrapper required property var panels readonly property color color: list.color readonly property int padding: 15 readonly property int rounding: 25 implicitWidth: listWrapper.width + padding * 2 implicitHeight: searchWrapper.height + listWrapper.height + padding * 2 anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter Item { id: listWrapper implicitWidth: list.width implicitHeight: list.height + root.padding anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: searchWrapper.top anchors.bottomMargin: root.padding ContentList { id: list uiState: root.uiState wrapper: root.wrapper panels: root.panels search: search padding: root.padding rounding: root.rounding } } CustomRect { id: searchWrapper color: Config.colors.container radius: 1000 anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: root.padding implicitHeight: Math.max(searchIcon.implicitHeight, search.implicitHeight, clearIcon.implicitHeight) MaterialIcon { id: searchIcon anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: root.padding text: "search" color: Config.colors.tertiary } CustomTextField { id: search anchors.left: searchIcon.right anchors.right: clearBtn.left anchors.leftMargin: 7 anchors.rightMargin: 7 topPadding: 12 bottomPadding: 12 placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix) onAccepted: { const currentItem = list.list?.currentItem; if (currentItem) { if (text.startsWith(Config.launcher.actionPrefix)) { currentItem.modelData.onClicked(root.uiState); } else { Apps.launch(currentItem.modelData); root.uiState.launcher = false; } } } Keys.onUpPressed: list.list?.decrementCurrentIndex() Keys.onDownPressed: list.list?.incrementCurrentIndex() Keys.onPressed: event => { if (event.modifiers & Qt.ControlModifier) { if (event.key === Qt.Key_J) { list.list?.incrementCurrentIndex(); event.accepted = true; } else if (event.key === Qt.Key_K) { list.list?.decrementCurrentIndex(); event.accepted = true; } } else if (event.key === Qt.Key_Tab) { list.list?.incrementCurrentIndex(); event.accepted = true; } else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) { list.list?.decrementCurrentIndex(); event.accepted = true; } } Connections { target: root.uiState function onLauncherChanged(): void { if (root.uiState.launcher) search.focus = true; else { search.text = ""; list.list.currentIndex = 0; } } } } StateLayer { id: clearBtn anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: root.padding implicitWidth: 24 implicitHeight: 24 disabled: search.text === "" onClicked: search.text = "" opacity: disabled ? 0 : 1 MaterialIcon { id: clearIcon anchors.centerIn: parent width: search.text ? implicitWidth : implicitWidth / 2 text: "close" color: Config.colors.tertiary Behavior on width { Anim { duration: Config.anim.durations.small } } } Behavior on opacity { Anim { duration: Config.anim.durations.small } } } } }