formatter
This commit is contained in:
+378
-387
@@ -9,391 +9,382 @@ import qs.Config
|
||||
import qs.Modules
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property var lock
|
||||
readonly property real centerScale: Math.min(1, (lock.screen?.height ?? 1440) / 1440)
|
||||
readonly property int centerWidth: Config.lock.sizes.centerWidth * centerScale
|
||||
|
||||
Layout.preferredWidth: centerWidth
|
||||
Layout.fillWidth: false
|
||||
Layout.fillHeight: true
|
||||
|
||||
spacing: Appearance.spacing.large * 2
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: Time.hourStr
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
font.family: Appearance.font.family.clock
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: ":"
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
font.family: Appearance.font.family.clock
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: Time.minuteStr
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
font.family: Appearance.font.family.clock
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: -Appearance.padding.large * 2
|
||||
|
||||
text: Time.format("dddd, d MMMM yyyy")
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * root.centerScale)
|
||||
font.family: Appearance.font.family.mono
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
Layout.topMargin: Appearance.spacing.large * 2
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
implicitWidth: root.centerWidth / 2
|
||||
implicitHeight: root.centerWidth / 2
|
||||
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
|
||||
text: "person"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Math.floor(root.centerWidth / 4)
|
||||
}
|
||||
|
||||
CachingImage {
|
||||
id: pfp
|
||||
|
||||
anchors.fill: parent
|
||||
path: `${Paths.home}/.face`
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
implicitWidth: root.centerWidth * 0.8
|
||||
implicitHeight: input.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
focus: true
|
||||
onActiveFocusChanged: {
|
||||
if (!activeFocus)
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (root.lock.unlocking)
|
||||
return;
|
||||
|
||||
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return)
|
||||
inputField.placeholder.animate = false;
|
||||
|
||||
root.lock.pam.handleKey(event);
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
hoverEnabled: false
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
function onClicked(): void {
|
||||
parent.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.small
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Item {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: fprintIcon.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
MaterialIcon {
|
||||
id: fprintIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
text: {
|
||||
if (root.lock.pam.fprint.tries >= Config.lock.maxFprintTries)
|
||||
return "fingerprint_off";
|
||||
if (root.lock.pam.fprint.active)
|
||||
return "fingerprint";
|
||||
return "lock";
|
||||
}
|
||||
color: root.lock.pam.fprint.tries >= Config.lock.maxFprintTries ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
|
||||
opacity: root.lock.pam.passwd.active ? 0 : 1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
CircularIndicator {
|
||||
anchors.fill: parent
|
||||
running: root.lock.pam.passwd.active
|
||||
}
|
||||
}
|
||||
|
||||
InputField {
|
||||
id: inputField
|
||||
|
||||
pam: root.lock.pam
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: enterIcon.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
StateLayer {
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
|
||||
function onClicked(): void {
|
||||
root.lock.pam.passwd.start();
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: enterIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "arrow_forward"
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
font.weight: 500
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -Appearance.spacing.large
|
||||
|
||||
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: stateMessage
|
||||
|
||||
readonly property string msg: {
|
||||
if (Hypr.kbLayout !== Hypr.defaultKbLayout) {
|
||||
if (Hypr.capsLock && Hypr.numLock)
|
||||
return qsTr("Caps lock and Num lock are ON.\nKeyboard layout: %1").arg(Hypr.kbLayoutFull);
|
||||
if (Hypr.capsLock)
|
||||
return qsTr("Caps lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
|
||||
if (Hypr.numLock)
|
||||
return qsTr("Num lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
|
||||
return qsTr("Keyboard layout: %1").arg(Hypr.kbLayoutFull);
|
||||
}
|
||||
|
||||
if (Hypr.capsLock && Hypr.numLock)
|
||||
return qsTr("Caps lock and Num lock are ON.");
|
||||
if (Hypr.capsLock)
|
||||
return qsTr("Caps lock is ON.");
|
||||
if (Hypr.numLock)
|
||||
return qsTr("Num lock is ON.");
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
property bool shouldBeVisible
|
||||
|
||||
onMsgChanged: {
|
||||
if (msg) {
|
||||
if (opacity > 0) {
|
||||
animate = true;
|
||||
text = msg;
|
||||
animate = false;
|
||||
} else {
|
||||
text = msg;
|
||||
}
|
||||
shouldBeVisible = true;
|
||||
} else {
|
||||
shouldBeVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
scale: shouldBeVisible && !message.msg ? 1 : 0.7
|
||||
opacity: shouldBeVisible && !message.msg ? 1 : 0
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
animateProp: "opacity"
|
||||
|
||||
font.family: Appearance.font.family.mono
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
lineHeight: 1.2
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: message
|
||||
|
||||
readonly property Pam pam: root.lock.pam
|
||||
readonly property string msg: {
|
||||
if (pam.fprintState === "error")
|
||||
return qsTr("FP ERROR: %1").arg(pam.fprint.message);
|
||||
if (pam.state === "error")
|
||||
return qsTr("PW ERROR: %1").arg(pam.passwd.message);
|
||||
|
||||
if (pam.lockMessage)
|
||||
return pam.lockMessage;
|
||||
|
||||
if (pam.state === "max" && pam.fprintState === "max")
|
||||
return qsTr("Maximum password and fingerprint attempts reached.");
|
||||
if (pam.state === "max") {
|
||||
if (pam.fprint.available)
|
||||
return qsTr("Maximum password attempts reached. Please use fingerprint.");
|
||||
return qsTr("Maximum password attempts reached.");
|
||||
}
|
||||
if (pam.fprintState === "max")
|
||||
return qsTr("Maximum fingerprint attempts reached. Please use password.");
|
||||
|
||||
if (pam.state === "fail") {
|
||||
if (pam.fprint.available)
|
||||
return qsTr("Incorrect password. Please try again or use fingerprint.");
|
||||
return qsTr("Incorrect password. Please try again.");
|
||||
}
|
||||
if (pam.fprintState === "fail")
|
||||
return qsTr("Fingerprint not recognized (%1/%2). Please try again or use password.").arg(pam.fprint.tries).arg(Config.lock.maxFprintTries);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
scale: 0.7
|
||||
opacity: 0
|
||||
color: DynamicColors.palette.m3error
|
||||
|
||||
font.pointSize: Appearance.font.size.small
|
||||
font.family: Appearance.font.family.mono
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
onMsgChanged: {
|
||||
if (msg) {
|
||||
if (opacity > 0) {
|
||||
animate = true;
|
||||
text = msg;
|
||||
animate = false;
|
||||
|
||||
exitAnim.stop();
|
||||
if (scale < 1)
|
||||
appearAnim.restart();
|
||||
else
|
||||
flashAnim.restart();
|
||||
} else {
|
||||
text = msg;
|
||||
exitAnim.stop();
|
||||
appearAnim.restart();
|
||||
}
|
||||
} else {
|
||||
appearAnim.stop();
|
||||
flashAnim.stop();
|
||||
exitAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.lock.pam
|
||||
|
||||
function onFlashMsg(): void {
|
||||
exitAnim.stop();
|
||||
if (message.scale < 1)
|
||||
appearAnim.restart();
|
||||
else
|
||||
flashAnim.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Anim {
|
||||
id: appearAnim
|
||||
|
||||
target: message
|
||||
properties: "scale,opacity"
|
||||
to: 1
|
||||
onFinished: flashAnim.restart()
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: flashAnim
|
||||
|
||||
loops: 2
|
||||
|
||||
FlashAnim {
|
||||
to: 0.3
|
||||
}
|
||||
FlashAnim {
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: exitAnim
|
||||
|
||||
Anim {
|
||||
target: message
|
||||
property: "scale"
|
||||
to: 0.7
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
Anim {
|
||||
target: message
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component FlashAnim: NumberAnimation {
|
||||
target: message
|
||||
property: "opacity"
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.type: Easing.Linear
|
||||
}
|
||||
id: root
|
||||
|
||||
readonly property real centerScale: Math.min(1, (lock.screen?.height ?? 1440) / 1440)
|
||||
readonly property int centerWidth: Config.lock.sizes.centerWidth * centerScale
|
||||
required property var lock
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: false
|
||||
Layout.preferredWidth: centerWidth
|
||||
spacing: Appearance.spacing.large * 2
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.clock
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
text: Time.hourStr
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.clock
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
text: ":"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.clock
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
text: Time.minuteStr
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: -Appearance.padding.large * 2
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * root.centerScale)
|
||||
text: Time.format("dddd, d MMMM yyyy")
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Appearance.spacing.large * 2
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: root.centerWidth / 2
|
||||
implicitWidth: root.centerWidth / 2
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Math.floor(root.centerWidth / 4)
|
||||
text: "person"
|
||||
}
|
||||
|
||||
CachingImage {
|
||||
id: pfp
|
||||
|
||||
anchors.fill: parent
|
||||
path: `${Paths.home}/.face`
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
focus: true
|
||||
implicitHeight: input.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: root.centerWidth * 0.8
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (root.lock.unlocking)
|
||||
return;
|
||||
|
||||
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return)
|
||||
inputField.placeholder.animate = false;
|
||||
|
||||
root.lock.pam.handleKey(event);
|
||||
}
|
||||
onActiveFocusChanged: {
|
||||
if (!activeFocus)
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
parent.forceActiveFocus();
|
||||
}
|
||||
|
||||
cursorShape: Qt.IBeamCursor
|
||||
hoverEnabled: false
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.small
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Item {
|
||||
implicitHeight: fprintIcon.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: implicitHeight
|
||||
|
||||
MaterialIcon {
|
||||
id: fprintIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
color: root.lock.pam.fprint.tries >= Config.lock.maxFprintTries ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
|
||||
opacity: root.lock.pam.passwd.active ? 0 : 1
|
||||
text: {
|
||||
if (root.lock.pam.fprint.tries >= Config.lock.maxFprintTries)
|
||||
return "fingerprint_off";
|
||||
if (root.lock.pam.fprint.active)
|
||||
return "fingerprint";
|
||||
return "lock";
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CircularIndicator {
|
||||
anchors.fill: parent
|
||||
running: root.lock.pam.passwd.active
|
||||
}
|
||||
}
|
||||
|
||||
InputField {
|
||||
id: inputField
|
||||
|
||||
pam: root.lock.pam
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitHeight: enterIcon.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: implicitHeight
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.lock.pam.passwd.start();
|
||||
}
|
||||
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: enterIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
font.weight: 500
|
||||
text: "arrow_forward"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -Appearance.spacing.large
|
||||
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: stateMessage
|
||||
|
||||
readonly property string msg: {
|
||||
if (Hypr.kbLayout !== Hypr.defaultKbLayout) {
|
||||
if (Hypr.capsLock && Hypr.numLock)
|
||||
return qsTr("Caps lock and Num lock are ON.\nKeyboard layout: %1").arg(Hypr.kbLayoutFull);
|
||||
if (Hypr.capsLock)
|
||||
return qsTr("Caps lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
|
||||
if (Hypr.numLock)
|
||||
return qsTr("Num lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
|
||||
return qsTr("Keyboard layout: %1").arg(Hypr.kbLayoutFull);
|
||||
}
|
||||
|
||||
if (Hypr.capsLock && Hypr.numLock)
|
||||
return qsTr("Caps lock and Num lock are ON.");
|
||||
if (Hypr.capsLock)
|
||||
return qsTr("Caps lock is ON.");
|
||||
if (Hypr.numLock)
|
||||
return qsTr("Num lock is ON.");
|
||||
|
||||
return "";
|
||||
}
|
||||
property bool shouldBeVisible
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
animateProp: "opacity"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
lineHeight: 1.2
|
||||
opacity: shouldBeVisible && !message.msg ? 1 : 0
|
||||
scale: shouldBeVisible && !message.msg ? 1 : 0.7
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onMsgChanged: {
|
||||
if (msg) {
|
||||
if (opacity > 0) {
|
||||
animate = true;
|
||||
text = msg;
|
||||
animate = false;
|
||||
} else {
|
||||
text = msg;
|
||||
}
|
||||
shouldBeVisible = true;
|
||||
} else {
|
||||
shouldBeVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: message
|
||||
|
||||
readonly property string msg: {
|
||||
if (pam.fprintState === "error")
|
||||
return qsTr("FP ERROR: %1").arg(pam.fprint.message);
|
||||
if (pam.state === "error")
|
||||
return qsTr("PW ERROR: %1").arg(pam.passwd.message);
|
||||
|
||||
if (pam.lockMessage)
|
||||
return pam.lockMessage;
|
||||
|
||||
if (pam.state === "max" && pam.fprintState === "max")
|
||||
return qsTr("Maximum password and fingerprint attempts reached.");
|
||||
if (pam.state === "max") {
|
||||
if (pam.fprint.available)
|
||||
return qsTr("Maximum password attempts reached. Please use fingerprint.");
|
||||
return qsTr("Maximum password attempts reached.");
|
||||
}
|
||||
if (pam.fprintState === "max")
|
||||
return qsTr("Maximum fingerprint attempts reached. Please use password.");
|
||||
|
||||
if (pam.state === "fail") {
|
||||
if (pam.fprint.available)
|
||||
return qsTr("Incorrect password. Please try again or use fingerprint.");
|
||||
return qsTr("Incorrect password. Please try again.");
|
||||
}
|
||||
if (pam.fprintState === "fail")
|
||||
return qsTr("Fingerprint not recognized (%1/%2). Please try again or use password.").arg(pam.fprint.tries).arg(Config.lock.maxFprintTries);
|
||||
|
||||
return "";
|
||||
}
|
||||
readonly property Pam pam: root.lock.pam
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: DynamicColors.palette.m3error
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.small
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
opacity: 0
|
||||
scale: 0.7
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
onMsgChanged: {
|
||||
if (msg) {
|
||||
if (opacity > 0) {
|
||||
animate = true;
|
||||
text = msg;
|
||||
animate = false;
|
||||
|
||||
exitAnim.stop();
|
||||
if (scale < 1)
|
||||
appearAnim.restart();
|
||||
else
|
||||
flashAnim.restart();
|
||||
} else {
|
||||
text = msg;
|
||||
exitAnim.stop();
|
||||
appearAnim.restart();
|
||||
}
|
||||
} else {
|
||||
appearAnim.stop();
|
||||
flashAnim.stop();
|
||||
exitAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onFlashMsg(): void {
|
||||
exitAnim.stop();
|
||||
if (message.scale < 1)
|
||||
appearAnim.restart();
|
||||
else
|
||||
flashAnim.restart();
|
||||
}
|
||||
|
||||
target: root.lock.pam
|
||||
}
|
||||
|
||||
Anim {
|
||||
id: appearAnim
|
||||
|
||||
properties: "scale,opacity"
|
||||
target: message
|
||||
to: 1
|
||||
|
||||
onFinished: flashAnim.restart()
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: flashAnim
|
||||
|
||||
loops: 2
|
||||
|
||||
FlashAnim {
|
||||
to: 0.3
|
||||
}
|
||||
|
||||
FlashAnim {
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: exitAnim
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
property: "scale"
|
||||
target: message
|
||||
to: 0.7
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
property: "opacity"
|
||||
target: message
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component FlashAnim: NumberAnimation {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.type: Easing.Linear
|
||||
property: "opacity"
|
||||
target: message
|
||||
}
|
||||
}
|
||||
|
||||
+57
-59
@@ -5,78 +5,76 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var lock
|
||||
required property var lock
|
||||
|
||||
spacing: Appearance.spacing.large * 2
|
||||
spacing: Appearance.spacing.large * 2
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: weather.implicitHeight
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: weather.implicitHeight
|
||||
radius: Appearance.rounding.small
|
||||
topLeftRadius: Appearance.rounding.large
|
||||
|
||||
topLeftRadius: Appearance.rounding.large
|
||||
radius: Appearance.rounding.small
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
WeatherInfo {
|
||||
id: weather
|
||||
|
||||
WeatherInfo {
|
||||
id: weather
|
||||
|
||||
rootHeight: root.height
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: resources.implicitHeight
|
||||
|
||||
radius: Appearance.rounding.small
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
|
||||
Resources {
|
||||
id: resources
|
||||
rootHeight: root.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: resources.implicitHeight
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
bottomLeftRadius: Appearance.rounding.large
|
||||
radius: Appearance.rounding.small
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
Resources {
|
||||
id: resources
|
||||
|
||||
Media {
|
||||
id: media
|
||||
}
|
||||
}
|
||||
|
||||
lock: root.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomClippingRect {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
bottomLeftRadius: Appearance.rounding.large
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Center {
|
||||
lock: root.lock
|
||||
}
|
||||
Media {
|
||||
id: media
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
lock: root.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
topRightRadius: Appearance.rounding.large
|
||||
bottomRightRadius: Appearance.rounding.large
|
||||
radius: Appearance.rounding.small
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
Center {
|
||||
lock: root.lock
|
||||
}
|
||||
|
||||
NotifDock {
|
||||
lock: root.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
CustomRect {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
bottomRightRadius: Appearance.rounding.large
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.small
|
||||
topRightRadius: Appearance.rounding.large
|
||||
|
||||
NotifDock {
|
||||
lock: root.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+120
-123
@@ -9,156 +9,153 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
anchors.topMargin: Appearance.padding.large
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
anchors.topMargin: Appearance.padding.large
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
spacing: Appearance.spacing.small
|
||||
RowLayout {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: false
|
||||
spacing: Appearance.spacing.normal
|
||||
CustomRect {
|
||||
color: DynamicColors.palette.m3primary
|
||||
implicitHeight: prompt.implicitHeight + Appearance.padding.normal * 2
|
||||
implicitWidth: prompt.implicitWidth + Appearance.padding.normal * 2
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
implicitWidth: prompt.implicitWidth + Appearance.padding.normal * 2
|
||||
implicitHeight: prompt.implicitHeight + Appearance.padding.normal * 2
|
||||
MonoText {
|
||||
id: prompt
|
||||
|
||||
color: DynamicColors.palette.m3primary
|
||||
radius: Appearance.rounding.small
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
text: ">"
|
||||
}
|
||||
}
|
||||
|
||||
MonoText {
|
||||
id: prompt
|
||||
MonoText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
text: "caelestiafetch.sh"
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: ">"
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
}
|
||||
}
|
||||
WrappedLoader {
|
||||
Layout.fillHeight: true
|
||||
active: !iconLoader.active
|
||||
|
||||
MonoText {
|
||||
Layout.fillWidth: true
|
||||
text: "caelestiafetch.sh"
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
sourceComponent: OsLogo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
Layout.fillHeight: true
|
||||
active: !iconLoader.active
|
||||
RowLayout {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
spacing: height * 0.15
|
||||
|
||||
sourceComponent: OsLogo {}
|
||||
}
|
||||
}
|
||||
WrappedLoader {
|
||||
id: iconLoader
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: false
|
||||
spacing: height * 0.15
|
||||
Layout.fillHeight: true
|
||||
active: root.width > 320
|
||||
|
||||
WrappedLoader {
|
||||
id: iconLoader
|
||||
sourceComponent: OsLogo {
|
||||
}
|
||||
}
|
||||
|
||||
Layout.fillHeight: true
|
||||
active: root.width > 320
|
||||
ColumnLayout {
|
||||
Layout.bottomMargin: Appearance.padding.normal
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: iconLoader.active ? 0 : width * 0.1
|
||||
Layout.topMargin: Appearance.padding.normal
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
sourceComponent: OsLogo {}
|
||||
}
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: !batLoader.active && root.height > 200
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Appearance.padding.normal
|
||||
Layout.bottomMargin: Appearance.padding.normal
|
||||
Layout.leftMargin: iconLoader.active ? 0 : width * 0.1
|
||||
spacing: Appearance.spacing.normal
|
||||
sourceComponent: FetchText {
|
||||
text: `OS : ${SystemInfo.osPrettyName || SysInfo.osName}`
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: !batLoader.active && root.height > 200
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: root.height > (batLoader.active ? 200 : 110)
|
||||
|
||||
sourceComponent: FetchText {
|
||||
text: `OS : ${SystemInfo.osPrettyName || SysInfo.osName}`
|
||||
}
|
||||
}
|
||||
sourceComponent: FetchText {
|
||||
text: `WM : ${SystemInfo.wm}`
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: root.height > (batLoader.active ? 200 : 110)
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: !batLoader.active || root.height > 110
|
||||
|
||||
sourceComponent: FetchText {
|
||||
text: `WM : ${SystemInfo.wm}`
|
||||
}
|
||||
}
|
||||
sourceComponent: FetchText {
|
||||
text: `USER: ${SystemInfo.user}`
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: !batLoader.active || root.height > 110
|
||||
FetchText {
|
||||
text: `UP : ${SystemInfo.uptime}`
|
||||
}
|
||||
|
||||
sourceComponent: FetchText {
|
||||
text: `USER: ${SystemInfo.user}`
|
||||
}
|
||||
}
|
||||
WrappedLoader {
|
||||
id: batLoader
|
||||
|
||||
FetchText {
|
||||
text: `UP : ${SystemInfo.uptime}`
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
active: UPower.displayDevice.isLaptopBattery
|
||||
|
||||
WrappedLoader {
|
||||
id: batLoader
|
||||
sourceComponent: FetchText {
|
||||
text: `BATT: ${[UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state) ? "(+) " : ""}${Math.round(UPower.displayDevice.percentage * 100)}%`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
active: UPower.displayDevice.isLaptopBattery
|
||||
WrappedLoader {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
active: root.height > 180
|
||||
|
||||
sourceComponent: FetchText {
|
||||
text: `BATT: ${[UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state) ? "(+) " : ""}${Math.round(UPower.displayDevice.percentage * 100)}%`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceComponent: RowLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
WrappedLoader {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
active: root.height > 180
|
||||
Repeater {
|
||||
model: Math.max(0, Math.min(8, root.width / (Appearance.font.size.larger * 2 + Appearance.spacing.large)))
|
||||
|
||||
sourceComponent: RowLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
CustomRect {
|
||||
required property int index
|
||||
|
||||
Repeater {
|
||||
model: Math.max(0, Math.min(8, root.width / (Appearance.font.size.larger * 2 + Appearance.spacing.large)))
|
||||
color: DynamicColors.palette[`term${index}`]
|
||||
implicitHeight: Appearance.font.size.larger * 2
|
||||
implicitWidth: implicitHeight
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
required property int index
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: Appearance.font.size.larger * 2
|
||||
color: DynamicColors.palette[`term${index}`]
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component WrappedLoader: Loader {
|
||||
visible: active
|
||||
}
|
||||
|
||||
component OsLogo: ColoredIcon {
|
||||
source: SystemInfo.osLogo
|
||||
implicitSize: height
|
||||
color: DynamicColors.palette.m3primary
|
||||
layer.enabled: Config.lock.recolorLogo || SystemInfo.isDefaultLogo
|
||||
}
|
||||
|
||||
component FetchText: MonoText {
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
component MonoText: CustomText {
|
||||
font.family: Appearance.font.family.mono
|
||||
}
|
||||
component FetchText: MonoText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
}
|
||||
component MonoText: CustomText {
|
||||
font.family: Appearance.font.family.mono
|
||||
}
|
||||
component OsLogo: ColoredIcon {
|
||||
color: DynamicColors.palette.m3primary
|
||||
implicitSize: height
|
||||
layer.enabled: Config.lock.recolorLogo || SystemInfo.isDefaultLogo
|
||||
source: SystemInfo.osLogo
|
||||
}
|
||||
component WrappedLoader: Loader {
|
||||
visible: active
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
required property Lock lock
|
||||
readonly property bool enabled: !Players.list.some( p => p.isPlaying )
|
||||
|
||||
function handleIdleAction( action: var ): void {
|
||||
if ( !action )
|
||||
return;
|
||||
|
||||
if ( action === "lock" )
|
||||
lock.lock.locked = true;
|
||||
else if ( action === "unlock" )
|
||||
lock.lock.locked = false;
|
||||
else if ( typeof action === "string" )
|
||||
Hypr.dispatch( action );
|
||||
else
|
||||
Quickshell.execDetached( action );
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Config.general.idle.timeouts
|
||||
|
||||
IdleMonitor {
|
||||
required property var modelData
|
||||
|
||||
enabled: root.enabled && modelData.timeout > 0 ? true : false
|
||||
timeout: modelData.timeout
|
||||
onIsIdleChanged: root.handleIdleAction( isIdle ? modelData.idleAction : modelData.activeAction )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
required property Lock lock
|
||||
readonly property bool enabled: !Players.list.some( p => p.isPlaying )
|
||||
|
||||
function handleIdleAction( action: var ): void {
|
||||
if ( !action )
|
||||
return;
|
||||
|
||||
if ( action === "lock" )
|
||||
lock.lock.locked = true;
|
||||
else if ( action === "unlock" )
|
||||
lock.lock.locked = false;
|
||||
else if ( typeof action === "string" )
|
||||
Hypr.dispatch( action );
|
||||
else
|
||||
Quickshell.execDetached( action );
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Config.general.idle.timeouts
|
||||
|
||||
IdleMonitor {
|
||||
required property var modelData
|
||||
|
||||
enabled: root.enabled && modelData.timeout > 0 ? true : false
|
||||
timeout: modelData.timeout
|
||||
onIsIdleChanged: root.handleIdleAction( isIdle ? modelData.idleAction : modelData.activeAction )
|
||||
}
|
||||
}
|
||||
}
|
||||
+112
-116
@@ -9,142 +9,138 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Pam pam
|
||||
readonly property alias placeholder: placeholder
|
||||
property string buffer
|
||||
property string buffer
|
||||
required property Pam pam
|
||||
readonly property alias placeholder: placeholder
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
|
||||
clip: true
|
||||
Connections {
|
||||
function onBufferChanged(): void {
|
||||
if (root.pam.buffer.length > root.buffer.length) {
|
||||
charList.bindImWidth();
|
||||
} else if (root.pam.buffer.length === 0) {
|
||||
charList.implicitWidth = charList.implicitWidth;
|
||||
placeholder.animate = true;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.pam
|
||||
root.buffer = root.pam.buffer;
|
||||
}
|
||||
|
||||
function onBufferChanged(): void {
|
||||
if (root.pam.buffer.length > root.buffer.length) {
|
||||
charList.bindImWidth();
|
||||
} else if (root.pam.buffer.length === 0) {
|
||||
charList.implicitWidth = charList.implicitWidth;
|
||||
placeholder.animate = true;
|
||||
}
|
||||
target: root.pam
|
||||
}
|
||||
|
||||
root.buffer = root.pam.buffer;
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
id: placeholder
|
||||
|
||||
CustomText {
|
||||
id: placeholder
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
color: root.pam.passwd.active ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
opacity: root.buffer ? 0 : 1
|
||||
text: {
|
||||
if (root.pam.passwd.active)
|
||||
return qsTr("Loading...");
|
||||
if (root.pam.state === "max")
|
||||
return qsTr("You have reached the maximum number of tries");
|
||||
return qsTr("Enter your password");
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text: {
|
||||
if (root.pam.passwd.active)
|
||||
return qsTr("Loading...");
|
||||
if (root.pam.state === "max")
|
||||
return qsTr("You have reached the maximum number of tries");
|
||||
return qsTr("Enter your password");
|
||||
}
|
||||
ListView {
|
||||
id: charList
|
||||
|
||||
animate: true
|
||||
color: root.pam.passwd.active ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.family: Appearance.font.family.mono
|
||||
readonly property int fullWidth: count * (implicitHeight + spacing) - spacing
|
||||
|
||||
opacity: root.buffer ? 0 : 1
|
||||
function bindImWidth(): void {
|
||||
imWidthBehavior.enabled = false;
|
||||
implicitWidth = Qt.binding(() => fullWidth);
|
||||
imWidthBehavior.enabled = true;
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
|
||||
implicitHeight: Appearance.font.size.normal
|
||||
implicitWidth: fullWidth
|
||||
interactive: false
|
||||
orientation: Qt.Horizontal
|
||||
spacing: Appearance.spacing.small / 2
|
||||
|
||||
ListView {
|
||||
id: charList
|
||||
delegate: CustomRect {
|
||||
id: ch
|
||||
|
||||
readonly property int fullWidth: count * (implicitHeight + spacing) - spacing
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
implicitHeight: charList.implicitHeight
|
||||
implicitWidth: implicitHeight
|
||||
opacity: 0
|
||||
radius: Appearance.rounding.small / 2
|
||||
scale: 0
|
||||
|
||||
function bindImWidth(): void {
|
||||
imWidthBehavior.enabled = false;
|
||||
implicitWidth = Qt.binding(() => fullWidth);
|
||||
imWidthBehavior.enabled = true;
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
|
||||
Component.onCompleted: {
|
||||
opacity = 1;
|
||||
scale = 1;
|
||||
}
|
||||
ListView.onRemove: removeAnim.start()
|
||||
|
||||
implicitWidth: fullWidth
|
||||
implicitHeight: Appearance.font.size.normal
|
||||
SequentialAnimation {
|
||||
id: removeAnim
|
||||
|
||||
orientation: Qt.Horizontal
|
||||
spacing: Appearance.spacing.small / 2
|
||||
interactive: false
|
||||
PropertyAction {
|
||||
property: "ListView.delayRemove"
|
||||
target: ch
|
||||
value: true
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: root.buffer.split("")
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: ch
|
||||
to: 0
|
||||
}
|
||||
|
||||
delegate: CustomRect {
|
||||
id: ch
|
||||
Anim {
|
||||
property: "scale"
|
||||
target: ch
|
||||
to: 0.5
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: charList.implicitHeight
|
||||
PropertyAction {
|
||||
property: "ListView.delayRemove"
|
||||
target: ch
|
||||
value: false
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on implicitWidth {
|
||||
id: imWidthBehavior
|
||||
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
radius: Appearance.rounding.small / 2
|
||||
|
||||
opacity: 0
|
||||
scale: 0
|
||||
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 {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
id: imWidthBehavior
|
||||
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: root.buffer.split("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ Scope {
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
|
||||
signal unlock
|
||||
signal requestLock
|
||||
signal unlock
|
||||
|
||||
LockSurface {
|
||||
id: lockSurface
|
||||
|
||||
lock: lock
|
||||
pam: pam
|
||||
}
|
||||
@@ -35,16 +36,17 @@ Scope {
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "lock"
|
||||
|
||||
function lock() {
|
||||
return lock.locked = true;
|
||||
}
|
||||
|
||||
target: "lock"
|
||||
}
|
||||
|
||||
CustomShortcut {
|
||||
name: "lock"
|
||||
description: "Lock the current session"
|
||||
name: "lock"
|
||||
|
||||
onPressed: {
|
||||
lock.locked = true;
|
||||
}
|
||||
|
||||
+165
-154
@@ -8,185 +8,196 @@ import qs.Helpers
|
||||
import qs.Components
|
||||
|
||||
WlSessionLockSurface {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property WlSessionLock lock
|
||||
required property Pam pam
|
||||
required property WlSessionLock lock
|
||||
required property Pam pam
|
||||
readonly property alias unlocking: unlockAnim.running
|
||||
|
||||
readonly property alias unlocking: unlockAnim.running
|
||||
color: "transparent"
|
||||
|
||||
color: "transparent"
|
||||
Connections {
|
||||
function onUnlock(): void {
|
||||
unlockAnim.start();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.lock
|
||||
target: root.lock
|
||||
}
|
||||
|
||||
function onUnlock(): void {
|
||||
unlockAnim.start();
|
||||
}
|
||||
}
|
||||
SequentialAnimation {
|
||||
id: unlockAnim
|
||||
|
||||
SequentialAnimation {
|
||||
id: unlockAnim
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
target: lockContent
|
||||
to: lockContent.size
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: lockContent
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
to: lockContent.size
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
Anim {
|
||||
target: lockBg
|
||||
property: "radius"
|
||||
to: lockContent.radius
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "scale"
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
Anim {
|
||||
target: lockIcon
|
||||
property: "opacity"
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
SequentialAnimation {
|
||||
PauseAnimation {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: root.lock
|
||||
property: "locked"
|
||||
value: false
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
property: "radius"
|
||||
target: lockBg
|
||||
to: lockContent.radius
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: initAnim
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "scale"
|
||||
target: content
|
||||
to: 0
|
||||
}
|
||||
|
||||
running: true
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
property: "opacity"
|
||||
target: content
|
||||
to: 0
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "scale"
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: lockIcon
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "opacity"
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "scale"
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
Anim {
|
||||
target: lockBg
|
||||
property: "radius"
|
||||
to: Appearance.rounding.large * 1.5
|
||||
}
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "implicitWidth"
|
||||
to: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "implicitHeight"
|
||||
to: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
property: "opacity"
|
||||
target: lockIcon
|
||||
to: 1
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
PauseAnimation {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: lockContent
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyAction {
|
||||
property: "locked"
|
||||
target: root.lock
|
||||
value: false
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: initAnim
|
||||
|
||||
running: true
|
||||
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
property: "scale"
|
||||
target: lockContent
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: lockIcon
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: content
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "scale"
|
||||
target: content
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "radius"
|
||||
target: lockBg
|
||||
to: Appearance.rounding.large * 1.5
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "implicitWidth"
|
||||
target: lockContent
|
||||
to: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "implicitHeight"
|
||||
target: lockContent
|
||||
to: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: background
|
||||
|
||||
anchors.fill: parent
|
||||
source: WallpaperPath.lockscreenBg
|
||||
}
|
||||
|
||||
Item {
|
||||
id: lockContent
|
||||
Item {
|
||||
id: lockContent
|
||||
|
||||
readonly property int size: lockIcon.implicitHeight + Appearance.padding.large * 4
|
||||
readonly property int radius: size / 4 * Appearance.rounding.scale
|
||||
readonly property int radius: size / 4 * Appearance.rounding.scale
|
||||
readonly property int size: lockIcon.implicitHeight + Appearance.padding.large * 4
|
||||
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: size
|
||||
implicitHeight: size
|
||||
anchors.centerIn: parent
|
||||
implicitHeight: size
|
||||
implicitWidth: size
|
||||
scale: 0
|
||||
|
||||
scale: 0
|
||||
CustomRect {
|
||||
id: lockBg
|
||||
|
||||
CustomRect {
|
||||
id: lockBg
|
||||
|
||||
anchors.fill: parent
|
||||
color: DynamicColors.palette.m3surface
|
||||
anchors.fill: parent
|
||||
color: DynamicColors.palette.m3surface
|
||||
layer.enabled: true
|
||||
opacity: DynamicColors.transparency.enabled ? DynamicColors.transparency.base : 1
|
||||
radius: lockContent.radius
|
||||
opacity: DynamicColors.transparency.enabled ? DynamicColors.transparency.base : 1
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
blurMax: 15
|
||||
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, 0.7)
|
||||
}
|
||||
}
|
||||
layer.effect: MultiEffect {
|
||||
blurMax: 15
|
||||
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, 0.7)
|
||||
shadowEnabled: true
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: lockIcon
|
||||
MaterialIcon {
|
||||
id: lockIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "lock"
|
||||
font.pointSize: Appearance.font.size.extraLarge * 4
|
||||
font.bold: true
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
font.bold: true
|
||||
font.pointSize: Appearance.font.size.extraLarge * 4
|
||||
text: "lock"
|
||||
}
|
||||
|
||||
Content {
|
||||
id: content
|
||||
Content {
|
||||
id: content
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio - Appearance.padding.large * 2
|
||||
height: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult - Appearance.padding.large * 2
|
||||
|
||||
lock: root
|
||||
opacity: 0
|
||||
scale: 0
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
height: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult - Appearance.padding.large * 2
|
||||
lock: root
|
||||
opacity: 0
|
||||
scale: 0
|
||||
width: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio - Appearance.padding.large * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+156
-159
@@ -8,198 +8,195 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var lock
|
||||
required property var lock
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: Players.active?.trackArtUrl ?? ""
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
layer.enabled: true
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
source: Players.active?.trackArtUrl ?? ""
|
||||
sourceSize.height: height
|
||||
sourceSize.width: width
|
||||
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: mask
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: mask
|
||||
}
|
||||
Rectangle {
|
||||
id: mask
|
||||
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Horizontal
|
||||
|
||||
Rectangle {
|
||||
id: mask
|
||||
GradientStop {
|
||||
color: Qt.rgba(0, 0, 0, 0.5)
|
||||
position: 0
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
GradientStop {
|
||||
color: Qt.rgba(0, 0, 0, 0.2)
|
||||
position: 0.4
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Horizontal
|
||||
GradientStop {
|
||||
color: Qt.rgba(0, 0, 0, 0)
|
||||
position: 0.8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: Qt.rgba(0, 0, 0, 0.5)
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.4
|
||||
color: Qt.rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.8
|
||||
color: Qt.rgba(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
CustomText {
|
||||
Layout.bottomMargin: Appearance.spacing.larger
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
text: qsTr("Now playing")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
Layout.bottomMargin: Appearance.spacing.larger
|
||||
text: qsTr("Now playing")
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
}
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3primary
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.weight: 600
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Players.active?.trackArtist ?? qsTr("No media")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
text: Players.active?.trackArtist ?? qsTr("No media")
|
||||
color: DynamicColors.palette.m3primary
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 600
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Players.active?.trackTitle ?? qsTr("No media")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
text: Players.active?.trackTitle ?? qsTr("No media")
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
font.family: Appearance.font.family.mono
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
Layout.topMargin: Appearance.spacing.large * 1.2
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Appearance.spacing.large * 1.2
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
PlayerControl {
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canGoPrevious)
|
||||
Players.active.previous();
|
||||
}
|
||||
|
||||
spacing: Appearance.spacing.large
|
||||
icon: "skip_previous"
|
||||
}
|
||||
|
||||
PlayerControl {
|
||||
icon: "skip_previous"
|
||||
PlayerControl {
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canTogglePlaying)
|
||||
Players.active.togglePlaying();
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canGoPrevious)
|
||||
Players.active.previous();
|
||||
}
|
||||
}
|
||||
active: Players.active?.isPlaying ?? false
|
||||
animate: true
|
||||
colour: "Primary"
|
||||
icon: active ? "pause" : "play_arrow"
|
||||
level: active ? 2 : 1
|
||||
}
|
||||
|
||||
PlayerControl {
|
||||
animate: true
|
||||
icon: active ? "pause" : "play_arrow"
|
||||
colour: "Primary"
|
||||
level: active ? 2 : 1
|
||||
active: Players.active?.isPlaying ?? false
|
||||
PlayerControl {
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canGoNext)
|
||||
Players.active.next();
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canTogglePlaying)
|
||||
Players.active.togglePlaying();
|
||||
}
|
||||
}
|
||||
icon: "skip_next"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlayerControl {
|
||||
icon: "skip_next"
|
||||
component PlayerControl: CustomRect {
|
||||
id: control
|
||||
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canGoNext)
|
||||
Players.active.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
property bool active
|
||||
property alias animate: controlIcon.animate
|
||||
property string colour: "Secondary"
|
||||
property alias icon: controlIcon.text
|
||||
property int level: 1
|
||||
|
||||
component PlayerControl: CustomRect {
|
||||
id: control
|
||||
function onClicked(): void {
|
||||
}
|
||||
|
||||
property alias animate: controlIcon.animate
|
||||
property alias icon: controlIcon.text
|
||||
property bool active
|
||||
property string colour: "Secondary"
|
||||
property int level: 1
|
||||
Layout.preferredWidth: implicitWidth + (controlState.pressed ? Appearance.padding.normal * 2 : active ? Appearance.padding.small * 2 : 0)
|
||||
color: active ? DynamicColors.palette[`m3${colour.toLowerCase()}`] : DynamicColors.palette[`m3${colour.toLowerCase()}Container`]
|
||||
implicitHeight: controlIcon.implicitHeight + Appearance.padding.normal * 2
|
||||
implicitWidth: controlIcon.implicitWidth + Appearance.padding.large * 2
|
||||
radius: active || controlState.pressed ? Appearance.rounding.small : Appearance.rounding.normal
|
||||
|
||||
function onClicked(): void {
|
||||
}
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Layout.preferredWidth: implicitWidth + (controlState.pressed ? Appearance.padding.normal * 2 : active ? Appearance.padding.small * 2 : 0)
|
||||
implicitWidth: controlIcon.implicitWidth + Appearance.padding.large * 2
|
||||
implicitHeight: controlIcon.implicitHeight + Appearance.padding.normal * 2
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
level: controlState.containsMouse && !controlState.pressed ? control.level + 1 : control.level
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
}
|
||||
|
||||
color: active ? DynamicColors.palette[`m3${colour.toLowerCase()}`] : DynamicColors.palette[`m3${colour.toLowerCase()}Container`]
|
||||
radius: active || controlState.pressed ? Appearance.rounding.small : Appearance.rounding.normal
|
||||
StateLayer {
|
||||
id: controlState
|
||||
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
level: controlState.containsMouse && !controlState.pressed ? control.level + 1 : control.level
|
||||
}
|
||||
function onClicked(): void {
|
||||
control.onClicked();
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
id: controlState
|
||||
color: control.active ? DynamicColors.palette[`m3on${control.colour}`] : DynamicColors.palette[`m3on${control.colour}Container`]
|
||||
}
|
||||
|
||||
color: control.active ? DynamicColors.palette[`m3on${control.colour}`] : DynamicColors.palette[`m3on${control.colour}Container`]
|
||||
MaterialIcon {
|
||||
id: controlIcon
|
||||
|
||||
function onClicked(): void {
|
||||
control.onClicked();
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
color: control.active ? DynamicColors.palette[`m3on${control.colour}`] : DynamicColors.palette[`m3on${control.colour}Container`]
|
||||
fill: control.active ? 1 : 0
|
||||
font.pointSize: Appearance.font.size.large
|
||||
|
||||
MaterialIcon {
|
||||
id: controlIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: control.active ? DynamicColors.palette[`m3on${control.colour}`] : DynamicColors.palette[`m3on${control.colour}Container`]
|
||||
font.pointSize: Appearance.font.size.large
|
||||
fill: control.active ? 1 : 0
|
||||
|
||||
Behavior on fill {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+111
-115
@@ -11,135 +11,131 @@ import qs.Config
|
||||
import qs.Daemons
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var lock
|
||||
required property var lock
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
spacing: Appearance.spacing.smaller
|
||||
|
||||
spacing: Appearance.spacing.smaller
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
ClippingRectangle {
|
||||
id: clipRect
|
||||
|
||||
ClippingRectangle {
|
||||
id: clipRect
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Loader {
|
||||
active: opacity > 0
|
||||
anchors.centerIn: parent
|
||||
opacity: NotifServer.list.length > 0 ? 0 : 1
|
||||
|
||||
radius: Appearance.rounding.small
|
||||
color: "transparent"
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: opacity > 0
|
||||
opacity: NotifServer.list.length > 0 ? 0 : 1
|
||||
Image {
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
layer.enabled: true
|
||||
source: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/dino.png`)
|
||||
sourceSize.width: clipRect.width * 0.8
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
layer.effect: Coloriser {
|
||||
brightness: 1
|
||||
colorizationColor: DynamicColors.palette.m3outlineVariant
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
asynchronous: true
|
||||
source: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/dino.png`)
|
||||
fillMode: Image.PreserveAspectFit
|
||||
sourceSize.width: clipRect.width * 0.8
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.weight: 500
|
||||
text: qsTr("No Notifications")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: Coloriser {
|
||||
colorizationColor: DynamicColors.palette.m3outlineVariant
|
||||
brightness: 1
|
||||
}
|
||||
}
|
||||
CustomListView {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("No Notifications")
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
}
|
||||
}
|
||||
add: Transition {
|
||||
Anim {
|
||||
from: 0
|
||||
property: "opacity"
|
||||
to: 1
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
from: 0
|
||||
property: "scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
delegate: NotifGroup {
|
||||
}
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
|
||||
CustomListView {
|
||||
anchors.fill: parent
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const list = NotifServer.notClosed.map(n => [n.appName, null]);
|
||||
return [...new Map(list).keys()];
|
||||
}
|
||||
}
|
||||
move: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
|
||||
spacing: Appearance.spacing.small
|
||||
clip: true
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
remove: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const list = NotifServer.notClosed.map(n => [n.appName, null]);
|
||||
return [...new Map(list).keys()];
|
||||
}
|
||||
}
|
||||
|
||||
delegate: NotifGroup {}
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
property: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
property: "scale"
|
||||
to: 0.6
|
||||
}
|
||||
}
|
||||
|
||||
move: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
property: "y"
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
property: "y"
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
property: "scale"
|
||||
to: 0.6
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+247
-248
@@ -12,305 +12,304 @@ import qs.Config
|
||||
import qs.Daemons
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property string modelData
|
||||
readonly property string appIcon: notifs.find(n => n.appIcon.length > 0)?.appIcon ?? ""
|
||||
property bool expanded
|
||||
readonly property string image: notifs.find(n => n.image.length > 0)?.image ?? ""
|
||||
required property string modelData
|
||||
readonly property list<var> notifs: NotifServer.list.filter(notif => notif.appName === modelData)
|
||||
readonly property string urgency: notifs.some(n => n.urgency === NotificationUrgency.Critical) ? "critical" : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? "normal" : "low"
|
||||
|
||||
readonly property list<var> notifs: NotifServer.list.filter(notif => notif.appName === modelData)
|
||||
readonly property string image: notifs.find(n => n.image.length > 0)?.image ?? ""
|
||||
readonly property string appIcon: notifs.find(n => n.appIcon.length > 0)?.appIcon ?? ""
|
||||
readonly property string urgency: notifs.some(n => n.urgency === NotificationUrgency.Critical) ? "critical" : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? "normal" : "low"
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
clip: true
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3secondaryContainer : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitHeight: content.implicitHeight + Appearance.padding.normal * 2
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
property bool expanded
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
implicitHeight: content.implicitHeight + Appearance.padding.normal * 2
|
||||
RowLayout {
|
||||
id: content
|
||||
|
||||
clip: true
|
||||
radius: Appearance.rounding.normal
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3secondaryContainer : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Appearance.padding.normal
|
||||
Component {
|
||||
id: imageComp
|
||||
|
||||
spacing: Appearance.spacing.normal
|
||||
Image {
|
||||
asynchronous: true
|
||||
cache: false
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
height: Config.notifs.sizes.image
|
||||
source: Qt.resolvedUrl(root.image)
|
||||
width: Config.notifs.sizes.image
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
Component {
|
||||
id: appIconComp
|
||||
|
||||
Component {
|
||||
id: imageComp
|
||||
ColoredIcon {
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
source: Qt.resolvedUrl(root.image)
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
cache: false
|
||||
asynchronous: true
|
||||
width: Config.notifs.sizes.image
|
||||
height: Config.notifs.sizes.image
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: materialIconComp
|
||||
|
||||
Component {
|
||||
id: appIconComp
|
||||
MaterialIcon {
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: Icons.getNotifIcon(root.notifs[0]?.summary, root.urgency)
|
||||
}
|
||||
}
|
||||
|
||||
ColoredIcon {
|
||||
implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
}
|
||||
}
|
||||
ClippingRectangle {
|
||||
anchors.fill: parent
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : root.urgency === "low" ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 3) : DynamicColors.palette.m3secondaryContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Component {
|
||||
id: materialIconComp
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
text: Icons.getNotifIcon(root.notifs[0]?.summary, root.urgency)
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
active: root.appIcon && root.image
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
|
||||
ClippingRectangle {
|
||||
anchors.fill: parent
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : root.urgency === "low" ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 3) : DynamicColors.palette.m3secondaryContainer
|
||||
radius: Appearance.rounding.full
|
||||
sourceComponent: CustomRect {
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : root.urgency === "low" ? DynamicColors.palette.m3surfaceContainerHighest : DynamicColors.palette.m3secondaryContainer
|
||||
implicitHeight: Config.notifs.sizes.badge
|
||||
implicitWidth: Config.notifs.sizes.badge
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
|
||||
}
|
||||
}
|
||||
ColoredIcon {
|
||||
anchors.centerIn: parent
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
active: root.appIcon && root.image
|
||||
ColumnLayout {
|
||||
Layout.bottomMargin: -Appearance.padding.small / 2 - (root.expanded ? 0 : spacing)
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -Appearance.padding.small
|
||||
spacing: Math.round(Appearance.spacing.small / 2)
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
implicitWidth: Config.notifs.sizes.badge
|
||||
implicitHeight: Config.notifs.sizes.badge
|
||||
RowLayout {
|
||||
Layout.bottomMargin: -parent.spacing
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.smaller
|
||||
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : root.urgency === "low" ? DynamicColors.palette.m3surfaceContainerHighest : DynamicColors.palette.m3secondaryContainer
|
||||
radius: Appearance.rounding.full
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.modelData
|
||||
}
|
||||
|
||||
ColoredIcon {
|
||||
anchors.centerIn: parent
|
||||
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.notifs[0]?.timeStr ?? ""
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: -Appearance.padding.small
|
||||
Layout.bottomMargin: -Appearance.padding.small / 2 - (root.expanded ? 0 : spacing)
|
||||
Layout.fillWidth: true
|
||||
spacing: Math.round(Appearance.spacing.small / 2)
|
||||
CustomRect {
|
||||
Layout.preferredWidth: root.notifs.length > Config.notifs.groupPreviewNum ? implicitWidth : 0
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
implicitHeight: groupCount.implicitHeight + Appearance.padding.small
|
||||
implicitWidth: expandBtn.implicitWidth + Appearance.padding.smaller * 2
|
||||
opacity: root.notifs.length > Config.notifs.groupPreviewNum ? 1 : 0
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
RowLayout {
|
||||
Layout.bottomMargin: -parent.spacing
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.smaller
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: root.modelData
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.small
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.expanded = !root.expanded;
|
||||
}
|
||||
|
||||
CustomText {
|
||||
animate: true
|
||||
text: root.notifs[0]?.timeStr ?? ""
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
implicitWidth: expandBtn.implicitWidth + Appearance.padding.smaller * 2
|
||||
implicitHeight: groupCount.implicitHeight + Appearance.padding.small
|
||||
RowLayout {
|
||||
id: expandBtn
|
||||
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
radius: Appearance.rounding.full
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.small / 2
|
||||
|
||||
opacity: root.notifs.length > Config.notifs.groupPreviewNum ? 1 : 0
|
||||
Layout.preferredWidth: root.notifs.length > Config.notifs.groupPreviewNum ? implicitWidth : 0
|
||||
CustomText {
|
||||
id: groupCount
|
||||
|
||||
StateLayer {
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
Layout.leftMargin: Appearance.padding.small / 2
|
||||
animate: true
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.notifs.length
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
root.expanded = !root.expanded;
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
Layout.rightMargin: -Appearance.padding.small / 2
|
||||
animate: true
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
text: root.expanded ? "expand_less" : "expand_more"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: expandBtn
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.notifs.slice(0, Config.notifs.groupPreviewNum)
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.small / 2
|
||||
NotifLine {
|
||||
id: notif
|
||||
|
||||
CustomText {
|
||||
id: groupCount
|
||||
ParallelAnimation {
|
||||
running: true
|
||||
|
||||
Layout.leftMargin: Appearance.padding.small / 2
|
||||
animate: true
|
||||
text: root.notifs.length
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
Anim {
|
||||
from: 0
|
||||
property: "opacity"
|
||||
target: notif
|
||||
to: 1
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.rightMargin: -Appearance.padding.small / 2
|
||||
animate: true
|
||||
text: root.expanded ? "expand_less" : "expand_more"
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
from: 0.7
|
||||
property: "scale"
|
||||
target: notif
|
||||
to: 1
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
Anim {
|
||||
from: 0
|
||||
property: "preferredHeight"
|
||||
target: notif.Layout
|
||||
to: notif.implicitHeight
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
running: notif.modelData.closed
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.notifs.slice(0, Config.notifs.groupPreviewNum)
|
||||
}
|
||||
onFinished: notif.modelData.unlock(notif)
|
||||
|
||||
NotifLine {
|
||||
id: notif
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: notif
|
||||
to: 0
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: true
|
||||
Anim {
|
||||
property: "scale"
|
||||
target: notif
|
||||
to: 0.7
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: notif
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: notif
|
||||
property: "scale"
|
||||
from: 0.7
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: notif.Layout
|
||||
property: "preferredHeight"
|
||||
from: 0
|
||||
to: notif.implicitHeight
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
property: "preferredHeight"
|
||||
target: notif.Layout
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: notif.modelData.closed
|
||||
onFinished: notif.modelData.unlock(notif)
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: root.expanded ? implicitHeight : 0
|
||||
active: opacity > 0
|
||||
opacity: root.expanded ? 1 : 0
|
||||
|
||||
Anim {
|
||||
target: notif
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: notif
|
||||
property: "scale"
|
||||
to: 0.7
|
||||
}
|
||||
Anim {
|
||||
target: notif.Layout
|
||||
property: "preferredHeight"
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.notifs.slice(Config.notifs.groupPreviewNum)
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
NotifLine {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opacity: root.expanded ? 1 : 0
|
||||
Layout.preferredHeight: root.expanded ? implicitHeight : 0
|
||||
active: opacity > 0
|
||||
component NotifLine: CustomText {
|
||||
id: notifLine
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.notifs.slice(Config.notifs.groupPreviewNum)
|
||||
}
|
||||
required property NotifServer.Notif modelData
|
||||
|
||||
NotifLine {}
|
||||
}
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
text: {
|
||||
const summary = modelData.summary.replace(/\n/g, " ");
|
||||
const body = modelData.body.replace(/\n/g, " ");
|
||||
const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline;
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (metrics.text === metrics.elidedText)
|
||||
return `${summary} <span style='color:${color}'>${body}</span>`;
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
const t = metrics.elidedText.length - 3;
|
||||
if (t < summary.length)
|
||||
return `${summary.slice(0, t)}...`;
|
||||
|
||||
component NotifLine: CustomText {
|
||||
id: notifLine
|
||||
return `${summary} <span style='color:${color}'>${body.slice(0, t - summary.length)}...</span>`;
|
||||
}
|
||||
textFormat: Text.MarkdownText
|
||||
|
||||
required property NotifServer.Notif modelData
|
||||
Component.onCompleted: modelData.lock(this)
|
||||
Component.onDestruction: modelData.unlock(this)
|
||||
|
||||
Layout.fillWidth: true
|
||||
textFormat: Text.MarkdownText
|
||||
text: {
|
||||
const summary = modelData.summary.replace(/\n/g, " ");
|
||||
const body = modelData.body.replace(/\n/g, " ");
|
||||
const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline;
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
|
||||
if (metrics.text === metrics.elidedText)
|
||||
return `${summary} <span style='color:${color}'>${body}</span>`;
|
||||
|
||||
const t = metrics.elidedText.length - 3;
|
||||
if (t < summary.length)
|
||||
return `${summary.slice(0, t)}...`;
|
||||
|
||||
return `${summary} <span style='color:${color}'>${body.slice(0, t - summary.length)}...</span>`;
|
||||
}
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
|
||||
Component.onCompleted: modelData.lock(this)
|
||||
Component.onDestruction: modelData.unlock(this)
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
|
||||
text: `${notifLine.modelData.summary} ${notifLine.modelData.body}`.replace(/\n/g, " ")
|
||||
font.pointSize: notifLine.font.pointSize
|
||||
font.family: notifLine.font.family
|
||||
elideWidth: notifLine.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
elide: Text.ElideRight
|
||||
elideWidth: notifLine.width
|
||||
font.family: notifLine.font.family
|
||||
font.pointSize: notifLine.font.pointSize
|
||||
text: `${notifLine.modelData.summary} ${notifLine.modelData.body}`.replace(/\n/g, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+150
-149
@@ -6,188 +6,189 @@ import QtQuick
|
||||
import qs.Config
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property WlSessionLock lock
|
||||
property string buffer
|
||||
readonly property alias fprint: fprint
|
||||
property string fprintState
|
||||
required property WlSessionLock lock
|
||||
property string lockMessage
|
||||
readonly property alias passwd: passwd
|
||||
property string state
|
||||
|
||||
readonly property alias passwd: passwd
|
||||
readonly property alias fprint: fprint
|
||||
property string lockMessage
|
||||
property string state
|
||||
property string fprintState
|
||||
property string buffer
|
||||
signal flashMsg
|
||||
|
||||
signal flashMsg
|
||||
function handleKey(event: KeyEvent): void {
|
||||
if (passwd.active || state === "max")
|
||||
return;
|
||||
|
||||
function handleKey(event: KeyEvent): void {
|
||||
if (passwd.active || state === "max")
|
||||
return;
|
||||
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
|
||||
passwd.start();
|
||||
} else if (event.key === Qt.Key_Backspace) {
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
buffer = "";
|
||||
} else {
|
||||
buffer = buffer.slice(0, -1);
|
||||
}
|
||||
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
|
||||
// No illegal characters (you are insane if you use unicode in your password)
|
||||
buffer += event.text;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
|
||||
passwd.start();
|
||||
} else if (event.key === Qt.Key_Backspace) {
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
buffer = "";
|
||||
} else {
|
||||
buffer = buffer.slice(0, -1);
|
||||
}
|
||||
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
|
||||
// No illegal characters (you are insane if you use unicode in your password)
|
||||
buffer += event.text;
|
||||
}
|
||||
}
|
||||
PamContext {
|
||||
id: passwd
|
||||
|
||||
PamContext {
|
||||
id: passwd
|
||||
config: "passwd"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||
|
||||
config: "passwd"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||
onCompleted: res => {
|
||||
if (res === PamResult.Success)
|
||||
return root.lock.unlock();
|
||||
|
||||
onMessageChanged: {
|
||||
if (message.startsWith("The account is locked"))
|
||||
root.lockMessage = message;
|
||||
else if (root.lockMessage && message.endsWith(" left to unlock)"))
|
||||
root.lockMessage += "\n" + message;
|
||||
}
|
||||
if (res === PamResult.Error)
|
||||
root.state = "error";
|
||||
else if (res === PamResult.MaxTries)
|
||||
root.state = "max";
|
||||
else if (res === PamResult.Failed)
|
||||
root.state = "fail";
|
||||
|
||||
onResponseRequiredChanged: {
|
||||
if (!responseRequired)
|
||||
return;
|
||||
root.flashMsg();
|
||||
stateReset.restart();
|
||||
}
|
||||
onMessageChanged: {
|
||||
if (message.startsWith("The account is locked"))
|
||||
root.lockMessage = message;
|
||||
else if (root.lockMessage && message.endsWith(" left to unlock)"))
|
||||
root.lockMessage += "\n" + message;
|
||||
}
|
||||
onResponseRequiredChanged: {
|
||||
if (!responseRequired)
|
||||
return;
|
||||
|
||||
respond(root.buffer);
|
||||
root.buffer = "";
|
||||
}
|
||||
respond(root.buffer);
|
||||
root.buffer = "";
|
||||
}
|
||||
}
|
||||
|
||||
onCompleted: res => {
|
||||
if (res === PamResult.Success)
|
||||
return root.lock.unlock();
|
||||
PamContext {
|
||||
id: fprint
|
||||
|
||||
if (res === PamResult.Error)
|
||||
root.state = "error";
|
||||
else if (res === PamResult.MaxTries)
|
||||
root.state = "max";
|
||||
else if (res === PamResult.Failed)
|
||||
root.state = "fail";
|
||||
property bool available
|
||||
property int errorTries
|
||||
property int tries
|
||||
|
||||
root.flashMsg();
|
||||
stateReset.restart();
|
||||
}
|
||||
}
|
||||
function checkAvail(): void {
|
||||
if (!available || !Config.lock.enableFprint || !root.lock.secure) {
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
PamContext {
|
||||
id: fprint
|
||||
tries = 0;
|
||||
errorTries = 0;
|
||||
start();
|
||||
}
|
||||
|
||||
property bool available
|
||||
property int tries
|
||||
property int errorTries
|
||||
config: "fprint"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||
|
||||
function checkAvail(): void {
|
||||
if (!available || !Config.lock.enableFprint || !root.lock.secure) {
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
onCompleted: res => {
|
||||
if (!available)
|
||||
return;
|
||||
|
||||
tries = 0;
|
||||
errorTries = 0;
|
||||
start();
|
||||
}
|
||||
if (res === PamResult.Success)
|
||||
return root.lock.unlock();
|
||||
|
||||
config: "fprint"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||
if (res === PamResult.Error) {
|
||||
root.fprintState = "error";
|
||||
errorTries++;
|
||||
if (errorTries < 5) {
|
||||
abort();
|
||||
errorRetry.restart();
|
||||
}
|
||||
} else if (res === PamResult.MaxTries) {
|
||||
// Isn't actually the real max tries as pam only reports completed
|
||||
// when max tries is reached.
|
||||
tries++;
|
||||
if (tries < Config.lock.maxFprintTries) {
|
||||
// Restart if not actually real max tries
|
||||
root.fprintState = "fail";
|
||||
start();
|
||||
} else {
|
||||
root.fprintState = "max";
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
onCompleted: res => {
|
||||
if (!available)
|
||||
return;
|
||||
root.flashMsg();
|
||||
fprintStateReset.start();
|
||||
}
|
||||
}
|
||||
|
||||
if (res === PamResult.Success)
|
||||
return root.lock.unlock();
|
||||
Process {
|
||||
id: availProc
|
||||
|
||||
if (res === PamResult.Error) {
|
||||
root.fprintState = "error";
|
||||
errorTries++;
|
||||
if (errorTries < 5) {
|
||||
abort();
|
||||
errorRetry.restart();
|
||||
}
|
||||
} else if (res === PamResult.MaxTries) {
|
||||
// Isn't actually the real max tries as pam only reports completed
|
||||
// when max tries is reached.
|
||||
tries++;
|
||||
if (tries < Config.lock.maxFprintTries) {
|
||||
// Restart if not actually real max tries
|
||||
root.fprintState = "fail";
|
||||
start();
|
||||
} else {
|
||||
root.fprintState = "max";
|
||||
abort();
|
||||
}
|
||||
}
|
||||
command: ["sh", "-c", "fprintd-list $USER"]
|
||||
|
||||
root.flashMsg();
|
||||
fprintStateReset.start();
|
||||
}
|
||||
}
|
||||
onExited: code => {
|
||||
fprint.available = code === 0;
|
||||
fprint.checkAvail();
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: availProc
|
||||
Timer {
|
||||
id: errorRetry
|
||||
|
||||
command: ["sh", "-c", "fprintd-list $USER"]
|
||||
onExited: code => {
|
||||
fprint.available = code === 0;
|
||||
fprint.checkAvail();
|
||||
}
|
||||
}
|
||||
interval: 800
|
||||
|
||||
Timer {
|
||||
id: errorRetry
|
||||
onTriggered: fprint.start()
|
||||
}
|
||||
|
||||
interval: 800
|
||||
onTriggered: fprint.start()
|
||||
}
|
||||
Timer {
|
||||
id: stateReset
|
||||
|
||||
Timer {
|
||||
id: stateReset
|
||||
interval: 4000
|
||||
|
||||
interval: 4000
|
||||
onTriggered: {
|
||||
if (root.state !== "max")
|
||||
root.state = "";
|
||||
}
|
||||
}
|
||||
onTriggered: {
|
||||
if (root.state !== "max")
|
||||
root.state = "";
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: fprintStateReset
|
||||
Timer {
|
||||
id: fprintStateReset
|
||||
|
||||
interval: 4000
|
||||
onTriggered: {
|
||||
root.fprintState = "";
|
||||
fprint.errorTries = 0;
|
||||
}
|
||||
}
|
||||
interval: 4000
|
||||
|
||||
Connections {
|
||||
target: root.lock
|
||||
onTriggered: {
|
||||
root.fprintState = "";
|
||||
fprint.errorTries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function onSecureChanged(): void {
|
||||
if (root.lock.secure) {
|
||||
availProc.running = true;
|
||||
root.buffer = "";
|
||||
root.state = "";
|
||||
root.fprintState = "";
|
||||
root.lockMessage = "";
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
function onSecureChanged(): void {
|
||||
if (root.lock.secure) {
|
||||
availProc.running = true;
|
||||
root.buffer = "";
|
||||
root.state = "";
|
||||
root.fprintState = "";
|
||||
root.lockMessage = "";
|
||||
}
|
||||
}
|
||||
|
||||
function onUnlock(): void {
|
||||
fprint.abort();
|
||||
}
|
||||
}
|
||||
function onUnlock(): void {
|
||||
fprint.abort();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Config.lock
|
||||
target: root.lock
|
||||
}
|
||||
|
||||
function onEnableFprintChanged(): void {
|
||||
fprint.checkAvail();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
function onEnableFprintChanged(): void {
|
||||
fprint.checkAvail();
|
||||
}
|
||||
|
||||
target: Config.lock
|
||||
}
|
||||
}
|
||||
|
||||
+57
-59
@@ -6,75 +6,73 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
GridLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.padding.large
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.large
|
||||
anchors.right: parent.right
|
||||
columnSpacing: Appearance.spacing.large
|
||||
columns: 2
|
||||
rowSpacing: Appearance.spacing.large
|
||||
rows: 1
|
||||
|
||||
rowSpacing: Appearance.spacing.large
|
||||
columnSpacing: Appearance.spacing.large
|
||||
rows: 1
|
||||
columns: 2
|
||||
Ref {
|
||||
service: SystemUsage
|
||||
}
|
||||
|
||||
Ref {
|
||||
service: SystemUsage
|
||||
}
|
||||
Resource {
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
colour: DynamicColors.palette.m3primary
|
||||
icon: "memory"
|
||||
value: SystemUsage.cpuPerc
|
||||
}
|
||||
|
||||
Resource {
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
icon: "memory"
|
||||
value: SystemUsage.cpuPerc
|
||||
colour: DynamicColors.palette.m3primary
|
||||
}
|
||||
Resource {
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
colour: DynamicColors.palette.m3secondary
|
||||
icon: "memory_alt"
|
||||
value: SystemUsage.memPerc
|
||||
}
|
||||
|
||||
Resource {
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
icon: "memory_alt"
|
||||
value: SystemUsage.memPerc
|
||||
colour: DynamicColors.palette.m3secondary
|
||||
}
|
||||
component Resource: CustomRect {
|
||||
id: res
|
||||
|
||||
component Resource: CustomRect {
|
||||
id: res
|
||||
required property color colour
|
||||
required property string icon
|
||||
required property real value
|
||||
|
||||
required property string icon
|
||||
required property real value
|
||||
required property color colour
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitHeight: width
|
||||
radius: Appearance.rounding.large
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: width
|
||||
Behavior on value {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
radius: Appearance.rounding.large
|
||||
CircularProgress {
|
||||
id: circ
|
||||
|
||||
CircularProgress {
|
||||
id: circ
|
||||
anchors.fill: parent
|
||||
bgColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 3)
|
||||
fgColour: res.colour
|
||||
padding: Appearance.padding.large * 3
|
||||
strokeWidth: width < 200 ? Appearance.padding.smaller : Appearance.padding.normal
|
||||
value: res.value
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
value: res.value
|
||||
padding: Appearance.padding.large * 3
|
||||
fgColour: res.colour
|
||||
bgColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 3)
|
||||
strokeWidth: width < 200 ? Appearance.padding.smaller : Appearance.padding.normal
|
||||
}
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: res.icon
|
||||
color: res.colour
|
||||
font.pointSize: (circ.arcRadius * 0.7) || 1
|
||||
font.weight: 600
|
||||
}
|
||||
|
||||
Behavior on value {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
color: res.colour
|
||||
font.pointSize: (circ.arcRadius * 0.7) || 1
|
||||
font.weight: 600
|
||||
text: res.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,18 +7,18 @@ Item {
|
||||
id: root
|
||||
|
||||
ClippingRectangle {
|
||||
radius: 1000
|
||||
anchors.fill: parent
|
||||
radius: 1000
|
||||
|
||||
Image {
|
||||
id: userImage
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
sourceSize.width: parent.width
|
||||
sourceSize.height: parent.height
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: `${Paths.home}/.face`
|
||||
sourceSize.height: parent.height
|
||||
sourceSize.width: parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+133
-139
@@ -7,169 +7,163 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property int rootHeight
|
||||
required property int rootHeight
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
anchors.right: parent.right
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
spacing: Appearance.spacing.small
|
||||
Loader {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: -Appearance.padding.large
|
||||
Layout.topMargin: Appearance.padding.large * 2
|
||||
active: root.rootHeight > 610
|
||||
visible: active
|
||||
|
||||
Loader {
|
||||
Layout.topMargin: Appearance.padding.large * 2
|
||||
Layout.bottomMargin: -Appearance.padding.large
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
sourceComponent: CustomText {
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
text: qsTr("Weather")
|
||||
}
|
||||
}
|
||||
|
||||
active: root.rootHeight > 610
|
||||
visible: active
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
sourceComponent: CustomText {
|
||||
text: qsTr("Weather")
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.extraLarge * 2.5
|
||||
text: Weather.icon
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.large
|
||||
ColumnLayout {
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
animate: true
|
||||
text: Weather.icon
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.extraLarge * 2.5
|
||||
}
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3secondary
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.weight: 500
|
||||
text: Weather.description
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Appearance.spacing.small
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: qsTr("Humidity: %1%").arg(Weather.humidity)
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
Loader {
|
||||
Layout.rightMargin: Appearance.padding.smaller
|
||||
active: root.width > 400
|
||||
visible: active
|
||||
|
||||
animate: true
|
||||
text: Weather.description
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.weight: 500
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3primary
|
||||
elide: Text.ElideLeft
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: Weather.temp
|
||||
}
|
||||
|
||||
animate: true
|
||||
text: qsTr("Humidity: %1%").arg(Weather.humidity)
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideLeft
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: qsTr("Feels like: %1").arg(Weather.feelsLike)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.rightMargin: Appearance.padding.smaller
|
||||
active: root.width > 400
|
||||
visible: active
|
||||
Loader {
|
||||
id: forecastLoader
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.small
|
||||
Layout.bottomMargin: Appearance.padding.large * 2
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Appearance.spacing.smaller
|
||||
active: root.rootHeight > 820
|
||||
visible: active
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
sourceComponent: RowLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
animate: true
|
||||
text: Weather.temp
|
||||
color: DynamicColors.palette.m3primary
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
elide: Text.ElideLeft
|
||||
}
|
||||
Repeater {
|
||||
model: {
|
||||
const forecast = Weather.hourlyForecast;
|
||||
const count = root.width < 320 ? 3 : root.width < 400 ? 4 : 5;
|
||||
if (!forecast)
|
||||
return Array.from({
|
||||
length: count
|
||||
}, () => null);
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
return forecast.slice(0, count);
|
||||
}
|
||||
|
||||
animate: true
|
||||
text: qsTr("Feels like: %1").arg(Weather.feelsLike)
|
||||
color: DynamicColors.palette.m3outline
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
elide: Text.ElideLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
id: forecastHour
|
||||
|
||||
Loader {
|
||||
id: forecastLoader
|
||||
required property var modelData
|
||||
|
||||
Layout.topMargin: Appearance.spacing.smaller
|
||||
Layout.bottomMargin: Appearance.padding.large * 2
|
||||
Layout.fillWidth: true
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
active: root.rootHeight > 820
|
||||
visible: active
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: {
|
||||
const hour = forecastHour.modelData?.hour ?? 0;
|
||||
return hour > 12 ? `${(hour - 12).toString().padStart(2, "0")} PM` : `${hour.toString().padStart(2, "0")} AM`;
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: RowLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pointSize: Appearance.font.size.extraLarge * 1.5
|
||||
font.weight: 500
|
||||
text: forecastHour.modelData?.icon ?? "cloud_alert"
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: {
|
||||
const forecast = Weather.hourlyForecast;
|
||||
const count = root.width < 320 ? 3 : root.width < 400 ? 4 : 5;
|
||||
if (!forecast)
|
||||
return Array.from({
|
||||
length: count
|
||||
}, () => null);
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: Config.services.useFahrenheit ? `${forecastHour.modelData?.tempF ?? 0}°F` : `${forecastHour.modelData?.tempC ?? 0}°C`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return forecast.slice(0, count);
|
||||
}
|
||||
Timer {
|
||||
interval: 900000 // 15 minutes
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
|
||||
ColumnLayout {
|
||||
id: forecastHour
|
||||
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: {
|
||||
const hour = forecastHour.modelData?.hour ?? 0;
|
||||
return hour > 12 ? `${(hour - 12).toString().padStart(2, "0")} PM` : `${hour.toString().padStart(2, "0")} AM`;
|
||||
}
|
||||
color: DynamicColors.palette.m3outline
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: forecastHour.modelData?.icon ?? "cloud_alert"
|
||||
font.pointSize: Appearance.font.size.extraLarge * 1.5
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Config.services.useFahrenheit ? `${forecastHour.modelData?.tempF ?? 0}°F` : `${forecastHour.modelData?.tempC ?? 0}°C`
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
repeat: true
|
||||
interval: 900000 // 15 minutes
|
||||
onTriggered: Weather.reload()
|
||||
}
|
||||
onTriggered: Weather.reload()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user