diff options
Diffstat (limited to 'modules/lock/Status.qml')
| -rw-r--r-- | modules/lock/Status.qml | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/modules/lock/Status.qml b/modules/lock/Status.qml new file mode 100644 index 0000000..e11fa25 --- /dev/null +++ b/modules/lock/Status.qml @@ -0,0 +1,249 @@ +import "root:/widgets" +import "root:/services" +import "root:/config" +import "root:/utils" +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.UPower +import QtQuick +import QtQuick.Layouts + +WrapperItem { + readonly property real nonAnimWidth: (notifs.count > 0 ? Config.notifs.sizes.width : status.implicitWidth) + margin + readonly property real nonAnimHeight: { + if (notifs.count > 0) { + const count = Math.min(notifs.count, Config.lock.maxNotifs); + let height = status.implicitHeight + Appearance.spacing.normal + Appearance.spacing.smaller * (count - 1); + for (let i = 0; i < count; i++) + height += notifs.itemAtIndex(i)?.nonAnimHeight ?? 0; + return height + margin; + } + + return status.implicitHeight + margin; + } + + implicitWidth: nonAnimWidth + implicitHeight: nonAnimHeight + + margin: Appearance.padding.large * 2 + rightMargin: 0 + topMargin: 0 + + Behavior on implicitWidth { + Anim { + duration: Appearance.anim.durations.large + easing.bezierCurve: Appearance.anim.curves.emphasized + } + } + + Behavior on implicitHeight { + Anim { + duration: Appearance.anim.durations.large + easing.bezierCurve: Appearance.anim.curves.emphasized + } + } + + ColumnLayout { + spacing: Appearance.spacing.normal + + RowLayout { + id: status + + Layout.fillWidth: true + spacing: Appearance.spacing.small + + Loader { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + + active: UPower.displayDevice.isLaptopBattery + asynchronous: true + + sourceComponent: StyledText { + animate: true + text: qsTr("%1%2 remaining").arg(UPower.onBattery ? "" : "(+) ").arg(UPower.displayDevice.percentage) + color: !UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? Colours.palette.m3onSurface : Colours.palette.m3error + } + } + + MaterialIcon { + Layout.alignment: Qt.AlignVCenter + + animate: true + text: Network.active ? Icons.getNetworkIcon(Network.active.strength ?? 0) : "wifi_off" + font.pointSize: Appearance.font.size.large + } + + Loader { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + Layout.maximumWidth: item?.implicitWidth ?? 0 + + active: !UPower.displayDevice.isLaptopBattery + asynchronous: true + + sourceComponent: StyledText { + animate: true + text: Network.active?.ssid ?? "" + font.pointSize: Appearance.font.size.normal + elide: Text.ElideRight + } + } + + MaterialIcon { + Layout.alignment: Qt.AlignVCenter + + animate: true + text: Bluetooth.powered ? "bluetooth" : "bluetooth_disabled" + font.pointSize: Appearance.font.size.large + } + + Loader { + Layout.alignment: Qt.AlignVCenter + active: !UPower.displayDevice.isLaptopBattery + asynchronous: true + + sourceComponent: StyledText { + animate: true + text: qsTr("%n device(s) connected", "", Bluetooth.devices.filter(d => d.connected).length) + font.pointSize: Appearance.font.size.normal + } + } + } + + ListView { + id: notifs + + Layout.fillWidth: true + Layout.fillHeight: true + + model: ScriptModel { + values: [...Notifs.popups].reverse() + } + + orientation: Qt.Vertical + spacing: 0 + clip: true + interactive: false + + delegate: Item { + id: wrapper + + required property Notifs.Notif modelData + required property int index + readonly property alias nonAnimHeight: notif.nonAnimHeight + property int idx + + onIndexChanged: { + if (index !== -1) + idx = index; + } + + implicitWidth: notif.implicitWidth + implicitHeight: notif.nonAnimHeight + (idx === 0 ? 0 : Appearance.spacing.smaller) + + ListView.onRemove: removeAnim.start() + + SequentialAnimation { + id: removeAnim + + PropertyAction { + target: wrapper + property: "ListView.delayRemove" + value: true + } + PropertyAction { + target: wrapper + property: "enabled" + value: false + } + PropertyAction { + target: wrapper + property: "implicitHeight" + value: 0 + } + PropertyAction { + target: wrapper + property: "z" + value: 1 + } + Anim { + target: notif + property: "x" + to: (notif.x >= 0 ? Config.notifs.sizes.width : -Config.notifs.sizes.width) * 2 + duration: Appearance.anim.durations.normal + easing.bezierCurve: Appearance.anim.curves.emphasized + } + PropertyAction { + target: wrapper + property: "ListView.delayRemove" + value: false + } + } + + ClippingRectangle { + anchors.top: parent.top + anchors.topMargin: wrapper.idx === 0 ? 0 : Appearance.spacing.smaller + + color: "transparent" + radius: notif.radius + implicitWidth: notif.implicitWidth + implicitHeight: notif.nonAnimHeight + + Notification { + id: notif + + modelData: wrapper.modelData + } + } + } + + move: Transition { + Anim { + property: "y" + } + } + + displaced: Transition { + Anim { + property: "y" + } + } + + StyledRect { + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.margins: Appearance.padding.normal + + color: Colours.palette.m3tertiaryContainer + radius: Appearance.rounding.small + + implicitWidth: count.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: count.implicitHeight + Appearance.padding.small * 2 + + scale: Notifs.popups.length > Config.lock.maxNotifs ? 1 : 0 + + StyledText { + id: count + + anchors.centerIn: parent + text: qsTr("+%1").arg(Notifs.popups.length - Config.lock.maxNotifs) + color: Colours.palette.m3onTertiaryContainer + } + + Behavior on scale { + Anim { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } + } + } + } + + component Anim: NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } +} |