From d2747e2ca1e211a32e91e44010f40a00e0ac97e4 Mon Sep 17 00:00:00 2001 From: Leander Scherer Date: Sat, 30 May 2026 15:35:27 +0200 Subject: feat(quickshell): add popup controls and privacy indicators --- modules/system/quickshell/Launcher.qml | 313 +++++++++++++++++---------------- 1 file changed, 164 insertions(+), 149 deletions(-) (limited to 'modules/system/quickshell/Launcher.qml') diff --git a/modules/system/quickshell/Launcher.qml b/modules/system/quickshell/Launcher.qml index fc0dc3d..184271f 100644 --- a/modules/system/quickshell/Launcher.qml +++ b/modules/system/quickshell/Launcher.qml @@ -11,25 +11,26 @@ PanelWindow { WlrLayershell.namespace: "launcher" WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive WlrLayershell.exclusiveZone: -1 - + exclusionMode: ExclusionMode.Ignore - - // Centering logic + anchors { top: true + bottom: true + left: true + right: true } - margins.top: 200 implicitWidth: 600 implicitHeight: 400 - + color: "transparent" visible: GlobalState.activePopup === "Launcher" - + onVisibleChanged: { if (visible) { - searchInput.forceActiveFocus() - searchInput.text = "" + searchInput.forceActiveFocus(); + searchInput.text = ""; } } @@ -37,21 +38,35 @@ PanelWindow { id: internal function filterApps(searchText) { - const search = searchText.toLowerCase() - const apps = DesktopEntries.applications.values - + const search = searchText.toLowerCase(); + const apps = DesktopEntries.applications.values; + return apps.filter(app => { - if (app.noDisplay) return false - if (!search) return true - - return app.name.toLowerCase().includes(search) || - (app.comment && app.comment.toLowerCase().includes(search)) - }) + if (app.noDisplay) + return false; + if (!search) + return true; + + return app.name.toLowerCase().includes(search) || (app.comment && app.comment.toLowerCase().includes(search)); + }); } } - FocusScope { + MouseArea { anchors.fill: parent + acceptedButtons: Qt.AllButtons + onClicked: GlobalState.close() + onWheel: wheel => wheel.accepted = true + } + + FocusScope { + width: 600 + height: 400 + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + topMargin: 200 + } focus: true Squircle { @@ -62,7 +77,6 @@ PanelWindow { strokeColor: Theme.border strokeWidth: 1 - // Capture Escape to close Keys.onEscapePressed: GlobalState.close() ColumnLayout { @@ -70,159 +84,160 @@ PanelWindow { anchors.margins: 12 spacing: 8 - // Search Header - RowLayout { - Layout.fillWidth: true - spacing: 12 - - Image { - width: 24; height: 24 - source: Quickshell.iconPath("system-search-symbolic") - sourceSize: Qt.size(24, 24) - smooth: true - mipmap: true - opacity: 0.7 - } - - TextInput { - id: searchInput + RowLayout { Layout.fillWidth: true - font { - family: Theme.mainFont - pixelSize: 20 + spacing: 12 + + Image { + width: 24 + height: 24 + source: Quickshell.iconPath("system-search-symbolic") + sourceSize: Qt.size(24, 24) + smooth: true + mipmap: true + opacity: 0.7 } - color: Theme.text - clip: true - - focus: true - cursorVisible: true - selectByMouse: true - inputMethodHints: Qt.ImhNoPredictiveText - - Text { - visible: searchInput.text === "" - text: "Search Applications..." + + TextInput { + id: searchInput + Layout.fillWidth: true + font { + family: Theme.mainFont + pixelSize: 20 + } color: Theme.text - opacity: 0.3 - font: searchInput.font - } + clip: true + + focus: true + cursorVisible: true + selectByMouse: true + inputMethodHints: Qt.ImhNoPredictiveText + + Text { + visible: searchInput.text === "" + text: "Search Applications..." + color: Theme.text + opacity: 0.3 + font: searchInput.font + } - onTextChanged: resultsList.currentIndex = 0 - - Keys.onPressed: event => { - if (event.key === Qt.Key_Down) { - resultsList.incrementCurrentIndex() - event.accepted = true - } else if (event.key === Qt.Key_Up) { - resultsList.decrementCurrentIndex() - event.accepted = true - } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { - if (resultsList.count > 0 && resultsList.currentIndex >= 0) { - if (resultsList.currentItem) { - resultsList.currentItem.launch() + onTextChanged: resultsList.currentIndex = 0 + + Keys.onPressed: event => { + if (event.key === Qt.Key_Down) { + resultsList.incrementCurrentIndex(); + event.accepted = true; + } else if (event.key === Qt.Key_Up) { + resultsList.decrementCurrentIndex(); + event.accepted = true; + } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + if (resultsList.count > 0 && resultsList.currentIndex >= 0) { + if (resultsList.currentItem) { + resultsList.currentItem.launch(); + } } + event.accepted = true; } - event.accepted = true } } } - } - Rectangle { - Layout.fillWidth: true - height: 1 - color: Theme.border - } - - ListView { - id: resultsList - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - boundsBehavior: Flickable.StopAtBounds - highlightFollowsCurrentItem: true - highlightMoveDuration: 150 - highlightResizeDuration: 0 - - highlight: Squircle { - width: resultsList.width - height: 44 - cornerRadius: 8 - fillColor: Theme.surface - z: 1 + Rectangle { + Layout.fillWidth: true + height: 1 + color: Theme.border } - - model: internal.filterApps(searchInput.text) - - delegate: Squircle { - id: delegateRoot - required property var modelData - required property int index - - height: 44 - width: resultsList.width - - cornerRadius: 8 - fillColor: "transparent" - z: 5 - - function launch() { - if (modelData && modelData.execute) { - modelData.execute() - GlobalState.close() - } + + ListView { + id: resultsList + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + boundsBehavior: Flickable.StopAtBounds + highlightFollowsCurrentItem: true + highlightMoveDuration: 150 + highlightResizeDuration: 0 + + highlight: Squircle { + width: resultsList.width + height: 44 + cornerRadius: 8 + fillColor: Theme.surface + z: 1 } - RowLayout { - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - spacing: 12 - - Image { - width: 24; height: 24 - source: Quickshell.iconPath(modelData.icon) - sourceSize: Qt.size(32, 32) - smooth: true - mipmap: true + model: internal.filterApps(searchInput.text) + + delegate: Squircle { + id: delegateRoot + required property var modelData + required property int index + + height: 44 + width: resultsList.width + + cornerRadius: 8 + fillColor: "transparent" + z: 5 + + function launch() { + if (modelData && modelData.execute) { + modelData.execute(); + GlobalState.close(); + } } - Column { - Layout.fillWidth: true - Text { - text: delegateRoot.modelData.name - color: Theme.text - font { - family: Theme.mainFont - pixelSize: 14 - weight: Font.Medium - } + RowLayout { + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + spacing: 12 + + Image { + width: 24 + height: 24 + source: Quickshell.iconPath(modelData.icon) + sourceSize: Qt.size(32, 32) + smooth: true + mipmap: true } - Text { - visible: delegateRoot.modelData.comment !== "" - text: delegateRoot.modelData.comment - color: Theme.textMuted - font { - family: Theme.mainFont - pixelSize: 11 + + Column { + Layout.fillWidth: true + Text { + text: delegateRoot.modelData.name + color: Theme.text + font { + family: Theme.mainFont + pixelSize: 14 + weight: Font.Medium + } + } + Text { + visible: delegateRoot.modelData.comment !== "" + text: delegateRoot.modelData.comment + color: Theme.textMuted + font { + family: Theme.mainFont + pixelSize: 11 + } + elide: Text.ElideRight + width: parent.width } - elide: Text.ElideRight - width: parent.width } } - } - MouseArea { - anchors.fill: parent - hoverEnabled: true - onEntered: resultsList.currentIndex = index - onClicked: delegateRoot.launch() + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: resultsList.currentIndex = index + onClicked: delegateRoot.launch() + } } + + ScrollBar.vertical: ScrollBar {} } - - ScrollBar.vertical: ScrollBar {} } } } } -} -- cgit v1.3.1