Add: Lockscreen (wip)
This commit is contained in:
parent
0069463069
commit
71433c7807
5 changed files with 804 additions and 2 deletions
780
Modules/Lockscreen/Lockscreen.qml
Normal file
780
Modules/Lockscreen/Lockscreen.qml
Normal file
|
|
@ -0,0 +1,780 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Services.Pam
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
|
||||
property string errorMessage: ""
|
||||
property bool authenticating: false
|
||||
property string password: ""
|
||||
property bool pamAvailable: typeof PamContext !== "undefined"
|
||||
locked: false
|
||||
|
||||
function unlockAttempt() {
|
||||
console.log("Unlock attempt started");
|
||||
if (!pamAvailable) {
|
||||
lock.errorMessage = "PAM authentication not available.";
|
||||
console.log("PAM not available");
|
||||
return;
|
||||
}
|
||||
if (!lock.password) {
|
||||
lock.errorMessage = "Password required.";
|
||||
console.log("No password entered");
|
||||
return;
|
||||
}
|
||||
console.log("Starting PAM authentication...");
|
||||
lock.authenticating = true;
|
||||
lock.errorMessage = "";
|
||||
|
||||
console.log("[LockScreen] About to create PAM context with userName:", Quickshell.env("USER"));
|
||||
var pam = Qt.createQmlObject('import Quickshell.Services.Pam; PamContext { config: "login"; user: "' + Quickshell.env("USER") + '" }', lock);
|
||||
console.log("PamContext created", pam);
|
||||
|
||||
pam.onCompleted.connect(function (result) {
|
||||
console.log("PAM completed with result:", result);
|
||||
lock.authenticating = false;
|
||||
if (result === PamResult.Success) {
|
||||
console.log("Authentication successful, unlocking...");
|
||||
lock.locked = false;
|
||||
lock.password = "";
|
||||
lock.errorMessage = "";
|
||||
} else {
|
||||
console.log("Authentication failed");
|
||||
lock.errorMessage = "Authentication failed.";
|
||||
lock.password = "";
|
||||
}
|
||||
pam.destroy();
|
||||
});
|
||||
|
||||
pam.onError.connect(function (error) {
|
||||
console.log("PAM error:", error);
|
||||
lock.authenticating = false;
|
||||
lock.errorMessage = pam.message || "Authentication error.";
|
||||
lock.password = "";
|
||||
pam.destroy();
|
||||
});
|
||||
|
||||
pam.onPamMessage.connect(function () {
|
||||
console.log("PAM message:", pam.message, "isError:", pam.messageIsError);
|
||||
if (pam.messageIsError) {
|
||||
lock.errorMessage = pam.message;
|
||||
}
|
||||
});
|
||||
|
||||
pam.onResponseRequiredChanged.connect(function () {
|
||||
console.log("PAM response required:", pam.responseRequired);
|
||||
if (pam.responseRequired && lock.authenticating) {
|
||||
console.log("Responding to PAM with password");
|
||||
pam.respond(lock.password);
|
||||
}
|
||||
});
|
||||
|
||||
var started = pam.start();
|
||||
console.log("PAM start result:", started);
|
||||
}
|
||||
|
||||
WlSessionLockSurface {
|
||||
// Wallpaper image
|
||||
Image {
|
||||
id: lockBgImage
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: Wallpapers.currentWallpaper !== "" ? Wallpapers.currentWallpaper : ""
|
||||
cache: true
|
||||
smooth: true
|
||||
mipmap: false
|
||||
}
|
||||
|
||||
// Blurred background
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
|
||||
// Simple blur effect
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
layer.samples: 4
|
||||
}
|
||||
|
||||
// Animated gradient overlay
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: Qt.rgba(0, 0, 0, 0.6) }
|
||||
GradientStop { position: 0.3; color: Qt.rgba(0, 0, 0, 0.3) }
|
||||
GradientStop { position: 0.7; color: Qt.rgba(0, 0, 0, 0.4) }
|
||||
GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.7) }
|
||||
}
|
||||
|
||||
// Subtle animated particles
|
||||
Repeater {
|
||||
model: 20
|
||||
Rectangle {
|
||||
width: Math.random() * 4 + 2
|
||||
height: width
|
||||
radius: width * 0.5
|
||||
color: Qt.rgba(Colors.accentPrimary.r, Colors.accentPrimary.g, Colors.accentPrimary.b, 0.3)
|
||||
x: Math.random() * parent.width
|
||||
y: Math.random() * parent.height
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 0.8; duration: 2000 + Math.random() * 3000 }
|
||||
NumberAnimation { to: 0.1; duration: 2000 + Math.random() * 3000 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main content - Centered design
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
// Top section - Time, date, and user info
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 80 * Scaling.scale(screen)
|
||||
spacing: 40 * Scaling.scale(screen)
|
||||
|
||||
// Time display - Large and prominent with pulse animation
|
||||
Column {
|
||||
spacing: 8 * Scaling.scale(screen)
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
Text {
|
||||
id: timeText
|
||||
text: Qt.formatDateTime(new Date(), "HH:mm")
|
||||
font.family: "Inter"
|
||||
font.pixelSize: 140 * Scaling.scale(screen)
|
||||
font.weight: Font.Bold
|
||||
color: Colors.textPrimary
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
SequentialAnimation on scale {
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 1.02; duration: 2000; easing.type: Easing.InOutQuad }
|
||||
NumberAnimation { to: 1.0; duration: 2000; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: dateText
|
||||
text: Qt.formatDateTime(new Date(), "dddd, MMMM d")
|
||||
font.family: "Inter"
|
||||
font.pixelSize: 26 * Scaling.scale(screen)
|
||||
font.weight: Font.Light
|
||||
color: Colors.textSecondary
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: timeText.width
|
||||
}
|
||||
}
|
||||
|
||||
// User section with animated avatar
|
||||
Column {
|
||||
spacing: 16 * Scaling.scale(screen)
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
// Animated avatar with glow effect
|
||||
Rectangle {
|
||||
width: 120 * Scaling.scale(screen)
|
||||
height: 120 * Scaling.scale(screen)
|
||||
radius: width * 0.5
|
||||
color: "transparent"
|
||||
border.color: Colors.accentPrimary
|
||||
border.width: 3 * Scaling.scale(screen)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
// Glow effect
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 24 * Scaling.scale(screen)
|
||||
height: parent.height + 24 * Scaling.scale(screen)
|
||||
radius: width * 0.5
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(Colors.accentPrimary.r, Colors.accentPrimary.g, Colors.accentPrimary.b, 0.3)
|
||||
border.width: 2 * Scaling.scale(screen)
|
||||
z: -1
|
||||
|
||||
SequentialAnimation on scale {
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 1.1; duration: 1500; easing.type: Easing.InOutQuad }
|
||||
NumberAnimation { to: 1.0; duration: 1500; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
}
|
||||
|
||||
NImageRounded {
|
||||
anchors.centerIn: parent
|
||||
width: 100 * Scaling.scale(screen)
|
||||
height: 100 * Scaling.scale(screen)
|
||||
imagePath: Quickshell.env("HOME") + "/.face"
|
||||
fallbackIcon: "person"
|
||||
imageRadius: width * 0.5
|
||||
}
|
||||
|
||||
// Hover animation
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: parent.scale = 1.05
|
||||
onExited: parent.scale = 1.0
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 200; easing.type: Easing.OutBack }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Centered terminal section
|
||||
Item {
|
||||
width: 520 * Scaling.scale(screen)
|
||||
height: 200 * Scaling.scale(screen)
|
||||
anchors.centerIn: parent
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 20 * Scaling.scale(screen)
|
||||
width: parent.width
|
||||
|
||||
|
||||
|
||||
// Futuristic Terminal-Style Input
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 200 * Scaling.scale(screen)
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Terminal background with scanlines
|
||||
Rectangle {
|
||||
id: terminalBackground
|
||||
anchors.fill: parent
|
||||
radius: 16
|
||||
color: Colors.applyOpacity(Colors.backgroundPrimary, "E6")
|
||||
border.color: Colors.accentPrimary
|
||||
border.width: 2 * Scaling.scale(screen)
|
||||
|
||||
// Scanline effect
|
||||
Repeater {
|
||||
model: 20
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Colors.applyOpacity(Colors.accentPrimary, "1A")
|
||||
y: index * 10
|
||||
opacity: 0.3
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 0.6; duration: 2000 + Math.random() * 1000 }
|
||||
NumberAnimation { to: 0.1; duration: 2000 + Math.random() * 1000 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Terminal header
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 40 * Scaling.scale(screen)
|
||||
color: Colors.applyOpacity(Colors.accentPrimary, "33")
|
||||
topLeftRadius: 14
|
||||
topRightRadius: 14
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12 * Scaling.scale(screen)
|
||||
spacing: 12 * Scaling.scale(screen)
|
||||
|
||||
Text {
|
||||
text: "●"
|
||||
color: Colors.error
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "●"
|
||||
color: Colors.warning
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "●"
|
||||
color: Colors.accentPrimary
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "SECURE TERMINAL"
|
||||
color: Colors.textPrimary
|
||||
font.family: "Monaco"
|
||||
font.pixelSize: 14 * Scaling.scale(screen)
|
||||
font.weight: Font.Bold
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Terminal content area
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.topMargin: 50 * Scaling.scale(screen)
|
||||
anchors.margins: 12 * Scaling.scale(screen)
|
||||
spacing: 12 * Scaling.scale(screen)
|
||||
|
||||
// Welcome back typing effect
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 12 * Scaling.scale(screen)
|
||||
|
||||
Text {
|
||||
text: "root@noctalia:~$"
|
||||
color: Colors.accentPrimary
|
||||
font.family: "Monaco"
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
Text {
|
||||
id: welcomeText
|
||||
text: ""
|
||||
color: Colors.textPrimary
|
||||
font.family: "Monaco"
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
property int currentIndex: 0
|
||||
property string fullText: "echo 'Welcome back, " + Quickshell.env("USER") + "!'"
|
||||
|
||||
Timer {
|
||||
interval: 100
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (parent.currentIndex < parent.fullText.length) {
|
||||
parent.text = parent.fullText.substring(0, parent.currentIndex + 1)
|
||||
parent.currentIndex++
|
||||
} else {
|
||||
running = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Command line with integrated password input
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 12 * Scaling.scale(screen)
|
||||
|
||||
Text {
|
||||
text: "root@noctalia:~$"
|
||||
color: Colors.accentPrimary
|
||||
font.family: "Monaco"
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "sudo unlock_session"
|
||||
color: Colors.textPrimary
|
||||
font.family: "Monaco"
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
}
|
||||
|
||||
// Integrated password input (invisible, just for functionality)
|
||||
TextInput {
|
||||
id: passwordInput
|
||||
width: 0
|
||||
height: 0
|
||||
visible: false
|
||||
font.family: "Monaco"
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
color: Colors.textPrimary
|
||||
echoMode: TextInput.Password
|
||||
passwordCharacter: "*"
|
||||
passwordMaskDelay: 0
|
||||
|
||||
text: lock.password
|
||||
onTextChanged: {
|
||||
lock.password = text
|
||||
// Terminal typing sound effect (visual)
|
||||
typingEffect.start()
|
||||
}
|
||||
|
||||
Keys.onPressed: function (event) {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
lock.unlockAttempt();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
// Visual password display with integrated cursor
|
||||
Text {
|
||||
id: asterisksText
|
||||
text: "*".repeat(passwordInput.text.length)
|
||||
color: Colors.textPrimary
|
||||
font.family: "Monaco"
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
visible: passwordInput.activeFocus
|
||||
|
||||
// Typing effect animation
|
||||
SequentialAnimation {
|
||||
id: typingEffect
|
||||
NumberAnimation {
|
||||
target: passwordInput
|
||||
property: "scale"
|
||||
to: 1.01
|
||||
duration: 50
|
||||
}
|
||||
NumberAnimation {
|
||||
target: passwordInput
|
||||
property: "scale"
|
||||
to: 1.0
|
||||
duration: 50
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blinking cursor positioned right after the asterisks
|
||||
Rectangle {
|
||||
width: 8 * Scaling.scale(screen)
|
||||
height: 20 * Scaling.scale(screen)
|
||||
color: Colors.accentPrimary
|
||||
visible: passwordInput.activeFocus
|
||||
anchors.left: asterisksText.right
|
||||
anchors.leftMargin: 2 * Scaling.scale(screen)
|
||||
anchors.verticalCenter: asterisksText.verticalCenter
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 1.0; duration: 500 }
|
||||
NumberAnimation { to: 0.0; duration: 500 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Status messages
|
||||
Text {
|
||||
text: lock.authenticating ? "Authenticating..." : (lock.errorMessage !== "" ? "Authentication failed." : "")
|
||||
color: lock.authenticating ? Colors.accentPrimary : (lock.errorMessage !== "" ? Colors.error : "transparent")
|
||||
font.family: "Monaco"
|
||||
font.pixelSize: 14 * Scaling.scale(screen)
|
||||
Layout.fillWidth: true
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
running: lock.authenticating
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 1.0; duration: 800 }
|
||||
NumberAnimation { to: 0.5; duration: 800 }
|
||||
}
|
||||
}
|
||||
|
||||
// Execute button
|
||||
Rectangle {
|
||||
width: 120 * Scaling.scale(screen)
|
||||
height: 40 * Scaling.scale(screen)
|
||||
radius: 12
|
||||
color: executeButtonArea.containsMouse ? Colors.accentPrimary : Colors.applyOpacity(Colors.accentPrimary, "33")
|
||||
border.color: Colors.accentPrimary
|
||||
border.width: 1
|
||||
enabled: !lock.authenticating
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: lock.authenticating ? "EXECUTING..." : "EXECUTE"
|
||||
color: executeButtonArea.containsMouse ? Colors.onAccent : Colors.accentPrimary
|
||||
font.family: "Monaco"
|
||||
font.pixelSize: 12 * Scaling.scale(screen)
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: executeButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: lock.unlockAttempt()
|
||||
|
||||
SequentialAnimation on scale {
|
||||
running: containsMouse
|
||||
NumberAnimation { to: 1.05; duration: 150; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
SequentialAnimation on scale {
|
||||
running: !containsMouse
|
||||
NumberAnimation { to: 1.0; duration: 150; easing.type: Easing.OutCubic }
|
||||
}
|
||||
}
|
||||
|
||||
// Processing animation
|
||||
SequentialAnimation on scale {
|
||||
loops: Animation.Infinite
|
||||
running: lock.authenticating
|
||||
NumberAnimation { to: 1.02; duration: 600; easing.type: Easing.InOutQuad }
|
||||
NumberAnimation { to: 1.0; duration: 600; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Terminal glow effect
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: "transparent"
|
||||
border.color: Colors.applyOpacity(Colors.accentPrimary, "4D")
|
||||
border.width: 1
|
||||
z: -1
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 0.6; duration: 2000; easing.type: Easing.InOutQuad }
|
||||
NumberAnimation { to: 0.2; duration: 2000; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Error message with modern styling
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 56 * Scaling.scale(screen)
|
||||
radius: 28
|
||||
color: Qt.rgba(Colors.error.r, Colors.error.g, Colors.error.b, 0.15)
|
||||
border.color: Colors.error
|
||||
border.width: 1 * Scaling.scale(screen)
|
||||
visible: lock.errorMessage !== ""
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18 * Scaling.scale(screen)
|
||||
spacing: 12 * Scaling.scale(screen)
|
||||
|
||||
Text {
|
||||
text: "error"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 22 * Scaling.scale(screen)
|
||||
color: Colors.error
|
||||
}
|
||||
|
||||
Text {
|
||||
text: lock.errorMessage
|
||||
color: Colors.error
|
||||
font.family: "Inter"
|
||||
font.pixelSize: 16 * Scaling.scale(screen)
|
||||
font.weight: Font.Medium
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation on opacity {
|
||||
from: 0
|
||||
to: 1
|
||||
duration: 300
|
||||
running: lock.errorMessage !== ""
|
||||
}
|
||||
|
||||
// Shake animation on error
|
||||
SequentialAnimation on x {
|
||||
running: lock.errorMessage !== ""
|
||||
NumberAnimation { to: 10; duration: 50 }
|
||||
NumberAnimation { to: -10; duration: 100 }
|
||||
NumberAnimation { to: 10; duration: 100 }
|
||||
NumberAnimation { to: 0; duration: 50 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced power buttons with hover effects
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 50 * Scaling.scale(screen)
|
||||
spacing: 20 * Scaling.scale(screen)
|
||||
|
||||
// Shutdown with enhanced styling
|
||||
Rectangle {
|
||||
width: 64 * Scaling.scale(screen)
|
||||
height: 64 * Scaling.scale(screen)
|
||||
radius: 32
|
||||
color: Qt.rgba(Colors.error.r, Colors.error.g, Colors.error.b, shutdownArea.containsMouse ? 0.9 : 0.2)
|
||||
border.color: Colors.error
|
||||
border.width: 2 * Scaling.scale(screen)
|
||||
|
||||
// Glow effect
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 10 * Scaling.scale(screen)
|
||||
height: parent.height + 10 * Scaling.scale(screen)
|
||||
radius: width * 0.5
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(Colors.error.r, Colors.error.g, Colors.error.b, 0.3)
|
||||
border.width: 2 * Scaling.scale(screen)
|
||||
opacity: shutdownArea.containsMouse ? 1 : 0
|
||||
z: -1
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: shutdownArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Qt.createQmlObject('import Quickshell.Io; Process { command: ["shutdown", "-h", "now"]; running: true }', lock);
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "power_settings_new"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 28 * Scaling.scale(screen)
|
||||
color: shutdownArea.containsMouse ? Colors.onAccent : Colors.error
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 200; easing.type: Easing.OutCubic }
|
||||
}
|
||||
scale: shutdownArea.containsMouse ? 1.1 : 1.0
|
||||
}
|
||||
|
||||
// Reboot with enhanced styling
|
||||
Rectangle {
|
||||
width: 64 * Scaling.scale(screen)
|
||||
height: 64 * Scaling.scale(screen)
|
||||
radius: 32
|
||||
color: Qt.rgba(Colors.accentPrimary.r, Colors.accentPrimary.g, Colors.accentPrimary.b, rebootArea.containsMouse ? 0.9 : 0.2)
|
||||
border.color: Colors.accentPrimary
|
||||
border.width: 2 * Scaling.scale(screen)
|
||||
|
||||
// Glow effect
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 10 * Scaling.scale(screen)
|
||||
height: parent.height + 10 * Scaling.scale(screen)
|
||||
radius: width * 0.5
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(Colors.accentPrimary.r, Colors.accentPrimary.g, Colors.accentPrimary.b, 0.3)
|
||||
border.width: 2 * Scaling.scale(screen)
|
||||
opacity: rebootArea.containsMouse ? 1 : 0
|
||||
z: -1
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rebootArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Qt.createQmlObject('import Quickshell.Io; Process { command: ["reboot"]; running: true }', lock);
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "refresh"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 28 * Scaling.scale(screen)
|
||||
color: rebootArea.containsMouse ? Colors.onAccent : Colors.accentPrimary
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 200; easing.type: Easing.OutCubic }
|
||||
}
|
||||
scale: rebootArea.containsMouse ? 1.1 : 1.0
|
||||
}
|
||||
|
||||
// Logout with enhanced styling
|
||||
Rectangle {
|
||||
width: 64 * Scaling.scale(screen)
|
||||
height: 64 * Scaling.scale(screen)
|
||||
radius: 32
|
||||
color: Qt.rgba(Colors.accentSecondary.r, Colors.accentSecondary.g, Colors.accentSecondary.b, logoutArea.containsMouse ? 0.9 : 0.2)
|
||||
border.color: Colors.accentSecondary
|
||||
border.width: 2 * Scaling.scale(screen)
|
||||
|
||||
// Glow effect
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 10 * Scaling.scale(screen)
|
||||
height: parent.height + 10 * Scaling.scale(screen)
|
||||
radius: width * 0.5
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(Colors.accentSecondary.r, Colors.accentSecondary.g, Colors.accentSecondary.b, 0.3)
|
||||
border.width: 2 * Scaling.scale(screen)
|
||||
opacity: logoutArea.containsMouse ? 1 : 0
|
||||
z: -1
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: logoutArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Qt.createQmlObject('import Quickshell.Io; Process { command: ["loginctl", "terminate-user", "' + Quickshell.env("USER") + '"]; running: true }', lock);
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "exit_to_app"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 28 * Scaling.scale(screen)
|
||||
color: logoutArea.containsMouse ? Colors.onAccent : Colors.accentSecondary
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 200; easing.type: Easing.OutCubic }
|
||||
}
|
||||
scale: logoutArea.containsMouse ? 1.1 : 1.0
|
||||
}
|
||||
}
|
||||
|
||||
// Timer for updating time
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
timeText.text = Qt.formatDateTime(new Date(), "HH:mm");
|
||||
dateText.text = Qt.formatDateTime(new Date(), "dddd, MMMM d");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ NBox {
|
|||
|
||||
readonly property real scaling: Scaling.scale(screen)
|
||||
property string uptimeText: "--"
|
||||
|
||||
|
||||
|
||||
Layout.fillWidth: true
|
||||
// Height driven by content
|
||||
|
|
|
|||
|
|
@ -6,10 +6,13 @@ import Quickshell.Io
|
|||
import Quickshell.Widgets
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Lockscreen
|
||||
|
||||
NPanel {
|
||||
id: powerMenu
|
||||
visible: false
|
||||
|
||||
|
||||
|
||||
// Anchors will be set by the parent component
|
||||
function show() {
|
||||
|
|
@ -110,8 +113,9 @@ NPanel {
|
|||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
// TODO: Implement lock screen functionality
|
||||
console.log("Lock screen requested")
|
||||
// Lock the screen
|
||||
lockScreen.locked = true
|
||||
powerMenu.visible = false
|
||||
}
|
||||
}
|
||||
|
|
@ -416,4 +420,9 @@ NPanel {
|
|||
command: ["loginctl", "terminate-user", Quickshell.env("USER")]
|
||||
running: false
|
||||
}
|
||||
|
||||
// Lockscreen instance
|
||||
Lockscreen {
|
||||
id: lockScreen
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ NLoader {
|
|||
// Target screen to open on
|
||||
property var targetScreen: null
|
||||
|
||||
|
||||
function openAt(x, screen) {
|
||||
anchorX = x
|
||||
targetScreen = screen
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.Modules.Lockscreen
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// Reference to the lockscreen component
|
||||
property var lockscreen: null
|
||||
|
||||
IpcHandler {
|
||||
target: "settings"
|
||||
|
|
@ -40,7 +44,13 @@ Item {
|
|||
IpcHandler {
|
||||
target: "lockScreen"
|
||||
|
||||
function toggle() {// TODO
|
||||
function toggle() {
|
||||
lockScreen.locked = !lockScreen.locked
|
||||
}
|
||||
}
|
||||
|
||||
// Lockscreen instance
|
||||
Lockscreen {
|
||||
id: lockScreen
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue