From 14b01ad539e1de538f4f7404072d94bd34ff2e9c Mon Sep 17 00:00:00 2001 From: Zacharias-Brohn Date: Wed, 12 Nov 2025 13:16:01 +0100 Subject: [PATCH] change to using loaders --- Config/Config.qml | 2 + Helpers/InitialTitle.qml | 22 ++ Helpers/SearchWallpapers.qml | 35 +++ Modules/Launcher.qml | 449 +++++++++++++++++++-------------- Modules/NotificationCenter.qml | 2 + Modules/TrayItem.qml | 4 +- Modules/TrayMenu.qml | 8 +- Modules/WallpaperItem.qml | 18 ++ Modules/WindowTitle.qml | 18 +- 9 files changed, 350 insertions(+), 208 deletions(-) create mode 100644 Helpers/InitialTitle.qml create mode 100644 Helpers/SearchWallpapers.qml create mode 100644 Modules/WallpaperItem.qml diff --git a/Config/Config.qml b/Config/Config.qml index 15a8728..166dbdc 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -8,6 +8,7 @@ Singleton { property alias appCount: adapter.appCount property alias baseBgColor: adapter.baseBgColor property alias accentColor: adapter.accentColor + property alias wallpaperPath: adapter.wallpaperPath FileView { id: root @@ -23,6 +24,7 @@ Singleton { JsonAdapter { id: adapter property int appCount: 20 + property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers" property string baseBgColor: "#801a1a1a" property AccentColor accentColor: AccentColor {} } diff --git a/Helpers/InitialTitle.qml b/Helpers/InitialTitle.qml new file mode 100644 index 0000000..d587a63 --- /dev/null +++ b/Helpers/InitialTitle.qml @@ -0,0 +1,22 @@ +pragma Singleton +import Quickshell.Io +import Quickshell + +Singleton { + + function getInitialTitle(callback) { + initialTitleProc.running = true + initialTitleProc.stdout.streamFinished.connect( function() { + let cleaned = initialTitleProc.stdout.text.trim().replace(/\"/g, "") + callback(cleaned === "null" ? "" : cleaned) + }) + } + + Process { + id: initialTitleProc + command: ["./scripts/initialTitle.sh"] + running: false + stdout: StdioCollector { + } + } +} diff --git a/Helpers/SearchWallpapers.qml b/Helpers/SearchWallpapers.qml new file mode 100644 index 0000000..0d79662 --- /dev/null +++ b/Helpers/SearchWallpapers.qml @@ -0,0 +1,35 @@ +pragma Singleton + +import Quickshell +import Quickshell.Io +import qs.Config +import qs.Modules +import Caelestia.Models + +Searcher { + id: root + + list: wallpapers.entries + key: "relativePath" + useFuzzy: true + extraOpts: useFuzzy ? ({}) : ({ + forward: false + }) + + // FileView { + // path: root.currentNamePath + // watchChanges: true + // onFileChanged: reload() + // onLoaded: { + // root.actualCurrent = text().trim(); + // } + // } + + FileSystemModel { + id: wallpapers + + recursive: true + path: Config.wallpaperPath + filter: FileSystemModel.Images + } +} diff --git a/Modules/Launcher.qml b/Modules/Launcher.qml index c40794d..fa6bc9c 100644 --- a/Modules/Launcher.qml +++ b/Modules/Launcher.qml @@ -6,6 +6,7 @@ import QtQuick.Controls import QtQuick.Effects import QtQuick.Layouts import qs.Config +import qs.Helpers Scope { id: root @@ -187,8 +188,8 @@ Scope { Rectangle { id: appListRect x: Math.round(( parent.width - width ) / 2 ) - implicitWidth: backgroundRect.implicitWidth - implicitHeight: appListView.implicitHeight + 20 + implicitWidth: appListContainer.implicitWidth + 20 + implicitHeight: appListContainer.implicitHeight + 20 anchors.bottom: backgroundRect.top anchors.bottomMargin: -1 color: backgroundRect.color @@ -204,217 +205,287 @@ Scope { } Item { - anchors.fill: parent + anchors.centerIn: parent + id: appListContainer visible: appListView.count > 0 - anchors.margins: 10 clip: true - ListView { - id: appListView + property var showWallpapers: searchInput.text.startsWith(">") + state: showWallpapers ? "wallpaperpicker" : "apps" + states: [ + State { + name: "apps" + PropertyChanges { + appListLoader.active: true + appListContainer.implicitHeight: appListView.implicitHeight + 20 + appListContainer.implicitWidth: 600 + } + }, + State { + name: "wallpaperpicker" + PropertyChanges { + wallpaperPickerLoader.active: true + appListContainer.implicitHeight: wallpaperPickerView.implicitHeight + 20 + appListContainer.implicitWidth: wallpaperPickerView.implicitWidth + 20 + } + } + ] + Loader { + id: wallpaperPickerLoader + active: false anchors.fill: parent - model: ScriptModel { - id: appModel + sourceComponent: ListView { + id: wallpaperPickerView + anchors.fill: parent + model: ScriptModel { + id: wallpaperModel + readonly property string search: searchInput.text.split(" ").slice(1).join(" ") - onValuesChanged: { - appListView.currentIndex = 0; + values: SearchWallpapers.query( search ) } + + orientation: ListView.Horizontal + spacing: 10 + implicitHeight: 300 + implicitWidth: Math.min( wallpaperModel.count, 7 ) * 192 + Math.max(0, wallpaperModel.count -1) * 10 + + focus: true + + delegate: WallpaperItem { } + } + } + Loader { + id: appListLoader + active: false + anchors.fill: parent + sourceComponent: ListView { + id: appListView + anchors.fill: parent + model: ScriptModel { + id: appModel - verticalLayoutDirection: ListView.BottomToTop - implicitHeight: Math.min( count, Config.appCount ) * 48 + onValuesChanged: { + appListView.currentIndex = 0; + } + } - preferredHighlightBegin: 0 - preferredHighlightEnd: appListView.height - highlightFollowsCurrentItem: false - highlightRangeMode: ListView.ApplyRange - focus: true - highlight: Rectangle { - radius: 4 - color: "#FFFFFF" - opacity: 0.08 + verticalLayoutDirection: ListView.BottomToTop + implicitHeight: Math.min( count, Config.appCount ) * 48 - y: appListView.currentItem?.y - implicitWidth: appListView.width - implicitHeight: appListView.currentItem?.implicitHeight ?? 0 + preferredHighlightBegin: 0 + preferredHighlightEnd: appListView.height + highlightFollowsCurrentItem: false + highlightRangeMode: ListView.ApplyRange + focus: true + highlight: Rectangle { + radius: 4 + color: "#FFFFFF" + opacity: 0.08 - Behavior on y { + y: appListView.currentItem?.y + implicitWidth: appListView.width + implicitHeight: appListView.currentItem?.implicitHeight ?? 0 + + Behavior on y { + Anim { + duration: MaterialEasing.expressiveEffectsTime + easing.bezierCurve: MaterialEasing.expressiveEffects + } + } + } + + property list search: Search.search( searchInput.text ) + + state: { + const text = searchInput.text + if ( search.length === 0 ) { + return "noresults" + } else { + return "apps" + } + } + + states: [ + State { + name: "apps" + PropertyChanges { + appModel.values: Search.search(searchInput.text) + appListView.delegate: appItem + } + }, + State { + name: "noresults" + PropertyChanges { + appModel.values: [1] + appListView.delegate: noResultsItem + } + } + // State { + // name: "wallpaperpicker" + // PropertyChanges { + // appModel.values: SearchWallpapers.query( searchInput.text.split(" ").slice(1).join(" ") ) + // appListView.delegate: wallpaperItem + // appListView.orientation: ListView.Horizontal + // } + // } + ] + + Component { + id: appItem + AppItem { + } + } + + Component { + id: noResultsItem + Item { + width: appListView.width + height: 48 + Text { + id: icon + anchors.verticalCenter: parent.verticalCenter + property real fill: 0 + text: "\ue000" + color: "#cccccc" + renderType: Text.NativeRendering + font.pointSize: 28 + font.family: "Material Symbols Outlined" + font.variableAxes: ({ + FILL: fill.toFixed(1), + GRAD: -25, + opsz: fontInfo.pixelSize, + wght: fontInfo.weight + }) + } + + Text { + anchors.left: icon.right + anchors.leftMargin: 10 + anchors.verticalCenter: parent.verticalCenter + text: "No results found" + color: "#cccccc" + renderType: Text.NativeRendering + + font.pointSize: 12 + font.family: "Rubik" + } + } + } + + Component { + id: wallpaperItem + WallpaperItem { } + } + + transitions: Transition { + SequentialAnimation { + ParallelAnimation { + Anim { + target: appListView + property: "opacity" + from: 1 + to: 0 + duration: 200 + easing.bezierCurve: MaterialEasing.standardAccel + } + Anim { + target: appListView + property: "scale" + from: 1 + to: 0.9 + duration: 200 + easing.bezierCurve: MaterialEasing.standardAccel + } + } + PropertyAction { + targets: [model, appListView] + properties: "values,delegate" + } + ParallelAnimation { + Anim { + target: appListView + property: "opacity" + from: 0 + to: 1 + duration: 200 + easing.bezierCurve: MaterialEasing.standardDecel + } + Anim { + target: appListView + property: "scale" + from: 0.9 + to: 1 + duration: 200 + easing.bezierCurve: MaterialEasing.standardDecel + } + } + PropertyAction { + targets: [appListView.add, appListView.remove] + property: "enabled" + value: true + } + } + } + + add: Transition { + enabled: !appListView.state Anim { - duration: MaterialEasing.expressiveEffectsTime - easing.bezierCurve: MaterialEasing.expressiveEffects - } - } - } - - property list search: Search.search( searchInput.text ) - - state: search.length === 0 ? "noresults" : "apps" - - states: [ - State { - name: "apps" - PropertyChanges { - appModel.values: Search.search(searchInput.text) - appListView.delegate: appItem - } - }, - State { - name: "noresults" - PropertyChanges { - appModel.values: [1] - appListView.delegate: noResultsItem - } - } - ] - - Component { - id: appItem - AppItem { - } - } - - Component { - id: noResultsItem - Item { - width: appListView.width - height: 48 - Text { - id: icon - anchors.verticalCenter: parent.verticalCenter - property real fill: 0 - text: "\ue000" - color: "#cccccc" - renderType: Text.NativeRendering - font.pointSize: 28 - font.family: "Material Symbols Outlined" - font.variableAxes: ({ - FILL: fill.toFixed(1), - GRAD: -25, - opsz: fontInfo.pixelSize, - wght: fontInfo.weight - }) + properties: "opacity" + from: 0 + to: 1 } - Text { - anchors.left: icon.right - anchors.leftMargin: 10 - anchors.verticalCenter: parent.verticalCenter - text: "No results found" - color: "#cccccc" - renderType: Text.NativeRendering - - font.pointSize: 12 - font.family: "Rubik" + Anim { + properties: "scale" + from: 0.95 + to: 1 } } - } - transitions: Transition { - SequentialAnimation { - ParallelAnimation { - Anim { - target: appListView - property: "opacity" - from: 1 - to: 0 - duration: 200 - easing.bezierCurve: MaterialEasing.standardAccel - } - Anim { - target: appListView - property: "scale" - from: 1 - to: 0.9 - duration: 200 - easing.bezierCurve: MaterialEasing.standardAccel - } + remove: Transition { + enabled: !appListView.state + Anim { + properties: "opacity" + from: 1 + to: 0 } - PropertyAction { - targets: [model, appListView] - properties: "values,delegate" - } - ParallelAnimation { - Anim { - target: appListView - property: "opacity" - from: 0 - to: 1 - duration: 200 - easing.bezierCurve: MaterialEasing.standardDecel - } - Anim { - target: appListView - property: "scale" - from: 0.9 - to: 1 - duration: 200 - easing.bezierCurve: MaterialEasing.standardDecel - } - } - PropertyAction { - targets: [appListView.add, appListView.remove] - property: "enabled" - value: true + + Anim { + properties: "scale" + from: 1 + to: 0.95 } } - } - add: Transition { - enabled: !appListView.state - Anim { - properties: "opacity" - from: 0 - to: 1 + move: Transition { + Anim { + property: "y" + } + Anim { + properties: "opacity,scale" + to: 1 + } } - Anim { - properties: "scale" - from: 0.95 - to: 1 - } - } - - remove: Transition { - enabled: !appListView.state - Anim { - properties: "opacity" - from: 1 - to: 0 + addDisplaced: Transition { + Anim { + property: "y" + duration: 200 + } + Anim { + properties: "opacity,scale" + to: 1 + } } - Anim { - properties: "scale" - from: 1 - to: 0.95 - } - } - - move: Transition { - Anim { - property: "y" - } - Anim { - properties: "opacity,scale" - to: 1 - } - } - - addDisplaced: Transition { - Anim { - property: "y" - duration: 200 - } - Anim { - properties: "opacity,scale" - to: 1 - } - } - - displaced: Transition { - Anim { - property: "y" - } - Anim { - properties: "opacity,scale" - to: 1 + displaced: Transition { + Anim { + property: "y" + } + Anim { + properties: "opacity,scale" + to: 1 + } } } } diff --git a/Modules/NotificationCenter.qml b/Modules/NotificationCenter.qml index ca022f2..9fa73ad 100644 --- a/Modules/NotificationCenter.qml +++ b/Modules/NotificationCenter.qml @@ -143,6 +143,8 @@ PanelWindow { Layout.fillWidth: true text: "Do Not Disturb" focus: false + activeFocusOnTab: false + focusPolicy: Qt.NoFocus } RowLayout { Layout.alignment: Qt.AlignRight | Qt.AlignVCenter diff --git a/Modules/TrayItem.qml b/Modules/TrayItem.qml index f750d89..4f89f12 100644 --- a/Modules/TrayItem.qml +++ b/Modules/TrayItem.qml @@ -59,8 +59,10 @@ MouseArea { if ( mouse.button === Qt.LeftButton ) { root.item.activate(); } else if ( mouse.button === Qt.RightButton ) { + if ( root.item?.menu !== trayMenu.trayMenu ) { + trayMenu.trayMenu = root.item?.menu; + } trayMenu.visible = !trayMenu.visible; - trayMenu.trayMenu = root.item?.menu; trayMenu.focusGrab = true; } } diff --git a/Modules/TrayMenu.qml b/Modules/TrayMenu.qml index 81b6804..db9b8d8 100644 --- a/Modules/TrayMenu.qml +++ b/Modules/TrayMenu.qml @@ -163,15 +163,15 @@ PanelWindow { Behavior on implicitWidth { NumberAnimation { - duration: MaterialEasing.standardTime - easing.bezierCurve: MaterialEasing.standard + duration: MaterialEasing.expressiveEffectsTime + easing.bezierCurve: MaterialEasing.expressiveEffects } } Behavior on implicitHeight { NumberAnimation { - duration: MaterialEasing.standardTime - easing.bezierCurve: MaterialEasing.standard + duration: MaterialEasing.expressiveEffectsTime + easing.bezierCurve: MaterialEasing.expressiveEffects } } diff --git a/Modules/WallpaperItem.qml b/Modules/WallpaperItem.qml new file mode 100644 index 0000000..5ed7f04 --- /dev/null +++ b/Modules/WallpaperItem.qml @@ -0,0 +1,18 @@ +import Quickshell +import QtQuick +import Caelestia.Models + +Item { + id: root + required property FileSystemEntry modelData + implicitWidth: 192 + implicitHeight: 108 + + Image { + id: thumbnailImage + + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + source: root.modelData.path + } +} diff --git a/Modules/WindowTitle.qml b/Modules/WindowTitle.qml index 0094ccd..6d8b57e 100644 --- a/Modules/WindowTitle.qml +++ b/Modules/WindowTitle.qml @@ -1,7 +1,7 @@ import QtQuick import QtQuick.Layouts -import Quickshell.Io import Quickshell.Hyprland +import qs.Helpers Item { id: root @@ -12,22 +12,12 @@ Item { property bool showFirst: true - Process { - id: initialTitleProc - command: ["./scripts/initialTitle.sh"] - running: false - stdout: StdioCollector { - onStreamFinished: { - let cleaned = this.text.trim().replace(/\"/g, "") - root.currentTitle = ( cleaned === "null" ) ? "" : cleaned - } - } - } - Component.onCompleted: { Hyprland.rawEvent.connect(( event ) => { if (event.name === "activewindow") { - initialTitleProc.running = true + InitialTitle.getInitialTitle( function( initialTitle ) { + root.currentTitle = initialTitle + }) } }) }