mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-26 22:42:50 -05:00
switch hto monorepo structure
This commit is contained in:
41
quickshell/Modules/Lock/CustomButtonKeyboard.qml
Normal file
41
quickshell/Modules/Lock/CustomButtonKeyboard.qml
Normal file
@@ -0,0 +1,41 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
DankActionButton {
|
||||
id: customButtonKeyboard
|
||||
circular: false
|
||||
property string text: ""
|
||||
width: 40
|
||||
height: 40
|
||||
property bool isShift: false
|
||||
color: Theme.surface
|
||||
|
||||
property bool isIcon: text === "keyboard_hide" || text === "Backspace" || text === "Enter"
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: {
|
||||
if (parent.text === "keyboard_hide") return "keyboard_hide"
|
||||
if (parent.text === "Backspace") return "backspace"
|
||||
if (parent.text === "Enter") return "keyboard_return"
|
||||
return ""
|
||||
}
|
||||
size: 20
|
||||
color: Theme.surfaceText
|
||||
visible: parent.isIcon
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: contentItem
|
||||
anchors.centerIn: parent
|
||||
text: parent.text
|
||||
color: Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
font.weight: Font.Normal
|
||||
visible: !parent.isIcon
|
||||
}
|
||||
}
|
||||
344
quickshell/Modules/Lock/Keyboard.qml
Normal file
344
quickshell/Modules/Lock/Keyboard.qml
Normal file
@@ -0,0 +1,344 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property Item target
|
||||
height: 60 * 5
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: Theme.widgetBackground
|
||||
|
||||
signal dismissed
|
||||
|
||||
property double rowSpacing: 0.01 * width // horizontal spacing between keyboard
|
||||
property double columnSpacing: 0.02 * height // vertical spacing between keyboard
|
||||
property bool shift: false //Boolean for the shift state
|
||||
property bool symbols: false //Boolean for the symbol state
|
||||
property double columns: 10 // Number of column
|
||||
property double rows: 4 // Number of row
|
||||
|
||||
property string strShift: '\u2191'
|
||||
property string strBackspace: "Backspace"
|
||||
property string strEnter: "Enter"
|
||||
property string strClose: "keyboard_hide"
|
||||
|
||||
property var modelKeyboard: {
|
||||
"row_1": [{
|
||||
"text": 'q',
|
||||
"symbol": '1',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'w',
|
||||
"symbol": '2',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'e',
|
||||
"symbol": '3',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'r',
|
||||
"symbol": '4',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 't',
|
||||
"symbol": '5',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'y',
|
||||
"symbol": '6',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'u',
|
||||
"symbol": '7',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'i',
|
||||
"symbol": '8',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'o',
|
||||
"symbol": '9',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'p',
|
||||
"symbol": '0',
|
||||
"width": 1
|
||||
}],
|
||||
"row_2": [{
|
||||
"text": 'a',
|
||||
"symbol": '-',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 's',
|
||||
"symbol": '/',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'd',
|
||||
"symbol": ':',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'f',
|
||||
"symbol": ';',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'g',
|
||||
"symbol": '(',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'h',
|
||||
"symbol": ')',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'j',
|
||||
"symbol": '€',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'k',
|
||||
"symbol": '&',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'l',
|
||||
"symbol": '@',
|
||||
"width": 1
|
||||
}],
|
||||
"row_3": [{
|
||||
"text": strShift,
|
||||
"symbol": strShift,
|
||||
"width": 1.5
|
||||
}, {
|
||||
"text": 'z',
|
||||
"symbol": '.',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'x',
|
||||
"symbol": ',',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'c',
|
||||
"symbol": '?',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'v',
|
||||
"symbol": '!',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'b',
|
||||
"symbol": "'",
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'n',
|
||||
"symbol": "%",
|
||||
"width": 1
|
||||
}, {
|
||||
"text": 'm',
|
||||
"symbol": '"',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": strBackspace,
|
||||
"symbol": strBackspace,
|
||||
"width": 1.5
|
||||
}],
|
||||
"row_4": [{
|
||||
"text": strClose,
|
||||
"symbol": strClose,
|
||||
"width": 1.5
|
||||
}, {
|
||||
"text": "123",
|
||||
"symbol": 'ABC',
|
||||
"width": 1.5
|
||||
}, {
|
||||
"text": ' ',
|
||||
"symbol": ' ',
|
||||
"width": 4.5
|
||||
}, {
|
||||
"text": '.',
|
||||
"symbol": '.',
|
||||
"width": 1
|
||||
}, {
|
||||
"text": strEnter,
|
||||
"symbol": strEnter,
|
||||
"width": 1.5
|
||||
}]
|
||||
}
|
||||
|
||||
//Here is the corresponding table between the ascii and the key event
|
||||
property var tableKeyEvent: {
|
||||
"_0": Qt.Key_0,
|
||||
"_1": Qt.Key_1,
|
||||
"_2": Qt.Key_2,
|
||||
"_3": Qt.Key_3,
|
||||
"_4": Qt.Key_4,
|
||||
"_5": Qt.Key_5,
|
||||
"_6": Qt.Key_6,
|
||||
"_7": Qt.Key_7,
|
||||
"_8": Qt.Key_8,
|
||||
"_9": Qt.Key_9,
|
||||
"_a": Qt.Key_A,
|
||||
"_b": Qt.Key_B,
|
||||
"_c": Qt.Key_C,
|
||||
"_d": Qt.Key_D,
|
||||
"_e": Qt.Key_E,
|
||||
"_f": Qt.Key_F,
|
||||
"_g": Qt.Key_G,
|
||||
"_h": Qt.Key_H,
|
||||
"_i": Qt.Key_I,
|
||||
"_j": Qt.Key_J,
|
||||
"_k": Qt.Key_K,
|
||||
"_l": Qt.Key_L,
|
||||
"_m": Qt.Key_M,
|
||||
"_n": Qt.Key_N,
|
||||
"_o": Qt.Key_O,
|
||||
"_p": Qt.Key_P,
|
||||
"_q": Qt.Key_Q,
|
||||
"_r": Qt.Key_R,
|
||||
"_s": Qt.Key_S,
|
||||
"_t": Qt.Key_T,
|
||||
"_u": Qt.Key_U,
|
||||
"_v": Qt.Key_V,
|
||||
"_w": Qt.Key_W,
|
||||
"_x": Qt.Key_X,
|
||||
"_y": Qt.Key_Y,
|
||||
"_z": Qt.Key_Z,
|
||||
"_←": Qt.Key_Backspace,
|
||||
"_return": Qt.Key_Return,
|
||||
"_ ": Qt.Key_Space,
|
||||
"_-": Qt.Key_Minus,
|
||||
"_/": Qt.Key_Slash,
|
||||
"_:": Qt.Key_Colon,
|
||||
"_;": Qt.Key_Semicolon,
|
||||
"_(": Qt.Key_BracketLeft,
|
||||
"_)": Qt.Key_BracketRight,
|
||||
"_€": parseInt(
|
||||
"20ac",
|
||||
16) // I didn't find the appropriate Qt event so I used the hex format
|
||||
,
|
||||
"_&": Qt.Key_Ampersand,
|
||||
"_@": Qt.Key_At,
|
||||
'_"': Qt.Key_QuoteDbl,
|
||||
"_.": Qt.Key_Period,
|
||||
"_,": Qt.Key_Comma,
|
||||
"_?": Qt.Key_Question,
|
||||
"_!": Qt.Key_Exclam,
|
||||
"_'": Qt.Key_Apostrophe,
|
||||
"_%": Qt.Key_Percent,
|
||||
"_*": Qt.Key_Asterisk
|
||||
}
|
||||
|
||||
Item {
|
||||
id: keyboard_container
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 5
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 5
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 5
|
||||
|
||||
//One column which contains 5 rows
|
||||
Column {
|
||||
spacing: columnSpacing
|
||||
|
||||
Row {
|
||||
id: row_1
|
||||
spacing: rowSpacing
|
||||
Repeater {
|
||||
model: modelKeyboard["row_1"]
|
||||
delegate: CustomButtonKeyboard {
|
||||
text: symbols ? modelData.symbol : shift ? modelData.text.toUpperCase
|
||||
() : modelData.text
|
||||
width: modelData.width * keyboard_container.width / columns - rowSpacing
|
||||
height: keyboard_container.height / rows - columnSpacing
|
||||
|
||||
onClicked: root.clicked(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
id: row_2
|
||||
spacing: rowSpacing
|
||||
Repeater {
|
||||
model: modelKeyboard["row_2"]
|
||||
delegate: CustomButtonKeyboard {
|
||||
text: symbols ? modelData.symbol : shift ? modelData.text.toUpperCase
|
||||
() : modelData.text
|
||||
width: modelData.width * keyboard_container.width / columns - rowSpacing
|
||||
height: keyboard_container.height / rows - columnSpacing
|
||||
|
||||
onClicked: root.clicked(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
id: row_3
|
||||
spacing: rowSpacing
|
||||
Repeater {
|
||||
model: modelKeyboard["row_3"]
|
||||
delegate: CustomButtonKeyboard {
|
||||
text: symbols ? modelData.symbol : shift ? modelData.text.toUpperCase
|
||||
() : modelData.text
|
||||
width: modelData.width * keyboard_container.width / columns - rowSpacing
|
||||
height: keyboard_container.height / rows - columnSpacing
|
||||
isShift: shift && text === strShift
|
||||
|
||||
onClicked: root.clicked(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
id: row_4
|
||||
spacing: rowSpacing
|
||||
Repeater {
|
||||
model: modelKeyboard["row_4"]
|
||||
delegate: CustomButtonKeyboard {
|
||||
text: symbols ? modelData.symbol : shift ? modelData.text.toUpperCase
|
||||
() : modelData.text
|
||||
width: modelData.width * keyboard_container.width / columns - rowSpacing
|
||||
height: keyboard_container.height / rows - columnSpacing
|
||||
|
||||
onClicked: root.clicked(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
signal clicked(string text)
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onClicked(text) {
|
||||
if (!keyboard_controller.target)
|
||||
return
|
||||
if (text === strShift) {
|
||||
root.shift = !root.shift
|
||||
} else if (text === '123') {
|
||||
root.symbols = true
|
||||
} else if (text === 'ABC') {
|
||||
root.symbols = false
|
||||
} else if (text === strEnter) {
|
||||
if (keyboard_controller.target.accepted) {
|
||||
keyboard_controller.target.accepted()
|
||||
}
|
||||
} else if (text === strClose) {
|
||||
root.dismissed()
|
||||
} else {
|
||||
if (text === strBackspace) {
|
||||
var current = keyboard_controller.target.text
|
||||
keyboard_controller.target.text = current.slice(0, current.length - 1)
|
||||
} else {
|
||||
var charToInsert = root.symbols ? text : (root.shift ? text.toUpperCase
|
||||
() : text)
|
||||
var current = keyboard_controller.target.text
|
||||
var cursorPos = keyboard_controller.target.cursorPosition
|
||||
keyboard_controller.target.text = current.slice(0,
|
||||
cursorPos) + charToInsert + current.slice(cursorPos)
|
||||
keyboard_controller.target.cursorPosition = cursorPos + 1
|
||||
}
|
||||
|
||||
if (root.shift && text !== strShift)
|
||||
root.shift = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
quickshell/Modules/Lock/KeyboardController.qml
Normal file
40
quickshell/Modules/Lock/KeyboardController.qml
Normal file
@@ -0,0 +1,40 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: keyboard_controller
|
||||
|
||||
// reference on the TextInput
|
||||
property Item target
|
||||
//Booléan on the state of the keyboard
|
||||
property bool isKeyboardActive: false
|
||||
|
||||
property var rootObject
|
||||
|
||||
function show() {
|
||||
if (!isKeyboardActive && keyboard === null) {
|
||||
keyboard = keyboardComponent.createObject(
|
||||
keyboard_controller.rootObject)
|
||||
keyboard.target = keyboard_controller.target
|
||||
keyboard.dismissed.connect(hide)
|
||||
isKeyboardActive = true
|
||||
} else
|
||||
console.log("The keyboard is already shown")
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (isKeyboardActive && keyboard !== null) {
|
||||
keyboard.destroy()
|
||||
isKeyboardActive = false
|
||||
} else
|
||||
console.log("The keyboard is already hidden")
|
||||
}
|
||||
|
||||
// private
|
||||
property Item keyboard: null
|
||||
Component {
|
||||
id: keyboardComponent
|
||||
Keyboard {}
|
||||
}
|
||||
}
|
||||
133
quickshell/Modules/Lock/Lock.qml
Normal file
133
quickshell/Modules/Lock/Lock.qml
Normal file
@@ -0,0 +1,133 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
property string sharedPasswordBuffer: ""
|
||||
property bool shouldLock: false
|
||||
property bool processingExternalEvent: false
|
||||
|
||||
Component.onCompleted: {
|
||||
IdleService.lockComponent = this
|
||||
}
|
||||
|
||||
function lock() {
|
||||
if (SettingsData.customPowerActionLock && SettingsData.customPowerActionLock.length > 0) {
|
||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLock])
|
||||
return
|
||||
}
|
||||
if (!processingExternalEvent && SettingsData.loginctlLockIntegration && DMSService.isConnected) {
|
||||
DMSService.lockSession(response => {
|
||||
if (response.error) {
|
||||
console.warn("Lock: Failed to call loginctl.lock:", response.error)
|
||||
shouldLock = true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
shouldLock = true
|
||||
}
|
||||
}
|
||||
|
||||
function unlock() {
|
||||
if (!processingExternalEvent && SettingsData.loginctlLockIntegration && DMSService.isConnected) {
|
||||
DMSService.unlockSession(response => {
|
||||
if (response.error) {
|
||||
console.warn("Lock: Failed to call loginctl.unlock:", response.error)
|
||||
shouldLock = false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
shouldLock = false
|
||||
}
|
||||
}
|
||||
|
||||
function activate() {
|
||||
lock()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionService
|
||||
|
||||
function onSessionLocked() {
|
||||
processingExternalEvent = true
|
||||
shouldLock = true
|
||||
processingExternalEvent = false
|
||||
}
|
||||
|
||||
function onSessionUnlocked() {
|
||||
processingExternalEvent = true
|
||||
shouldLock = false
|
||||
processingExternalEvent = false
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: IdleService
|
||||
|
||||
function onLockRequested() {
|
||||
lock()
|
||||
}
|
||||
}
|
||||
|
||||
WlSessionLock {
|
||||
id: sessionLock
|
||||
|
||||
locked: shouldLock
|
||||
|
||||
WlSessionLockSurface {
|
||||
id: lockSurface
|
||||
|
||||
color: "transparent"
|
||||
|
||||
LockSurface {
|
||||
anchors.fill: parent
|
||||
lock: sessionLock
|
||||
sharedPasswordBuffer: root.sharedPasswordBuffer
|
||||
screenName: lockSurface.screen?.name ?? ""
|
||||
isLocked: shouldLock
|
||||
onUnlockRequested: {
|
||||
root.unlock()
|
||||
}
|
||||
onPasswordChanged: newPassword => {
|
||||
root.sharedPasswordBuffer = newPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LockScreenDemo {
|
||||
id: demoWindow
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "lock"
|
||||
|
||||
function lock() {
|
||||
if (!root.processingExternalEvent && SettingsData.loginctlLockIntegration && DMSService.isConnected) {
|
||||
DMSService.lockSession(response => {
|
||||
if (response.error) {
|
||||
console.warn("Lock: Failed to call loginctl.lock:", response.error)
|
||||
root.shouldLock = true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
root.shouldLock = true
|
||||
}
|
||||
}
|
||||
|
||||
function demo() {
|
||||
demoWindow.showDemo()
|
||||
}
|
||||
|
||||
function isLocked(): bool {
|
||||
return sessionLock.locked
|
||||
}
|
||||
}
|
||||
}
|
||||
459
quickshell/Modules/Lock/LockPowerMenu.qml
Normal file
459
quickshell/Modules/Lock/LockPowerMenu.qml
Normal file
@@ -0,0 +1,459 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVisible: false
|
||||
property bool showLogout: true
|
||||
property int selectedIndex: 0
|
||||
property int optionCount: {
|
||||
let count = 0
|
||||
if (showLogout) count++
|
||||
count++
|
||||
if (SessionService.hibernateSupported) count++
|
||||
count += 2
|
||||
return count
|
||||
}
|
||||
|
||||
signal closed()
|
||||
|
||||
function show() {
|
||||
isVisible = true
|
||||
selectedIndex = 0
|
||||
Qt.callLater(() => {
|
||||
if (powerMenuFocusScope && powerMenuFocusScope.forceActiveFocus) {
|
||||
powerMenuFocusScope.forceActiveFocus()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function hide() {
|
||||
isVisible = false
|
||||
closed()
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(0, 0, 0, 0.5)
|
||||
visible: isVisible
|
||||
z: 1000
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.hide()
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
id: powerMenuFocusScope
|
||||
anchors.fill: parent
|
||||
focus: root.isVisible
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
Qt.callLater(() => forceActiveFocus())
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
root.hide()
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
case Qt.Key_Backtab:
|
||||
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount
|
||||
event.accepted = true
|
||||
break
|
||||
case Qt.Key_Down:
|
||||
case Qt.Key_Tab:
|
||||
selectedIndex = (selectedIndex + 1) % optionCount
|
||||
event.accepted = true
|
||||
break
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
const actions = []
|
||||
if (showLogout) actions.push("logout")
|
||||
actions.push("suspend")
|
||||
if (SessionService.hibernateSupported) actions.push("hibernate")
|
||||
actions.push("reboot", "poweroff")
|
||||
if (selectedIndex < actions.length) {
|
||||
const action = actions[selectedIndex]
|
||||
hide()
|
||||
switch (action) {
|
||||
case "logout":
|
||||
SessionService.logout()
|
||||
break
|
||||
case "suspend":
|
||||
SessionService.suspend()
|
||||
break
|
||||
case "hibernate":
|
||||
SessionService.hibernate()
|
||||
break
|
||||
case "reboot":
|
||||
SessionService.reboot()
|
||||
break
|
||||
case "poweroff":
|
||||
SessionService.poweroff()
|
||||
break
|
||||
}
|
||||
}
|
||||
event.accepted = true
|
||||
break
|
||||
case Qt.Key_N:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
selectedIndex = (selectedIndex + 1) % optionCount
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_P:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_J:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
selectedIndex = (selectedIndex + 1) % optionCount
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_K:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: 320
|
||||
implicitHeight: mainColumn.implicitHeight + Theme.spacingL * 2
|
||||
height: implicitHeight
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainer
|
||||
border.color: Theme.outlineMedium
|
||||
border.width: 1
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Power Options")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width - 150
|
||||
height: 1
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
iconName: "close"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
onClicked: root.hide()
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
visible: showLogout
|
||||
color: {
|
||||
if (selectedIndex === 0) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||
} else if (logoutArea.containsMouse) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
||||
} else {
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
||||
}
|
||||
}
|
||||
border.color: selectedIndex === 0 ? Theme.primary : "transparent"
|
||||
border.width: selectedIndex === 0 ? 1 : 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "logout"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Log Out")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: logoutArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.hide()
|
||||
SessionService.logout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
const suspendIdx = showLogout ? 1 : 0
|
||||
if (selectedIndex === suspendIdx) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||
} else if (suspendArea.containsMouse) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
||||
} else {
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
||||
}
|
||||
}
|
||||
border.color: selectedIndex === (showLogout ? 1 : 0) ? Theme.primary : "transparent"
|
||||
border.width: selectedIndex === (showLogout ? 1 : 0) ? 1 : 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "bedtime"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Suspend")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: suspendArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.hide()
|
||||
SessionService.suspend()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
const hibernateIdx = showLogout ? 2 : 1
|
||||
if (selectedIndex === hibernateIdx) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||
} else if (hibernateArea.containsMouse) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
||||
} else {
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
||||
}
|
||||
}
|
||||
border.color: selectedIndex === (showLogout ? 2 : 1) ? Theme.primary : "transparent"
|
||||
border.width: selectedIndex === (showLogout ? 2 : 1) ? 1 : 0
|
||||
visible: SessionService.hibernateSupported
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "ac_unit"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Hibernate")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: hibernateArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.hide()
|
||||
SessionService.hibernate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
let rebootIdx = showLogout ? 3 : 2
|
||||
if (!SessionService.hibernateSupported) rebootIdx--
|
||||
if (selectedIndex === rebootIdx) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||
} else if (rebootArea.containsMouse) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
||||
} else {
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
||||
}
|
||||
}
|
||||
border.color: {
|
||||
let rebootIdx = showLogout ? 3 : 2
|
||||
if (!SessionService.hibernateSupported) rebootIdx--
|
||||
return selectedIndex === rebootIdx ? Theme.primary : "transparent"
|
||||
}
|
||||
border.width: {
|
||||
let rebootIdx = showLogout ? 3 : 2
|
||||
if (!SessionService.hibernateSupported) rebootIdx--
|
||||
return selectedIndex === rebootIdx ? 1 : 0
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "restart_alt"
|
||||
size: Theme.iconSize
|
||||
color: rebootArea.containsMouse ? Theme.warning : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Reboot")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: rebootArea.containsMouse ? Theme.warning : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rebootArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.hide()
|
||||
SessionService.reboot()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
let powerOffIdx = showLogout ? 4 : 3
|
||||
if (!SessionService.hibernateSupported) powerOffIdx--
|
||||
if (selectedIndex === powerOffIdx) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||
} else if (powerOffArea.containsMouse) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
||||
} else {
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
||||
}
|
||||
}
|
||||
border.color: {
|
||||
let powerOffIdx = showLogout ? 4 : 3
|
||||
if (!SessionService.hibernateSupported) powerOffIdx--
|
||||
return selectedIndex === powerOffIdx ? Theme.primary : "transparent"
|
||||
}
|
||||
border.width: {
|
||||
let powerOffIdx = showLogout ? 4 : 3
|
||||
if (!SessionService.hibernateSupported) powerOffIdx--
|
||||
return selectedIndex === powerOffIdx ? 1 : 0
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "power_settings_new"
|
||||
size: Theme.iconSize
|
||||
color: powerOffArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Power Off")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: powerOffArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: powerOffArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.hide()
|
||||
SessionService.poweroff()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: Theme.spacingS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1289
quickshell/Modules/Lock/LockScreenContent.qml
Normal file
1289
quickshell/Modules/Lock/LockScreenContent.qml
Normal file
File diff suppressed because it is too large
Load Diff
47
quickshell/Modules/Lock/LockScreenDemo.qml
Normal file
47
quickshell/Modules/Lock/LockScreenDemo.qml
Normal file
@@ -0,0 +1,47 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
|
||||
property bool demoActive: false
|
||||
|
||||
visible: demoActive
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
|
||||
color: "transparent"
|
||||
|
||||
function showDemo(): void {
|
||||
console.log("Showing lock screen demo")
|
||||
demoActive = true
|
||||
}
|
||||
|
||||
function hideDemo(): void {
|
||||
console.log("Hiding lock screen demo")
|
||||
demoActive = false
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: demoActive
|
||||
sourceComponent: LockScreenContent {
|
||||
demoMode: true
|
||||
screenName: root.screen?.name ?? ""
|
||||
onUnlockRequested: root.hideDemo()
|
||||
}
|
||||
}
|
||||
}
|
||||
41
quickshell/Modules/Lock/LockSurface.qml
Normal file
41
quickshell/Modules/Lock/LockSurface.qml
Normal file
@@ -0,0 +1,41 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
required property WlSessionLock lock
|
||||
required property string sharedPasswordBuffer
|
||||
required property string screenName
|
||||
required property bool isLocked
|
||||
|
||||
signal passwordChanged(string newPassword)
|
||||
signal unlockRequested()
|
||||
|
||||
color: "transparent"
|
||||
|
||||
LockScreenContent {
|
||||
id: lockContent
|
||||
|
||||
anchors.fill: parent
|
||||
demoMode: false
|
||||
passwordBuffer: root.sharedPasswordBuffer
|
||||
screenName: root.screenName
|
||||
onUnlockRequested: root.unlockRequested()
|
||||
onPasswordBufferChanged: {
|
||||
if (root.sharedPasswordBuffer !== passwordBuffer) {
|
||||
root.passwordChanged(passwordBuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onIsLockedChanged: {
|
||||
if (!isLocked) {
|
||||
lockContent.unlocking = false
|
||||
}
|
||||
}
|
||||
}
|
||||
189
quickshell/Modules/Lock/Pam.qml
Normal file
189
quickshell/Modules/Lock/Pam.qml
Normal file
@@ -0,0 +1,189 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Services.Pam
|
||||
import qs.Common
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
property bool lockSecured: false
|
||||
property bool unlockInProgress: false
|
||||
|
||||
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 unlockRequested
|
||||
|
||||
FileView {
|
||||
id: pamConfigWatcher
|
||||
|
||||
path: "/etc/pam.d/dankshell"
|
||||
printErrors: false
|
||||
}
|
||||
|
||||
PamContext {
|
||||
id: passwd
|
||||
|
||||
config: pamConfigWatcher.loaded ? "dankshell" : "login"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
onCompleted: res => {
|
||||
if (res === PamResult.Success) {
|
||||
if (!root.unlockInProgress) {
|
||||
root.unlockInProgress = true;
|
||||
fprint.abort();
|
||||
root.unlockRequested();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (res === PamResult.Error)
|
||||
root.state = "error";
|
||||
else if (res === PamResult.MaxTries)
|
||||
root.state = "max";
|
||||
else if (res === PamResult.Failed)
|
||||
root.state = "fail";
|
||||
|
||||
root.flashMsg();
|
||||
stateReset.restart();
|
||||
}
|
||||
}
|
||||
|
||||
PamContext {
|
||||
id: fprint
|
||||
|
||||
property bool available
|
||||
property int tries
|
||||
property int errorTries
|
||||
|
||||
function checkAvail(): void {
|
||||
if (!available || !SettingsData.enableFprint || !root.lockSecured) {
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
tries = 0;
|
||||
errorTries = 0;
|
||||
start();
|
||||
}
|
||||
|
||||
config: "fprint"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam"
|
||||
|
||||
onCompleted: res => {
|
||||
if (!available)
|
||||
return;
|
||||
|
||||
if (res === PamResult.Success) {
|
||||
if (!root.unlockInProgress) {
|
||||
root.unlockInProgress = true;
|
||||
passwd.abort();
|
||||
root.unlockRequested();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (res === PamResult.Error) {
|
||||
root.fprintState = "error";
|
||||
errorTries++;
|
||||
if (errorTries < 5) {
|
||||
abort();
|
||||
errorRetry.restart();
|
||||
}
|
||||
} else if (res === PamResult.MaxTries) {
|
||||
tries++;
|
||||
if (tries < SettingsData.maxFprintTries) {
|
||||
root.fprintState = "fail";
|
||||
start();
|
||||
} else {
|
||||
root.fprintState = "max";
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
root.flashMsg();
|
||||
fprintStateReset.start();
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: availProc
|
||||
|
||||
command: ["sh", "-c", "fprintd-list $USER"]
|
||||
onExited: code => {
|
||||
fprint.available = code === 0;
|
||||
fprint.checkAvail();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: errorRetry
|
||||
|
||||
interval: 800
|
||||
onTriggered: fprint.start()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: stateReset
|
||||
|
||||
interval: 4000
|
||||
onTriggered: {
|
||||
if (root.state !== "max")
|
||||
root.state = "";
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: fprintStateReset
|
||||
|
||||
interval: 4000
|
||||
onTriggered: {
|
||||
root.fprintState = "";
|
||||
fprint.errorTries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
onLockSecuredChanged: {
|
||||
if (lockSecured) {
|
||||
availProc.running = true;
|
||||
root.state = "";
|
||||
root.fprintState = "";
|
||||
root.lockMessage = "";
|
||||
root.unlockInProgress = false;
|
||||
} else {
|
||||
fprint.abort();
|
||||
passwd.abort();
|
||||
root.unlockInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
|
||||
function onEnableFprintChanged(): void {
|
||||
fprint.checkAvail();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user