Invisto
Invisto · your interface partner
Mailinfo@invisto.beTelefoon+32 477 42 11 14KantoorHangar K, Kortrijknlen
Alle inzichten
inzichten/QML

Hoe bouw je een virtueel QML toetsenbord?

Hoe bouw je een virtueel QML toetsenbord?

1. Introductie

Een virtueel toetsenbord is nodig wanneer er geen fysiek toetsenbord beschikbaar is. Meestal is dit het geval bij elektronische apparaten met een touchscreen.

Voor Qt-applicaties zijn er verschillende manieren om een virtueel toetsenbord toe te voegen. Doorgaans wordt één van de volgende opties gebruikt:

  • Het Qt Virtual Keyboard
  • Het toetsenbord dat door het platform zelf wordt voorzien, zoals het Apple-toetsenbord op een iPad
  • Een custom-built QML-toetsenbord

Invisto maakt vaak gebruik van de custom-built QML-methode, omdat we geen beperkingen willen opleggen op vlak van design of gebruiksgemak. Dit wordt bovendien ondersteund door het feit dat je met QML een grafisch ontwerp heel eenvoudig kunt reproduceren.

2. Toetsenbord

Het toetsenbord voor het bewerken van patiënteninformatie
Het toetsenbord voor het bewerken van patiënteninformatie

In dit artikel focussen we op een toetsenbord dat ontworpen is voor het bewerken van patiënteninformatie. We bekijken hoe je:

  • Een basis lay-out voor een toetsenbord opzet
  • Het toetsenbord koppelt aan een tekstveld
  • Toetsen dynamisch verandert bij het schakelen tussen alfabetisch en hoofdletters

2.1 Lay-out van het toetsenbord

Voor deze toepassing houden we geen rekening met een responsive design: de schermdimensies zijn vast.

QML
import QtQuickimport QtQuick.ControlsItem {    id: keyboardRoot    width: 1194    height: 418    property var focusedTextField: null    enum Modes {        HIDDEN,        ALPHABETICAL,        ALPHABETICAL_CAPS,        NUMBERS_1,        NUMBERS_2    }    QtObject {        id: internals        property bool singleShift: false    }    property int mode: Keyboard.ALPHABETICAL    Item { id: keyboardHeader ... }    Column {        id: mainButtons        Row { id: buttonsFirstRow ... }        Row { id: buttonsSecondRow ... }        Row { id: buttonsThirdRow ... }        Row { id: buttonsFourthRow ... }    }    Rectangle { id: horizontalLine ... }}

Zoals je kunt zien, gebruiken we een flexibele gridstructuur door een kolom te combineren met meerdere rijen. Elke rij bevat een reeks toetsenbordknoppen.

QML
Row {    id: buttonsFirstRow    spacing: 12    KeyboardButton {        dark: true        width: 105        SingleLabel { text: "tab" }    }    ListModel {        id: firstRowAlphabetical        ListElement { first: "1"; second: "q" }        ListElement { first: "2"; second: "w" }        ...        ListElement { first: "0"; second: "p" }    }    ListModel {        id: firstRowAlphabeticalCaps        ListElement { first: "1"; second: "Q" }        ListElement { first: "2"; second: "W" }        ...        ListElement { first: "0"; second: "P" }    }    Repeater {        model: keyboardRoot.mode === Keyboard.ALPHABETICAL ? firstRowAlphabetical : firstRowAlphabeticalCaps        KeyboardButton {            DoubleCharacters {                first: model.first                second: model.second            }        }    }    KeyboardButton {        dark: true        width: 105        SingleLabel { text: "delete"; alignLeft: false }    }}

Laten we eens kijken hoe de knoppen zijn opgebouwd. In ons scenario is een knop gewoon een rechthoekige vorm met wat styling.

QMLKeyboardButton.qml
import QtQuickRectangle {    id: root    property bool dark: false    color: dark ? "#45454B" : "#7C7C86"    width: 82    height: 74    radius: 7    border.color: "#222"    MouseArea {        id: mouseArea        anchors.fill: root        onPressed: root.border.width = 2        onReleased: root.border.width = 0    }}

Om de gewenste inhoud boven op de knop weer te geven, kunnen we een child-element toevoegen, zoals SingleLabel, SingleIcon of DoubleLabel.

2.2 Het toetsenbord verbinden met een tekstveld

Om de koppeling tussen een toetsenbord en een bewerkbaar tekstveld te demonstreren, hebben we onze EditText.qml vereenvoudigd door overbodige elementen te verwijderen en enkel de relevante onderdelen te behouden. In essentie hebben we een QML TextInput met visuele verbeteringen, met daar bovenop een MouseArea.

QMLEditText.qml
import QtQuickimport QtQuick.ControlsItem {    id: root    property string title: ""    property string text: ""    property alias cursorPosition: textInputField.cursorPosition    width: 550    height: 88    TextInput {        id: textInputField        text: root.text        color: "white"        font.family: theme.fontRegular        font.pixelSize: 18        anchors.verticalCenter: textBox.verticalCenter        x: 27    }    MouseArea {        anchors.fill: root        onClicked: {            textInputField.focus = true            keyboard.focusedTextField = root        }    }}

Wat gebeurt er wanneer je op een tekstveld klikt?

  • We geven de focus aan onze TextInput. Hierdoor wordt de cursor zichtbaar.
  • We informeren het toetsenbord welk tekstveld de focus heeft gekregen: keyboard.focusedTextField = root

Wat gebeurt er aan de kant van het toetsenbord wanneer een knop wordt ingedrukt? De inhoudselementen (SingleIcon, SingleLabel, DoubleLabel) roepen elk de functie keyboard.handleButtonPressed(label) aan. In deze handler worden acties afgehandeld zoals een karakter toevoegen of verwijderen.

QML
function handleButtonPressed(button){    if (button === "delete") {        var str = keyboardRoot.focusedTextField.text        if (str.length) {            str = str.substring(0, str.length - 1);            keyboardRoot.focusedTextField.text = str            keyboardRoot.focusedTextField.cursorPosition = str.length        }    }    else if (button === "caps lock") {        if (keyboardRoot.mode === Keyboard.ALPHABETICAL)            keyboardRoot.mode = Keyboard.ALPHABETICAL_CAPS        else if (keyboardRoot.mode === Keyboard.ALPHABETICAL_CAPS)            keyboardRoot.mode = Keyboard.ALPHABETICAL    }    else if (button === "shift") {        if (keyboardRoot.mode === Keyboard.ALPHABETICAL) {            keyboardRoot.mode = Keyboard.ALPHABETICAL_CAPS            internals.singleShift = true        }    }    else if (button === ".?123") {        keyboardRoot.mode = Keyboard.NUMBERS_1    }    else if (button === "return") {        keyboardRoot.mode = Keyboard.HIDDEN    }    else {        keyboardRoot.focusedTextField.text = keyboardRoot.focusedTextField.text + button        keyboardRoot.focusedTextField.cursorPosition = keyboardRoot.focusedTextField.text.length        if (internals.singleShift) {            internals.singleShift = false            keyboardRoot.mode = Keyboard.ALPHABETICAL        }    }}

2.3 Wisselen tussen alfabetisch en hoofdletters

Voor elke rij hebben we twee modellen gedefinieerd: firstRowAlphabetical en firstRowAlphabeticalCaps. Afhankelijk van de modus van het toetsenbord geven we één van deze modellen door aan de model-property van de Repeater (zie het fragment in 2.1).

3. Qt WebBrowser als vereiste? No pasaran!

De custom-built QML-implementatie die in dit artikel wordt beschreven, voldoet niet aan de vereisten wanneer je HTML -velden gebruikt in combinatie met Qt WebBrowser. In dat geval wordt de aanpak complexer en moet je mogelijk kijken naar QInputMethod, QPlatformInputContext, QPA en andere componenten. Hoewel dit buiten de scope van deze case valt, is het wel de moeite waard om te vermelden.

4. Conclusie

Een mooi en functioneel toetsenbord dat volledig op jouw noden is afgestemd, vereist een custom-built QML-toetsenbord. Dit artikel wil je een introductie geven over hoe je hiermee kunt starten.

Heb je meer info nodig over dit onderwerp? Neem gerust contact met ons op. We delen hier niet de volledige broncode, omdat die deel uitmaakt van een project. Maar we kunnen je altijd een werkend voorbeeld bezorgen indien gewenst.

Ook interesse in een interface die uw product naar een hoger niveau tilt?

Neem contact op