internal: clean up old lock files

This commit is contained in:
2 * r + 2 * t 2025-08-12 00:37:38 +10:00
parent 019f8b9570
commit 1665228cd9
7 changed files with 0 additions and 1614 deletions

View file

@ -1,480 +0,0 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.services
import qs.config
import QtQuick
import QtQuick.Shapes
import QtQuick.Effects
Item {
id: root
required property bool locked
required property real weatherWidth
required property real buttonsWidth
required property real buttonsHeight
required property real statusWidth
required property real statusHeight
required property bool isNormal
required property bool isLarge
readonly property real clockBottom: innerMask.anchors.margins + clockPath.height
readonly property real inputTop: innerMask.anchors.margins + inputPath.height
readonly property real weatherTop: innerMask.anchors.margins + weatherPath.height
readonly property real weatherRight: innerMask.anchors.margins + weatherPath.width
readonly property real buttonsTop: innerMask.anchors.margins + buttonsPath.height
readonly property real buttonsLeft: innerMask.anchors.margins + buttonsPath.width
readonly property real statusBottom: innerMask.anchors.margins + statusPath.height
readonly property real statusLeft: innerMask.anchors.margins + statusPath.width
readonly property real mediaX: innerMask.anchors.margins + mediaPath.width
readonly property real mediaY: innerMask.anchors.margins + mediaPath.height
anchors.fill: parent
StyledRect {
id: base
anchors.fill: parent
color: Colours.tPalette.m3surface
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: mask
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
}
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
id: innerMask
anchors.fill: parent
anchors.margins: root.locked ? Config.lock.sizes.border : -radius / 2
radius: Appearance.rounding.large * 2
Behavior on anchors.margins {
Anim {}
}
}
}
Shape {
anchors.fill: parent
anchors.margins: Math.floor(innerMask.anchors.margins)
preferredRendererType: Shape.CurveRenderer
ShapePath {
id: clockPath
readonly property int width: Config.lock.sizes.clockWidth
property real height: root.locked ? Config.lock.sizes.clockHeight : 0
readonly property real rounding: Appearance.rounding.large * 4
readonly property bool flatten: height < rounding * 2
readonly property real roundingY: flatten ? height / 2 : rounding
strokeWidth: -1
fillColor: Colours.tPalette.m3surface
startX: (innerMask.width - width) / 2 - rounding
PathArc {
relativeX: clockPath.rounding
relativeY: clockPath.roundingY
radiusX: clockPath.rounding
radiusY: Math.min(clockPath.rounding, clockPath.height)
}
PathLine {
relativeX: 0
relativeY: clockPath.height - clockPath.roundingY * 2
}
PathArc {
relativeX: clockPath.rounding
relativeY: clockPath.roundingY
radiusX: clockPath.rounding
radiusY: Math.min(clockPath.rounding, clockPath.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: clockPath.width - clockPath.rounding * 2
relativeY: 0
}
PathArc {
relativeX: clockPath.rounding
relativeY: -clockPath.roundingY
radiusX: clockPath.rounding
radiusY: Math.min(clockPath.rounding, clockPath.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: 0
relativeY: -(clockPath.height - clockPath.roundingY * 2)
}
PathArc {
relativeX: clockPath.rounding
relativeY: -clockPath.roundingY
radiusX: clockPath.rounding
radiusY: Math.min(clockPath.rounding, clockPath.height)
}
Behavior on height {
Anim {}
}
Behavior on fillColor {
ColorAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
ShapePath {
id: inputPath
readonly property int width: Config.lock.sizes.inputWidth
property real height: root.locked ? Config.lock.sizes.inputHeight : 0
readonly property real rounding: Appearance.rounding.large * 2
readonly property bool flatten: height < rounding * 2
readonly property real roundingY: flatten ? height / 2 : rounding
strokeWidth: -1
fillColor: Colours.tPalette.m3surface
startX: (innerMask.width - width) / 2 - rounding
startY: Math.ceil(innerMask.height)
PathArc {
relativeX: inputPath.rounding
relativeY: -inputPath.roundingY
radiusX: inputPath.rounding
radiusY: Math.min(inputPath.rounding, inputPath.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: 0
relativeY: -(inputPath.height - inputPath.roundingY * 2)
}
PathArc {
relativeX: inputPath.rounding
relativeY: -inputPath.roundingY
radiusX: inputPath.rounding
radiusY: Math.min(inputPath.rounding, inputPath.height)
}
PathLine {
relativeX: inputPath.width - inputPath.rounding * 2
relativeY: 0
}
PathArc {
relativeX: inputPath.rounding
relativeY: inputPath.roundingY
radiusX: inputPath.rounding
radiusY: Math.min(inputPath.rounding, inputPath.height)
}
PathLine {
relativeX: 0
relativeY: inputPath.height - inputPath.roundingY * 2
}
PathArc {
relativeX: inputPath.rounding
relativeY: inputPath.roundingY
radiusX: inputPath.rounding
radiusY: Math.min(inputPath.rounding, inputPath.height)
direction: PathArc.Counterclockwise
}
Behavior on height {
Anim {}
}
Behavior on fillColor {
ColorAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
ShapePath {
id: weatherPath
property int width: root.locked ? root.weatherWidth - Config.lock.sizes.border / 4 : 0
property real height: root.locked ? Config.lock.sizes.weatherHeight : 0
readonly property real rounding: Appearance.rounding.large * 2
readonly property real roundingX: width < rounding * 2 ? width / 2 : rounding
readonly property real roundingY: height < rounding * 2 ? height / 2 : rounding
strokeWidth: -1
fillColor: Colours.tPalette.m3surface
startY: Math.ceil(innerMask.height) - height - roundingY
PathArc {
relativeX: weatherPath.roundingX
relativeY: weatherPath.roundingY
radiusX: Math.min(weatherPath.rounding, weatherPath.width)
radiusY: Math.min(weatherPath.rounding, weatherPath.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: weatherPath.width - weatherPath.roundingX * 2
relativeY: 0
}
PathArc {
relativeX: weatherPath.roundingX
relativeY: weatherPath.roundingY
radiusX: Math.min(weatherPath.rounding, weatherPath.width)
radiusY: Math.min(weatherPath.rounding, weatherPath.height)
}
PathLine {
relativeX: 0
relativeY: weatherPath.height - weatherPath.roundingY * 2
}
PathArc {
relativeX: weatherPath.roundingX
relativeY: weatherPath.roundingY
radiusX: Math.min(weatherPath.rounding, weatherPath.width)
radiusY: Math.min(weatherPath.rounding, weatherPath.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: -weatherPath.width - weatherPath.roundingX
relativeY: 0
}
Behavior on width {
Anim {}
}
Behavior on height {
Anim {}
}
Behavior on fillColor {
ColorAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
ShapePath {
id: mediaPath
property int width: root.locked ? (root.isLarge ? Config.lock.sizes.mediaWidth : Config.lock.sizes.mediaWidthSmall) - Config.lock.sizes.border / 4 : 0
property real height: root.locked ? (root.isLarge ? Config.lock.sizes.mediaHeight : Config.lock.sizes.mediaHeightSmall) : 0
readonly property real rounding: Appearance.rounding.large * 2
readonly property real roundingX: width < rounding * 2 ? width / 2 : rounding
readonly property real roundingY: height < rounding * 2 ? height / 2 : rounding
strokeWidth: -1
fillColor: root.isNormal ? Colours.tPalette.m3surface : "transparent"
startX: root.isLarge ? 0 : Math.ceil(innerMask.width)
startY: root.isLarge ? height + roundingY : Math.ceil(innerMask.height) - height - roundingY
PathArc {
relativeX: mediaPath.roundingX * (root.isLarge ? 1 : -1)
relativeY: mediaPath.roundingY * (root.isLarge ? -1 : 1)
radiusX: Math.min(mediaPath.rounding, mediaPath.width)
radiusY: Math.min(mediaPath.rounding, mediaPath.height)
}
PathLine {
relativeX: (mediaPath.width - mediaPath.roundingX * 2) * (root.isLarge ? 1 : -1)
relativeY: 0
}
PathArc {
relativeX: mediaPath.roundingX * (root.isLarge ? 1 : -1)
relativeY: mediaPath.roundingY * (root.isLarge ? -1 : 1)
radiusX: Math.min(mediaPath.rounding, mediaPath.width)
radiusY: Math.min(mediaPath.rounding, mediaPath.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: 0
relativeY: (mediaPath.height - mediaPath.roundingY * 2) * (root.isLarge ? -1 : 1)
}
PathArc {
relativeX: mediaPath.roundingX * (root.isLarge ? 1 : -1)
relativeY: mediaPath.roundingY * (root.isLarge ? -1 : 1)
radiusX: Math.min(mediaPath.rounding, mediaPath.width)
radiusY: Math.min(mediaPath.rounding, mediaPath.height)
}
PathLine {
relativeX: (-mediaPath.width - mediaPath.roundingX) * (root.isLarge ? 1 : -1)
relativeY: 0
}
Behavior on width {
Anim {}
}
Behavior on height {
Anim {}
}
Behavior on fillColor {
ColorAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
ShapePath {
id: buttonsPath
property int width: root.locked ? root.buttonsWidth - Config.lock.sizes.border / 4 : 0
property real height: root.locked ? root.buttonsHeight - Config.lock.sizes.border / 4 : 0
readonly property real rounding: Appearance.rounding.large * 2
readonly property real roundingX: width < rounding * 2 ? width / 2 : rounding
readonly property real roundingY: height < rounding * 2 ? height / 2 : rounding
strokeWidth: -1
fillColor: root.isLarge ? Colours.tPalette.m3surface : "transparent"
startX: Math.ceil(innerMask.width)
startY: Math.ceil(innerMask.height) - height - rounding
PathArc {
relativeX: -buttonsPath.roundingX
relativeY: buttonsPath.rounding
radiusX: Math.min(buttonsPath.rounding, buttonsPath.width)
radiusY: buttonsPath.rounding, buttonsPath.height
}
PathLine {
relativeX: -(buttonsPath.width - buttonsPath.roundingX * 2)
relativeY: 0
}
PathArc {
relativeX: -buttonsPath.roundingX
relativeY: buttonsPath.roundingY
radiusX: Math.min(buttonsPath.rounding, buttonsPath.width)
radiusY: Math.min(buttonsPath.rounding, buttonsPath.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: 0
relativeY: buttonsPath.height - buttonsPath.roundingY * 2
}
PathArc {
relativeX: -buttonsPath.roundingX
relativeY: buttonsPath.roundingY
radiusX: Math.min(buttonsPath.rounding, buttonsPath.width)
radiusY: Math.min(buttonsPath.rounding, buttonsPath.height)
}
PathLine {
relativeX: buttonsPath.width + buttonsPath.roundingX
relativeY: 0
}
Behavior on width {
Anim {}
}
Behavior on height {
Anim {}
}
Behavior on fillColor {
ColorAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
ShapePath {
id: statusPath
property int width: root.locked ? root.statusWidth - Config.lock.sizes.border / 4 : 0
property real height: root.locked ? root.statusHeight - Config.lock.sizes.border / 4 : 0
readonly property real rounding: Appearance.rounding.large * 2
readonly property real roundingX: width < rounding * 2 ? width / 2 : rounding
readonly property real roundingY: height < rounding * 2 ? height / 2 : rounding
strokeWidth: -1
fillColor: root.isLarge ? Colours.tPalette.m3surface : "transparent"
startX: Math.ceil(innerMask.width)
startY: height + rounding
PathArc {
relativeX: -statusPath.roundingX
relativeY: -statusPath.rounding
radiusX: Math.min(statusPath.rounding, statusPath.width)
radiusY: statusPath.rounding
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: -(statusPath.width - statusPath.roundingX * 2)
relativeY: 0
}
PathArc {
relativeX: -statusPath.roundingX
relativeY: -statusPath.roundingY
radiusX: Math.min(statusPath.rounding, statusPath.width)
radiusY: Math.min(statusPath.rounding, statusPath.height)
}
PathLine {
relativeX: 0
relativeY: -(statusPath.height - statusPath.roundingY * 2)
}
PathArc {
relativeX: -statusPath.roundingX
relativeY: -statusPath.roundingY
radiusX: Math.min(statusPath.rounding, statusPath.width)
radiusY: Math.min(statusPath.rounding, statusPath.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: statusPath.width + statusPath.roundingX
relativeY: 0
}
Behavior on width {
Anim {}
}
Behavior on height {
Anim {}
}
Behavior on fillColor {
ColorAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
}
component Anim: NumberAnimation {
duration: Appearance.anim.durations.large
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.emphasized
}
}

View file

@ -1,109 +0,0 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.services
import qs.config
import Quickshell
import QtQuick
import QtQuick.Layouts
Item {
id: root
readonly property real nonAnimMargin: handler.hovered ? Appearance.padding.large * 1.5 : Appearance.padding.large
readonly property real nonAnimWidth: handler.hovered ? Config.lock.sizes.buttonsWidth : Config.lock.sizes.buttonsWidthSmall
readonly property real nonAnimHeight: (nonAnimWidth + nonAnimMargin * 2) / 4
implicitWidth: nonAnimWidth
implicitHeight: nonAnimHeight
Behavior on implicitWidth {
NumberAnimation {
duration: Appearance.anim.durations.large
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.emphasized
}
}
Behavior on implicitHeight {
NumberAnimation {
duration: Appearance.anim.durations.large
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.emphasized
}
}
HoverHandler {
id: handler
target: parent
}
RowLayout {
id: layout
anchors.fill: parent
anchors.margins: root.nonAnimMargin
anchors.rightMargin: 0
anchors.bottomMargin: 0
spacing: Appearance.spacing.normal
SessionButton {
icon: "logout"
command: Config.session.commands.logout
}
SessionButton {
icon: "power_settings_new"
command: Config.session.commands.shutdown
}
SessionButton {
icon: "downloading"
command: Config.session.commands.hibernate
}
SessionButton {
icon: "cached"
command: Config.session.commands.reboot
}
Behavior on anchors.margins {
NumberAnimation {
duration: Appearance.anim.durations.large
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.emphasized
}
}
}
component SessionButton: StyledRect {
required property string icon
required property list<string> command
Layout.fillWidth: true
Layout.preferredHeight: width
radius: Appearance.rounding.large * 1.2
color: Colours.palette.m3secondaryContainer
StateLayer {
id: stateLayer
color: Colours.palette.m3onSecondaryContainer
function onClicked(): void {
Quickshell.execDetached(parent.command);
}
}
MaterialIcon {
anchors.centerIn: parent
text: parent.icon
color: Colours.palette.m3onSecondaryContainer
font.pointSize: (parent.width * 0.4) || 1
font.weight: handler.hovered ? 500 : 400
}
}
}

View file

@ -1,74 +0,0 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.services
import qs.config
import QtQuick
import QtQuick.Layouts
ColumnLayout {
id: root
spacing: 0
readonly property list<string> timeComponents: Time.format(Config.services.useTwelveHourClock ? "hh:mm:A" : "hh:mm").split(":")
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Appearance.spacing.small
StyledText {
Layout.alignment: Qt.AlignVCenter
text: root.timeComponents[0]
color: Colours.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge * 4
font.family: Appearance.font.family.mono
font.weight: 800
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: ":"
color: Colours.palette.m3primary
font.pointSize: Appearance.font.size.extraLarge * 4
font.family: Appearance.font.family.mono
font.weight: 800
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: root.timeComponents[1]
color: Colours.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge * 4
font.family: Appearance.font.family.mono
font.weight: 800
}
Loader {
Layout.leftMargin: Appearance.spacing.normal
Layout.alignment: Qt.AlignVCenter
asynchronous: true
active: Config.services.useTwelveHourClock
visible: active
sourceComponent: StyledText {
text: root.timeComponents[2] ?? ""
color: Colours.palette.m3primary
font.pointSize: Appearance.font.size.extraLarge * 3
font.weight: 700
}
}
}
StyledText {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: Appearance.padding.large * 3
text: Time.format("dddd, d MMMM yyyy")
color: Colours.palette.m3tertiary
font.pointSize: Appearance.font.size.extraLarge
font.family: Appearance.font.family.mono
font.bold: true
}
}

View file

@ -1,242 +0,0 @@
import qs.components
import qs.components.images
import qs.services
import qs.config
import qs.utils
import Quickshell
import Quickshell.Wayland
import Quickshell.Services.Pam
import QtQuick
import QtQuick.Layouts
ColumnLayout {
id: root
required property var lock
property string passwordBuffer
Layout.preferredWidth: Config.lock.sizes.faceSize * 2
Layout.fillWidth: false
spacing: Appearance.spacing.large * 2
StyledRect {
Layout.fillWidth: true
implicitHeight: user.implicitHeight + Appearance.padding.small * 2
color: Colours.tPalette.m3surfaceContainer
radius: Appearance.rounding.small
RowLayout {
id: user
anchors.centerIn: parent
spacing: Appearance.spacing.normal
MaterialIcon {
text: "account_circle"
font.pointSize: Appearance.font.size.large * 1.4
font.weight: 500
}
StyledText {
// Layout.fillWidth: true
text: Quickshell.env("USER")
font.pointSize: Appearance.font.size.large
// font.capitalization: Font.Capitalize
font.weight: 500
elide: Text.ElideRight
}
}
}
StyledRect {
Layout.fillWidth: true
Layout.preferredWidth: charList.implicitWidth + Appearance.padding.large * 2
Layout.preferredHeight: Appearance.font.size.normal + Appearance.padding.large * 2
focus: true
color: Colours.tPalette.m3surfaceContainer
radius: Appearance.rounding.small
clip: true
onFocusChanged: {
if (!focus)
focus = true;
}
Keys.onPressed: event => {
if (pam.active)
return;
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
placeholder.animate = false;
pam.start();
} else if (event.key === Qt.Key_Backspace) {
if (event.modifiers & Qt.ControlModifier) {
charList.implicitWidth = charList.implicitWidth; // Break binding
root.passwordBuffer = "";
} else {
root.passwordBuffer = root.passwordBuffer.slice(0, -1);
}
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
charList.bindImWidth();
root.passwordBuffer += event.text;
}
}
PamContext {
id: pam
onResponseRequiredChanged: {
if (!responseRequired)
return;
respond(root.passwordBuffer);
charList.implicitWidth = charList.implicitWidth; // Break binding
root.passwordBuffer = "";
placeholder.animate = true;
}
onCompleted: res => {
if (res === PamResult.Success)
return root.lock.lock.unlock();
if (res === PamResult.Error)
placeholder.pamState = "error";
else if (res === PamResult.MaxTries)
placeholder.pamState = "max";
else if (res === PamResult.Failed)
placeholder.pamState = "fail";
placeholderDelay.restart();
}
}
Timer {
id: placeholderDelay
interval: 3000
onTriggered: placeholder.pamState = ""
}
StyledText {
id: placeholder
property string pamState
anchors.centerIn: parent
text: {
if (pam.active)
return qsTr("Loading...");
if (pamState === "error")
return qsTr("An error occured");
if (pamState === "max")
return qsTr("You have reached the maximum number of tries");
if (pamState === "fail")
return qsTr("Incorrect password");
return qsTr("Enter your password");
}
animate: true
color: pam.active ? Colours.palette.m3secondary : pamState ? Colours.palette.m3error : Colours.palette.m3outline
font.pointSize: Appearance.font.size.larger
opacity: root.passwordBuffer ? 0 : 1
Behavior on opacity {
Anim {}
}
}
ListView {
id: charList
function bindImWidth(): void {
imWidthBehavior.enabled = false;
implicitWidth = Qt.binding(() => Math.min(count * (Appearance.font.size.normal + spacing) - spacing, Config.lock.sizes.inputWidth - Appearance.rounding.large * 2 - Appearance.padding.large * 5));
imWidthBehavior.enabled = true;
}
anchors.centerIn: parent
implicitWidth: Math.min(count * (Appearance.font.size.normal + spacing) - spacing, Config.lock.sizes.inputWidth - Appearance.rounding.large * 2 - Appearance.padding.large * 5)
implicitHeight: Appearance.font.size.normal
orientation: Qt.Horizontal
spacing: Appearance.spacing.small / 2
interactive: false
model: ScriptModel {
values: root.passwordBuffer.split("")
}
delegate: StyledRect {
id: ch
implicitWidth: Appearance.font.size.normal
implicitHeight: Appearance.font.size.normal
color: Colours.palette.m3onSurface
radius: Appearance.rounding.full
opacity: 0
scale: 0.5
Component.onCompleted: {
opacity = 1;
scale = 1;
}
ListView.onRemove: removeAnim.start()
SequentialAnimation {
id: removeAnim
PropertyAction {
target: ch
property: "ListView.delayRemove"
value: true
}
ParallelAnimation {
Anim {
target: ch
property: "opacity"
to: 0
}
Anim {
target: ch
property: "scale"
to: 0.5
}
}
PropertyAction {
target: ch
property: "ListView.delayRemove"
value: false
}
}
Behavior on opacity {
Anim {}
}
Behavior on scale {
Anim {}
}
}
Behavior on implicitWidth {
id: imWidthBehavior
Anim {}
}
}
}
component Anim: NumberAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}

View file

@ -1,257 +0,0 @@
import qs.components
import qs.components.controls
import qs.services
import qs.config
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
RowLayout {
id: root
required property bool isLarge
spacing: Appearance.spacing.large * (isLarge ? 2 : 1.5)
width: isLarge ? Config.lock.sizes.mediaWidth : Config.lock.sizes.mediaWidthSmall
property real playerProgress: {
const active = Players.active;
return active?.length ? active.position / active.length : 0;
}
Behavior on playerProgress {
NumberAnimation {
duration: Appearance.anim.durations.large
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
Timer {
running: Players.active?.isPlaying ?? false
interval: Config.dashboard.mediaUpdateInterval
triggeredOnStart: true
repeat: true
onTriggered: Players.active?.positionChanged()
}
Item {
Layout.alignment: Qt.AlignVCenter
Layout.topMargin: root.isLarge ? 0 : Config.lock.sizes.border / 2
Layout.bottomMargin: root.isLarge ? Config.lock.sizes.border / 2 : 0
Layout.leftMargin: root.isLarge ? 0 : Config.lock.sizes.border / 2
implicitWidth: root.isLarge ? Config.lock.sizes.mediaCoverSize : Config.lock.sizes.mediaCoverSizeSmall
implicitHeight: root.isLarge ? Config.lock.sizes.mediaCoverSize : Config.lock.sizes.mediaCoverSizeSmall
ClippingWrapperRectangle {
anchors.fill: parent
color: Colours.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.small
rotation: 9
Image {
anchors.fill: parent
source: Players.active?.trackArtUrl ?? ""
asynchronous: true
fillMode: Image.PreserveAspectCrop
sourceSize.width: width
sourceSize.height: height
}
Behavior on color {
ColorAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -1
border.width: Config.lock.sizes.mediaCoverBorder
border.color: Colours.palette.m3primary
color: "transparent"
radius: Appearance.rounding.small
rotation: 9
Behavior on border.color {
ColorAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
}
StyledClippingRect {
anchors.fill: parent
color: Colours.tPalette.m3surfaceContainerHigh
radius: Appearance.rounding.small
border.width: Config.lock.sizes.mediaCoverBorder
border.color: Colours.palette.m3primary
MaterialIcon {
anchors.centerIn: parent
grade: 200
text: "art_track"
color: Colours.palette.m3onSurfaceVariant
font.pointSize: (root.isLarge ? Config.lock.sizes.mediaCoverSize : Config.lock.sizes.mediaCoverSizeSmall) * 0.4
}
Image {
anchors.fill: parent
source: Players.active?.trackArtUrl ?? ""
asynchronous: true
fillMode: Image.PreserveAspectCrop
sourceSize.width: width
sourceSize.height: height
}
}
}
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
Layout.topMargin: root.isLarge ? 0 : Config.lock.sizes.border / 2
Layout.bottomMargin: root.isLarge ? Config.lock.sizes.border / 2 : 0
Layout.rightMargin: root.isLarge ? Config.lock.sizes.border / 2 : 0
Layout.fillWidth: true
spacing: root.isLarge ? Appearance.spacing.small : Appearance.spacing.small / 2
StyledText {
Layout.fillWidth: true
animate: true
text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
color: Colours.palette.m3primary
font.pointSize: root.isLarge ? Appearance.font.size.large : Appearance.font.size.larger
elide: Text.ElideRight
}
StyledText {
Layout.fillWidth: true
animate: true
text: (Players.active?.trackAlbum ?? qsTr("No media")) || qsTr("Unknown album")
color: Colours.palette.m3outline
font.pointSize: root.isLarge ? Appearance.font.size.larger : Appearance.font.size.normal
elide: Text.ElideRight
}
StyledText {
Layout.fillWidth: true
animate: true
text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist")
color: Colours.palette.m3secondary
font.pointSize: root.isLarge ? Appearance.font.size.larger : Appearance.font.size.normal
elide: Text.ElideRight
}
RowLayout {
id: controls
Layout.fillWidth: true
spacing: Appearance.spacing.small
StyledSlider {
id: slider
Layout.rightMargin: root.isLarge ? Appearance.spacing.small : 0
Layout.fillWidth: true
implicitHeight: Appearance.padding.normal * 3
value: root.playerProgress
onMoved: {
const active = Players.active;
if (active?.canSeek && active?.positionSupported)
active.position = value * active.length;
}
}
Control {
icon: "skip_previous"
canUse: Players.active?.canGoPrevious ?? false
fontSize: root.isLarge ? Appearance.font.size.extraLarge : Appearance.font.size.large * 1.2
function onClicked(): void {
Players.active?.previous();
}
}
Control {
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
canUse: Players.active?.canTogglePlaying ?? false
fontSize: root.isLarge ? Appearance.font.size.extraLarge : Appearance.font.size.large * 1.2
primary: true
function onClicked(): void {
Players.active?.togglePlaying();
}
}
Control {
icon: "skip_next"
canUse: Players.active?.canGoNext ?? false
fontSize: root.isLarge ? Appearance.font.size.extraLarge : Appearance.font.size.large * 1.2
function onClicked(): void {
Players.active?.next();
}
}
}
}
component Control: StyledRect {
id: control
required property string icon
required property bool canUse
required property int fontSize
property int padding
property bool fill: true
property bool primary
function onClicked(): void {
}
implicitWidth: Math.max(icon.implicitWidth, icon.implicitHeight) + padding * 2
implicitHeight: implicitWidth
radius: Appearance.rounding.full
color: Qt.alpha(Colours.palette.m3primary, primary && canUse ? 1 : 0)
StateLayer {
disabled: !control.canUse
radius: parent.radius
color: control.primary ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
function onClicked(): void {
control.onClicked();
}
}
MaterialIcon {
id: icon
anchors.centerIn: parent
anchors.horizontalCenterOffset: -font.pointSize * 0.02
anchors.verticalCenterOffset: font.pointSize * 0.02
animate: true
fill: control.fill ? 1 : 0
text: control.icon
color: control.canUse ? control.primary ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface : Colours.palette.m3outline
font.pointSize: control.fontSize
}
}
}

View file

@ -1,211 +0,0 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.components.effects
import qs.services
import qs.config
import qs.utils
import Quickshell
import Quickshell.Widgets
import Quickshell.Services.Notifications
import QtQuick
import QtQuick.Layouts
StyledRect {
id: root
required property Notifs.Notif modelData
readonly property bool hasImage: modelData.image.length > 0
readonly property bool hasAppIcon: modelData.appIcon.length > 0
readonly property int nonAnimHeight: Math.max(image.height, details.implicitHeight) + Appearance.padding.normal * 2
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondaryContainer : Colours.tPalette.m3surfaceContainer
radius: Appearance.rounding.normal
implicitWidth: Config.notifs.sizes.width
Component.onCompleted: implicitHeight = Qt.binding(() => nonAnimHeight)
Behavior on implicitHeight {
NumberAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}
Behavior on x {
NumberAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
}
}
RetainableLock {
object: root.modelData.notification
locked: true
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: pressed ? Qt.ClosedHandCursor : undefined
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
preventStealing: true
onEntered: root.modelData.timer.stop()
onExited: root.modelData.timer.start()
drag.target: parent
drag.axis: Drag.XAxis
onPressed: event => {
if (event.button === Qt.MiddleButton)
root.modelData.notification.dismiss();
}
onReleased: event => {
if (Math.abs(root.x) < Config.notifs.sizes.width * Config.notifs.clearThreshold)
root.x = 0;
else
root.modelData.notification.dismiss(); // TODO: change back to popup when notif dock impled
}
}
Loader {
id: image
active: root.hasImage
asynchronous: true
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Appearance.padding.normal
width: Config.notifs.sizes.image
height: Config.notifs.sizes.image
visible: root.hasImage || root.hasAppIcon
sourceComponent: ClippingRectangle {
radius: Appearance.rounding.full
implicitWidth: Config.notifs.sizes.image
implicitHeight: Config.notifs.sizes.image
Image {
anchors.fill: parent
source: Qt.resolvedUrl(root.modelData.image)
fillMode: Image.PreserveAspectCrop
cache: false
asynchronous: true
}
}
}
Loader {
id: appIcon
active: root.hasAppIcon || !root.hasImage
asynchronous: true
anchors.horizontalCenter: root.hasImage ? undefined : image.horizontalCenter
anchors.verticalCenter: root.hasImage ? undefined : image.verticalCenter
anchors.right: root.hasImage ? image.right : undefined
anchors.bottom: root.hasImage ? image.bottom : undefined
sourceComponent: StyledRect {
radius: Appearance.rounding.full
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? Colours.tPalette.m3surfaceContainerHighest : Colours.palette.m3tertiaryContainer
implicitWidth: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
implicitHeight: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
Loader {
id: icon
active: root.hasAppIcon
asynchronous: true
anchors.centerIn: parent
width: Math.round(parent.width * 0.6)
height: Math.round(parent.width * 0.6)
sourceComponent: IconImage {
anchors.fill: parent
source: Quickshell.iconPath(root.modelData.appIcon)
asynchronous: true
layer.enabled: root.modelData.appIcon.endsWith("symbolic")
layer.effect: Colouriser {
colorizationColor: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? Colours.palette.m3onSurface : Colours.palette.m3onTertiaryContainer
}
}
}
Loader {
active: !root.hasAppIcon
asynchronous: true
anchors.centerIn: parent
anchors.horizontalCenterOffset: -Appearance.font.size.large * 0.02
anchors.verticalCenterOffset: Appearance.font.size.large * 0.02
sourceComponent: MaterialIcon {
text: Icons.getNotifIcon(root.modelData.summary.toLowerCase(), root.modelData.urgency)
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? Colours.palette.m3onSurface : Colours.palette.m3onTertiaryContainer
font.pointSize: Appearance.font.size.large
}
}
}
}
ColumnLayout {
id: details
anchors.verticalCenter: parent.verticalCenter
anchors.left: image.right
anchors.right: parent.right
anchors.leftMargin: Appearance.spacing.smaller
anchors.rightMargin: Appearance.padding.larger
spacing: 0
RowLayout {
Layout.fillWidth: true
spacing: Appearance.spacing.small
StyledText {
Layout.fillWidth: true
Layout.maximumWidth: implicitWidth
animate: true
text: root.modelData.summary
elide: Text.ElideRight
maximumLineCount: 1
}
StyledText {
text: "•"
color: Colours.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.small
}
StyledText {
animate: true
text: root.modelData.timeStr
color: Colours.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.small
}
}
StyledText {
Layout.fillWidth: true
animate: true
text: root.modelData.body
color: Colours.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.small
elide: Text.ElideRight
maximumLineCount: 1
}
}
}

View file

@ -1,241 +0,0 @@
import qs.components
import qs.components.widgets
import qs.services
import qs.config
import qs.utils
import Quickshell
import Quickshell.Widgets
import Quickshell.Bluetooth
import Quickshell.Services.UPower
import QtQuick
import QtQuick.Layouts
WrapperItem {
property alias showNotifs: notifs.active
readonly property real nonAnimWidth: (notifs.item?.count > 0 ? Config.notifs.sizes.width : status.implicitWidth) + margin
readonly property real nonAnimHeight: {
if (notifs.active && notifs.item.count > 0) {
const count = Math.min(notifs.item.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.item.itemAtIndex(i)?.nonAnimHeight ?? 0;
return height + margin;
}
return status.implicitHeight + margin;
}
implicitWidth: nonAnimWidth
implicitHeight: nonAnimHeight
margin: Appearance.padding.large * 2
rightMargin: 0
topMargin: 0
Timer {
running: true
interval: 10
onTriggered: notifs.item?.countChanged()
}
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(Math.round(UPower.displayDevice.percentage * 100))
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.defaultAdapter.enabled ? "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.values.filter(d => d.connected).length)
font.pointSize: Appearance.font.size.normal
}
}
}
Loader {
id: notifs
Layout.fillWidth: true
Layout.fillHeight: true
sourceComponent: ListView {
model: ScriptModel {
values: [...Notifs.list].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"
duration: Appearance.anim.durations.large
easing.bezierCurve: Appearance.anim.curves.emphasized
}
}
displaced: Transition {
Anim {
property: "y"
duration: Appearance.anim.durations.large
easing.bezierCurve: Appearance.anim.curves.emphasized
}
}
ExtraIndicator {
anchors.bottom: parent.bottom
extra: Notifs.list.length - Config.lock.maxNotifs
}
}
}
}
component Anim: NumberAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}