wip
This commit is contained in:
parent
32edcff102
commit
0296117901
110 changed files with 9713 additions and 5 deletions
69
modules/launcher/ActionItem.qml
Normal file
69
modules/launcher/ActionItem.qml
Normal file
|
@ -0,0 +1,69 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Actions.Action modelData
|
||||
required property var list
|
||||
|
||||
implicitHeight: LauncherConfig.sizes.itemHeight
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
|
||||
StateLayer {
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
function onClicked(): void {
|
||||
root.modelData?.onClicked(root.list);
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.larger
|
||||
anchors.rightMargin: Appearance.padding.larger
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
|
||||
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: name.implicitHeight + desc.implicitHeight
|
||||
|
||||
StyledText {
|
||||
id: name
|
||||
|
||||
text: root.modelData?.name ?? ""
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: desc
|
||||
|
||||
text: root.modelData?.desc ?? ""
|
||||
font.pointSize: Appearance.font.size.small
|
||||
color: Colours.alpha(Colours.palette.m3outline, true)
|
||||
|
||||
elide: Text.ElideRight
|
||||
width: root.width - icon.width - Appearance.rounding.normal * 2
|
||||
|
||||
anchors.top: name.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
130
modules/launcher/Actions.qml
Normal file
130
modules/launcher/Actions.qml
Normal file
|
@ -0,0 +1,130 @@
|
|||
pragma Singleton
|
||||
|
||||
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
|
||||
import "root:/services"
|
||||
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.visibilities.launcher = false;
|
||||
Colours.setMode("light");
|
||||
}
|
||||
},
|
||||
Action {
|
||||
name: qsTr("Dark")
|
||||
desc: qsTr("Change the scheme to dark mode")
|
||||
icon: "dark_mode"
|
||||
|
||||
function onClicked(list: AppList): void {
|
||||
list.visibilities.launcher = false;
|
||||
Colours.setMode("dark");
|
||||
}
|
||||
},
|
||||
Action {
|
||||
name: qsTr("Lock")
|
||||
desc: qsTr("Lock the current session")
|
||||
icon: "lock"
|
||||
|
||||
function onClicked(list: AppList): void {
|
||||
list.visibilities.launcher = false;
|
||||
lock.running = true;
|
||||
}
|
||||
},
|
||||
Action {
|
||||
name: qsTr("Sleep")
|
||||
desc: qsTr("Suspend then hibernate")
|
||||
icon: "bedtime"
|
||||
|
||||
function onClicked(list: AppList): void {
|
||||
list.visibilities.launcher = false;
|
||||
sleep.running = true;
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
readonly property list<var> preppedActions: list.map(a => ({
|
||||
name: Fuzzy.prepare(a.name),
|
||||
desc: Fuzzy.prepare(a.desc),
|
||||
action: a
|
||||
}))
|
||||
|
||||
function fuzzyQuery(search: string): var {
|
||||
return Fuzzy.go(search.slice(LauncherConfig.actionPrefix.length), preppedActions, {
|
||||
all: true,
|
||||
keys: ["name", "desc"],
|
||||
scoreFn: r => r[0].score > 0 ? r[0].score * 0.9 + r[1].score * 0.1 : 0
|
||||
}).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 {
|
||||
}
|
||||
}
|
||||
}
|
72
modules/launcher/AppItem.qml
Normal file
72
modules/launcher/AppItem.qml
Normal file
|
@ -0,0 +1,72 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property DesktopEntry modelData
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitHeight: LauncherConfig.sizes.itemHeight
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
|
||||
StateLayer {
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
function onClicked(): void {
|
||||
Apps.launch(root.modelData);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.larger
|
||||
anchors.rightMargin: Appearance.padding.larger
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
|
||||
IconImage {
|
||||
id: icon
|
||||
|
||||
source: Quickshell.iconPath(root.modelData?.icon, "image-missing")
|
||||
implicitSize: parent.height * 0.8
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: Appearance.spacing.normal
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
|
||||
implicitWidth: parent.width - icon.width
|
||||
implicitHeight: name.implicitHeight + comment.implicitHeight
|
||||
|
||||
StyledText {
|
||||
id: name
|
||||
|
||||
text: root.modelData?.name ?? ""
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: comment
|
||||
|
||||
text: (root.modelData?.comment || root.modelData?.genericName || root.modelData?.name) ?? ""
|
||||
font.pointSize: Appearance.font.size.small
|
||||
color: Colours.alpha(Colours.palette.m3outline, true)
|
||||
|
||||
elide: Text.ElideRight
|
||||
width: root.width - icon.width - Appearance.rounding.normal * 2
|
||||
|
||||
anchors.top: name.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
160
modules/launcher/AppList.qml
Normal file
160
modules/launcher/AppList.qml
Normal file
|
@ -0,0 +1,160 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
ListView {
|
||||
id: root
|
||||
|
||||
required property int padding
|
||||
required property TextField search
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
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 {
|
||||
values: root.getModelValues()
|
||||
onValuesChanged: root.currentIndex = 0
|
||||
}
|
||||
|
||||
spacing: Appearance.spacing.small
|
||||
orientation: Qt.Vertical
|
||||
implicitHeight: (LauncherConfig.sizes.itemHeight + spacing) * Math.min(LauncherConfig.maxShown, count) - spacing
|
||||
|
||||
highlightMoveDuration: Appearance.anim.durations.normal
|
||||
highlightResizeDuration: 0
|
||||
|
||||
highlight: StyledRect {
|
||||
radius: Appearance.rounding.full
|
||||
color: Colours.palette.m3onSurface
|
||||
opacity: 0.08
|
||||
}
|
||||
|
||||
delegate: isAction ? actionItem : appItem
|
||||
|
||||
ScrollBar.vertical: StyledScrollBar {}
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
from: 1
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
|
||||
move: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
addDisplaced: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: appItem
|
||||
|
||||
AppItem {
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: actionItem
|
||||
|
||||
ActionItem {
|
||||
list: root
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
63
modules/launcher/Background.qml
Normal file
63
modules/launcher/Background.qml
Normal file
|
@ -0,0 +1,63 @@
|
|||
import "root:/services"
|
||||
import "root:/config"
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
readonly property real rounding: BorderConfig.rounding
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: BorderConfig.colour
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.rounding * 2
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY * 2
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
ColorAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
168
modules/launcher/Content.qml
Normal file
168
modules/launcher/Content.qml
Normal file
|
@ -0,0 +1,168 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
readonly property int padding: Appearance.padding.large
|
||||
readonly property int rounding: Appearance.rounding.large
|
||||
|
||||
implicitWidth: listWrapper.width + padding * 2
|
||||
implicitHeight: searchWrapper.height + listWrapper.height + padding * 2
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Item {
|
||||
id: listWrapper
|
||||
|
||||
implicitWidth: list.width
|
||||
implicitHeight: list.height + root.padding
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: searchWrapper.top
|
||||
anchors.bottomMargin: root.padding
|
||||
|
||||
ContentList {
|
||||
id: list
|
||||
|
||||
visibilities: root.visibilities
|
||||
search: search
|
||||
padding: root.padding
|
||||
rounding: root.rounding
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
id: searchWrapper
|
||||
|
||||
color: Colours.alpha(Colours.palette.m3surfaceContainer, true)
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: root.padding
|
||||
|
||||
implicitHeight: Math.max(searchIcon.implicitHeight, search.implicitHeight, clearIcon.implicitHeight)
|
||||
|
||||
MaterialIcon {
|
||||
id: searchIcon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.padding
|
||||
|
||||
text: "search"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
}
|
||||
|
||||
StyledTextField {
|
||||
id: search
|
||||
|
||||
anchors.left: searchIcon.right
|
||||
anchors.right: clearIcon.left
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.rightMargin: Appearance.spacing.small
|
||||
|
||||
topPadding: Appearance.padding.larger
|
||||
bottomPadding: Appearance.padding.larger
|
||||
|
||||
placeholderText: qsTr("Type \"%1\" for commands").arg(LauncherConfig.actionPrefix)
|
||||
background: null
|
||||
|
||||
onAccepted: {
|
||||
const currentItem = list.currentList?.currentItem;
|
||||
if (currentItem) {
|
||||
if (list.showWallpapers) {
|
||||
Wallpapers.setWallpaper(currentItem.modelData.path);
|
||||
root.visibilities.launcher = false;
|
||||
} else if (text.startsWith(LauncherConfig.actionPrefix)) {
|
||||
currentItem.modelData.onClicked(list.currentList);
|
||||
} else {
|
||||
Apps.launch(currentItem.modelData);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onUpPressed: list.currentList?.decrementCurrentIndex()
|
||||
Keys.onDownPressed: list.currentList?.incrementCurrentIndex()
|
||||
|
||||
Keys.onEscapePressed: root.visibilities.launcher = false
|
||||
|
||||
Connections {
|
||||
target: root.visibilities
|
||||
|
||||
function onLauncherChanged(): void {
|
||||
if (root.visibilities.launcher)
|
||||
search.forceActiveFocus();
|
||||
else {
|
||||
search.text = "";
|
||||
const current = list.currentList;
|
||||
if (current)
|
||||
current.currentIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: clearIcon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.padding
|
||||
|
||||
width: search.text ? implicitWidth : implicitWidth / 2
|
||||
opacity: {
|
||||
if (!search.text)
|
||||
return 0;
|
||||
if (mouse.pressed)
|
||||
return 0.7;
|
||||
if (mouse.hovered)
|
||||
return 0.8;
|
||||
return 1;
|
||||
}
|
||||
|
||||
text: "close"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
property bool hovered
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: search.text ? Qt.PointingHandCursor : undefined
|
||||
|
||||
onEntered: hovered = true
|
||||
onExited: hovered = false
|
||||
onClicked: search.text = ""
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
188
modules/launcher/ContentList.qml
Normal file
188
modules/launcher/ContentList.qml
Normal file
|
@ -0,0 +1,188 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
required property TextField search
|
||||
required property int padding
|
||||
required property int rounding
|
||||
|
||||
property bool showWallpapers: search.text.startsWith(`${LauncherConfig.actionPrefix}wallpaper `)
|
||||
property var currentList: (showWallpapers ? wallpaperList : appList).item
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
clip: true
|
||||
state: showWallpapers ? "wallpapers" : "apps"
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "apps"
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitWidth: LauncherConfig.sizes.itemWidth
|
||||
root.implicitHeight: Math.max(empty.height, appList.height)
|
||||
appList.active: true
|
||||
}
|
||||
|
||||
AnchorChanges {
|
||||
anchors.left: root.parent.left
|
||||
anchors.right: root.parent.right
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "wallpapers"
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitWidth: Math.max(LauncherConfig.sizes.itemWidth, wallpaperList.width)
|
||||
root.implicitHeight: LauncherConfig.sizes.wallpaperHeight
|
||||
wallpaperList.active: true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
SequentialAnimation {
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
PropertyAction {
|
||||
targets: [appList, wallpaperList]
|
||||
properties: "active"
|
||||
}
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
target: root
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: appList
|
||||
|
||||
active: false
|
||||
asynchronous: true
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
sourceComponent: AppList {
|
||||
padding: root.padding
|
||||
search: root.search
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: wallpaperList
|
||||
|
||||
active: false
|
||||
asynchronous: true
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
sourceComponent: WallpaperList {
|
||||
search: root.search
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: empty
|
||||
|
||||
opacity: root.currentList?.count === 0 ? 1 : 0
|
||||
scale: root.currentList?.count === 0 ? 1 : 0.5
|
||||
|
||||
implicitWidth: icon.width + text.width + Appearance.spacing.small
|
||||
implicitHeight: icon.height
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
text: "manage_search"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: text
|
||||
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: qsTr("No results")
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||
}
|
||||
}
|
||||
}
|
109
modules/launcher/WallpaperItem.qml
Normal file
109
modules/launcher/WallpaperItem.qml
Normal file
|
@ -0,0 +1,109 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
required property Wallpapers.Wallpaper modelData
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
scale: 0.5
|
||||
opacity: 0
|
||||
z: PathView.z ?? 0
|
||||
|
||||
Component.onCompleted: {
|
||||
scale = Qt.binding(() => PathView.isCurrentItem ? 1 : PathView.onPath ? 0.8 : 0);
|
||||
opacity = Qt.binding(() => PathView.onPath ? 1 : 0);
|
||||
}
|
||||
|
||||
implicitWidth: image.width + Appearance.padding.larger * 2
|
||||
implicitHeight: image.height + label.height + Appearance.spacing.small / 2 + Appearance.padding.large + Appearance.padding.normal
|
||||
|
||||
StateLayer {
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
function onClicked(): void {
|
||||
Wallpapers.setWallpaper(root.modelData.path);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
|
||||
CachingImage {
|
||||
id: image
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: Appearance.padding.large
|
||||
|
||||
visible: false
|
||||
path: root.modelData.path
|
||||
smooth: !root.PathView.view.moving
|
||||
|
||||
width: LauncherConfig.sizes.wallpaperWidth
|
||||
height: width / 16 * 9
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mask
|
||||
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
visible: false
|
||||
anchors.fill: image
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
|
||||
RectangularShadow {
|
||||
opacity: root.PathView.isCurrentItem ? 0.7 : 0
|
||||
anchors.fill: mask
|
||||
radius: mask.radius
|
||||
color: Colours.palette.m3shadow
|
||||
blur: 10
|
||||
spread: 3
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: image
|
||||
source: image
|
||||
maskEnabled: true
|
||||
maskSource: mask
|
||||
maskSpreadAtMin: 1
|
||||
maskThresholdMin: 0.5
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: label
|
||||
|
||||
anchors.top: image.bottom
|
||||
anchors.topMargin: Appearance.spacing.small / 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
width: image.width - Appearance.padding.normal * 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideRight
|
||||
renderType: Text.QtRendering
|
||||
text: root.modelData.name
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
79
modules/launcher/WallpaperList.qml
Normal file
79
modules/launcher/WallpaperList.qml
Normal file
|
@ -0,0 +1,79 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
PathView {
|
||||
id: root
|
||||
|
||||
required property TextField search
|
||||
required property PersistentProperties visibilities
|
||||
readonly property int numItems: {
|
||||
const screenWidth = QsWindow.window?.screen.width * 0.8;
|
||||
if (!screenWidth)
|
||||
return 0;
|
||||
const itemWidth = LauncherConfig.sizes.wallpaperWidth * 0.8;
|
||||
const max = LauncherConfig.maxWallpapers;
|
||||
if (max * itemWidth > screenWidth) {
|
||||
const items = Math.floor(screenWidth / itemWidth);
|
||||
return items > 1 && items % 2 === 0 ? items - 1 : items;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
readonly property string search: root.search.text.split(" ").slice(1).join(" ")
|
||||
|
||||
values: {
|
||||
const list = Wallpapers.fuzzyQuery(search);
|
||||
if (list.length > 1 && list.length % 2 === 0)
|
||||
list.length -= 1; // Always show odd number
|
||||
return list;
|
||||
}
|
||||
onValuesChanged: root.currentIndex = search ? 0 : values.findIndex(w => w.path === Wallpapers.actualCurrent)
|
||||
}
|
||||
|
||||
Component.onCompleted: currentIndex = Wallpapers.list.findIndex(w => w.path === Wallpapers.actualCurrent)
|
||||
Component.onDestruction: Wallpapers.stopPreview()
|
||||
|
||||
onCurrentItemChanged: {
|
||||
if (currentItem)
|
||||
Wallpapers.preview(currentItem.modelData.path);
|
||||
}
|
||||
|
||||
implicitWidth: Math.min(numItems, count) * (LauncherConfig.sizes.wallpaperWidth * 0.8 + Appearance.padding.larger * 2)
|
||||
pathItemCount: numItems
|
||||
cacheItemCount: 4
|
||||
|
||||
snapMode: PathView.SnapToItem
|
||||
preferredHighlightBegin: 0.5
|
||||
preferredHighlightEnd: 0.5
|
||||
highlightRangeMode: PathView.StrictlyEnforceRange
|
||||
|
||||
delegate: WallpaperItem {
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
|
||||
path: Path {
|
||||
startY: root.height / 2
|
||||
|
||||
PathAttribute {
|
||||
name: "z"
|
||||
value: 0
|
||||
}
|
||||
PathLine {
|
||||
x: root.width / 2
|
||||
relativeY: 0
|
||||
}
|
||||
PathAttribute {
|
||||
name: "z"
|
||||
value: 1
|
||||
}
|
||||
PathLine {
|
||||
x: root.width
|
||||
relativeY: 0
|
||||
}
|
||||
}
|
||||
}
|
55
modules/launcher/Wrapper.qml
Normal file
55
modules/launcher/Wrapper.qml
Normal file
|
@ -0,0 +1,55 @@
|
|||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
visible: height > 0
|
||||
implicitHeight: 0
|
||||
implicitWidth: content.implicitWidth
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.visibilities.launcher
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitHeight: content.implicitHeight
|
||||
}
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Content {
|
||||
id: content
|
||||
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue