launcher v1

This commit is contained in:
Benjamin Palko 2025-08-19 23:31:39 -04:00
parent 8504f8ddd2
commit d71c9604a6
12 changed files with 1608 additions and 4 deletions

View file

@ -17,6 +17,7 @@ Singleton {
property string gpu: "\u{E66f}" property string gpu: "\u{E66f}"
property string hardDrive: "\u{E0f1}" property string hardDrive: "\u{E0f1}"
property string memoryStick: "\u{E44a}" property string memoryStick: "\u{E44a}"
property string search: "\u{E155}"
property string wifiOff: "\u{E1af}" property string wifiOff: "\u{E1af}"
property string wifiLow: "\u{E5fd}" property string wifiLow: "\u{E5fd}"
property string wifiHigh: "\u{E5fc}" property string wifiHigh: "\u{E5fc}"

View file

@ -1,8 +1,8 @@
import "bar" import "bar"
import "launcher"
import "powermenu" import "powermenu"
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
@ -15,6 +15,7 @@ Variants {
required property ShellScreen modelData required property ShellScreen modelData
Bar {} Bar {}
Launcher {}
PowerMenu {} PowerMenu {}
} }
} }

View file

@ -8,9 +8,7 @@ Scope {
LuxShortcut { LuxShortcut {
name: 'launcher' name: 'launcher'
description: 'Open the application launcher' description: 'Open the application launcher'
onPressed: { onPressed: Visibility.launcher = !Visibility.launcher
console.log("Launcher shortcut pressed");
}
} }
LuxShortcut { LuxShortcut {

View file

@ -0,0 +1,51 @@
pragma ComponentBehavior: Bound
import "items"
import "services"
import qs.config
import qs.widgets
import Quickshell
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
StyledListView {
id: root
required property TextField search
Layout.fillWidth: true
model: ScriptModel {
id: model
onValuesChanged: root.currentIndex = 0
values: Apps.query(search.text)
}
spacing: 12
implicitWidth: 500
implicitHeight: 500
highlightMoveDuration: 100
highlightResizeDuration: 0
highlight: Rectangle {
radius: 8
color: Theme.palette.primary
}
header: StyledText {
text: "Apps"
font.pixelSize: 18
padding: 8
}
delegate: appItem
Component {
id: appItem
AppItem {
active: ListView.isCurrentItem
}
}
}

View file

@ -0,0 +1,105 @@
pragma ComponentBehavior: Bound
import "services"
import qs.config
import qs.constants
import qs.services
import qs.widgets
import Quickshell.Hyprland
import Quickshell.Wayland
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
StyledWindow {
id: root
name: "launcher"
visible: Visibility.launcher
implicitWidth: rect.width
implicitHeight: rect.height
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.keyboardFocus: root.visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
HyprlandFocusGrab {
active: root.visible
windows: [search]
onCleared: {
root.reset();
}
}
function reset() {
Visibility.launcher = false;
list.currentIndex = 0;
search.clear();
}
WrapperRectangle {
id: rect
color: Theme.palette.base300
margin: 18
radius: 8
ColumnLayout {
WrapperRectangle {
id: searchWrapper
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
color: Theme.palette.base200
margin: 4
radius: 8
RowLayout {
LucideIcon {
id: icon
Layout.leftMargin: 8
text: Icons.search
}
StyledTextField {
id: search
Layout.fillWidth: true
implicitHeight: 40
cursorVisible: true
focus: root.visible
placeholderText: "Applications"
placeholderTextColor: "grey"
Keys.onEscapePressed: event => {
event.accepted = true;
root.reset();
}
Keys.onReturnPressed: event => {
event.accepted = true;
Apps.launch(list.currentItem.modelData);
root.reset();
}
Keys.onUpPressed: event => {
event.accepted = true;
list.decrementCurrentIndex();
}
Keys.onDownPressed: event => {
event.accepted = true;
list.incrementCurrentIndex();
}
}
}
}
Item {
implicitWidth: list.width
implicitHeight: list.height
AppList {
id: list
search: search
}
}
}
}
}

View file

@ -0,0 +1,60 @@
import qs.config
import qs.widgets
import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
Item {
id: root
required property DesktopEntry modelData
required property int index
property bool active: false
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: grid.height
GridLayout {
id: grid
columnSpacing: 12
rowSpacing: 1
columns: 2
rows: 2
anchors.left: root?.left
anchors.right: root?.right
IconImage {
Layout.leftMargin: 8
Layout.topMargin: 8
Layout.bottomMargin: 8
Layout.columnSpan: 1
Layout.rowSpan: 2
source: Quickshell.iconPath(root.modelData.runInTerminal ? "terminal-app" : root.modelData.icon, "image-missing")
implicitSize: 32
}
StyledText {
text: root.modelData.name.trim()
Layout.alignment: Qt.AlignBottom
Layout.fillWidth: true
color: root.active ? Theme.palette.primarycontent : Theme.palette.basecontent
font.pixelSize: 14
font.bold: true
}
StyledText {
text: root.modelData.comment != "" ? root.modelData.comment : root.modelData.genericName
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
color: root.active ? Theme.palette.primarycontent : Theme.palette.basecontent
font.pixelSize: 10
elide: Text.ElideRight
}
}
}

View file

@ -0,0 +1,22 @@
pragma Singleton
import qs.utils
import Quickshell
import QtQuick
FuzzySearch {
list: DesktopEntries.applications.values.filter(a => !a.noDisplay).sort((a, b) => a.name.localeCompare(b.name))
function launch(entry: DesktopEntry): void {
if (entry.runInTerminal)
Quickshell.execDetached({
command: ["kitty", "exec", ...entry.command],
workingDirectory: entry.workingDirectory
});
else
Quickshell.execDetached({
command: [...entry.command],
workingDirectory: entry.workingDirectory
});
}
}

View file

@ -4,6 +4,7 @@ import qs.widgets
import Quickshell import Quickshell
Singleton { Singleton {
property bool launcher
property bool powermenu property bool powermenu
property StyledPopupWindow activePopup property StyledPopupWindow activePopup

39
utils/FuzzySearch.qml Normal file
View file

@ -0,0 +1,39 @@
import Quickshell
import "scripts/fzf.js" as Fzf
import QtQuick
Singleton {
required property list<QtObject> list
property string key: "name"
property var extraOpts: ({})
// Extra stuff for fuzzy
property list<string> keys: [key]
property list<real> weights: [1]
readonly property var fzf: new Fzf.Finder(list, Object.assign({
selector
}, extraOpts))
function transformSearch(search: string): string {
return search.trim();
}
function selector(item: var): string {
// Only for fzf
return item[key];
}
function query(search: string): list<var> {
search = transformSearch(search);
if (!search)
return [...list];
return fzf.find(search).sort((a, b) => {
if (a.score === b.score)
return selector(a.item).trim().length - selector(b.item).trim().length;
return b.score - a.score;
}).map(r => r.item);
}
}

1307
utils/scripts/fzf.js Normal file

File diff suppressed because it is too large Load diff

11
widgets/LucideIcon.qml Normal file
View file

@ -0,0 +1,11 @@
import qs.config
import QtQuick
Text {
color: Theme.palette.basecontent
font.family: Theme.lucide.font.family
font.pixelSize: Dimensions.gpu.iconSize
font.bold: true
}

View file

@ -0,0 +1,8 @@
import qs.config
import QtQuick.Controls
TextField {
color: Theme.palette.basecontent
background: null
}