diff --git a/Panes/Launcher.qml b/Panes/Launcher.qml index 1f5e42d..5cbf6e6 100644 --- a/Panes/Launcher.qml +++ b/Panes/Launcher.qml @@ -52,7 +52,7 @@ Item { onCursorPositionChanged: cursorPosition = text.length Keys.onPressed: (event) => { switch (event.key) { case Qt.Key_Escape: - root.close(); + background.index = 0; break; case Qt.Key_Return: list.currentItem.execute(); diff --git a/Panes/NotificationCenter.qml b/Panes/NotificationCenter.qml new file mode 100644 index 0000000..c3b45e0 --- /dev/null +++ b/Panes/NotificationCenter.qml @@ -0,0 +1,153 @@ +import QtQuick +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.Mpris +import Quickshell.Services.Notifications +import "../Widgets" as Widgets + +Item { + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + } + width: childrenRect.width + 12 + height: childrenRect.height + 12 + Row { + spacing: 6 + x: 6 + y: 6 + Rectangle { + width: 400 + height: 300 + color: "#181818" + radius: 10 + Column { + anchors.centerIn: parent + spacing: 2 + opacity: !notifServer.trackedNotifications.values.length + Behavior on opacity {NumberAnimation {duration: 150}} + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: Quickshell.shellPath("assets/notif.svg") + width: 24 + height: 24 + sourceSize {width: width; height: height} + opacity: 0.5 + } + Text { + anchors.horizontalCenter: parent.horizontalCenter + color: "#888" + text: "No notifications" + } + } + ListView { + anchors.fill: parent + clip: true + spacing: 4 + header: Item {height: 4; width: 1} + model: Array.from(notifServer.trackedNotifications.values) + delegate: Widgets.Notification { + color: "#222" + anchors { + left: parent.left + right: parent.right + margins: 4 + } + } + } + } + Item { + id: player + property var modelData: Mpris.players.values[0] + height: 300 + width: 250 + Column { + anchors.centerIn: parent + spacing: 2 + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: Quickshell.shellPath("assets/player-symbolic.svg") + width: 24 + height: 24 + sourceSize {width: width; height: height} + opacity: 0.5 + } + Text { + anchors.horizontalCenter: parent.horizontalCenter + color: "#888" + text: "Nothing playing" + } + } + Column { + anchors.centerIn: parent + opacity: Mpris.players.values.length + ClippingRectangle { + color: "transparent" + width: 180 + height: 180 + radius: height/2 + anchors.horizontalCenter: parent.horizontalCenter + Image { + anchors.fill: parent + source: player.modelData?.trackArtUrl ?? source + } + NumberAnimation on rotation { + id: spin + paused: !player.modelData?.isPlaying || !root.up || background.index !== 3 + duration: 30000 + loops: Animation.Infinite + from: 0 + to: 360 + } + } + Item {width: 1; height: 10} + Text { + color: "white" + text: player.modelData?.trackTitle ?? "" + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 16 + } + Text { + color: "white" + text: player.modelData?.trackArtist ?? "" + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 12 + } + Item {width: 1; height: 8} + Row { + height: childrenRect.height + anchors.horizontalCenter: parent.horizontalCenter + component Button: Rectangle { + id: button + property var action + property var clicked + radius: 12 + color: hover.hovered ? "#11ffffff" : "#00ffffff" + property var hover: HoverHandler {} + width: 40 + height: width + Image { + anchors.fill: parent + anchors.margins: 8 + sourceSize {width: width; height: height} + source: Quickshell.shellPath(`assets/lazer-${action}-symbolic.svg`) + } + TapHandler {onTapped: button.clicked()} + } + Button { + action: "previous" + clicked: player.modelData?.previous + } + Button { + action: player.modelData?.isPlaying ? "pause" : "play" + clicked: player.modelData?.togglePlaying + } + Button { + action: "next" + clicked: player.modelData?.next + } + } + } + } + } +} diff --git a/Panes/Notifications.qml b/Panes/Notifications.qml new file mode 100644 index 0000000..3198968 --- /dev/null +++ b/Panes/Notifications.qml @@ -0,0 +1,28 @@ +import Quickshell +import QtQuick +import Quickshell.Services.Notifications +import "../Widgets" as Widgets + +Item { + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + } + width: 350 + height: childrenRect.height + Connections { + target: notifServer + function onNotification(notif) { + if (root.up) return + background.index = 4 + notifPopup.modelData = notif + root.open() + } + } + Widgets.Notification { + id: notifPopup + anchors.margins: 6 + modelData: notifServer.trackedNotifications.values[0] ?? undefined + color: "transparent" + } +} diff --git a/Panes/Status.qml b/Panes/Status.qml index f3351f4..fc87006 100644 --- a/Panes/Status.qml +++ b/Panes/Status.qml @@ -3,7 +3,7 @@ import QtQuick import "../Widgets" as Widgets Item { - width: 800 + width: 900 height: 42 anchors { top: parent.top @@ -14,7 +14,6 @@ Item { top: parent.top left: parent.left bottom: parent.bottom - leftMargin: 0 } Widgets.Battery {} } @@ -31,13 +30,7 @@ Item { top: parent.top right: parent.right bottom: parent.bottom - rightMargin: 12 - } - Text { - anchors.verticalCenter: parent.verticalCenter - property var clock: SystemClock {} - color: "white" - text: Qt.formatDateTime(clock.date, "ddd MMM dd · hh:mm") } + Widgets.Time {} } } diff --git a/TopBar.qml b/TopBar.qml index 81956bc..05ab79b 100644 --- a/TopBar.qml +++ b/TopBar.qml @@ -17,8 +17,8 @@ PanelWindow { property var font: {family: "0xProto Nerd Font"} implicitWidth: 1024 - implicitHeight: 200 - WlrLayershell.keyboardFocus: (up && !shortcut.pressed) ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None + implicitHeight: 400 + WlrLayershell.keyboardFocus: (up && !background.exit.running && !shortcut.pressed && background.index <= 1) ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None WlrLayershell.layer: WlrLayer.Overlay exclusionMode: ExclusionMode.Ignore color: "transparent" @@ -39,6 +39,7 @@ PanelWindow { name: "topbar" description: "Hold to peek, tap to toggle topbar" onPressed: { + background.index = 0 root.open() } onReleased: { @@ -147,13 +148,14 @@ PanelWindow { onStarted: entry.stop() } } - Pane { opacity: 1; Panes.Status { }} + Pane { opacity: 1; Panes.Status {} } Pane { Panes.Launcher { id: launcher onShowChanged: if (show) background.index = 1; else background.index = 0 }} - Pane { Panes.Battery { - }} + Pane { Panes.Battery {} } + Pane { Panes.NotificationCenter {} } + Pane { Panes.Notifications {} } property var entry: SequentialAnimation { PauseAnimation {duration: 2} diff --git a/Widgets/Battery.qml b/Widgets/Battery.qml index 9cc2cd0..53c3a2a 100644 --- a/Widgets/Battery.qml +++ b/Widgets/Battery.qml @@ -50,7 +50,7 @@ Item { anchors.verticalCenter: parent.verticalCenter x: 10 color: "white" - text: `${Math.round(UPower.displayDevice.percentage * 100)}% · ${Math.round(UPower.displayDevice.changeRate)}W` + text: `${Math.round(UPower.displayDevice.percentage * 100)}% · ${UPower.displayDevice.changeRate.toPrecision(2)}W` } } HoverHandler {id: hover} diff --git a/Widgets/Notification.qml b/Widgets/Notification.qml new file mode 100644 index 0000000..0fe8f75 --- /dev/null +++ b/Widgets/Notification.qml @@ -0,0 +1,83 @@ +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.Notifications +import QtQuick +import QtQuick.Controls +import QtQuick.Effects + +Rectangle { + id: notifRect + required property var modelData + + property bool exiting: false + width: parent?.width ?? 0 + height: notifContent.childrenRect.height + 20 + radius: 8 + color: "#181818" + + Rectangle { + antialiasing: true + color: "transparent" + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: 12 + } + radius: 8 + width: 40 + height: 40 + Image { + id: notifIcon + anchors.fill: parent + source: modelData?.image ?? source + } + } + Column { + id: notifContent + anchors { + left: parent.left + right: parent.right + top: parent.top + topMargin: 10 + leftMargin: 16 + (notifIcon.source == "" ? 0 : 44) + rightMargin: 16 + } + spacing: 4 + Text { + anchors { + left: parent.left + right: parent.right + } + color: "white" + font.family: "Comfortaa" + font.pixelSize: 16 + text: modelData?.summary ?? text + elide: Text.ElideRight + wrapMode: Text.Wrap + maximumLineCount: 2 + } + Text { + anchors { + left: parent.left + right: parent.right + } + color: "white" + font.family: "Comfortaa" + font.pixelSize: 12 + text: modelData?.body ?? text + elide: Text.ElideRight + wrapMode: Text.Wrap + maximumLineCount: 4 + } + } + Timer { + running: modelData?.expireTimeout > 0 + interval: (modelData?.expireTimeout > 0) ? modelData?.expireTimeout : 0 + onTriggered: modelData.tracked = false + } + TapHandler { + acceptedButtons: Qt.MiddleButton + onTapped: modelData.tracked = false + } +} + diff --git a/Widgets/Time.qml b/Widgets/Time.qml new file mode 100644 index 0000000..554ab4c --- /dev/null +++ b/Widgets/Time.qml @@ -0,0 +1,48 @@ +import QtQuick +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.Mpris + +Item { + id: root + property var player: Mpris.players.values[0] + anchors { + top: parent.top + bottom: parent.bottom + } + width: children[1].width + 24 + Rectangle { + anchors.fill: parent + anchors.margins: 4 + radius: 8 + bottomRightRadius: 12 + color: hover.hovered ? "#11ffffff" : "#00ffffff" + Behavior on color {ColorAnimation {duration: 100}} + } + Row { + spacing: 12 + anchors.verticalCenter: parent.verticalCenter + x: 12 + ClippingRectangle { + visible: !!root.player + anchors.verticalCenter: parent.verticalCenter + width: 24 + height: width + radius: 6 + Image { + anchors.fill: parent + source: root.player?.trackArtUrl ?? source + } + opacity: root.player?.isPlaying ? 1 : 0.5 + Behavior on opacity {NumberAnimation {duration: 150}} + } + Text { + anchors.verticalCenter: parent.verticalCenter + property var clock: SystemClock {} + color: "white" + text: Qt.formatDateTime(clock.date, "ddd MMM dd · hh:mm") + } + } + HoverHandler {id: hover} + TapHandler {onTapped: background.index = 3} +} diff --git a/Widgets/Workspaces.qml b/Widgets/Workspaces.qml index 0bdbc75..3491d5b 100644 --- a/Widgets/Workspaces.qml +++ b/Widgets/Workspaces.qml @@ -18,7 +18,9 @@ Item { width: 24 property var workspace: Hyprland.workspaces.values.find(ws => ws.id === modelData+1) property var icon: { - const appId = workspace?.toplevels.values[0]?.wayland?.appId + const appId = workspace ? Array.from(workspace.toplevels.values).sort( + (a, b) => b.lastIpcObject.focusHistoryId - a.lastIpcObject.focusHistoryId + )[0]?.wayland?.appId : undefined return DesktopEntries.applications.values.find( app => app.startupClass === appId || app.id === appId )?.icon; diff --git a/assets/screenshots/battery.png b/assets/screenshots/battery.png deleted file mode 100644 index 25fdcda..0000000 Binary files a/assets/screenshots/battery.png and /dev/null differ diff --git a/assets/screenshots/bluetooth.png b/assets/screenshots/bluetooth.png deleted file mode 100644 index 9a4b63a..0000000 Binary files a/assets/screenshots/bluetooth.png and /dev/null differ diff --git a/assets/screenshots/brightness.png b/assets/screenshots/brightness.png deleted file mode 100644 index 6b91274..0000000 Binary files a/assets/screenshots/brightness.png and /dev/null differ diff --git a/assets/screenshots/media.png b/assets/screenshots/media.png deleted file mode 100644 index 53761a4..0000000 Binary files a/assets/screenshots/media.png and /dev/null differ diff --git a/assets/screenshots/notification.png b/assets/screenshots/notification.png deleted file mode 100644 index 6eb6fdb..0000000 Binary files a/assets/screenshots/notification.png and /dev/null differ diff --git a/assets/screenshots/notifications.png b/assets/screenshots/notifications.png deleted file mode 100644 index 15ef75e..0000000 Binary files a/assets/screenshots/notifications.png and /dev/null differ diff --git a/assets/screenshots/power.png b/assets/screenshots/power.png deleted file mode 100644 index 7263cf8..0000000 Binary files a/assets/screenshots/power.png and /dev/null differ diff --git a/assets/screenshots/volume.png b/assets/screenshots/volume.png deleted file mode 100644 index 843cb59..0000000 Binary files a/assets/screenshots/volume.png and /dev/null differ diff --git a/assets/screenshots/workspaces.png b/assets/screenshots/workspaces.png deleted file mode 100644 index 331f408..0000000 Binary files a/assets/screenshots/workspaces.png and /dev/null differ diff --git a/shell.qml b/shell.qml index 1888d2c..f064b1c 100644 --- a/shell.qml +++ b/shell.qml @@ -1,8 +1,13 @@ import Quickshell import Quickshell.Io +import Quickshell.Services.Notifications import "." as Shell ShellRoot { + NotificationServer { + id: notifServer + onNotification: (notif) => notif.tracked = true + } Shell.Wall {} Shell.TopBar {} Shell.Boateye {}