LockScreen: internal rework

This commit is contained in:
Ly-sec
2026-01-15 18:26:01 +01:00
parent 1b3b2f1232
commit 577269da78
4 changed files with 1206 additions and 1148 deletions
File diff suppressed because it is too large Load Diff
+284
View File
@@ -0,0 +1,284 @@
import QtQuick
import Quickshell
import qs.Commons
import qs.Services.Compositor
import qs.Services.UI
Item {
id: root
anchors.fill: parent
// Cached wallpaper path - exposed for parent components
property string resolvedWallpaperPath: ""
required property var screen
// Request preprocessed wallpaper when lock screen becomes active or dimensions change
Component.onCompleted: {
if (screen) {
Qt.callLater(requestCachedWallpaper);
}
}
onWidthChanged: {
if (screen && width > 0 && height > 0) {
Qt.callLater(requestCachedWallpaper);
}
}
onHeightChanged: {
if (screen && width > 0 && height > 0) {
Qt.callLater(requestCachedWallpaper);
}
}
// Listen for wallpaper changes
Connections {
target: WallpaperService
function onWallpaperChanged(screenName, path) {
if (screen && screenName === screen.name) {
Qt.callLater(requestCachedWallpaper);
}
}
}
// Listen for display scale changes
Connections {
target: CompositorService
function onDisplayScalesChanged() {
if (screen && width > 0 && height > 0) {
Qt.callLater(requestCachedWallpaper);
}
}
}
function requestCachedWallpaper() {
if (!screen || width <= 0 || height <= 0) {
return;
}
// Check for solid color mode first
if (Settings.data.wallpaper.useSolidColor) {
resolvedWallpaperPath = "";
return;
}
const originalPath = WallpaperService.getWallpaper(screen.name) || "";
if (originalPath === "") {
resolvedWallpaperPath = "";
return;
}
// Handle solid color paths
if (WallpaperService.isSolidColorPath(originalPath)) {
resolvedWallpaperPath = "";
return;
}
if (!ImageCacheService || !ImageCacheService.initialized) {
// Fallback to original if services not ready
resolvedWallpaperPath = originalPath;
return;
}
const compositorScale = CompositorService.getDisplayScale(screen.name);
const targetWidth = Math.round(width * compositorScale);
const targetHeight = Math.round(height * compositorScale);
if (targetWidth <= 0 || targetHeight <= 0) {
return;
}
// Don't set resolvedWallpaperPath until cache is ready
// This prevents loading the original huge image
ImageCacheService.getLarge(originalPath, targetWidth, targetHeight, function (cachedPath, success) {
if (success) {
resolvedWallpaperPath = cachedPath;
} else {
// Only fall back to original if caching failed
resolvedWallpaperPath = originalPath;
}
});
}
// Background - solid color or black fallback
Rectangle {
anchors.fill: parent
color: Settings.data.wallpaper.useSolidColor ? Settings.data.wallpaper.solidColor : "#000000"
}
Image {
id: lockBgImage
visible: source !== "" && Settings.data.wallpaper.enabled && !Settings.data.wallpaper.useSolidColor
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
source: resolvedWallpaperPath
cache: false
smooth: true
mipmap: false
antialiasing: true
}
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop {
position: 0.0
color: Qt.alpha(Color.mShadow, 0.8)
}
GradientStop {
position: 0.3
color: Qt.alpha(Color.mShadow, 0.4)
}
GradientStop {
position: 0.7
color: Qt.alpha(Color.mShadow, 0.5)
}
GradientStop {
position: 1.0
color: Qt.alpha(Color.mShadow, 0.9)
}
}
}
// Screen corners for lock screen
Item {
anchors.fill: parent
visible: Settings.data.general.showScreenCorners
property color cornerColor: Settings.data.general.forceBlackScreenCorners ? "black" : Color.mSurface
property real cornerRadius: Style.screenRadius
property real cornerSize: Style.screenRadius
// Top-left concave corner
Canvas {
anchors.top: parent.top
anchors.left: parent.left
width: parent.cornerSize
height: parent.cornerSize
antialiasing: true
renderTarget: Canvas.FramebufferObject
smooth: false
onPaint: {
const ctx = getContext("2d");
if (!ctx)
return;
ctx.reset();
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = parent.cornerColor;
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.arc(width, height, parent.cornerRadius, 0, 2 * Math.PI);
ctx.fill();
}
onWidthChanged: if (available)
requestPaint()
onHeightChanged: if (available)
requestPaint()
}
// Top-right concave corner
Canvas {
anchors.top: parent.top
anchors.right: parent.right
width: parent.cornerSize
height: parent.cornerSize
antialiasing: true
renderTarget: Canvas.FramebufferObject
smooth: true
onPaint: {
const ctx = getContext("2d");
if (!ctx)
return;
ctx.reset();
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = parent.cornerColor;
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.arc(0, height, parent.cornerRadius, 0, 2 * Math.PI);
ctx.fill();
}
onWidthChanged: if (available)
requestPaint()
onHeightChanged: if (available)
requestPaint()
}
// Bottom-left concave corner
Canvas {
anchors.bottom: parent.bottom
anchors.left: parent.left
width: parent.cornerSize
height: parent.cornerSize
antialiasing: true
renderTarget: Canvas.FramebufferObject
smooth: true
onPaint: {
const ctx = getContext("2d");
if (!ctx)
return;
ctx.reset();
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = parent.cornerColor;
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.arc(width, 0, parent.cornerRadius, 0, 2 * Math.PI);
ctx.fill();
}
onWidthChanged: if (available)
requestPaint()
onHeightChanged: if (available)
requestPaint()
}
// Bottom-right concave corner
Canvas {
anchors.bottom: parent.bottom
anchors.right: parent.right
width: parent.cornerSize
height: parent.cornerSize
antialiasing: true
renderTarget: Canvas.FramebufferObject
smooth: true
onPaint: {
const ctx = getContext("2d");
if (!ctx)
return;
ctx.reset();
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = parent.cornerColor;
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.arc(0, 0, parent.cornerRadius, 0, 2 * Math.PI);
ctx.fill();
}
onWidthChanged: if (available)
requestPaint()
onHeightChanged: if (available)
requestPaint()
}
}
}
+138
View File
@@ -0,0 +1,138 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services.System
import qs.Widgets
// Time, Date, and User Profile Container
Rectangle {
id: root
width: Math.max(500, contentRow.implicitWidth + 32)
height: Math.max(120, contentRow.implicitHeight + 32)
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 100
radius: Style.radiusL
color: Color.mSurface
border.color: Qt.alpha(Color.mOutline, 0.2)
border.width: Style.borderS
RowLayout {
id: contentRow
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginXL * 2
// Left side: Avatar
Rectangle {
Layout.preferredWidth: 70
Layout.preferredHeight: 70
Layout.alignment: Qt.AlignVCenter
radius: width / 2
color: "transparent"
Rectangle {
anchors.fill: parent
radius: parent.radius
color: "transparent"
border.color: Qt.alpha(Color.mPrimary, 0.8)
border.width: Style.borderM
SequentialAnimation on border.color {
loops: Animation.Infinite
ColorAnimation {
to: Qt.alpha(Color.mPrimary, 1.0)
duration: 2000
easing.type: Easing.InOutQuad
}
ColorAnimation {
to: Qt.alpha(Color.mPrimary, 0.8)
duration: 2000
easing.type: Easing.InOutQuad
}
}
}
NImageRounded {
anchors.centerIn: parent
width: 66
height: 66
radius: width / 2
imagePath: Settings.preprocessPath(Settings.data.general.avatarImage)
fallbackIcon: "person"
SequentialAnimation on scale {
loops: Animation.Infinite
NumberAnimation {
to: 1.02
duration: 4000
easing.type: Easing.InOutQuad
}
NumberAnimation {
to: 1.0
duration: 4000
easing.type: Easing.InOutQuad
}
}
}
}
// Center: User Info Column (left-aligned text)
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
spacing: Style.marginXXS
// Welcome back + Username on one line
NText {
text: I18n.tr("system.welcome-back") + " " + HostService.displayName + "!"
pointSize: Style.fontSizeXXL
color: Color.mOnSurface
horizontalAlignment: Text.AlignLeft
}
// Date below
NText {
text: {
var lang = I18n.locale.name.split("_")[0];
var formats = {
"de": "dddd, d. MMMM",
"en": "dddd, MMMM d",
"es": "dddd, d 'de' MMMM",
"fr": "dddd d MMMM",
"hu": "dddd, MMMM d.",
"ja": "yyyy年M月d日 dddd",
"ku": "dddd, dê MMMM",
"nl": "dddd d MMMM",
"pt": "dddd, d 'de' MMMM",
"zh": "yyyy年M月d日 dddd"
};
var dateString = I18n.locale.toString(Time.now, formats[lang] || "dddd, d MMMM");
return dateString.charAt(0).toUpperCase() + dateString.slice(1);
}
pointSize: Style.fontSizeXL
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignLeft
}
}
// Spacer to push time to the right
Item {
Layout.fillWidth: true
}
// Clock
NClock {
now: Time.now
clockStyle: Settings.data.location.analogClockInCalendar ? "analog" : "digital"
Layout.preferredWidth: 70
Layout.preferredHeight: 70
Layout.alignment: Qt.AlignVCenter
backgroundColor: Color.mSurface
clockColor: Color.mOnSurface
secondHandColor: Color.mPrimary
hoursFontSize: Style.fontSizeL
minutesFontSize: Style.fontSizeL
}
}
}
+744
View File
@@ -0,0 +1,744 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services.Compositor
import qs.Services.Hardware
import qs.Services.Keyboard
import qs.Services.Location
import qs.Services.Media
import qs.Widgets
import qs.Widgets.AudioSpectrum
Item {
id: root
anchors.fill: parent
required property var lockContext
required property var batteryIndicator
required property var keyboardLayout
required property TextInput passwordInput
// Compact status indicators container (compact mode only)
Rectangle {
width: {
var hasBattery = batteryIndicator.isReady && BatteryService.hasAnyBattery();
var hasKeyboard = keyboardLayout.currentLayout !== "Unknown";
if (hasBattery && hasKeyboard) {
return 200;
} else if (hasBattery || hasKeyboard) {
return 120;
} else {
return 0;
}
}
height: 40
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 96 + (Settings.data.general.compactLockScreen ? 116 : 220)
topLeftRadius: Style.radiusL
topRightRadius: Style.radiusL
color: Color.mSurface
visible: Settings.data.general.compactLockScreen && ((batteryIndicator.isReady && BatteryService.hasAnyBattery()) || keyboardLayout.currentLayout !== "Unknown")
RowLayout {
anchors.centerIn: parent
spacing: Style.marginL
// Battery indicator
RowLayout {
spacing: Style.marginS
visible: batteryIndicator.isReady && BatteryService.hasAnyBattery()
NIcon {
icon: BatteryService.getIcon(Math.round(batteryIndicator.percent), batteryIndicator.charging, batteryIndicator.isReady)
pointSize: Style.fontSizeM
color: batteryIndicator.charging ? Color.mPrimary : Color.mOnSurfaceVariant
}
NText {
text: Math.round(batteryIndicator.percent) + "%"
color: Color.mOnSurfaceVariant
pointSize: Style.fontSizeM
}
}
// Keyboard layout indicator
RowLayout {
spacing: 6
visible: keyboardLayout.currentLayout !== "Unknown"
NIcon {
icon: "keyboard"
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
}
NText {
text: keyboardLayout.currentLayout
color: Color.mOnSurfaceVariant
pointSize: Style.fontSizeM
elide: Text.ElideRight
}
}
}
}
// Bottom container with weather, password input and controls
Rectangle {
id: bottomContainer
// Support for removing the session/power buttons at the bottom.
readonly property int deltaY: Settings.data.general.showSessionButtonsOnLockScreen ? 0 : (Settings.data.general.compactLockScreen ? 36 : 48) + 14
height: {
let calcHeight = Settings.data.general.compactLockScreen ? 120 : 220;
if (!Settings.data.general.showSessionButtonsOnLockScreen) {
calcHeight -= bottomContainer.deltaY;
}
return calcHeight;
}
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 100 + bottomContainer.deltaY
radius: Style.radiusL
color: Color.mSurface
width: 750
ColumnLayout {
anchors.fill: parent
anchors.margins: 14
spacing: Style.marginL
// Top info row
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: 65
spacing: Style.marginXL
visible: !Settings.data.general.compactLockScreen
// Media widget with visualizer
Rectangle {
Layout.preferredWidth: 220
// Expand to take remaining space when weather is hidden
Layout.fillWidth: !(Settings.data.location.weatherEnabled && LocationService.data.weather !== null)
Layout.preferredHeight: 50
radius: Style.radiusL
color: "transparent"
clip: true
visible: MediaService.currentPlayer && MediaService.canPlay
Loader {
anchors.fill: parent
anchors.margins: 4
active: Settings.data.audio.visualizerType === "linear"
z: 0
sourceComponent: NLinearSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: Color.mPrimary
opacity: 0.4
}
}
Loader {
anchors.fill: parent
anchors.margins: 4
active: Settings.data.audio.visualizerType === "mirrored"
z: 0
sourceComponent: NMirroredSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: Color.mPrimary
opacity: 0.4
}
}
Loader {
anchors.fill: parent
anchors.margins: 4
active: Settings.data.audio.visualizerType === "wave"
z: 0
sourceComponent: NWaveSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: Color.mPrimary
opacity: 0.4
}
}
RowLayout {
anchors.fill: parent
anchors.margins: 8
spacing: Style.marginM
z: 1
Rectangle {
Layout.preferredWidth: 34
Layout.preferredHeight: 34
radius: Math.min(Style.radiusL, width / 2)
color: "transparent"
clip: true
NImageRounded {
anchors.fill: parent
anchors.margins: 2
radius: Math.min(Style.radiusL, width / 2)
imagePath: MediaService.trackArtUrl
fallbackIcon: "disc"
fallbackIconSize: Style.fontSizeM
borderColor: Color.mOutline
borderWidth: Style.borderS
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NText {
text: MediaService.trackTitle || "No media"
pointSize: Style.fontSizeM
color: Color.mOnSurface
Layout.fillWidth: true
elide: Text.ElideRight
}
NText {
text: MediaService.trackArtist || ""
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
Layout.fillWidth: true
elide: Text.ElideRight
}
}
}
}
Rectangle {
Layout.preferredWidth: 1
Layout.fillHeight: true
Layout.rightMargin: 4
color: Qt.alpha(Color.mOutline, 0.3)
visible: MediaService.currentPlayer && MediaService.canPlay
}
Item {
Layout.preferredWidth: Style.marginM
visible: !(MediaService.currentPlayer && MediaService.canPlay)
}
// Current weather
RowLayout {
visible: Settings.data.location.weatherEnabled && LocationService.data.weather !== null
Layout.preferredWidth: 180
spacing: Style.marginM
NIcon {
Layout.alignment: Qt.AlignVCenter
icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.current_weather.weathercode)
pointSize: Style.fontSizeXXXL
color: Color.mPrimary
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
RowLayout {
Layout.fillWidth: true
spacing: Style.marginL
NText {
text: {
var temp = LocationService.data.weather.current_weather.temperature;
var suffix = "C";
if (Settings.data.location.useFahrenheit) {
temp = LocationService.celsiusToFahrenheit(temp);
suffix = "F";
}
temp = Math.round(temp);
return temp + "°" + suffix;
}
pointSize: Style.fontSizeXL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
}
NText {
text: {
var wind = LocationService.data.weather.current_weather.windspeed;
var unit = "km/h";
if (Settings.data.location.useFahrenheit) {
wind = wind * 0.621371; // Convert km/h to mph
unit = "mph";
}
wind = Math.round(wind);
return wind + " " + unit;
}
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM
NText {
text: Settings.data.location.name.split(",")[0]
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
visible: !Settings.data.location.hideWeatherCityName
}
NText {
text: (LocationService.data.weather.current && LocationService.data.weather.current.relativehumidity_2m) ? LocationService.data.weather.current.relativehumidity_2m + "% humidity" : ""
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
}
}
}
}
// Forecast
RowLayout {
visible: Settings.data.location.weatherEnabled && LocationService.data.weather !== null
Layout.preferredWidth: 260
Layout.rightMargin: 8
spacing: Style.marginXS
Repeater {
model: MediaService.currentPlayer && MediaService.canPlay ? 3 : 4
delegate: ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS + 1
NText {
text: {
var weatherDate = new Date(LocationService.data.weather.daily.time[index].replace(/-/g, "/"));
return I18n.locale.toString(weatherDate, "ddd");
}
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
}
NIcon {
Layout.alignment: Qt.AlignHCenter
icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index])
pointSize: Style.fontSizeXL
color: Color.mOnSurfaceVariant
}
NText {
text: {
var max = LocationService.data.weather.daily.temperature_2m_max[index];
var min = LocationService.data.weather.daily.temperature_2m_min[index];
if (Settings.data.location.useFahrenheit) {
max = LocationService.celsiusToFahrenheit(max);
min = LocationService.celsiusToFahrenheit(min);
}
max = Math.round(max);
min = Math.round(min);
return max + "°/" + min + "°";
}
pointSize: Style.fontSizeM
font.weight: Style.fontWeightMedium
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
}
}
}
}
Item {
Layout.fillWidth: batteryIndicator.isReady && BatteryService.hasAnyBattery()
}
// Battery and Keyboard Layout (full mode only)
ColumnLayout {
Layout.alignment: (batteryIndicator.isReady && BatteryService.hasAnyBattery()) ? (Qt.AlignRight | Qt.AlignVCenter) : Qt.AlignVCenter
spacing: Style.marginM
visible: (batteryIndicator.isReady && BatteryService.hasAnyBattery()) || keyboardLayout.currentLayout !== "Unknown"
// Battery
RowLayout {
spacing: Style.marginXS
visible: batteryIndicator.isReady && BatteryService.hasAnyBattery()
NIcon {
icon: BatteryService.getIcon(Math.round(batteryIndicator.percent), batteryIndicator.charging, batteryIndicator.isReady)
pointSize: Style.fontSizeM
color: batteryIndicator.charging ? Color.mPrimary : Color.mOnSurfaceVariant
}
NText {
text: Math.round(batteryIndicator.percent) + "%"
color: Color.mOnSurfaceVariant
pointSize: Style.fontSizeM
}
}
// Keyboard Layout
RowLayout {
spacing: Style.marginXS
visible: keyboardLayout.currentLayout !== "Unknown"
NIcon {
icon: "keyboard"
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
}
NText {
text: keyboardLayout.currentLayout
color: Color.mOnSurfaceVariant
pointSize: Style.fontSizeM
elide: Text.ElideRight
}
}
}
Item {
Layout.preferredWidth: Style.marginM
}
}
// Password input
RowLayout {
Layout.fillWidth: true
spacing: 0
Item {
Layout.preferredWidth: Style.marginM
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 48
radius: Style.iRadiusL
color: Color.mSurface
border.color: passwordInput.activeFocus ? Color.mPrimary : Qt.alpha(Color.mOutline, 0.3)
border.width: passwordInput.activeFocus ? 2 : 1
property bool passwordVisible: false
Row {
anchors.left: parent.left
anchors.leftMargin: 18
anchors.verticalCenter: parent.verticalCenter
spacing: Style.marginL
NIcon {
icon: "lock"
pointSize: Style.fontSizeL
color: passwordInput.activeFocus ? Color.mPrimary : Color.mOnSurfaceVariant
anchors.verticalCenter: parent.verticalCenter
}
Row {
spacing: 0
Rectangle {
width: 2
height: 20
color: Color.mPrimary
visible: passwordInput.activeFocus && passwordInput.text.length === 0
anchors.verticalCenter: parent.verticalCenter
SequentialAnimation on opacity {
loops: Animation.Infinite
running: passwordInput.activeFocus && passwordInput.text.length === 0
NumberAnimation {
to: 0
duration: 530
}
NumberAnimation {
to: 1
duration: 530
}
}
}
// Password display - show dots or actual text based on passwordVisible
Item {
width: Math.min(passwordDisplayContent.width, 550)
height: 20
visible: passwordInput.text.length > 0 && !parent.parent.parent.passwordVisible
anchors.verticalCenter: parent.verticalCenter
clip: true
Row {
id: passwordDisplayContent
spacing: Style.marginS
anchors.verticalCenter: parent.verticalCenter
Repeater {
model: passwordInput.text.length
NIcon {
icon: "circle-filled"
pointSize: Style.fontSizeS
color: Color.mPrimary
opacity: 1.0
}
}
}
}
NText {
text: passwordInput.text
color: Color.mPrimary
pointSize: Style.fontSizeM
visible: passwordInput.text.length > 0 && parent.parent.parent.passwordVisible
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight
width: Math.min(implicitWidth, 550)
}
Rectangle {
width: 2
height: 20
color: Color.mPrimary
visible: passwordInput.activeFocus && passwordInput.text.length > 0
anchors.verticalCenter: parent.verticalCenter
SequentialAnimation on opacity {
loops: Animation.Infinite
running: passwordInput.activeFocus && passwordInput.text.length > 0
NumberAnimation {
to: 0
duration: 530
}
NumberAnimation {
to: 1
duration: 530
}
}
}
}
}
// Eye button to toggle password visibility
Rectangle {
anchors.right: submitButton.left
anchors.rightMargin: 4
anchors.verticalCenter: parent.verticalCenter
width: 36
height: 36
radius: Math.min(Style.iRadiusL, width / 2)
color: eyeButtonArea.containsMouse ? Color.mPrimary : "transparent"
visible: passwordInput.text.length > 0
enabled: !lockContext || !lockContext.unlockInProgress || lockContext.waitingForPassword
NIcon {
anchors.centerIn: parent
icon: parent.parent.passwordVisible ? "eye-off" : "eye"
pointSize: Style.fontSizeM
color: eyeButtonArea.containsMouse ? Color.mOnPrimary : Color.mOnSurfaceVariant
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
MouseArea {
id: eyeButtonArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: parent.parent.passwordVisible = !parent.parent.passwordVisible
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
// Submit button
Rectangle {
id: submitButton
anchors.right: parent.right
anchors.rightMargin: 8
anchors.verticalCenter: parent.verticalCenter
width: 36
height: 36
radius: Math.min(Style.iRadiusL, width / 2)
color: submitButtonArea.containsMouse ? Color.mPrimary : "transparent"
border.color: Color.mPrimary
border.width: Style.borderS
enabled: !lockContext || !lockContext.unlockInProgress || lockContext.waitingForPassword
NIcon {
anchors.centerIn: parent
icon: "arrow-forward"
pointSize: Style.fontSizeM
color: submitButtonArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
MouseArea {
id: submitButtonArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: if (lockContext) lockContext.tryUnlock()
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
Behavior on border.color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
Item {
Layout.preferredWidth: Style.marginM
}
}
// Session control buttons
RowLayout {
id: sessionButtonRow
Layout.fillWidth: true
Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48
Layout.alignment: Qt.AlignHCenter
spacing: Style.marginM
visible: Settings.data.general.showSessionButtonsOnLockScreen
readonly property int buttonCount: Settings.data.general.showHibernateOnLockScreen ? 5 : 4
readonly property real availableWidth: bottomContainer.width - 48
readonly property real buttonWidth: (availableWidth - (buttonCount - 1) * spacing) / buttonCount
readonly property real buttonHeight: sessionButtonRow.height
Item {
Layout.preferredWidth: sessionButtonRow.buttonWidth
Layout.preferredHeight: sessionButtonRow.buttonHeight
NButton {
anchors.fill: parent
icon: "logout"
text: I18n.tr("common.logout")
outlined: true
backgroundColor: Color.mOnSurfaceVariant
textColor: Color.mOnPrimary
hoverColor: Color.mPrimary
fontSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM
iconSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL
horizontalAlignment: Qt.AlignHCenter
buttonRadius: Style.radiusL
onClicked: CompositorService.logout()
}
}
Item {
Layout.preferredWidth: sessionButtonRow.buttonWidth
Layout.preferredHeight: sessionButtonRow.buttonHeight
NButton {
anchors.fill: parent
icon: "suspend"
text: I18n.tr("common.suspend")
outlined: true
backgroundColor: Color.mOnSurfaceVariant
textColor: Color.mOnPrimary
hoverColor: Color.mPrimary
fontSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM
iconSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL
horizontalAlignment: Qt.AlignHCenter
buttonRadius: Style.radiusL
onClicked: CompositorService.suspend()
}
}
Item {
Layout.preferredWidth: sessionButtonRow.buttonWidth
Layout.preferredHeight: sessionButtonRow.buttonHeight
visible: Settings.data.general.showHibernateOnLockScreen
NButton {
anchors.fill: parent
icon: "hibernate"
text: I18n.tr("common.hibernate")
outlined: true
backgroundColor: Color.mOnSurfaceVariant
textColor: Color.mOnPrimary
hoverColor: Color.mPrimary
fontSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM
iconSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL
horizontalAlignment: Qt.AlignHCenter
buttonRadius: Style.radiusL
onClicked: CompositorService.hibernate()
}
}
Item {
Layout.preferredWidth: sessionButtonRow.buttonWidth
Layout.preferredHeight: sessionButtonRow.buttonHeight
NButton {
anchors.fill: parent
icon: "reboot"
text: I18n.tr("common.reboot")
outlined: true
backgroundColor: Color.mOnSurfaceVariant
textColor: Color.mOnPrimary
hoverColor: Color.mPrimary
fontSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM
iconSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL
horizontalAlignment: Qt.AlignHCenter
buttonRadius: Style.radiusL
onClicked: CompositorService.reboot()
}
}
Item {
Layout.preferredWidth: sessionButtonRow.buttonWidth
Layout.preferredHeight: sessionButtonRow.buttonHeight
NButton {
anchors.fill: parent
icon: "shutdown"
text: I18n.tr("common.shutdown")
outlined: true
backgroundColor: Color.mError
textColor: Color.mOnError
hoverColor: Color.mError
fontSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM
iconSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL
horizontalAlignment: Qt.AlignHCenter
buttonRadius: Style.radiusL
onClicked: CompositorService.shutdown()
}
}
}
}
}
}