import Quickshell import Quickshell.Io import Quickshell.Wayland import QtQuick import QtQuick.Effects import "./emojis.mjs" as Emojis PanelWindow { id: root anchors { top: true } visible: false implicitHeight: 400 implicitWidth: 1000 exclusionMode: ExclusionMode.Ignore color: "transparent" WlrLayershell.layer: WlrLayershell.Overlay WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive IpcHandler { target: "launcher" function open() {root.open()} function clone() {root.close()} function toggle() {root.visible ? root.close() : root.open()} } function open() { input.text = "" root.visible = true exit.stop(); entry.start(); } function close() { input.text = "" entry.stop(); exit.start(); } property var currentItems: { if (input.text.length === 0 || input.text === ":") { return [] } const searchEmojis = (input.text[0] === ':') const items = searchEmojis ? Emojis.emojis.emojis : Array.from(DesktopEntries.applications.values) for (let item of items) { item.searchPos = item.name.toLowerCase().search(input.text.slice(searchEmojis)) } return items .filter(item => item.searchPos !== -1) .sort((a, b) => a.searchPos - b.searchPos) } Rectangle { anchors { top: parent.top horizontalCenter: parent.horizontalCenter topMargin: 100 } color: "#181818" height: currentItems.length ? 180 : 60 Behavior on height { NumberAnimation { duration: 300 easing.type: Easing.OutQuint }} width: 700 radius: 12 RectangularShadow { z: -1 anchors.fill: parent radius: 12 blur: 10 spread: 2 } Rectangle { anchors { top: parent.top left: parent.left right: parent.right margins: 6 } height: 48 color: "#222222" radius: 10 TextInput { id: input anchors.verticalCenter: parent.verticalCenter x: parent.width/2 - this.width/2 Behavior on x {NumberAnimation {duration: 300; easing.type: Easing.OutQuint}} focus: true font.pixelSize: 20 color: "white" cursorDelegate: Item {} onCursorPositionChanged: cursorPosition = text.length Keys.onPressed: (event) => { switch (event.key) { case Qt.Key_Escape: root.close(); break; case Qt.Key_Return: list.currentItem.execute(); root.close(); break; case Qt.Key_Right: list.incrementCurrentIndex(); break; case Qt.Key_Left: list.decrementCurrentIndex(); break; }} } } ListView { id: list anchors { top: input.parent.bottom left: parent.left right: parent.right margins: 6 } height: 114 highlightFollowsCurrentItem: true orientation: ListView.Horizontal spacing: 6 add: Transition { NumberAnimation { property: "opacity" from: 0 to: 1 duration: 150 }} remove: Transition { NumberAnimation { property: "opacity" from: 1 to: 0 duration: 150 }} displaced: Transition { NumberAnimation { property: "x" duration: 300 easing.type: Easing.OutQuint onStarted: console.log("displaced") } NumberAnimation { property: "opacity" to: 1 duration: 150 } } model: ScriptModel {values: currentItems.slice(0, 5)} delegate: Rectangle { id: item anchors { top: parent?.top bottom: parent?.bottom } width: (700 - 6*6)/5 color: "#20181818" ListView.onIsCurrentItemChanged: ListView.isCurrentItem ? selectAnim.start() : deselectAnim.start() property var selectAnim: ColorAnimation { target: item property: "color" to: "#10ffffff" duration: 200 easing.type: Easing.OutQuint } property var deselectAnim: ColorAnimation { target: item onStarted: selectAnim.stop() property: "color" to: "#00181818" duration: 200 easing.type: Easing.InQuint } radius: 10 function execute() { modelData.emoji ? Quickshell.clipboardText = modelData.emoji : modelData.execute() } Column { anchors { left: parent.left right: parent.right verticalCenter: parent.verticalCenter } Image { visible: !modelData.emoji anchors.horizontalCenter: parent.horizontalCenter width: 64 height: width source: Quickshell.iconPath(modelData.icon) } Text { anchors.horizontalCenter: parent.horizontalCenter font.pixelSize: 64 text: modelData.emoji ?? "" } Text { anchors { left: parent.left right: parent.right } width: parent.width - 12 text: modelData.name color: "white" font.pixelSize: 16 horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight } } } } transform: Translate { y: -50 NumberAnimation on y { id: entry to: 0 duration: 300 easing.type: Easing.OutQuint } NumberAnimation on y { id: exit to: -50 duration: 300 easing.type: Easing.InQuint onFinished: root.visible = false } } opacity: 0 NumberAnimation on opacity { running: entry.running to: 1 duration: entry.duration/2 } SequentialAnimation on opacity { running: exit.running PauseAnimation {duration: exit.duration/2} NumberAnimation { to: 0 duration: exit.duration/2 } } } }