diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-05-03 23:56:37 +1000 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-05-03 23:56:37 +1000 |
| commit | 00d3c1a472888817d7649391d4a8485c5fc6f6f5 (patch) | |
| tree | 45b351c62f61ede55acfc209dde686f1c161a942 /modules/launcher | |
| parent | feat: use multieffect instead of qt5compat (diff) | |
| download | caelestia-shell-00d3c1a472888817d7649391d4a8485c5fc6f6f5.tar.gz caelestia-shell-00d3c1a472888817d7649391d4a8485c5fc6f6f5.tar.bz2 caelestia-shell-00d3c1a472888817d7649391d4a8485c5fc6f6f5.zip | |
feat: launcher wallpaper selector
Diffstat (limited to 'modules/launcher')
| -rw-r--r-- | modules/launcher/ActionItem.qml | 4 | ||||
| -rw-r--r-- | modules/launcher/AppItem.qml | 4 | ||||
| -rw-r--r-- | modules/launcher/AppList.qml | 14 | ||||
| -rw-r--r-- | modules/launcher/Background.qml | 5 | ||||
| -rw-r--r-- | modules/launcher/Content.qml | 47 | ||||
| -rw-r--r-- | modules/launcher/ContentList.qml | 193 | ||||
| -rw-r--r-- | modules/launcher/EmptyIndicator.qml | 62 | ||||
| -rw-r--r-- | modules/launcher/Launcher.qml | 10 | ||||
| -rw-r--r-- | modules/launcher/WallpaperItem.qml | 91 | ||||
| -rw-r--r-- | modules/launcher/WallpaperList.qml | 44 | ||||
| -rw-r--r-- | modules/launcher/Wrapper.qml | 2 |
11 files changed, 372 insertions, 104 deletions
diff --git a/modules/launcher/ActionItem.qml b/modules/launcher/ActionItem.qml index 06a9342..f23cf0a 100644 --- a/modules/launcher/ActionItem.qml +++ b/modules/launcher/ActionItem.qml @@ -9,10 +9,12 @@ PaddedRect { required property Actions.Action modelData required property var list - implicitWidth: ListView.view.width implicitHeight: LauncherConfig.sizes.itemHeight padding: [Appearance.padding.smaller, Appearance.padding.larger] + anchors.left: parent.left + anchors.right: parent.right + StateLayer { radius: Appearance.rounding.normal diff --git a/modules/launcher/AppItem.qml b/modules/launcher/AppItem.qml index 744c2d0..ea67682 100644 --- a/modules/launcher/AppItem.qml +++ b/modules/launcher/AppItem.qml @@ -10,10 +10,12 @@ PaddedRect { required property DesktopEntry modelData required property Scope launcher - implicitWidth: ListView.view.width implicitHeight: LauncherConfig.sizes.itemHeight padding: [Appearance.padding.smaller, Appearance.padding.normal] + anchors.left: parent.left + anchors.right: parent.right + StateLayer { radius: Appearance.rounding.normal diff --git a/modules/launcher/AppList.qml b/modules/launcher/AppList.qml index ab08db7..2f352ba 100644 --- a/modules/launcher/AppList.qml +++ b/modules/launcher/AppList.qml @@ -30,17 +30,12 @@ ListView { onValuesChanged: root.currentIndex = 0 } - clip: true spacing: Appearance.spacing.small orientation: Qt.Vertical implicitHeight: (LauncherConfig.sizes.itemHeight + spacing) * Math.min(LauncherConfig.maxShown, count) - spacing - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: root.padding - highlightMoveDuration: Appearance.anim.durations.normal + highlightResizeDuration: 0 highlight: StyledRect { radius: Appearance.rounding.normal @@ -111,13 +106,6 @@ ListView { } } - Behavior on implicitHeight { - Anim { - duration: Appearance.anim.durations.large - easing.bezierCurve: Appearance.anim.curves.emphasizedDecel - } - } - Behavior on isAction { SequentialAnimation { ParallelAnimation { diff --git a/modules/launcher/Background.qml b/modules/launcher/Background.qml index ae12712..8cfaac8 100644 --- a/modules/launcher/Background.qml +++ b/modules/launcher/Background.qml @@ -5,13 +5,12 @@ import QtQuick.Shapes Shape { id: root + required property real wrapperWidth required property real realWrapperHeight readonly property int rounding: Appearance.rounding.large readonly property int roundingY: Math.min(rounding, realWrapperHeight / 2) readonly property real wrapperHeight: realWrapperHeight - 1 // Pixel issues :sob: - anchors.left: parent.left - anchors.right: parent.right anchors.bottom: parent.bottom preferredRendererType: Shape.CurveRenderer @@ -41,7 +40,7 @@ Shape { radiusY: root.roundingY } PathLine { - x: wrapper.width - root.rounding * 2 + x: root.wrapperWidth - root.rounding * 2 } PathArc { relativeX: root.rounding diff --git a/modules/launcher/Content.qml b/modules/launcher/Content.qml index 3f9fbe2..be6be2e 100644 --- a/modules/launcher/Content.qml +++ b/modules/launcher/Content.qml @@ -1,3 +1,5 @@ +pragma ComponentBehavior: Bound + import "root:/widgets" import "root:/services" import "root:/config" @@ -13,7 +15,7 @@ Item { readonly property int spacing: Appearance.spacing.normal readonly property int rounding: Appearance.rounding.large - implicitWidth: LauncherConfig.sizes.width + implicitWidth: listWrapper.width + padding * 2 implicitHeight: search.height + listWrapper.height + padding * 2 + spacing anchors.bottom: parent.bottom @@ -24,26 +26,23 @@ Item { color: Appearance.alpha(Appearance.colours.m3surfaceContainerHigh, true) radius: root.rounding - implicitHeight: Math.max(empty.height, list.height) + root.padding * 2 - anchors.left: parent.left - anchors.right: parent.right + implicitWidth: list.width + root.padding * 2 + implicitHeight: list.height + root.padding * 2 + + anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: search.top anchors.bottomMargin: root.spacing anchors.margins: root.padding - AppList { + ContentList { id: list - padding: root.padding - search: search launcher: root.launcher - } - - EmptyIndicator { - id: empty - - empty: list.count === 0 + search: search + padding: root.padding + spacing: root.spacing + rounding: root.rounding } } @@ -68,18 +67,22 @@ Item { } onAccepted: { - if (list.currentItem) { - if (list.isAction) - list.currentItem.modelData.onClicked(list); - else { - Apps.launch(list.currentItem?.modelData); + const currentItem = list.currentList?.currentItem; + if (currentItem) { + if (list.showWallpapers) { + // TODO + console.log(currentItem.modelData.path); + } else if (text.startsWith(LauncherConfig.actionPrefix)) { + currentItem.modelData.onClicked(list.currentList); + } else { + Apps.launch(currentItem.modelData); root.launcher.launcherVisible = false; } } } - Keys.onUpPressed: list.decrementCurrentIndex() - Keys.onDownPressed: list.incrementCurrentIndex() + Keys.onUpPressed: list.currentList?.decrementCurrentIndex() + Keys.onDownPressed: list.currentList?.incrementCurrentIndex() Keys.onEscapePressed: root.launcher.launcherVisible = false @@ -91,7 +94,9 @@ Item { search.forceActiveFocus(); else { search.text = ""; - list.currentIndex = 0; + const current = list.currentList; + if (current) + current.currentIndex = 0; } } } diff --git a/modules/launcher/ContentList.qml b/modules/launcher/ContentList.qml new file mode 100644 index 0000000..74c229a --- /dev/null +++ b/modules/launcher/ContentList.qml @@ -0,0 +1,193 @@ +pragma ComponentBehavior: Bound + +import "root:/widgets" +import "root:/services" +import "root:/config" +import Quickshell +import QtQuick +import QtQuick.Controls + +Item { + id: root + + required property Scope launcher + required property TextField search + required property int padding + required property int spacing + 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 + anchors.bottomMargin: root.padding + + 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 { + from: "*" + to: "*" + + 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 + launcher: root.launcher + } + } + + Loader { + id: wallpaperList + + active: false + asynchronous: true + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + + sourceComponent: WallpaperList { + search: root.search + launcher: root.launcher + } + } + + 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: Appearance.colours.m3outline + 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: Appearance.colours.m3outline + 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 + } + } +} diff --git a/modules/launcher/EmptyIndicator.qml b/modules/launcher/EmptyIndicator.qml deleted file mode 100644 index 9593a2e..0000000 --- a/modules/launcher/EmptyIndicator.qml +++ /dev/null @@ -1,62 +0,0 @@ -import "root:/widgets" -import "root:/config" -import QtQuick - -Loader { - id: root - - required property bool empty - - active: false - opacity: 0 - scale: 0 - asynchronous: true - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - - sourceComponent: Item { - implicitWidth: childrenRect.width - implicitHeight: icon.height - - MaterialIcon { - id: icon - - text: "manage_search" - color: Appearance.colours.m3outline - font.pointSize: Appearance.font.size.extraLarge - - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - anchors.left: icon.right - anchors.leftMargin: Appearance.spacing.small - anchors.verticalCenter: icon.verticalCenter - - text: qsTr("No matching apps found") - color: Appearance.colours.m3outline - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - } - - states: State { - name: "visible" - when: root.empty - - PropertyChanges { - root.active: true - root.opacity: 1 - root.scale: 1 - } - } - - transitions: Transition { - NumberAnimation { - properties: "opacity,scale" - duration: Appearance.anim.durations.large - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } -} diff --git a/modules/launcher/Launcher.qml b/modules/launcher/Launcher.qml index d5c43db..bbd2f91 100644 --- a/modules/launcher/Launcher.qml +++ b/modules/launcher/Launcher.qml @@ -21,23 +21,31 @@ Scope { keyboardFocus: root.launcherVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None visible: wrapper.shouldBeVisible - width: content.width + bg.rounding * 2 mask: Region { item: wrapper } anchors.top: true + anchors.left: true anchors.bottom: true + anchors.right: true Background { id: bg + anchors.horizontalCenter: parent.horizontalCenter + + wrapperWidth: wrapper.width realWrapperHeight: Math.min(wrapper.height, content.height) } Wrapper { id: wrapper + anchors.horizontalCenter: parent.horizontalCenter + + implicitWidth: content.width + bg.rounding * 2 + launcherVisible: root.launcherVisible contentHeight: content.height diff --git a/modules/launcher/WallpaperItem.qml b/modules/launcher/WallpaperItem.qml new file mode 100644 index 0000000..bbd611c --- /dev/null +++ b/modules/launcher/WallpaperItem.qml @@ -0,0 +1,91 @@ +import "root:/widgets" +import "root:/services" +import "root:/config" +import Quickshell +import QtQuick +import QtQuick.Effects + +StyledRect { + id: root + + required property Wallpapers.Wallpaper modelData + + scale: PathView.isCurrentItem ? 1 : PathView.onPath ? 0.8 : 0 + opacity: PathView.onPath ? 1 : 0 + z: PathView.isCurrentItem ? 1 : 0 + + implicitWidth: image.width + Appearance.padding.larger * 2 + implicitHeight: image.height + label.height + Appearance.spacing.small / 2 + Appearance.padding.normal * 2 + + StateLayer { + radius: Appearance.rounding.normal + + function onClicked(): void { + console.log("clicked"); + } + } + + Image { + id: image + + anchors.horizontalCenter: parent.horizontalCenter + y: Appearance.padding.normal + + visible: false + source: `file://${root.modelData.path}` + asynchronous: true + fillMode: Image.PreserveAspectCrop + smooth: !root.PathView.view.moving + + width: LauncherConfig.sizes.wallpaperWidth + height: width / 16 * 9 + sourceSize.width: width + sourceSize.height: height + } + + Rectangle { + id: mask + + layer.enabled: true + visible: false + anchors.fill: image + width: image.width + height: image.height + radius: Appearance.rounding.normal + } + + MultiEffect { + anchors.fill: image + source: image + maskEnabled: true + maskSource: mask + } + + StyledText { + id: label + + anchors.top: image.bottom + anchors.topMargin: Appearance.spacing.small / 2 + anchors.horizontalCenter: parent.horizontalCenter + + renderType: Text.QtRendering + text: root.modelData.name + font.pointSize: Appearance.font.size.small + } + + Behavior on scale { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + Behavior on opacity { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } +} diff --git a/modules/launcher/WallpaperList.qml b/modules/launcher/WallpaperList.qml new file mode 100644 index 0000000..ecf7976 --- /dev/null +++ b/modules/launcher/WallpaperList.qml @@ -0,0 +1,44 @@ +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 Scope launcher + + model: ScriptModel { + values: { + const list = Wallpapers.fuzzyQuery(root.search.text.split(" ").slice(1).join(" ")); + if (list.length > 1 && list.length % 2 === 0) + list.length -= 1; // Always show odd number + return list; + } + onValuesChanged: root.currentIndex = 0 + } + + implicitWidth: Math.min(LauncherConfig.maxWallpapers, count) * (LauncherConfig.sizes.wallpaperWidth * 0.8 + Appearance.padding.larger * 2) + pathItemCount: LauncherConfig.maxWallpapers + cacheItemCount: 4 + + snapMode: PathView.SnapToItem + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + highlightRangeMode: PathView.StrictlyEnforceRange + highlightMoveDuration: Appearance.anim.durations.short + + delegate: WallpaperItem {} + + path: Path { + startY: root.height / 2 + + PathLine { + x: root.width + relativeY: 0 + } + } +} diff --git a/modules/launcher/Wrapper.qml b/modules/launcher/Wrapper.qml index d7385c1..132a130 100644 --- a/modules/launcher/Wrapper.qml +++ b/modules/launcher/Wrapper.qml @@ -8,8 +8,6 @@ Item { required property real contentHeight property bool shouldBeVisible - anchors.left: parent.left - anchors.right: parent.right anchors.bottom: parent.bottom height: 0 |