From 47c5320d67578f61693e59fc0d36923a08af1e02 Mon Sep 17 00:00:00 2001 From: bbedward Date: Sun, 9 Nov 2025 15:28:21 -0500 Subject: [PATCH] lock/greeter: keyboard accessibility improvements --- Modules/Lock/CustomButtonKeyboard.qml | 16 + Modules/Lock/Keyboard.qml | 543 +++++++++++++------------- Modules/Lock/KeyboardController.qml | 18 +- 3 files changed, 289 insertions(+), 288 deletions(-) diff --git a/Modules/Lock/CustomButtonKeyboard.qml b/Modules/Lock/CustomButtonKeyboard.qml index 8f3e5f67..639389c7 100644 --- a/Modules/Lock/CustomButtonKeyboard.qml +++ b/Modules/Lock/CustomButtonKeyboard.qml @@ -14,6 +14,21 @@ DankActionButton { 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 @@ -21,5 +36,6 @@ DankActionButton { color: Theme.surfaceText font.pixelSize: Theme.fontSizeXLarge font.weight: Font.Normal + visible: !parent.isIcon } } diff --git a/Modules/Lock/Keyboard.qml b/Modules/Lock/Keyboard.qml index 0eb6cacb..86ace9b4 100644 --- a/Modules/Lock/Keyboard.qml +++ b/Modules/Lock/Keyboard.qml @@ -10,185 +10,157 @@ Rectangle { anchors.right: parent.right color: Theme.widgetBackground - property double rowSpacing: 0.01 * width // horizontal spacing between keyboard + 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' // UPWARDS ARROW unicode + 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: "'", - symbol: "*", - width: 1.5 - } - ], - "row_4": [ - { - text: "123", - symbol: 'ABC', - width: 1.5 - }, - { - text: ' ', - symbol: ' ', - width: 6 - }, - { - text: '.', - symbol: '.', - width: 1 - }, - { - text: strBackspace, - symbol: strBackspace, - width: 1.5 - } - ] + "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 @@ -229,7 +201,7 @@ Rectangle { "_x": Qt.Key_X, "_y": Qt.Key_Y, "_z": Qt.Key_Z, - "_\u2190": Qt.Key_Backspace, + "_←": Qt.Key_Backspace, "_return": Qt.Key_Return, "_ ": Qt.Key_Space, "_-": Qt.Key_Minus, @@ -238,7 +210,9 @@ Rectangle { "_;": 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 + "_€": parseInt( + "20ac", + 16) // I didn't find the appropriate Qt event so I used the hex format , "_&": Qt.Key_Ampersand, "_@": Qt.Key_At, @@ -250,112 +224,121 @@ Rectangle { "_'": 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; // toggle shift - } else if (text === '123') { - root.symbols = true; - } else if (text === 'ABC') { - root.symbols = false; - } else { - // insert text into target - if (text === strBackspace) { - var current = keyboard_controller.target.text; - keyboard_controller.target.text = current.slice(0, current.length - 1); - } else { - // normal character - 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; - } - - // shift is momentary - if (root.shift && text !== strShift) - root.shift = false; - } - } - } +} + +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 +} +} +} } diff --git a/Modules/Lock/KeyboardController.qml b/Modules/Lock/KeyboardController.qml index fb5dedca..38805e3b 100644 --- a/Modules/Lock/KeyboardController.qml +++ b/Modules/Lock/KeyboardController.qml @@ -1,4 +1,4 @@ -pragma ComponentBehavior: Bound +pragma ComponentBehavior import QtQuick @@ -14,19 +14,21 @@ Item { function show() { if (!isKeyboardActive && keyboard === null) { - keyboard = keyboardComponent.createObject(keyboard_controller.rootObject); - keyboard.target = keyboard_controller.target; - isKeyboardActive = true; + 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"); + console.log("The keyboard is already shown") } function hide() { if (isKeyboardActive && keyboard !== null) { - keyboard.destroy(); - isKeyboardActive = false; + keyboard.destroy() + isKeyboardActive = false } else - console.log("The keyboard is already hidden"); + console.log("The keyboard is already hidden") } // private