From dbb930f39a0f69c96082bb9bd4eefab343a2995d Mon Sep 17 00:00:00 2001 From: Zacharias-Brohn Date: Tue, 17 Feb 2026 16:32:43 +0100 Subject: [PATCH] launcher overhaul --- Bar.qml | 17 +- Components/CustomTextField.qml | 76 +++ Config/BarConfig.qml | 1 + Config/Config.qml | 24 +- Config/General.qml | 8 + Config/Launcher.qml | 79 ++- Drawers/Backgrounds.qml | 8 + Drawers/Panels.qml | 13 + {Modules => Helpers}/Searcher.qml | 0 Helpers/Wallpapers.qml | 49 ++ Modules/AppItem.qml | 72 --- Modules/GroupListView.qml | 153 ----- Modules/Launcher.qml | 538 ------------------ Modules/Launcher/AppList.qml | 220 +++++++ Modules/Launcher/Background.qml | 61 ++ Modules/Launcher/Content.qml | 191 +++++++ Modules/Launcher/ContentList.qml | 170 ++++++ Modules/Launcher/Items/ActionItem.qml | 70 +++ Modules/Launcher/Items/AppItem.qml | 74 +++ Modules/Launcher/Items/CalcItem.qml | 124 ++++ Modules/Launcher/Items/WallpaperItem.qml | 97 ++++ Modules/Launcher/Services/Actions.qml | 51 ++ .../Services/Apps.qml} | 29 +- Modules/Launcher/WallpaperList.qml | 97 ++++ Modules/Launcher/Wrapper.qml | 131 +++++ Modules/Shortcuts.qml | 25 + Modules/TrackedNotification.qml | 248 -------- Modules/WallBackground.qml | 4 +- Modules/WallpaperItem.qml | 44 -- Modules/WindowTitleOld.qml | 68 --- shell.qml | 6 +- 31 files changed, 1603 insertions(+), 1145 deletions(-) create mode 100644 Components/CustomTextField.qml rename {Modules => Helpers}/Searcher.qml (100%) create mode 100644 Helpers/Wallpapers.qml delete mode 100644 Modules/AppItem.qml delete mode 100644 Modules/GroupListView.qml delete mode 100644 Modules/Launcher.qml create mode 100644 Modules/Launcher/AppList.qml create mode 100644 Modules/Launcher/Background.qml create mode 100644 Modules/Launcher/Content.qml create mode 100644 Modules/Launcher/ContentList.qml create mode 100644 Modules/Launcher/Items/ActionItem.qml create mode 100644 Modules/Launcher/Items/AppItem.qml create mode 100644 Modules/Launcher/Items/CalcItem.qml create mode 100644 Modules/Launcher/Items/WallpaperItem.qml create mode 100644 Modules/Launcher/Services/Actions.qml rename Modules/{Search.qml => Launcher/Services/Apps.qml} (72%) create mode 100644 Modules/Launcher/WallpaperList.qml create mode 100644 Modules/Launcher/Wrapper.qml create mode 100644 Modules/Shortcuts.qml delete mode 100644 Modules/TrackedNotification.qml delete mode 100644 Modules/WallpaperItem.qml delete mode 100644 Modules/WindowTitleOld.qml diff --git a/Bar.qml b/Bar.qml index fda6c64..e091ad3 100644 --- a/Bar.qml +++ b/Bar.qml @@ -4,6 +4,7 @@ import QtQuick import QtQuick.Effects import Quickshell import Quickshell.Wayland +import Quickshell.Hyprland import qs.Components import qs.Modules import qs.Modules.Bar @@ -25,7 +26,7 @@ Variants { WlrLayershell.namespace: "ZShell-Bar" WlrLayershell.exclusionMode: ExclusionMode.Ignore - + WlrLayershell.keyboardFocus: visibilities.launcher ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None PanelWindow { id: exclusionZone WlrLayershell.namespace: "ZShell-Bar-Exclusion" @@ -78,6 +79,19 @@ Variants { } } + HyprlandFocusGrab { + id: focusGrab + + active: visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.osd + windows: [bar] + onCleared: { + visibilities.launcher = false; + visibilities.sidebar = false; + visibilities.dashboard = false; + visibilities.osd = false; + } + } + CustomShortcut { name: "toggle-nc" @@ -93,6 +107,7 @@ Variants { property bool dashboard property bool bar property bool osd + property bool launcher Component.onCompleted: Visibilities.load(scope.modelData, this) } diff --git a/Components/CustomTextField.qml b/Components/CustomTextField.qml new file mode 100644 index 0000000..b739901 --- /dev/null +++ b/Components/CustomTextField.qml @@ -0,0 +1,76 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import qs.Helpers +import qs.Config +import qs.Modules + +TextField { + id: root + + color: DynamicColors.palette.m3onSurface + placeholderTextColor: DynamicColors.palette.m3outline + font.family: Appearance.font.family.sans + font.pointSize: Appearance.font.size.smaller + renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering + cursorVisible: !readOnly + + background: null + + cursorDelegate: CustomRect { + id: cursor + + property bool disableBlink + + implicitWidth: 2 + color: DynamicColors.palette.m3primary + radius: Appearance.rounding.normal + + Connections { + target: root + + function onCursorPositionChanged(): void { + if (root.activeFocus && root.cursorVisible) { + cursor.opacity = 1; + cursor.disableBlink = true; + enableBlink.restart(); + } + } + } + + Timer { + id: enableBlink + + interval: 100 + onTriggered: cursor.disableBlink = false + } + + Timer { + running: root.activeFocus && root.cursorVisible && !cursor.disableBlink + repeat: true + triggeredOnStart: true + interval: 500 + onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1 + } + + Binding { + when: !root.activeFocus || !root.cursorVisible + cursor.opacity: 0 + } + + Behavior on opacity { + Anim { + duration: Appearance.anim.durations.small + } + } + } + + Behavior on color { + CAnim {} + } + + Behavior on placeholderTextColor { + CAnim {} + } +} diff --git a/Config/BarConfig.qml b/Config/BarConfig.qml index 8219c92..cddf9e9 100644 --- a/Config/BarConfig.qml +++ b/Config/BarConfig.qml @@ -2,6 +2,7 @@ import Quickshell.Io JsonObject { property bool autoHide: false + property int rounding: 8 property Popouts popouts: Popouts {} property list entries: [ diff --git a/Config/Config.qml b/Config/Config.qml index cbb21f7..5e71887 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -101,6 +101,7 @@ Singleton { function serializeBar(): var { return { autoHide: barConfig.autoHide, + rounding: barConfig.rounding, popouts: { tray: barConfig.popouts.tray, audio: barConfig.popouts.audio, @@ -132,6 +133,12 @@ Singleton { logo: general.logo, wallpaperPath: general.wallpaperPath, wallust: general.wallust, + apps: { + terminal: general.apps.terminal, + audio: general.apps.audio, + playback: general.apps.playback, + explorer: general.apps.explorer, + }, idle: { timouts: general.idle.timeouts } @@ -245,7 +252,22 @@ Singleton { return { maxAppsShown: launcher.maxAppsShown, maxWallpapers: launcher.maxWallpapers, - wallpaperPrefix: launcher.wallpaperPrefix + actionPrefix: launcher.actionPrefix, + specialPrefix: launcher.specialPrefix, + useFuzzy: { + apps: launcher.useFuzzy.apps, + actions: launcher.useFuzzy.actions, + schemes: launcher.useFuzzy.schemes, + variants: launcher.useFuzzy.variants, + wallpapers: launcher.useFuzzy.wallpapers + }, + sizes: { + itemWidth: launcher.sizes.itemWidth, + itemHeight: launcher.sizes.itemHeight, + wallpaperWidth: launcher.sizes.wallpaperWidth, + wallpaperHeight: launcher.sizes.wallpaperHeight + }, + actions: launcher.actions } } diff --git a/Config/General.qml b/Config/General.qml index 5411a79..21702a8 100644 --- a/Config/General.qml +++ b/Config/General.qml @@ -5,8 +5,16 @@ JsonObject { property string logo: "" property string wallpaperPath: Quickshell.env("HOME") + "/Pictures/Wallpapers" property bool wallust: false + property Apps apps: Apps {} property Idle idle: Idle {} + component Apps: JsonObject { + property list terminal: ["foot"] + property list audio: ["pavucontrol"] + property list playback: ["mpv"] + property list explorer: ["thunar"] + } + component Idle: JsonObject { property list timeouts: [ { diff --git a/Config/Launcher.qml b/Config/Launcher.qml index 2fca110..6ce97f9 100644 --- a/Config/Launcher.qml +++ b/Config/Launcher.qml @@ -3,5 +3,82 @@ import Quickshell.Io JsonObject { property int maxAppsShown: 10 property int maxWallpapers: 7 - property string wallpaperPrefix: ">" + property string actionPrefix: ">" + property string specialPrefix: "@" + property Sizes sizes: Sizes {} + property UseFuzzy useFuzzy: UseFuzzy {} + + component UseFuzzy: JsonObject { + property bool apps: false + property bool actions: false + property bool schemes: false + property bool variants: false + property bool wallpapers: false + } + + component Sizes: JsonObject { + property int itemWidth: 600 + property int itemHeight: 50 + property int wallpaperWidth: 280 + property int wallpaperHeight: 200 + } + + property list actions: [ + { + name: "Calculator", + icon: "calculate", + description: "Do simple math equations (powered by Qalc)", + command: ["autocomplete", "calc"], + enabled: true, + dangerous: false + }, + { + name: "Wallpaper", + icon: "image", + description: "Change the current wallpaper", + command: ["autocomplete", "wallpaper"], + enabled: true, + dangerous: false + }, + { + name: "Shutdown", + icon: "power_settings_new", + description: "Shutdown the system", + command: ["systemctl", "poweroff"], + enabled: true, + dangerous: true + }, + { + name: "Reboot", + icon: "cached", + description: "Reboot the system", + command: ["systemctl", "reboot"], + enabled: true, + dangerous: true + }, + { + name: "Logout", + icon: "exit_to_app", + description: "Log out of the current session", + command: ["loginctl", "terminate-user", ""], + enabled: true, + dangerous: true + }, + { + name: "Lock", + icon: "lock", + description: "Lock the current session", + command: ["loginctl", "lock-session"], + enabled: true, + dangerous: false + }, + { + name: "Sleep", + icon: "bedtime", + description: "Suspend then hibernate", + command: ["systemctl", "suspend-then-hibernate"], + enabled: true, + dangerous: false + }, + ] } diff --git a/Drawers/Backgrounds.qml b/Drawers/Backgrounds.qml index c8dd994..32f6b3a 100644 --- a/Drawers/Backgrounds.qml +++ b/Drawers/Backgrounds.qml @@ -7,6 +7,7 @@ import qs.Modules.Notifications.Sidebar as Sidebar import qs.Modules.Notifications.Sidebar.Utils as Utils import qs.Modules.Dashboard as Dashboard import qs.Modules.Osd as Osd +import qs.Modules.Launcher as Launcher Shape { id: root @@ -45,6 +46,13 @@ Shape { startY: 0 } + Launcher.Background { + wrapper: root.panels.launcher + + startX: ( root.width - wrapper.width ) / 2 - rounding + startY: root.height + } + Dashboard.Background { wrapper: root.panels.dashboard diff --git a/Drawers/Panels.qml b/Drawers/Panels.qml index ffdab3a..1fa90cd 100644 --- a/Drawers/Panels.qml +++ b/Drawers/Panels.qml @@ -8,6 +8,7 @@ import qs.Modules.Notifications.Sidebar.Utils as Utils import qs.Modules.Dashboard as Dashboard import qs.Modules.Osd as Osd import qs.Components.Toast as Toasts +import qs.Modules.Launcher as Launcher import qs.Config Item { @@ -24,6 +25,7 @@ Item { readonly property alias dashboard: dashboard readonly property alias osd: osd readonly property alias toasts: toasts + readonly property alias launcher: launcher anchors.fill: parent // anchors.margins: 8 @@ -75,6 +77,17 @@ Item { anchors.right: parent.right } + Launcher.Wrapper { + id: launcher + + screen: root.screen + visibilities: root.visibilities + panels: root + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + } + Utils.Wrapper { id: utilities diff --git a/Modules/Searcher.qml b/Helpers/Searcher.qml similarity index 100% rename from Modules/Searcher.qml rename to Helpers/Searcher.qml diff --git a/Helpers/Wallpapers.qml b/Helpers/Wallpapers.qml new file mode 100644 index 0000000..1bbca74 --- /dev/null +++ b/Helpers/Wallpapers.qml @@ -0,0 +1,49 @@ +pragma Singleton + +import Quickshell +import Quickshell.Io +import ZShell.Models +import qs.Config +import qs.Modules +import qs.Helpers +import qs.Paths + +Searcher { + id: root + + property bool showPreview: false + readonly property string current: showPreview ? previewPath : actualCurrent + property string previewPath + property string actualCurrent: WallpaperPath.currentWallpaperPath + + function setWallpaper(path: string): void { + actualCurrent = path; + WallpaperPath.currentWallpaperPath = path; + Quickshell.execDetached(["sh", "-c", `python3 ${Quickshell.shellPath("scripts/LockScreenBg.py")} --input_image=${root.actualCurrent} --output_path=${Paths.state}/lockscreen_bg.png`]); + } + + function preview(path: string): void { + previewPath = path; + showPreview = true; + } + + function stopPreview(): void { + showPreview = false; + Quickshell.execDetached(["sh", "-c", `python3 ${Quickshell.shellPath("scripts/SchemeColorGen.py")} --path=${root.actualCurrent} --thumbnail=${Paths.cache}/imagecache/thumbnail.jpg --output=${Paths.state}/scheme.json --scheme=${Config.colors.schemeType}`]); + } + + list: wallpapers.entries + key: "relativePath" + useFuzzy: true + extraOpts: useFuzzy ? ({}) : ({ + forward: false + }) + + FileSystemModel { + id: wallpapers + + recursive: true + path: Config.general.wallpaperPath + filter: FileSystemModel.Images + } +} diff --git a/Modules/AppItem.qml b/Modules/AppItem.qml deleted file mode 100644 index d3b27b2..0000000 --- a/Modules/AppItem.qml +++ /dev/null @@ -1,72 +0,0 @@ -import Quickshell -import Quickshell.Widgets -import QtQuick -import qs - -Item { - id: root - - required property DesktopEntry modelData - - implicitHeight: 48 - - anchors.left: parent?.left - anchors.right: parent?.right - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: event => onClicked(event) - function onClicked(): void { - Search.launch(root.modelData); - } - } - - Item { - anchors.fill: parent - anchors.leftMargin: 8 - anchors.rightMargin: 8 - anchors.margins: 4 - - 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: 8 - anchors.verticalCenter: icon.verticalCenter - - implicitWidth: parent.width - icon.width - implicitHeight: name.implicitHeight + comment.implicitHeight - - Text { - id: name - - text: root.modelData?.name || qsTr("Unknown Application") - font.pointSize: 12 - color: mouseArea.containsMouse ? "#ffffff" : "#cccccc" - elide: Text.ElideRight - } - - Text { - id: comment - - text: ( root.modelData?.comment || root.modelData?.genericName || root.modelData?.name ) ?? "" - font.pointSize: 10 - color: mouseArea.containsMouse ? "#dddddd" : "#888888" - - elide: Text.ElideRight - width: root.width - icon.width - 4 * 2 - - anchors.top: name.bottom - } - } - } -} diff --git a/Modules/GroupListView.qml b/Modules/GroupListView.qml deleted file mode 100644 index 5088a30..0000000 --- a/Modules/GroupListView.qml +++ /dev/null @@ -1,153 +0,0 @@ -import Quickshell -import Quickshell.Widgets -import QtQuick -import QtQuick.Layouts -import qs.Config -import qs.Daemons - -Repeater { - model: ScriptModel { - values: { - const map = new Map(); - for ( const n of NotifServer.notClosed ) - map.set( n.appName, null ); - for ( const n of NotifServer.list ) - map.set( n.appName, null ); - return [ ...map.keys() ]; - } - onValuesChanged: { - root.flagChanged(); - } - } - - Column { - id: groupColumn - required property string modelData - property list notifications: NotifServer.list.filter( n => n.appName === modelData ) - width: parent.width - spacing: 10 - - property bool shouldShow: false - property bool isExpanded: false - property bool collapseAnimRunning: false - property color textColor: DynamicColors.palette.m3onSurface - - function closeAll(): void { - for ( const n of NotifServer.notClosed.filter( n => n.appName === modelData )) - n.close(); - } - - Behavior on height { - Anim {} - } - - Behavior on y { - Anim { - duration: MaterialEasing.expressiveEffectsTime - easing.bezierCurve: MaterialEasing.expressiveEffects - } - } - - add: Transition { - id: addTrans - SequentialAnimation { - PauseAnimation { - duration: ( addTrans.ViewTransition.index - addTrans.ViewTransition.targetIndexes[ 0 ]) * 30 - } - ParallelAnimation { - NumberAnimation { - properties: "y"; - from: addTrans.ViewTransition.destination.y - (height / 2); - to: addTrans.ViewTransition.destination.y; - duration: 100; - easing.type: Easing.OutCubic - } - NumberAnimation { - properties: "opacity"; - from: 0; - to: 1; - duration: 100; - easing.type: Easing.OutCubic - } - NumberAnimation { - properties: "scale"; - from: 0.7; - to: 1.0; - duration: 100 - easing.type: Easing.InOutQuad - } - } - } - } - - Timer { - interval: addTrans.ViewTransition.targetIndexes.length * 30 + 100 - running: groupColumn.isExpanded - repeat: false - onTriggered: { - groupColumn.shouldShow = true; - console.log("ran timer"); - } - } - - move: Transition { - id: moveTrans - NumberAnimation { - properties: "y"; - duration: 100; - easing.type: Easing.OutCubic - } - - NumberAnimation { - properties: "opacity, scale"; - to: 1.0; - } - } - - RowLayout { - width: parent.width - height: 30 - - Text { - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - Layout.leftMargin: 5 - text: groupColumn.modelData - color: groupColumn.textColor - font.pointSize: 14 - font.bold: true - } - - Rectangle { - id: collapseRect - - property color notifyBgColor: DynamicColors.palette.m3primary - property color notifyColor: DynamicColors.palette.m3onPrimary - - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - Layout.fillHeight: true - Layout.preferredWidth: 30 - color: !groupColumn.isExpanded ? collapseRect.notifyBgColor : ( collapseArea.containsMouse ? "#15FFFFFF" : "transparent" ) - radius: groupColumn.isExpanded ? 4 : height / 2 - visible: true - - Text { - anchors.centerIn: parent - text: groupColumn.isExpanded ? "\ue944" : groupColumn.notifications.length - font.family: groupColumn.isExpanded ? "Material Symbols Rounded" : "Rubik" - font.pointSize: 18 - color: groupColumn.isExpanded ? groupColumn.textColor : collapseRect.notifyColor - } - - MouseArea { - id: collapseArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - groupColumn.collapseAnimRunning = true; - } - } - } - } - NotifGroupRepeater { id: groupRepeater } - } -} diff --git a/Modules/Launcher.qml b/Modules/Launcher.qml deleted file mode 100644 index c72b873..0000000 --- a/Modules/Launcher.qml +++ /dev/null @@ -1,538 +0,0 @@ -import Quickshell -import Quickshell.Wayland -import Quickshell.Hyprland -import QtQuick -import QtQuick.Controls -import qs.Components -import qs.Config -import qs.Helpers -import qs.Effects -import qs.Paths - -Scope { - id: root - - PanelWindow { - id: launcherWindow - anchors { - top: true - left: true - right: true - bottom: true - } - color: "transparent" - visible: false - - WlrLayershell.namespace: "ZShell-Launcher" - WlrLayershell.layer: WlrLayer.Overlay - WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive - - onVisibleChanged: { - if ( !visible ) { - searchInput.text = ""; - appListLoader.item.currentIndex = 0; - appListLoader.item.positionViewAtBeginning(); - } - } - - CustomShortcut { - name: "toggle-launcher" - onPressed: { - if ( !launcherWindow.visible ) { - if ( !openAnim.running ) { - openAnim.start(); - } - } else if ( launcherWindow.visible ) { - if ( !closeAnim.running ) { - closeAnim.start(); - } - } - searchInput.forceActiveFocus(); - } - } - - ShadowRect { - id: effects - anchors { - top: appListRect.top - bottom: backgroundRect.bottom - left: appListRect.left - right: appListRect.right - } - radius: 8 - } - - Rectangle { - id: backgroundRect - - property color backgroundColor: DynamicColors.tPalette.m3surface - - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - implicitHeight: mainLayout.childrenRect.height + 20 - implicitWidth: appListRect.implicitWidth - x: Math.round(( parent.width - width ) / 2 ) - color: backgroundColor - opacity: 1 - - ParallelAnimation { - id: openAnim - Anim { - target: appListRect - duration: MaterialEasing.expressiveDefaultSpatialTime - easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial - property: "implicitHeight" - from: 40 - to: appListContainer.implicitHeight + 20 - } - Anim { - target: appListRect - duration: 50 - property: "opacity" - from: 0 - to: 1 - } - Anim { - target: backgroundRect - duration: 50 - property: "opacity" - from: 0 - to: 1 - } - Anim { - target: effects - duration: 50 - property: "opacity" - from: 0 - to: 1 - } - onStarted: { - launcherWindow.visible = true; - } - } - - ParallelAnimation { - id: closeAnim - Anim { - target: appListRect - duration: MaterialEasing.expressiveDefaultSpatialTime - easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial - property: "implicitHeight" - from: appListContainer.implicitHeight - to: 0 - } - SequentialAnimation { - PauseAnimation { duration: 120 } - - ParallelAnimation { - Anim { - target: backgroundRect - duration: 50 - property: "opacity" - from: 1 - to: 0 - } - Anim { - target: appListRect - duration: 50 - property: "opacity" - from: 1 - to: 0 - } - Anim { - target: effects - duration: 50 - property: "opacity" - from: 1 - to: 0 - } - } - } - onStopped: { - launcherWindow.visible = false; - } - } - - Column { - id: mainLayout - anchors.fill: parent - anchors.margins: 10 - spacing: 5 - clip: true - - CustomTextField { - id: searchInput - implicitHeight: 30 - implicitWidth: parent.width - } - } - } - - Rectangle { - id: appListRect - x: Math.round(( parent.width - width ) / 2 ) - implicitWidth: appListContainer.implicitWidth + 20 - implicitHeight: appListContainer.implicitHeight + 20 - anchors.bottom: backgroundRect.top - color: backgroundRect.color - topRightRadius: 8 - topLeftRadius: 8 - clip: true - - Behavior on implicitHeight { - Anim { - duration: MaterialEasing.expressiveFastSpatialTime - easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial - } - } - - Behavior on implicitWidth { - Anim { - duration: MaterialEasing.expressiveFastSpatialTime - easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial - } - } - - Item { - anchors.centerIn: parent - id: appListContainer - visible: true - clip: true - property var showWallpapers: searchInput.text.startsWith(">") - state: showWallpapers ? "wallpaperpicker" : "apps" - states: [ - State { - name: "apps" - PropertyChanges { - appListLoader.active: true - appListContainer.implicitHeight: appListLoader.implicitHeight - appListContainer.implicitWidth: 600 - } - }, - State { - name: "wallpaperpicker" - PropertyChanges { - wallpaperPickerLoader.active: true - appListContainer.implicitHeight: wallpaperPickerLoader.implicitHeight - appListContainer.implicitWidth: wallpaperPickerLoader.implicitWidth - } - } - ] - Loader { - id: wallpaperPickerLoader - active: false - anchors.fill: parent - sourceComponent: PathView { - id: wallpaperPickerView - anchors.fill: parent - model: ScriptModel { - id: wallpaperModel - readonly property string search: searchInput.text.split(" ").slice(1).join(" ") - - values: SearchWallpapers.query( search ) - onValuesChanged: wallpaperPickerView.currentIndex = SearchWallpapers.list.findIndex( w => w.path === WallpaperPath.currentWallpaperPath ) - } - - readonly property int itemWidth: 288 + 10 - readonly property int numItems: { - const screen = QsWindow.window?.screen; - if (!screen) - return 0; - - // Screen width - 4x outer rounding - 2x max side thickness (cause centered) - const margins = 10; - const maxWidth = screen.width - margins * 2; - - if ( maxWidth <= 0 ) - return 0; - - - const maxItemsOnScreen = Math.floor( maxWidth / itemWidth ); - const visible = Math.min( maxItemsOnScreen, Config.launcher.maxWallpapers, wallpaperModel.values.length ); - - if ( visible === 2 ) - return 1; - if ( visible > 1 && visible % 2 === 0 ) - return visible - 1; - return visible; - } - - Component.onCompleted: currentIndex = SearchWallpapers.list.findIndex( w => w.path === WallpaperPath.currentWallpaperPath ) - Component.onDestruction: SearchWallpapers.stopPreview() - - onCurrentItemChanged: { - if ( currentItem ) - SearchWallpapers.preview( currentItem.modelData.path ); - Quickshell.execDetached(["python3", Quickshell.shellPath("scripts/SchemeColorGen.py"), `--path=${currentItem.modelData.path}`, `--thumbnail=${Paths.cache}/imagecache/thumbnail.jpg`, `--output=${Paths.state}/scheme.json`, `--scheme=${Config.colors.schemeType}`]); - } - - cacheItemCount: 5 - snapMode: PathView.SnapToItem - preferredHighlightBegin: 0.5 - preferredHighlightEnd: 0.5 - highlightRangeMode: PathView.StrictlyEnforceRange - - pathItemCount: numItems - implicitHeight: 212 - implicitWidth: Math.min( numItems, count ) * itemWidth - - path: Path { - startY: wallpaperPickerView.height / 2 - - PathAttribute { - name: "z" - value: 0 - } - PathLine { - x: wallpaperPickerView.width / 2 - relativeY: 0 - } - PathAttribute { - name: "z" - value: 1 - } - PathLine { - x: wallpaperPickerView.width - relativeY: 0 - } - } - - focus: true - - delegate: WallpaperItem { } - } - } - Loader { - id: appListLoader - active: false - anchors.fill: parent - sourceComponent: ListView { - id: appListView - - property color highlightColor: DynamicColors.tPalette.m3onSurface - - anchors.fill: parent - model: ScriptModel { - id: appModel - - onValuesChanged: { - appListView.currentIndex = 0; - } - } - - verticalLayoutDirection: ListView.BottomToTop - implicitHeight: Math.min( count, Config.launcher.maxAppsShown ) * 48 - - preferredHighlightBegin: 0 - preferredHighlightEnd: appListView.height - highlightFollowsCurrentItem: false - highlightRangeMode: ListView.ApplyRange - focus: true - highlight: Rectangle { - radius: 4 - color: appListView.highlightColor - opacity: 0.20 - - 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 - } - } - ] - - 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 { - properties: "opacity" - from: 0 - to: 1 - } - - Anim { - properties: "scale" - from: 0.95 - to: 1 - } - } - - remove: Transition { - enabled: !appListView.state - Anim { - properties: "opacity" - from: 1 - to: 0 - } - - 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 - } - } - } - } - } - } - } -} diff --git a/Modules/Launcher/AppList.qml b/Modules/Launcher/AppList.qml new file mode 100644 index 0000000..1c3fdee --- /dev/null +++ b/Modules/Launcher/AppList.qml @@ -0,0 +1,220 @@ +pragma ComponentBehavior: Bound + +import Quickshell +import QtQuick +import qs.Modules.Launcher.Services +import qs.Modules.Launcher.Items +import qs.Components +import qs.Helpers +import qs.Config +import qs.Modules as Modules + +CustomListView { + id: root + + required property CustomTextField search + required property PersistentProperties visibilities + + model: ScriptModel { + id: model + + onValuesChanged: root.currentIndex = 0 + } + + verticalLayoutDirection: ListView.BottomToTop + spacing: Appearance.spacing.small + orientation: Qt.Vertical + implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing + + preferredHighlightBegin: 0 + preferredHighlightEnd: height + highlightRangeMode: ListView.ApplyRange + + highlightFollowsCurrentItem: false + highlight: CustomRect { + radius: 8 + color: DynamicColors.palette.m3onSurface + opacity: 0.08 + + y: root.currentItem?.y ?? 0 + implicitWidth: root.width + implicitHeight: root.currentItem?.implicitHeight ?? 0 + + Behavior on y { + Modules.Anim { + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + } + } + + state: { + const text = search.text; + const prefix = Config.launcher.actionPrefix; + if (text.startsWith(prefix)) { + for (const action of ["calc", "scheme", "variant"]) + if (text.startsWith(`${prefix}${action} `)) + return action; + + return "actions"; + } + + return "apps"; + } + + states: [ + State { + name: "apps" + + PropertyChanges { + model.values: Apps.search(search.text) + root.delegate: appItem + } + }, + State { + name: "actions" + + PropertyChanges { + model.values: Actions.query(search.text) + root.delegate: actionItem + } + }, + State { + name: "calc" + + PropertyChanges { + model.values: [0] + root.delegate: calcItem + } + }, + ] + + transitions: Transition { + SequentialAnimation { + ParallelAnimation { + Modules.Anim { + target: root + property: "opacity" + from: 1 + to: 0 + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + Modules.Anim { + target: root + property: "scale" + from: 1 + to: 0.9 + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + } + PropertyAction { + targets: [model, root] + properties: "values,delegate" + } + ParallelAnimation { + Modules.Anim { + target: root + property: "opacity" + from: 0 + to: 1 + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + Modules.Anim { + target: root + property: "scale" + from: 0.9 + to: 1 + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + } + PropertyAction { + targets: [root.add, root.remove] + property: "enabled" + value: true + } + } + } + + CustomScrollBar.vertical: CustomScrollBar { + flickable: root + } + + add: Transition { + enabled: !root.state + + Modules.Anim { + properties: "opacity,scale" + from: 0 + to: 1 + } + } + + remove: Transition { + enabled: !root.state + + Modules.Anim { + properties: "opacity,scale" + from: 1 + to: 0 + } + } + + move: Transition { + Modules.Anim { + property: "y" + } + Modules.Anim { + properties: "opacity,scale" + to: 1 + } + } + + addDisplaced: Transition { + Modules.Anim { + property: "y" + duration: Appearance.anim.durations.small + } + Modules.Anim { + properties: "opacity,scale" + to: 1 + } + } + + displaced: Transition { + Modules.Anim { + property: "y" + } + Modules.Anim { + properties: "opacity,scale" + to: 1 + } + } + + Component { + id: appItem + + AppItem { + visibilities: root.visibilities + } + } + + Component { + id: actionItem + + ActionItem { + list: root + } + } + + Component { + id: calcItem + + CalcItem { + list: root + } + } +} diff --git a/Modules/Launcher/Background.qml b/Modules/Launcher/Background.qml new file mode 100644 index 0000000..e5295ba --- /dev/null +++ b/Modules/Launcher/Background.qml @@ -0,0 +1,61 @@ +import QtQuick +import QtQuick.Shapes +import qs.Components +import qs.Helpers +import qs.Config +import qs.Modules as Modules + +ShapePath { + id: root + + required property Wrapper wrapper + readonly property real rounding: Config.barConfig.rounding + readonly property bool flatten: wrapper.height < rounding * 2 + readonly property real roundingY: flatten ? wrapper.height / 2 : rounding + + strokeWidth: -1 + fillColor: DynamicColors.palette.m3surface + + 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 { + Modules.CAnim {} + } +} diff --git a/Modules/Launcher/Content.qml b/Modules/Launcher/Content.qml new file mode 100644 index 0000000..252f15a --- /dev/null +++ b/Modules/Launcher/Content.qml @@ -0,0 +1,191 @@ +pragma ComponentBehavior: Bound + +import Quickshell +import QtQuick +import qs.Modules.Launcher.Services +import qs.Components +import qs.Helpers +import qs.Config +import qs.Modules as Modules + +Item { + id: root + + required property PersistentProperties visibilities + required property var panels + required property real maxHeight + + readonly property int padding: Appearance.padding.small + readonly property int rounding: Appearance.rounding.large + + implicitWidth: listWrapper.width + padding * 2 + implicitHeight: searchWrapper.height + listWrapper.height + padding * 2 + + 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 + + content: root + visibilities: root.visibilities + panels: root.panels + maxHeight: root.maxHeight - searchWrapper.implicitHeight - root.padding * 3 + search: search + padding: root.padding + rounding: root.rounding + } + } + + CustomRect { + id: searchWrapper + + color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2) + radius: 8 + + 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 + 10 + + text: "search" + color: DynamicColors.palette.m3onSurfaceVariant + } + + CustomTextField { + 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(Config.launcher.actionPrefix) + + onAccepted: { + const currentItem = list.currentList?.currentItem; + if (currentItem) { + if (list.showWallpapers) { + if (DynamicColors.scheme === "dynamic" && currentItem.modelData.path !== Wallpapers.actualCurrent) + Wallpapers.previewColourLock = true; + Wallpapers.setWallpaper(currentItem.modelData.path); + root.visibilities.launcher = false; + } else if (text.startsWith(Config.launcher.actionPrefix)) { + if (text.startsWith(`${Config.launcher.actionPrefix}calc `)) + currentItem.onClicked(); + else + currentItem.modelData.onClicked(list.currentList); + } else { + Apps.launch(currentItem.modelData); + root.visibilities.launcher = false; + } + } + } + + Keys.onUpPressed: list.currentList?.incrementCurrentIndex() + Keys.onDownPressed: list.currentList?.decrementCurrentIndex() + + Keys.onEscapePressed: root.visibilities.launcher = false + + Keys.onPressed: event => { + if (!Config.launcher.vimKeybinds) + return; + + if (event.modifiers & Qt.ControlModifier) { + if (event.key === Qt.Key_J) { + list.currentList?.incrementCurrentIndex(); + event.accepted = true; + } else if (event.key === Qt.Key_K) { + list.currentList?.decrementCurrentIndex(); + event.accepted = true; + } + } else if (event.key === Qt.Key_Tab) { + list.currentList?.incrementCurrentIndex(); + event.accepted = true; + } else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) { + list.currentList?.decrementCurrentIndex(); + event.accepted = true; + } + } + + Component.onCompleted: forceActiveFocus() + + Connections { + target: root.visibilities + + function onLauncherChanged(): void { + if (!root.visibilities.launcher) + search.text = ""; + } + + function onSessionChanged(): void { + if (!root.visibilities.session) + search.forceActiveFocus(); + } + } + } + + MaterialIcon { + id: clearIcon + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: root.padding + 10 + + width: search.text ? implicitWidth : implicitWidth / 2 + opacity: { + if (!search.text) + return 0; + if (mouse.pressed) + return 0.7; + if (mouse.containsMouse) + return 0.8; + return 1; + } + + text: "close" + color: DynamicColors.palette.m3onSurfaceVariant + + MouseArea { + id: mouse + + anchors.fill: parent + hoverEnabled: true + cursorShape: search.text ? Qt.PointingHandCursor : undefined + + onClicked: search.text = "" + } + + Behavior on width { + Modules.Anim { + duration: Appearance.anim.durations.small + } + } + + Behavior on opacity { + Modules.Anim { + duration: Appearance.anim.durations.small + } + } + } + } +} diff --git a/Modules/Launcher/ContentList.qml b/Modules/Launcher/ContentList.qml new file mode 100644 index 0000000..fadd172 --- /dev/null +++ b/Modules/Launcher/ContentList.qml @@ -0,0 +1,170 @@ +pragma ComponentBehavior: Bound + +import Quickshell +import QtQuick +import qs.Paths +import qs.Modules +import qs.Components +import qs.Helpers +import qs.Config + +Item { + id: root + + required property var content + required property PersistentProperties visibilities + required property var panels + required property real maxHeight + required property CustomTextField search + required property int padding + required property int rounding + + readonly property bool showWallpapers: search.text.startsWith(`${Config.launcher.actionPrefix}wallpaper `) + readonly property Item currentList: showWallpapers ? wallpaperList.item : appList.item + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + + clip: true + state: showWallpapers ? "wallpapers" : "apps" + + states: [ + State { + name: "apps" + + PropertyChanges { + root.implicitWidth: Config.launcher.sizes.itemWidth + root.implicitHeight: Math.min(root.maxHeight, appList.implicitHeight > 0 ? appList.implicitHeight : empty.implicitHeight) + appList.active: true + } + + AnchorChanges { + anchors.left: root.parent.left + anchors.right: root.parent.right + } + }, + State { + name: "wallpapers" + + PropertyChanges { + root.implicitWidth: Math.max(Config.launcher.sizes.itemWidth * 1.2, wallpaperList.implicitWidth) + root.implicitHeight: Config.launcher.sizes.wallpaperHeight + wallpaperList.active: true + } + } + ] + + Behavior on state { + SequentialAnimation { + Anim { + target: root + property: "opacity" + from: 1 + to: 0 + duration: Appearance.anim.durations.small + } + PropertyAction {} + Anim { + target: root + property: "opacity" + from: 0 + to: 1 + duration: Appearance.anim.durations.small + } + } + } + + Loader { + id: appList + + active: false + + anchors.fill: parent + + sourceComponent: AppList { + search: root.search + visibilities: root.visibilities + } + } + + Loader { + id: wallpaperList + + active: false + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + + sourceComponent: WallpaperList { + search: root.search + visibilities: root.visibilities + panels: root.panels + content: root.content + } + } + + Row { + id: empty + + opacity: root.currentList?.count === 0 ? 1 : 0 + scale: root.currentList?.count === 0 ? 1 : 0.5 + + spacing: Appearance.spacing.normal + padding: Appearance.padding.large + + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + MaterialIcon { + text: root.state === "wallpapers" ? "wallpaper_slideshow" : "manage_search" + color: DynamicColors.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.extraLarge + + anchors.verticalCenter: parent.verticalCenter + } + + Column { + anchors.verticalCenter: parent.verticalCenter + + CustomText { + text: root.state === "wallpapers" ? qsTr("No wallpapers found") : qsTr("No results") + color: DynamicColors.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + CustomText { + text: root.state === "wallpapers" && Wallpapers.list.length === 0 ? qsTr("Try putting some wallpapers in %1").arg(Paths.shortenHome(Paths.wallsdir)) : qsTr("Try searching for something else") + color: DynamicColors.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.normal + } + } + + Behavior on opacity { + Anim {} + } + + Behavior on scale { + Anim {} + } + } + + Behavior on implicitWidth { + enabled: root.visibilities.launcher + + Anim { + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + } + + Behavior on implicitHeight { + enabled: root.visibilities.launcher + + Anim { + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + } +} diff --git a/Modules/Launcher/Items/ActionItem.qml b/Modules/Launcher/Items/ActionItem.qml new file mode 100644 index 0000000..ea869ce --- /dev/null +++ b/Modules/Launcher/Items/ActionItem.qml @@ -0,0 +1,70 @@ +import QtQuick +import qs.Modules.Launcher.Services +import qs.Components +import qs.Helpers +import qs.Config + +Item { + id: root + + required property var modelData + required property var list + + implicitHeight: Config.launcher.sizes.itemHeight + + anchors.left: parent?.left + anchors.right: parent?.right + + StateLayer { + radius: Appearance.rounding.normal + + 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.normal + anchors.verticalCenter: icon.verticalCenter + + implicitWidth: parent.width - icon.width + implicitHeight: name.implicitHeight + desc.implicitHeight + + CustomText { + id: name + + text: root.modelData?.name ?? "" + font.pointSize: Appearance.font.size.normal + } + + CustomText { + id: desc + + text: root.modelData?.desc ?? "" + font.pointSize: Appearance.font.size.small + color: DynamicColors.palette.m3outline + + elide: Text.ElideRight + width: root.width - icon.width - Appearance.rounding.normal * 2 + + anchors.top: name.bottom + } + } + } +} diff --git a/Modules/Launcher/Items/AppItem.qml b/Modules/Launcher/Items/AppItem.qml new file mode 100644 index 0000000..5e4e8f2 --- /dev/null +++ b/Modules/Launcher/Items/AppItem.qml @@ -0,0 +1,74 @@ +import Quickshell +import Quickshell.Widgets +import QtQuick +import qs.Modules.Launcher.Services +import qs.Components +import qs.Helpers +import qs.Config +import qs.Modules + +Item { + id: root + + required property DesktopEntry modelData + required property PersistentProperties visibilities + + implicitHeight: Config.launcher.sizes.itemHeight + + anchors.left: parent?.left + anchors.right: parent?.right + + StateLayer { + radius: Appearance.rounding.normal + + 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 + + CustomText { + id: name + + text: root.modelData?.name ?? "" + font.pointSize: Appearance.font.size.normal + } + + CustomText { + id: comment + + text: (root.modelData?.comment || root.modelData?.genericName || root.modelData?.name) ?? "" + font.pointSize: Appearance.font.size.small + color: DynamicColors.palette.m3outline + + elide: Text.ElideRight + width: root.width - icon.width - Appearance.rounding.normal * 2 + + anchors.top: name.bottom + } + } + } +} diff --git a/Modules/Launcher/Items/CalcItem.qml b/Modules/Launcher/Items/CalcItem.qml new file mode 100644 index 0000000..56e937b --- /dev/null +++ b/Modules/Launcher/Items/CalcItem.qml @@ -0,0 +1,124 @@ +import ZShell +import Quickshell +import QtQuick +import QtQuick.Layouts +import qs.Components +import qs.Modules +import qs.Helpers +import qs.Config + +Item { + id: root + + required property var list + readonly property string math: list.search.text.slice(`${Config.launcher.actionPrefix}calc `.length) + + function onClicked(): void { + Quickshell.execDetached(["wl-copy", Qalculator.eval(math, false)]); + root.list.visibilities.launcher = false; + } + + implicitHeight: Config.launcher.sizes.itemHeight + + anchors.left: parent?.left + anchors.right: parent?.right + + StateLayer { + radius: Appearance.rounding.normal + + function onClicked(): void { + root.onClicked(); + } + } + + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.larger + + spacing: Appearance.spacing.normal + + MaterialIcon { + text: "function" + font.pointSize: Appearance.font.size.extraLarge + Layout.alignment: Qt.AlignVCenter + } + + CustomText { + id: result + + color: { + if (text.includes("error: ") || text.includes("warning: ")) + return DynamicColors.palette.m3error; + if (!root.math) + return DynamicColors.palette.m3onSurfaceVariant; + return DynamicColors.palette.m3onSurface; + } + + text: root.math.length > 0 ? Qalculator.eval(root.math) : qsTr("Type an expression to calculate") + elide: Text.ElideLeft + + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + } + + CustomRect { + color: DynamicColors.palette.m3tertiary + radius: Appearance.rounding.normal + clip: true + + implicitWidth: (stateLayer.containsMouse ? label.implicitWidth + label.anchors.rightMargin : 0) + icon.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: Math.max(label.implicitHeight, icon.implicitHeight) + Appearance.padding.small * 2 + + Layout.alignment: Qt.AlignVCenter + + StateLayer { + id: stateLayer + + color: DynamicColors.palette.m3onTertiary + + function onClicked(): void { + Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.terminal, "fish", "-C", `exec qalc -i '${root.math}'`]); + root.list.visibilities.launcher = false; + } + } + + CustomText { + id: label + + anchors.verticalCenter: parent.verticalCenter + anchors.right: icon.left + anchors.rightMargin: Appearance.spacing.small + + text: qsTr("Open in calculator") + color: DynamicColors.palette.m3onTertiary + font.pointSize: Appearance.font.size.normal + + opacity: stateLayer.containsMouse ? 1 : 0 + + Behavior on opacity { + Anim {} + } + } + + MaterialIcon { + id: icon + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: Appearance.padding.normal + + text: "open_in_new" + color: DynamicColors.palette.m3onTertiary + font.pointSize: Appearance.font.size.large + } + + Behavior on implicitWidth { + Anim { + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + } + } + } +} diff --git a/Modules/Launcher/Items/WallpaperItem.qml b/Modules/Launcher/Items/WallpaperItem.qml new file mode 100644 index 0000000..f309472 --- /dev/null +++ b/Modules/Launcher/Items/WallpaperItem.qml @@ -0,0 +1,97 @@ +import ZShell.Models +import Quickshell +import QtQuick +import qs.Components +import qs.Helpers +import qs.Config +import qs.Modules + +Item { + id: root + + required property FileSystemEntry 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; + } + } + + Elevation { + anchors.fill: image + radius: image.radius + opacity: root.PathView.isCurrentItem ? 1 : 0 + level: 4 + + Behavior on opacity { + Anim {} + } + } + + CustomClippingRect { + id: image + + anchors.horizontalCenter: parent.horizontalCenter + y: Appearance.padding.large + color: DynamicColors.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + + implicitWidth: Config.launcher.sizes.wallpaperWidth + implicitHeight: implicitWidth / 16 * 9 + + MaterialIcon { + anchors.centerIn: parent + text: "image" + color: DynamicColors.tPalette.m3outline + font.pointSize: Appearance.font.size.extraLarge * 2 + font.weight: 600 + } + + CachingImage { + path: root.modelData.path + smooth: !root.PathView.view.moving + cache: true + + anchors.fill: parent + } + } + + CustomText { + 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.relativePath + font.pointSize: Appearance.font.size.normal + } + + Behavior on scale { + Anim {} + } + + Behavior on opacity { + Anim {} + } +} diff --git a/Modules/Launcher/Services/Actions.qml b/Modules/Launcher/Services/Actions.qml new file mode 100644 index 0000000..f428656 --- /dev/null +++ b/Modules/Launcher/Services/Actions.qml @@ -0,0 +1,51 @@ +pragma Singleton + +import qs.Modules.Launcher +import qs.Helpers +import qs.Config +import Quickshell +import QtQuick + +Searcher { + id: root + + function transformSearch(search: string): string { + return search.slice(Config.launcher.actionPrefix.length); + } + + list: variants.instances + useFuzzy: Config.launcher.useFuzzy.actions + + Variants { + id: variants + + model: Config.launcher.actions.filter(a => (a.enabled ?? true)) + + Action {} + } + + component Action: QtObject { + required property var modelData + readonly property string name: modelData.name ?? qsTr("Unnamed") + readonly property string desc: modelData.description ?? qsTr("No description") + readonly property string icon: modelData.icon ?? "help_outline" + readonly property list command: modelData.command ?? [] + readonly property bool enabled: modelData.enabled ?? true + readonly property bool dangerous: modelData.dangerous ?? false + + function onClicked(list: AppList): void { + if (command.length === 0) + return; + + if (command[0] === "autocomplete" && command.length > 1) { + list.search.text = `${Config.launcher.actionPrefix}${command[1]} `; + } else if (command[0] === "setMode" && command.length > 1) { + list.visibilities.launcher = false; + Colours.setMode(command[1]); + } else { + list.visibilities.launcher = false; + Quickshell.execDetached(command); + } + } + } +} diff --git a/Modules/Search.qml b/Modules/Launcher/Services/Apps.qml similarity index 72% rename from Modules/Search.qml rename to Modules/Launcher/Services/Apps.qml index 060dca5..27d54a1 100644 --- a/Modules/Search.qml +++ b/Modules/Launcher/Services/Apps.qml @@ -2,26 +2,31 @@ pragma Singleton import ZShell import Quickshell -import Quickshell.Io +import qs.Config +import qs.Helpers +import qs.Paths Searcher { id: root - readonly property string home: Quickshell.env("HOME") - function launch(entry: DesktopEntry): void { appDb.incrementFrequency(entry.id); - console.log( "Search command:", entry.command ); - - Quickshell.execDetached({ - command: ["app2unit", "--", ...entry.command], - workingDirectory: entry.workingDirectory || Quickshell.env("HOME") - }); + if (entry.runInTerminal) + Quickshell.execDetached({ + command: ["app2unit", "--", ...Config.general.apps.terminal, `${Quickshell.shellDir}/assets/wrap_term_launch.sh`, ...entry.command], + workingDirectory: entry.workingDirectory + }); + else + Quickshell.execDetached({ + command: ["app2unit", "--", ...entry.command], + workingDirectory: entry.workingDirectory + }); } function search(search: string): list { - const prefix = ">"; + const prefix = Config.launcher.specialPrefix; + if (search.startsWith(`${prefix}i `)) { keys = ["id", "name"]; weights = [0.9, 0.1]; @@ -62,12 +67,12 @@ Searcher { } list: appDb.apps - useFuzzy: true + useFuzzy: Config.launcher.useFuzzy.apps AppDb { id: appDb - path: `${root.home}/.local/share/z-cast-qt/apps.sqlite` + path: `${Paths.state}/apps.sqlite` entries: DesktopEntries.applications.values } } diff --git a/Modules/Launcher/WallpaperList.qml b/Modules/Launcher/WallpaperList.qml new file mode 100644 index 0000000..a898190 --- /dev/null +++ b/Modules/Launcher/WallpaperList.qml @@ -0,0 +1,97 @@ +pragma ComponentBehavior: Bound + +import Quickshell +import QtQuick +import qs.Components +import qs.Helpers +import qs.Config +import qs.Modules.Launcher.Items + +PathView { + id: root + + required property CustomTextField search + required property var visibilities + required property var panels + required property var content + + readonly property int itemWidth: Config.launcher.sizes.wallpaperWidth * 0.8 + Appearance.padding.larger * 2 + + readonly property int numItems: { + const screen = QsWindow.window?.screen; + if (!screen) + return 0; + + // Screen width - 4x outer rounding - 2x max side thickness (cause centered) + const barMargins = panels.bar.implicitWidth; + let outerMargins = 0; + if (panels.popouts.hasCurrent && panels.popouts.currentCenter + panels.popouts.nonAnimHeight / 2 > screen.height - content.implicitHeight) + outerMargins = panels.popouts.nonAnimWidth; + if ((visibilities.utilities || visibilities.sidebar) && panels.utilities.implicitWidth > outerMargins) + outerMargins = panels.utilities.implicitWidth; + const maxWidth = screen.width - Config.barConfig.rounding * 4 - (barMargins + outerMargins) * 2; + + if (maxWidth <= 0) + return 0; + + const maxItemsOnScreen = Math.floor(maxWidth / itemWidth); + const visible = Math.min(maxItemsOnScreen, Config.launcher.maxWallpapers, scriptModel.values.length); + + if (visible === 2) + return 1; + if (visible > 1 && visible % 2 === 0) + return visible - 1; + return visible; + } + + model: ScriptModel { + id: scriptModel + + readonly property string search: root.search.text.split(" ").slice(1).join(" ") + + values: Wallpapers.query(search) + 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) * itemWidth + 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 + } + } +} diff --git a/Modules/Launcher/Wrapper.qml b/Modules/Launcher/Wrapper.qml new file mode 100644 index 0000000..cba412a --- /dev/null +++ b/Modules/Launcher/Wrapper.qml @@ -0,0 +1,131 @@ +pragma ComponentBehavior: Bound + +import Quickshell +import QtQuick +import qs.Components +import qs.Config +import qs.Modules as Modules + +Item { + id: root + + required property ShellScreen screen + required property PersistentProperties visibilities + required property var panels + + readonly property bool shouldBeActive: visibilities.launcher + property int contentHeight + + readonly property real maxHeight: { + let max = screen.height - Appearance.spacing.large; + if (visibilities.dashboard) + max -= panels.dashboard.nonAnimHeight; + return max; + } + + onMaxHeightChanged: timer.start() + + visible: height > 0 + implicitHeight: 0 + implicitWidth: content.implicitWidth + + onShouldBeActiveChanged: { + if (shouldBeActive) { + timer.stop(); + hideAnim.stop(); + showAnim.start(); + } else { + showAnim.stop(); + hideAnim.start(); + } + } + + SequentialAnimation { + id: showAnim + + Modules.Anim { + target: root + property: "implicitHeight" + to: root.contentHeight + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + ScriptAction { + script: root.implicitHeight = Qt.binding(() => content.implicitHeight) + } + } + + SequentialAnimation { + id: hideAnim + + ScriptAction { + script: root.implicitHeight = root.implicitHeight + } + Modules.Anim { + target: root + property: "implicitHeight" + to: 0 + easing.bezierCurve: Appearance.anim.curves.expressiveEffects + } + } + + Connections { + target: Config.launcher + + function onEnabledChanged(): void { + timer.start(); + } + + function onMaxShownChanged(): void { + timer.start(); + } + } + + Connections { + target: DesktopEntries.applications + + function onValuesChanged(): void { + if (DesktopEntries.applications.values.length < Config.launcher.maxAppsShown) + timer.start(); + } + } + + Timer { + id: timer + + interval: Appearance.anim.durations.small + onRunningChanged: { + if (running && !root.shouldBeActive) { + content.visible = false; + content.active = true; + } else { + root.contentHeight = Math.min(root.maxHeight, content.implicitHeight); + content.active = Qt.binding(() => root.shouldBeActive || root.visible); + content.visible = true; + if (showAnim.running) { + showAnim.stop(); + showAnim.start(); + } + } + } + } + + Loader { + id: content + + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + + visible: false + active: false + Component.onCompleted: timer.start() + + sourceComponent: Content { + visibilities: root.visibilities + panels: root.panels + maxHeight: root.maxHeight + + Component.onCompleted: root.contentHeight = implicitHeight + } + } +} diff --git a/Modules/Shortcuts.qml b/Modules/Shortcuts.qml new file mode 100644 index 0000000..7d9eac7 --- /dev/null +++ b/Modules/Shortcuts.qml @@ -0,0 +1,25 @@ +import ZShell +import Quickshell +import Quickshell.Io +import qs.Components +import qs.Helpers + +Scope { + id: root + + property bool launcherInterrupted + readonly property bool hasFullscreen: Hypr.focusedWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2) ?? false + + CustomShortcut { + name: "toggle-launcher" + description: "Toggle launcher" + onPressed: root.launcherInterrupted = false + onReleased: { + if (!root.launcherInterrupted && !root.hasFullscreen) { + const visibilities = Visibilities.getForActive(); + visibilities.launcher = !visibilities.launcher; + } + root.launcherInterrupted = false; + } + } +} diff --git a/Modules/TrackedNotification.qml b/Modules/TrackedNotification.qml deleted file mode 100644 index 6bf095c..0000000 --- a/Modules/TrackedNotification.qml +++ /dev/null @@ -1,248 +0,0 @@ -import Quickshell -import Quickshell.Widgets -import Quickshell.Wayland -import Quickshell.Hyprland -import QtQuick.Layouts -import QtQuick -import qs.Components -import qs.Config -import qs.Daemons -import qs.Helpers -import qs.Effects - -PanelWindow { - id: root - color: "transparent" - - anchors { - top: true - right: true - left: true - bottom: true - } - - WlrLayershell.namespace: "ZShell-Notifs" - WlrLayershell.layer: WlrLayer.Overlay - - mask: Region { regions: root.notifRegions } - exclusionMode: ExclusionMode.Ignore - property list notifRegions: [] - required property bool centerShown - property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3onSurface : "white" - property color backgroundColor: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : Config.baseBgColor - // visible: Hyprland.monitorFor(screen).focused - - ListView { - id: notifListView - model: ScriptModel { - values: NotifServer.list.filter( n => n.popup ) - onValuesChanged: { - if ( values.length === 0 ) - root.notifRegions = []; - } - } - anchors.top: parent.top - anchors.bottom: parent.bottom - x: root.centerShown ? root.screen.width - width - 420 : root.screen.width - width - 20 - z: 0 - anchors.topMargin: 54 - width: 400 - spacing: 10 - - Behavior on x { - NumberAnimation { - duration: MaterialEasing.expressiveEffectsTime - easing.bezierCurve: MaterialEasing.expressiveEffects - } - } - - displaced: Transition { - NumberAnimation { - property: "y" - duration: 100 - easing.type: Easing.InOutQuad - } - } - - remove: Transition { - id: hideTransition - ParallelAnimation { - NumberAnimation { - property: "opacity" - from: 1 - to: 0 - duration: 200 - easing.type: Easing.InOutQuad - } - - NumberAnimation { - property: "x" - to: hideTransition.ViewTransition.destination.x + 200 - duration: 200 - easing.type: Easing.InOutQuad - } - } - } - - add: Transition { - id: showTransition - ParallelAnimation { - NumberAnimation { - property: "opacity" - from: 0 - to: 1 - duration: 200 - easing.type: Easing.InOutQuad - } - - NumberAnimation { - property: "x" - from: showTransition.ViewTransition.destination.x + 200 - to: showTransition.ViewTransition.destination.x - duration: 200 - easing.type: Easing.InOutQuad - } - } - } - - component NotifRegion: Region { } - - Component { - id: notifRegion - NotifRegion {} - } - - delegate: Item { - id: rootItem - implicitWidth: 400 - implicitHeight: contentLayout.childrenRect.height + 16 - required property NotifServer.Notif modelData - - ShadowRect { - anchors.fill: backgroundRect - radius: backgroundRect.radius - } - - CustomClippingRect { - id: backgroundRect - implicitWidth: 400 - implicitHeight: contentLayout.childrenRect.height + 16 - color: root.backgroundColor - border.width: Config.useDynamicColors ? 0 : 1 - border.color: "#555555" - radius: 8 - - CustomRect { - anchors.bottom: parent.bottom - anchors.right: parent.right - color: DynamicColors.palette.m3primary - - implicitHeight: 4 - implicitWidth: ( rootItem.modelData.timer.remainingTime / rootItem.modelData.timer.totalTime ) * parent.width - - Behavior on implicitWidth { - Anim {} - } - } - - Component.onCompleted: { - root.notifRegions.push( notifRegion.createObject(root, { item: backgroundRect })); - } - - Column { - id: contentLayout - z: 0 - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: 10 - spacing: 8 - RowLayout { - spacing: 12 - IconImage { - source: rootItem.modelData.image === "" ? Qt.resolvedUrl(rootItem.modelData.appIcon) : Qt.resolvedUrl(rootItem.modelData.image) - Layout.preferredWidth: 48 - Layout.preferredHeight: 48 - Layout.alignment: Qt.AlignHCenter | Qt.AlignLeft - // visible: rootItem.modelData.image !== "" - } - - ColumnLayout { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.leftMargin: 0 - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - - Text { - text: rootItem.modelData.appName - color: root.textColor - font.bold: true - font.pointSize: 14 - elide: Text.ElideRight - wrapMode: Text.NoWrap - Layout.fillWidth: true - } - - Text { - text: rootItem.modelData.summary - color: root.textColor - font.pointSize: 12 - font.bold: true - elide: Text.ElideRight - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - } - Text { - text: rootItem.modelData.body - color: root.textColor - font.pointSize: 14 - textFormat: Text.MarkdownText - elide: Text.ElideRight - wrapMode: Text.WordWrap - maximumLineCount: 4 - width: parent.width - linkColor: Config.accentColor.accents.primaryAlt - - onLinkActivated: link => { - Quickshell.execDetached(["app2unit", "-O", "--", link]); - } - } - } - - Rectangle { - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 6 - anchors.topMargin: 6 - width: 18 - height: 18 - color: closeArea.containsMouse ? "#FF6077" : "transparent" - radius: 9 - - Text { - anchors.centerIn: parent - text: "✕" - color: closeArea.containsMouse ? "white" : "#888888" - font.pointSize: 12 - } - - MouseArea { - id: closeArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - rootItem.modelData.close(); - } - } - } - - ElapsedTimer { - id: timer - } - } - } - } -} diff --git a/Modules/WallBackground.qml b/Modules/WallBackground.qml index ab5041e..a6aacfb 100644 --- a/Modules/WallBackground.qml +++ b/Modules/WallBackground.qml @@ -7,7 +7,7 @@ import qs.Config Item { id: root - property string source: SearchWallpapers.current + property string source: Wallpapers.current property Image current: one anchors.fill: parent @@ -50,7 +50,7 @@ Item { anchors.fill: parent opacity: 0 - scale: SearchWallpapers.showPreview ? 1 : 0.8 + scale: Wallpapers.showPreview ? 1 : 0.8 asynchronous: true onStatusChanged: { if (status === Image.Ready) { diff --git a/Modules/WallpaperItem.qml b/Modules/WallpaperItem.qml deleted file mode 100644 index 719ab71..0000000 --- a/Modules/WallpaperItem.qml +++ /dev/null @@ -1,44 +0,0 @@ -import Quickshell -import Quickshell.Widgets -import QtQuick -import ZShell.Models - -Item { - id: root - required property FileSystemEntry modelData - implicitWidth: 288 - implicitHeight: 162 - - 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); - } - - ClippingRectangle { - anchors.fill: parent - radius: 8 - color: "#10FFFFFF" - Image { - id: thumbnailImage - - asynchronous: true - anchors.fill: parent - fillMode: Image.PreserveAspectCrop - source: root.modelData.path - sourceSize.width: 960 - sourceSize.height: 540 - } - } - - Behavior on scale { - Anim {} - } - - Behavior on opacity { - Anim {} - } -} diff --git a/Modules/WindowTitleOld.qml b/Modules/WindowTitleOld.qml deleted file mode 100644 index 4554fe1..0000000 --- a/Modules/WindowTitleOld.qml +++ /dev/null @@ -1,68 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import Quickshell.Hyprland -import qs.Helpers -import qs.Config -import qs.Components - -Item { - id: root - property string currentTitle: Hypr.activeName - Layout.fillHeight: true - Layout.preferredWidth: Math.max( titleText1.implicitWidth, titleText2.implicitWidth ) + 10 - clip: true - - property bool showFirst: true - property color textColor: Config.useDynamicColors ? DynamicColors.palette.m3primary : "white" - - // Component.onCompleted: { - // Hyprland.rawEvent.connect(( event ) => { - // if (event.name === "activewindow") { - // InitialTitle.getInitialTitle( function( initialTitle ) { - // root.currentTitle = initialTitle - // }) - // } - // }) - // } - - onCurrentTitleChanged: { - if (showFirst) { - titleText2.text = currentTitle - showFirst = false - } else { - titleText1.text = currentTitle - showFirst = true - } - } - - CustomText { - id: titleText1 - anchors.fill: parent - anchors.margins: 5 - text: root.currentTitle - color: root.textColor - elide: Text.ElideRight - font.pixelSize: 16 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - opacity: root.showFirst ? 1 : 0 - Behavior on opacity { - NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } - } - } - - CustomText { - id: titleText2 - anchors.fill: parent - anchors.margins: 5 - color: root.textColor - elide: Text.ElideRight - font.pixelSize: 16 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - opacity: root.showFirst ? 0 : 1 - Behavior on opacity { - NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } - } - } -} diff --git a/shell.qml b/shell.qml index c6d35b7..f65c607 100644 --- a/shell.qml +++ b/shell.qml @@ -10,19 +10,15 @@ import qs.Modules.Polkit ShellRoot { Bar {} Wallpaper {} - Launcher {} AreaPicker {} Lock.Lock { id: lock } + Shortcuts {} Lock.IdleInhibitor { lock: lock } - // NotificationCenter { - // - // } - Polkit {} }