845 lines
32 KiB
QML
845 lines
32 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Controls
|
|
import QtQuick.Effects
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import Quickshell.Services.Pam
|
|
import Quickshell.Services.UPower
|
|
import Quickshell.Io
|
|
import Quickshell.Widgets
|
|
import qs.Commons
|
|
import qs.Services
|
|
import qs.Widgets
|
|
import qs.Modules.Audio
|
|
|
|
Loader {
|
|
id: lockScreen
|
|
active: false
|
|
|
|
Timer {
|
|
id: unloadAfterUnlockTimer
|
|
interval: 250
|
|
repeat: false
|
|
onTriggered: {
|
|
lockScreen.active = false
|
|
}
|
|
}
|
|
|
|
function scheduleUnloadAfterUnlock() {
|
|
unloadAfterUnlockTimer.start()
|
|
}
|
|
|
|
sourceComponent: Component {
|
|
Item {
|
|
id: lockContainer
|
|
|
|
// Create the lock context
|
|
LockContext {
|
|
id: lockContext
|
|
onUnlocked: {
|
|
lockSession.locked = false
|
|
lockScreen.scheduleUnloadAfterUnlock()
|
|
lockContext.currentText = ""
|
|
}
|
|
}
|
|
|
|
WlSessionLock {
|
|
id: lockSession
|
|
locked: lockScreen.active
|
|
|
|
WlSessionLockSurface {
|
|
readonly property real scaling: ScalingService.dynamicScale(screen)
|
|
|
|
Item {
|
|
id: batteryIndicator
|
|
property var battery: UPower.displayDevice
|
|
property bool isReady: battery && battery.ready && battery.isLaptopBattery && battery.isPresent
|
|
property real percent: isReady ? (battery.percentage * 100) : 0
|
|
property bool charging: isReady ? battery.state === UPowerDeviceState.Charging : false
|
|
property bool batteryVisible: isReady && percent > 0
|
|
|
|
function getIcon() {
|
|
if (!batteryVisible)
|
|
return ""
|
|
if (charging)
|
|
return "battery_android_bolt"
|
|
if (percent >= 95)
|
|
return "battery_android_full"
|
|
if (percent >= 85)
|
|
return "battery_android_6"
|
|
if (percent >= 70)
|
|
return "battery_android_5"
|
|
if (percent >= 55)
|
|
return "battery_android_4"
|
|
if (percent >= 40)
|
|
return "battery_android_3"
|
|
if (percent >= 25)
|
|
return "battery_android_2"
|
|
if (percent >= 10)
|
|
return "battery_android_1"
|
|
if (percent >= 0)
|
|
return "battery_android_0"
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: keyboardLayout
|
|
property string currentLayout: (typeof KeyboardLayoutService !== 'undefined'
|
|
&& KeyboardLayoutService.currentLayout) ? KeyboardLayoutService.currentLayout : "Unknown"
|
|
}
|
|
|
|
Image {
|
|
id: lockBgImage
|
|
anchors.fill: parent
|
|
fillMode: Image.PreserveAspectCrop
|
|
source: WallpaperService.getWallpaper(screen.name)
|
|
cache: true
|
|
smooth: true
|
|
mipmap: false
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: Color.transparent
|
|
layer.enabled: true
|
|
layer.smooth: true
|
|
layer.samples: 4
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
Repeater {
|
|
model: 20
|
|
Rectangle {
|
|
width: Math.random() * 4 + 2
|
|
height: width
|
|
radius: width * 0.5
|
|
color: Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
anchors.fill: parent
|
|
|
|
ColumnLayout {
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.topMargin: 80 * scaling
|
|
spacing: 40 * scaling
|
|
|
|
Column {
|
|
spacing: Style.marginXS * scaling
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
NText {
|
|
id: timeText
|
|
text: Qt.formatDateTime(new Date(), "HH:mm")
|
|
font.family: Settings.data.ui.fontBillboard
|
|
font.pointSize: Style.fontSizeXXXL * 6 * scaling
|
|
font.weight: Style.fontWeightBold
|
|
font.letterSpacing: -2 * scaling
|
|
color: Color.mOnSurface
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
NText {
|
|
id: dateText
|
|
text: Qt.formatDateTime(new Date(), "dddd, MMMM d")
|
|
font.family: Settings.data.ui.fontBillboard
|
|
font.pointSize: Style.fontSizeXXL * scaling
|
|
font.weight: Font.Light
|
|
color: Color.mOnSurface
|
|
horizontalAlignment: Text.AlignHCenter
|
|
width: timeText.width
|
|
}
|
|
}
|
|
|
|
Column {
|
|
spacing: Style.marginM * scaling
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
Rectangle {
|
|
width: 108 * scaling
|
|
height: 108 * scaling
|
|
radius: width * 0.5
|
|
color: Color.transparent
|
|
border.color: Color.mPrimary
|
|
border.width: Math.max(1, Style.borderL * scaling)
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
z: 10
|
|
|
|
Loader {
|
|
active: MediaService.isPlaying && Settings.data.audio.visualizerType == "linear"
|
|
anchors.centerIn: parent
|
|
width: 160 * scaling
|
|
height: 160 * scaling
|
|
sourceComponent: Item {
|
|
Repeater {
|
|
model: CavaService.values.length
|
|
Rectangle {
|
|
property real linearAngle: (index / CavaService.values.length) * 2 * Math.PI
|
|
property real linearRadius: 70 * scaling
|
|
property real linearBarLength: Math.max(2, CavaService.values[index] * 30 * scaling)
|
|
property real linearBarWidth: 3 * scaling
|
|
width: linearBarWidth
|
|
height: linearBarLength
|
|
color: Color.mPrimary
|
|
radius: linearBarWidth * 0.5
|
|
x: parent.width * 0.5 + Math.cos(linearAngle) * linearRadius - width * 0.5
|
|
y: parent.height * 0.5 + Math.sin(linearAngle) * linearRadius - height * 0.5
|
|
transform: Rotation {
|
|
origin.x: linearBarWidth * 0.5
|
|
origin.y: linearBarLength * 0.5
|
|
angle: (linearAngle * 180 / Math.PI) + 90
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
active: MediaService.isPlaying && Settings.data.audio.visualizerType == "mirrored"
|
|
anchors.centerIn: parent
|
|
width: 160 * scaling
|
|
height: 160 * scaling
|
|
sourceComponent: Item {
|
|
Repeater {
|
|
model: CavaService.values.length * 2
|
|
Rectangle {
|
|
property int mirroredValueIndex: index < CavaService.values.length ? index : (CavaService.values.length
|
|
* 2 - 1 - index)
|
|
property real mirroredAngle: (index / (CavaService.values.length * 2)) * 2 * Math.PI
|
|
property real mirroredRadius: 70 * scaling
|
|
property real mirroredBarLength: Math.max(
|
|
2, CavaService.values[mirroredValueIndex] * 30 * scaling)
|
|
property real mirroredBarWidth: 3 * scaling
|
|
width: mirroredBarWidth
|
|
height: mirroredBarLength
|
|
color: Color.mPrimary
|
|
radius: mirroredBarWidth * 0.5
|
|
x: parent.width * 0.5 + Math.cos(mirroredAngle) * mirroredRadius - width * 0.5
|
|
y: parent.height * 0.5 + Math.sin(mirroredAngle) * mirroredRadius - height * 0.5
|
|
transform: Rotation {
|
|
origin.x: mirroredBarWidth * 0.5
|
|
origin.y: mirroredBarLength * 0.5
|
|
angle: (mirroredAngle * 180 / Math.PI) + 90
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
active: MediaService.isPlaying && Settings.data.audio.visualizerType == "wave"
|
|
anchors.centerIn: parent
|
|
width: 160 * scaling
|
|
height: 160 * scaling
|
|
sourceComponent: Item {
|
|
Canvas {
|
|
id: waveCanvas
|
|
anchors.fill: parent
|
|
antialiasing: true
|
|
onPaint: {
|
|
var ctx = getContext("2d")
|
|
ctx.reset()
|
|
if (CavaService.values.length === 0)
|
|
return
|
|
ctx.strokeStyle = Color.mPrimary
|
|
ctx.lineWidth = 2 * scaling
|
|
ctx.lineCap = "round"
|
|
var centerX = width * 0.5
|
|
var centerY = height * 0.5
|
|
var baseRadius = 60 * scaling
|
|
var maxAmplitude = 20 * scaling
|
|
ctx.beginPath()
|
|
for (var i = 0; i <= CavaService.values.length; i++) {
|
|
var index = i % CavaService.values.length
|
|
var angle = (i / CavaService.values.length) * 2 * Math.PI
|
|
var amplitude = CavaService.values[index] * maxAmplitude
|
|
var radius = baseRadius + amplitude
|
|
var x = centerX + Math.cos(angle) * radius
|
|
var y = centerY + Math.sin(angle) * radius
|
|
if (i === 0)
|
|
ctx.moveTo(x, y)
|
|
else
|
|
ctx.lineTo(x, y)
|
|
}
|
|
ctx.closePath()
|
|
ctx.stroke()
|
|
}
|
|
}
|
|
Timer {
|
|
interval: 16
|
|
running: true
|
|
repeat: true
|
|
onTriggered: waveCanvas.requestPaint()
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.centerIn: parent
|
|
width: parent.width + 24 * scaling
|
|
height: parent.height + 24 * scaling
|
|
radius: width * 0.5
|
|
color: Color.transparent
|
|
border.color: Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.3)
|
|
border.width: Math.max(1, Style.borderM * scaling)
|
|
z: -1
|
|
visible: !MediaService.isPlaying
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
NImageCircled {
|
|
anchors.centerIn: parent
|
|
width: 100 * scaling
|
|
height: 100 * scaling
|
|
imagePath: Settings.data.general.avatarImage
|
|
fallbackIcon: "person"
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
onEntered: parent.scale = 1.05
|
|
onExited: parent.scale = 1.0
|
|
}
|
|
|
|
Behavior on scale {
|
|
NumberAnimation {
|
|
duration: Style.animationFast
|
|
easing.type: Easing.OutBack
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
width: 720 * scaling
|
|
height: 280 * scaling
|
|
anchors.centerIn: parent
|
|
anchors.verticalCenterOffset: 50 * scaling
|
|
|
|
Item {
|
|
width: parent.width
|
|
height: 280 * scaling
|
|
Layout.fillWidth: true
|
|
|
|
Rectangle {
|
|
id: terminalBackground
|
|
anchors.fill: parent
|
|
radius: Style.radiusM * scaling
|
|
color: Color.applyOpacity(Color.mSurface, "E6")
|
|
border.color: Color.mPrimary
|
|
border.width: Math.max(1, Style.borderM * scaling)
|
|
|
|
Repeater {
|
|
model: 20
|
|
Rectangle {
|
|
width: parent.width
|
|
height: 1
|
|
color: Color.applyOpacity(Color.mPrimary, "1A")
|
|
y: index * 10 * scaling
|
|
opacity: Style.opacityMedium
|
|
SequentialAnimation on opacity {
|
|
loops: Animation.Infinite
|
|
NumberAnimation {
|
|
to: 0.6
|
|
duration: 2000 + Math.random() * 1000
|
|
}
|
|
NumberAnimation {
|
|
to: 0.1
|
|
duration: 2000 + Math.random() * 1000
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: 40 * scaling
|
|
color: Color.applyOpacity(Color.mPrimary, "33")
|
|
topLeftRadius: Style.radiusS * scaling
|
|
topRightRadius: Style.radiusS * scaling
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
anchors.topMargin: Style.marginM * scaling
|
|
anchors.bottomMargin: Style.marginM * scaling
|
|
anchors.leftMargin: Style.marginL * scaling
|
|
anchors.rightMargin: Style.marginL * scaling
|
|
spacing: Style.marginM * scaling
|
|
|
|
NText {
|
|
text: "SECURE TERMINAL"
|
|
color: Color.mOnSurface
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeL * scaling
|
|
font.weight: Style.fontWeightBold
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Row {
|
|
spacing: Style.marginS * scaling
|
|
visible: batteryIndicator.batteryVisible
|
|
NIcon {
|
|
text: batteryIndicator.getIcon()
|
|
font.pointSize: Style.fontSizeM * scaling
|
|
color: batteryIndicator.charging ? Color.mPrimary : Color.mOnSurface
|
|
}
|
|
NText {
|
|
text: Math.round(batteryIndicator.percent) + "%"
|
|
color: Color.mOnSurface
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeM * scaling
|
|
font.weight: Style.fontWeightBold
|
|
}
|
|
}
|
|
|
|
Row {
|
|
spacing: Style.marginS * scaling
|
|
NText {
|
|
text: keyboardLayout.currentLayout
|
|
color: Color.mOnSurface
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeM * scaling
|
|
font.weight: Style.fontWeightBold
|
|
}
|
|
NIcon {
|
|
text: "keyboard_alt"
|
|
font.pointSize: Style.fontSizeM * scaling
|
|
color: Color.mOnSurface
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
anchors.margins: Style.marginL * scaling
|
|
anchors.topMargin: 70 * scaling
|
|
spacing: Style.marginM * scaling
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Style.marginM * scaling
|
|
|
|
NText {
|
|
text: Quickshell.env("USER") + "@noctalia:~$"
|
|
color: Color.mPrimary
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeL * scaling
|
|
font.weight: Style.fontWeightBold
|
|
}
|
|
|
|
NText {
|
|
id: welcomeText
|
|
text: ""
|
|
color: Color.mOnSurface
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeL * scaling
|
|
property int currentIndex: 0
|
|
property string fullText: "Welcome back, " + Quickshell.env("USER") + "!"
|
|
|
|
Timer {
|
|
interval: Style.animationFast
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Style.marginM * scaling
|
|
|
|
NText {
|
|
text: Quickshell.env("USER") + "@noctalia:~$"
|
|
color: Color.mPrimary
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeL * scaling
|
|
font.weight: Style.fontWeightBold
|
|
}
|
|
|
|
NText {
|
|
text: "sudo unlock-session"
|
|
color: Color.mOnSurface
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeL * scaling
|
|
}
|
|
|
|
TextInput {
|
|
id: passwordInput
|
|
width: 0
|
|
height: 0
|
|
visible: false
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeL * scaling
|
|
color: Color.mOnSurface
|
|
echoMode: TextInput.Password
|
|
passwordCharacter: "*"
|
|
passwordMaskDelay: 0
|
|
|
|
text: lockContext.currentText
|
|
onTextChanged: {
|
|
lockContext.currentText = text
|
|
}
|
|
|
|
Keys.onPressed: function (event) {
|
|
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
|
lockContext.tryUnlock()
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
forceActiveFocus()
|
|
}
|
|
}
|
|
|
|
NText {
|
|
id: asterisksText
|
|
text: "*".repeat(passwordInput.text.length)
|
|
color: Color.mOnSurface
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeL * scaling
|
|
visible: passwordInput.activeFocus
|
|
|
|
SequentialAnimation {
|
|
id: typingEffect
|
|
NumberAnimation {
|
|
target: passwordInput
|
|
property: "scale"
|
|
to: 1.01
|
|
duration: 50
|
|
}
|
|
NumberAnimation {
|
|
target: passwordInput
|
|
property: "scale"
|
|
to: 1.0
|
|
duration: 50
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: 8 * scaling
|
|
height: 20 * scaling
|
|
color: Color.mPrimary
|
|
visible: passwordInput.activeFocus
|
|
Layout.leftMargin: -Style.marginS * scaling
|
|
Layout.alignment: Qt.AlignVCenter
|
|
|
|
SequentialAnimation on opacity {
|
|
loops: Animation.Infinite
|
|
NumberAnimation {
|
|
to: 1.0
|
|
duration: 500
|
|
}
|
|
NumberAnimation {
|
|
to: 0.0
|
|
duration: 500
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NText {
|
|
text: {
|
|
if (lockContext.unlockInProgress)
|
|
return "Authenticating..."
|
|
if (lockContext.showFailure && lockContext.errorMessage)
|
|
return lockContext.errorMessage
|
|
if (lockContext.showFailure)
|
|
return "Authentication failed."
|
|
return ""
|
|
}
|
|
color: {
|
|
if (lockContext.unlockInProgress)
|
|
return Color.mPrimary
|
|
if (lockContext.showFailure)
|
|
return Color.mError
|
|
return Color.transparent
|
|
}
|
|
font.family: "DejaVu Sans Mono"
|
|
font.pointSize: Style.fontSizeL * scaling
|
|
Layout.fillWidth: true
|
|
|
|
SequentialAnimation on opacity {
|
|
running: lockContext.unlockInProgress
|
|
loops: Animation.Infinite
|
|
NumberAnimation {
|
|
to: 1.0
|
|
duration: 800
|
|
}
|
|
NumberAnimation {
|
|
to: 0.5
|
|
duration: 800
|
|
}
|
|
}
|
|
}
|
|
|
|
Row {
|
|
Layout.alignment: Qt.AlignRight
|
|
Layout.bottomMargin: -10 * scaling
|
|
Rectangle {
|
|
width: 120 * scaling
|
|
height: 40 * scaling
|
|
radius: Style.radiusS * scaling
|
|
color: executeButtonArea.containsMouse ? Color.mPrimary : Color.applyOpacity(Color.mPrimary,
|
|
"33")
|
|
border.color: Color.mPrimary
|
|
border.width: Math.max(1, Style.borderS * scaling)
|
|
enabled: !lockContext.unlockInProgress
|
|
|
|
NText {
|
|
anchors.centerIn: parent
|
|
text: lockContext.unlockInProgress ? "EXECUTING" : "EXECUTE"
|
|
color: executeButtonArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
|
font.family: Settings.data.ui.fontFixed
|
|
font.pointSize: Style.fontSizeM * scaling
|
|
font.weight: Style.fontWeightBold
|
|
}
|
|
|
|
MouseArea {
|
|
id: executeButtonArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
onClicked: {
|
|
lockContext.tryUnlock()
|
|
}
|
|
|
|
SequentialAnimation on scale {
|
|
running: executeButtonArea.containsMouse
|
|
NumberAnimation {
|
|
to: 1.05
|
|
duration: Style.animationFast
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
|
|
SequentialAnimation on scale {
|
|
running: !executeButtonArea.containsMouse
|
|
NumberAnimation {
|
|
to: 1.0
|
|
duration: Style.animationFast
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
}
|
|
|
|
SequentialAnimation on scale {
|
|
loops: Animation.Infinite
|
|
running: lockContext.unlockInProgress
|
|
NumberAnimation {
|
|
to: 1.02
|
|
duration: 600
|
|
easing.type: Easing.InOutQuad
|
|
}
|
|
NumberAnimation {
|
|
to: 1.0
|
|
duration: 600
|
|
easing.type: Easing.InOutQuad
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: parent.radius
|
|
color: Color.transparent
|
|
border.color: Color.applyOpacity(Color.mPrimary, "4D")
|
|
border.width: Math.max(1, Style.borderS * scaling)
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Power buttons at bottom
|
|
Row {
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
anchors.margins: 50 * scaling
|
|
spacing: 20 * scaling
|
|
|
|
Rectangle {
|
|
width: 60 * scaling
|
|
height: 60 * scaling
|
|
radius: width * 0.5
|
|
color: powerButtonArea.containsMouse ? Color.mError : Color.applyOpacity(Color.mError, "33")
|
|
border.color: Color.mError
|
|
border.width: Math.max(1, Style.borderM * scaling)
|
|
|
|
NIcon {
|
|
anchors.centerIn: parent
|
|
text: "power_settings_new"
|
|
font.pointSize: Style.fontSizeXL * scaling
|
|
color: powerButtonArea.containsMouse ? Color.mOnError : Color.mError
|
|
}
|
|
|
|
MouseArea {
|
|
id: powerButtonArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
onClicked: {
|
|
CompositorService.shutdown()
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: 60 * scaling
|
|
height: 60 * scaling
|
|
radius: width * 0.5
|
|
color: restartButtonArea.containsMouse ? Color.mPrimary : Color.applyOpacity(Color.mPrimary, "33")
|
|
border.color: Color.mPrimary
|
|
border.width: Math.max(1, Style.borderM * scaling)
|
|
|
|
NIcon {
|
|
anchors.centerIn: parent
|
|
text: "restart_alt"
|
|
font.pointSize: Style.fontSizeXL * scaling
|
|
color: restartButtonArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
|
}
|
|
|
|
MouseArea {
|
|
id: restartButtonArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
onClicked: {
|
|
CompositorService.reboot()
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: 60 * scaling
|
|
height: 60 * scaling
|
|
radius: width * 0.5
|
|
color: suspendButtonArea.containsMouse ? Color.mSecondary : Color.applyOpacity(Color.mSecondary, "33")
|
|
border.color: Color.mSecondary
|
|
border.width: Math.max(1, Style.borderM * scaling)
|
|
|
|
NIcon {
|
|
anchors.centerIn: parent
|
|
text: "bedtime"
|
|
font.pointSize: Style.fontSizeXL * scaling
|
|
color: suspendButtonArea.containsMouse ? Color.mOnSecondary : Color.mSecondary
|
|
}
|
|
|
|
MouseArea {
|
|
id: suspendButtonArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
onClicked: {
|
|
CompositorService.suspend()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|