controlcenter: search applications in launcher pane
This commit is contained in:
parent
bd6122eaef
commit
d123dc0b7e
1 changed files with 149 additions and 3 deletions
|
|
@ -15,6 +15,7 @@ import Quickshell.Io
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import "../../../utils/scripts/fuzzysort.js" as Fuzzy
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
@ -109,6 +110,68 @@ RowLayout {
|
||||||
entries: DesktopEntries.applications.values // No filter - show all apps
|
entries: DesktopEntries.applications.values // No filter - show all apps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property string searchText: ""
|
||||||
|
|
||||||
|
function filterApps(search: string): list<var> {
|
||||||
|
// If search is empty, return all apps directly
|
||||||
|
if (!search || search.trim() === "") {
|
||||||
|
// Convert QQmlListProperty to array
|
||||||
|
const apps = [];
|
||||||
|
for (let i = 0; i < allAppsDb.apps.length; i++) {
|
||||||
|
apps.push(allAppsDb.apps[i]);
|
||||||
|
}
|
||||||
|
return apps;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allAppsDb.apps || allAppsDb.apps.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare apps for fuzzy search
|
||||||
|
const preparedApps = [];
|
||||||
|
for (let i = 0; i < allAppsDb.apps.length; i++) {
|
||||||
|
const app = allAppsDb.apps[i];
|
||||||
|
const name = app.name || app.entry?.name || "";
|
||||||
|
preparedApps.push({
|
||||||
|
_item: app,
|
||||||
|
name: Fuzzy.prepare(name)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform fuzzy search
|
||||||
|
const results = Fuzzy.go(search, preparedApps, {
|
||||||
|
all: true,
|
||||||
|
keys: ["name"],
|
||||||
|
scoreFn: r => r[0].score
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return sorted by score (highest first)
|
||||||
|
return results
|
||||||
|
.sort((a, b) => b._score - a._score)
|
||||||
|
.map(r => r.obj._item);
|
||||||
|
}
|
||||||
|
|
||||||
|
property list<var> filteredApps: []
|
||||||
|
|
||||||
|
function updateFilteredApps() {
|
||||||
|
filteredApps = filterApps(searchText);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchTextChanged: {
|
||||||
|
updateFilteredApps();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
updateFilteredApps();
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: allAppsDb
|
||||||
|
function onAppsChanged() {
|
||||||
|
updateFilteredApps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.preferredWidth: Math.floor(parent.width * 0.4)
|
Layout.preferredWidth: Math.floor(parent.width * 0.4)
|
||||||
Layout.minimumWidth: 420
|
Layout.minimumWidth: 420
|
||||||
|
|
@ -138,7 +201,7 @@ RowLayout {
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
Layout.topMargin: Appearance.spacing.large
|
Layout.topMargin: Appearance.spacing.large
|
||||||
text: qsTr("Applications (%1)").arg(allAppsDb.apps.length)
|
text: qsTr("Applications (%1)").arg(root.searchText ? root.filteredApps.length : allAppsDb.apps.length)
|
||||||
font.pointSize: Appearance.font.size.normal
|
font.pointSize: Appearance.font.size.normal
|
||||||
font.weight: 500
|
font.weight: 500
|
||||||
}
|
}
|
||||||
|
|
@ -148,12 +211,95 @@ RowLayout {
|
||||||
color: Colours.palette.m3outline
|
color: Colours.palette.m3outline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Appearance.spacing.normal
|
||||||
|
Layout.bottomMargin: Appearance.spacing.small
|
||||||
|
|
||||||
|
color: Colours.tPalette.m3surfaceContainer
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
implicitHeight: Math.max(searchIcon.implicitHeight, searchField.implicitHeight, clearIcon.implicitHeight)
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: searchIcon
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Appearance.padding.normal
|
||||||
|
|
||||||
|
text: "search"
|
||||||
|
color: Colours.palette.m3onSurfaceVariant
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledTextField {
|
||||||
|
id: searchField
|
||||||
|
|
||||||
|
anchors.left: searchIcon.right
|
||||||
|
anchors.right: clearIcon.left
|
||||||
|
anchors.leftMargin: Appearance.spacing.small
|
||||||
|
anchors.rightMargin: Appearance.spacing.small
|
||||||
|
|
||||||
|
topPadding: Appearance.padding.normal
|
||||||
|
bottomPadding: Appearance.padding.normal
|
||||||
|
|
||||||
|
placeholderText: qsTr("Search applications...")
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
root.searchText = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: clearIcon
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Appearance.padding.normal
|
||||||
|
|
||||||
|
width: searchField.text ? implicitWidth : implicitWidth / 2
|
||||||
|
opacity: {
|
||||||
|
if (!searchField.text)
|
||||||
|
return 0;
|
||||||
|
if (clearMouse.pressed)
|
||||||
|
return 0.7;
|
||||||
|
if (clearMouse.containsMouse)
|
||||||
|
return 0.8;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
text: "close"
|
||||||
|
color: Colours.palette.m3onSurfaceVariant
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: clearMouse
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: searchField.text ? Qt.PointingHandCursor : undefined
|
||||||
|
|
||||||
|
onClicked: searchField.text = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StyledListView {
|
StyledListView {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.topMargin: Appearance.spacing.normal
|
|
||||||
|
|
||||||
model: allAppsDb.apps
|
model: root.filteredApps
|
||||||
spacing: Appearance.spacing.small / 2
|
spacing: Appearance.spacing.small / 2
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue