feat: launcher actions
This commit is contained in:
parent
e3a27ec90b
commit
5978db658c
6 changed files with 277 additions and 13 deletions
|
|
@ -5,9 +5,11 @@ import QtQuick
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
readonly property int maxShown: 8
|
readonly property int maxShown: 8
|
||||||
|
readonly property string actionPrefix: ">"
|
||||||
readonly property Sizes sizes: Sizes {}
|
readonly property Sizes sizes: Sizes {}
|
||||||
|
|
||||||
component Sizes: QtObject {
|
component Sizes: QtObject {
|
||||||
property int width: 600
|
readonly property int width: 600
|
||||||
|
readonly property int itemHeight: 57
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
59
modules/launcher/ActionItem.qml
Normal file
59
modules/launcher/ActionItem.qml
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import "root:/widgets"
|
||||||
|
import "root:/config"
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
PaddedRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property Actions.Action modelData
|
||||||
|
required property var list
|
||||||
|
|
||||||
|
implicitWidth: ListView.view.width
|
||||||
|
implicitHeight: LauncherConfig.sizes.itemHeight
|
||||||
|
padding: [Appearance.padding.smaller, Appearance.padding.larger]
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
root.modelData.onClicked(root.list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
text: root.modelData.icon
|
||||||
|
font.pointSize: Appearance.font.size.extraLarge
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.left: icon.right
|
||||||
|
anchors.leftMargin: Appearance.spacing.larger
|
||||||
|
anchors.verticalCenter: icon.verticalCenter
|
||||||
|
|
||||||
|
implicitWidth: parent.width - icon.width
|
||||||
|
implicitHeight: childrenRect.height
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: name
|
||||||
|
|
||||||
|
text: root.modelData.name
|
||||||
|
font.pointSize: Appearance.font.size.normal
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: root.modelData.desc
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
color: Appearance.alpha(Appearance.colours.m3outline, true)
|
||||||
|
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: root.width - icon.width - Appearance.rounding.normal * 2
|
||||||
|
|
||||||
|
anchors.top: name.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
modules/launcher/Actions.qml
Normal file
127
modules/launcher/Actions.qml
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
|
||||||
|
import "root:/config"
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property list<Action> list: [
|
||||||
|
Action {
|
||||||
|
name: qsTr("Scheme")
|
||||||
|
desc: qsTr("Change the current colour scheme")
|
||||||
|
icon: "palette"
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
root.autocomplete(list, "scheme");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action {
|
||||||
|
name: qsTr("Wallpaper")
|
||||||
|
desc: qsTr("Change the current wallpaper")
|
||||||
|
icon: "image"
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
root.autocomplete(list, "wallpaper");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action {
|
||||||
|
name: qsTr("Variant")
|
||||||
|
desc: qsTr("Change the current scheme variant")
|
||||||
|
icon: "colors"
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
root.autocomplete(list, "variant");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action {
|
||||||
|
name: qsTr("Transparency")
|
||||||
|
desc: qsTr("Change shell transparency")
|
||||||
|
icon: "opacity"
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
root.autocomplete(list, "transparency");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action {
|
||||||
|
name: qsTr("Light")
|
||||||
|
desc: qsTr("Change the scheme to light mode")
|
||||||
|
icon: "light_mode"
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
list.launcher.launcherVisible = false;
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action {
|
||||||
|
name: qsTr("Dark")
|
||||||
|
desc: qsTr("Change the scheme to dark mode")
|
||||||
|
icon: "dark_mode"
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
list.launcher.launcherVisible = false;
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action {
|
||||||
|
name: qsTr("Lock")
|
||||||
|
desc: qsTr("Lock the current session")
|
||||||
|
icon: "lock"
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
list.launcher.launcherVisible = false;
|
||||||
|
lock.running = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action {
|
||||||
|
name: qsTr("Sleep")
|
||||||
|
desc: qsTr("Suspend then hibernate")
|
||||||
|
icon: "bedtime"
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
list.launcher.launcherVisible = false;
|
||||||
|
sleep.running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
readonly property list<var> preppedNames: list.map(a => ({
|
||||||
|
name: Fuzzy.prepare(a.name),
|
||||||
|
action: a
|
||||||
|
}))
|
||||||
|
|
||||||
|
function fuzzyQuery(search: string): var {
|
||||||
|
return Fuzzy.go(search.slice(LauncherConfig.actionPrefix.length), preppedNames, {
|
||||||
|
all: true,
|
||||||
|
key: "name"
|
||||||
|
}).map(r => r.obj.action);
|
||||||
|
}
|
||||||
|
|
||||||
|
function autocomplete(list: AppList, text: string): void {
|
||||||
|
list.search.text = `${LauncherConfig.actionPrefix}${text} `;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: lock
|
||||||
|
|
||||||
|
command: ["loginctl", "lock-session"]
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: sleep
|
||||||
|
|
||||||
|
command: ["systemctl", "suspend-then-hibernate"]
|
||||||
|
}
|
||||||
|
|
||||||
|
component Action: QtObject {
|
||||||
|
required property string name
|
||||||
|
required property string desc
|
||||||
|
required property string icon
|
||||||
|
|
||||||
|
function onClicked(list: AppList): void {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import "root:/widgets"
|
import "root:/widgets"
|
||||||
import "root:/services"
|
|
||||||
import "root:/config"
|
import "root:/config"
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
|
|
@ -12,6 +11,7 @@ PaddedRect {
|
||||||
required property Scope launcher
|
required property Scope launcher
|
||||||
|
|
||||||
implicitWidth: ListView.view.width
|
implicitWidth: ListView.view.width
|
||||||
|
implicitHeight: LauncherConfig.sizes.itemHeight
|
||||||
padding: [Appearance.padding.smaller, Appearance.padding.normal]
|
padding: [Appearance.padding.smaller, Appearance.padding.normal]
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import "root:/widgets"
|
import "root:/widgets"
|
||||||
import "root:/services"
|
import "root:/services"
|
||||||
import "root:/config"
|
import "root:/config"
|
||||||
|
|
@ -9,18 +11,29 @@ ListView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property int padding
|
required property int padding
|
||||||
required property string search
|
required property TextField search
|
||||||
required property Scope launcher
|
required property Scope launcher
|
||||||
|
|
||||||
|
property bool isAction: search.text.startsWith(LauncherConfig.actionPrefix)
|
||||||
|
|
||||||
|
function getModelValues() {
|
||||||
|
let text = search.text;
|
||||||
|
if (isAction)
|
||||||
|
return Actions.fuzzyQuery(text);
|
||||||
|
if (text.startsWith(LauncherConfig.actionPrefix))
|
||||||
|
text = search.text.slice(LauncherConfig.actionPrefix.length);
|
||||||
|
return Apps.fuzzyQuery(text);
|
||||||
|
}
|
||||||
|
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: Apps.fuzzyQuery(root.search)
|
values: root.getModelValues()
|
||||||
onValuesChanged: root.currentIndex = 0
|
onValuesChanged: root.currentIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
spacing: Appearance.spacing.small
|
spacing: Appearance.spacing.small
|
||||||
orientation: Qt.Vertical
|
orientation: Qt.Vertical
|
||||||
implicitHeight: ((currentItem?.height ?? 1) + spacing) * Math.min(LauncherConfig.maxShown, count) - spacing
|
implicitHeight: (LauncherConfig.sizes.itemHeight + spacing) * Math.min(LauncherConfig.maxShown, count) - spacing
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
@ -34,9 +47,7 @@ ListView {
|
||||||
color: Appearance.alpha(Appearance.colours.m3surfaceContainerHighest, true)
|
color: Appearance.alpha(Appearance.colours.m3surfaceContainerHighest, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: AppItem {
|
delegate: isAction ? actionItem : appItem
|
||||||
launcher: root.launcher
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollBar.vertical: StyledScrollBar {
|
ScrollBar.vertical: StyledScrollBar {
|
||||||
// Move half out
|
// Move half out
|
||||||
|
|
@ -84,8 +95,69 @@ ListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: appItem
|
||||||
|
|
||||||
|
AppItem {
|
||||||
|
launcher: root.launcher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: actionItem
|
||||||
|
|
||||||
|
ActionItem {
|
||||||
|
list: root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
Anim {}
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.large
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on isAction {
|
||||||
|
SequentialAnimation {
|
||||||
|
ParallelAnimation {
|
||||||
|
Anim {
|
||||||
|
target: root
|
||||||
|
property: "opacity"
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||||
|
}
|
||||||
|
Anim {
|
||||||
|
target: root
|
||||||
|
property: "scale"
|
||||||
|
from: 1
|
||||||
|
to: 0.9
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PropertyAction {}
|
||||||
|
ParallelAnimation {
|
||||||
|
Anim {
|
||||||
|
target: root
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||||
|
}
|
||||||
|
Anim {
|
||||||
|
target: root
|
||||||
|
property: "scale"
|
||||||
|
from: 0.9
|
||||||
|
to: 1
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component Anim: NumberAnimation {
|
component Anim: NumberAnimation {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ Item {
|
||||||
id: list
|
id: list
|
||||||
|
|
||||||
padding: root.padding
|
padding: root.padding
|
||||||
search: search.text
|
search: search
|
||||||
launcher: root.launcher
|
launcher: root.launcher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ Item {
|
||||||
leftPadding: root.padding
|
leftPadding: root.padding
|
||||||
rightPadding: root.padding
|
rightPadding: root.padding
|
||||||
|
|
||||||
placeholderText: qsTr("Type \">\" for commands")
|
placeholderText: qsTr(`Type "${LauncherConfig.actionPrefix}" for commands`)
|
||||||
|
|
||||||
background: StyledRect {
|
background: StyledRect {
|
||||||
color: Appearance.alpha(Appearance.colours.m3surfaceContainerHigh, true)
|
color: Appearance.alpha(Appearance.colours.m3surfaceContainerHigh, true)
|
||||||
|
|
@ -69,10 +69,14 @@ Item {
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (list.currentItem) {
|
if (list.currentItem) {
|
||||||
|
if (list.isAction)
|
||||||
|
list.currentItem.modelData.onClicked(list);
|
||||||
|
else {
|
||||||
Apps.launch(list.currentItem?.modelData);
|
Apps.launch(list.currentItem?.modelData);
|
||||||
root.launcher.launcherVisible = false;
|
root.launcher.launcherVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Keys.onUpPressed: list.decrementCurrentIndex()
|
Keys.onUpPressed: list.decrementCurrentIndex()
|
||||||
Keys.onDownPressed: list.incrementCurrentIndex()
|
Keys.onDownPressed: list.incrementCurrentIndex()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue