Skip to content
Kimmo edited this page Jun 1, 2018 · 11 revisions

Key Handling (P0 | 2h)

Explanation of the contents of a topic page @ Topic reference page

Back to Week 2

Objective: Controlling focus and handling key presses

Beginner

  • What is keyboard focus?
  • What is mouse grab?
  • What is keyboard grab?
  • What is an attached property (Keys, for example)?

Intermediate

  • How do you grab specific key presses?
  • How do you grab generic key presses?
  • How do you get focus?
  • How do you change focus?
  • What is FocusScope?

Expert

  • How do you forward a key press to a parent?

Omitted


Course material content

http://doc.qt.io/qt-5/qml-qtquick-keys.html#details
http://doc.qt.io/qt-5/qtquick-input-focus.html
http://doc.qt.io/qt-5/qml-qtquick-keyevent.html
http://doc.qt.io/qt-5/qml-qtquick-keynavigation.html

  • Navigation between items (Changing the focused item, directional(arrow keys), tab and backtab)

TextEdit and TextInput

Qt has two types that display text input - TextEdit and TextInput. TextEdit displays multiple lines of input, where as TextInput displays a single line of text input.

TextInput

The TextInput type displays a single line of editable plain text.

TextInput is used to accept a line of text input. Input constraints can be placed on a TextInput item (for example, through a validator or inputMask), and setting echoMode to an appropriate value enables TextInput to be used for a password input field.

TextEdit

The TextEdit item displays a block of editable, formatted text.

It can display both plain and rich text. For example:

TextEdit {
    width: 240
    text: "<b>Hello</b> <i>World!</i>"
    font.family: "Helvetica"
    font.pointSize: 20
    color: "blue"
    focus: true
}

Setting focus to true enables the TextEdit item to receive keyboard focus.

Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific to a look-and-feel. For example, to add flickable scrolling that follows the cursor:

Flickable {
    id: flick

    width: 300
    height: 200
    contentWidth: edit.paintedWidth
    contentHeight: edit.paintedHeight
    clip: true

    function ensureVisible(r) {
        if (contentX >= r.x)
            contentX = r.x
        else if (contentX + width <= r.x + r.width)
            contentX = r.x + r.width - width
        if (contentY >= r.y)
            contentY = r.y
        else if (contentY + height <= r.y + r.height)
            contentY = r.y + r.height - height
    }

    TextEdit {
        id: edit
        width: flick.width
        focus: true
        wrapMode: TextEdit.Wrap
        onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)
    }
}

A particular look-and-feel might use smooth scrolling (eg. using SmoothedAnimation), might have a visible scrollbar, or a scrollbar that fades in to show location, etc.

Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord().

You can translate between cursor positions (characters from the start of the document) and pixel points using positionAt() and positionToRectangle().

Keys

The Keys QML type is responsible for key handling. Keys can be handled via the onPressed and onReleased signal properties.

The signal properties have a KeyEvent parameter, named event which contains details of the event. If a key is handled event.accepted should be set to true to prevent the event from propagating up the item hierarchy.

The following example shows how the general onPressed handler can be used to test for a certain key; in this case, the left cursor key:

Item {
    anchors.fill: parent
    focus: true
    Keys.onPressed: {
        if (event.key === Qt.Key_Left) {
            console.log("move left")
            event.accepted = true
        }
    }
}

Some keys may alternatively be handled via specific signal properties, for example onSelectPressed. These handlers automatically set event.accepted to true.

Item {
    anchors.fill: parent
    focus: true
    Keys.onLeftPressed: console.log("move left")
}

The Keys attached property can be configured to handle key events before or after the item it is attached to. This makes it possible to intercept events in order to override an item's default behavior, or act as a fallback for keys not handled by the item.

Key handling priorities

If priority is Keys.BeforeItem (default) the order of key event processing is:

  1. Items specified in forwardTo
  2. specific key handlers, e.g. onReturnPressed
  3. onPressed, onReleased handlers
  4. Item specific key handling, e.g. TextInput key handling
  5. parent item

If priority is Keys.AfterItem the order of key event processing is:

  1. Item specific key handling, e.g. TextInput key handling 2.Items specified in forwardTo
  2. specific key handlers, e.g. onReturnPressed
  3. onPressed, onReleased handlers
  4. parent item

If the event is accepted during any of the above steps, key propagation stops.

Focus

In UIs with just one TextInput focus is assigned automatically. In UIs with more than one TextInput focus needs to be changed via clicking.
What happens if a TextInput has no text? In that case there is no way to click on it, unless it has a width or uses anchors. Focus is assigned trough setting the focus property.

TextInput {
    id: upperTextInput
    anchors.left: parent.left
    anchors.right: parent.right
    text: "Field 1"
    font.pixelSize: 32
    color: focus ? "black" : "gray"
}

TextInput {
    anchors.left: parent.left
    anchors.top: upperTextInput.bottom
    anchors.right: parent.right
    text: qsTr("Field 2")
    font.pixelSize: 32
    color: focus ? "black" : "gray"
}

The nameField item defines KeyNavigation.tab, which results to pressing Tab moving the focus to the addressField item

TextInput {
    id: nameField
    focus: true
    KeyNavigation.tab: addressField
}

The addressField item defines KeyNavigation.backtab, which results to pressing Shift+Tab moving focus to the nameField item.

TextInput {
    id: addressField
    KeyNavigation.backtab: nameField
}

FocusScope

http://doc.qt.io/qt-5/qml-qtquick-focusscope.html#details

Focus scopes assist in keyboard focus handling when building reusable QML components.

A focus scope declares that the last child element with focus:true receives the focus if the focus scope receives the focus. So it’s forward the focus to the last focus requesting child element. We will create a 2nd version of our TLineEdit component called TLineEditV2 using the focus scope as root element.

When a key is pressed or released, a key event is generated and delivered to the focused Qt Quick Item. To facilitate the construction of reusable components and to address some of the cases unique to fluid user interfaces, the Qt Quick items add a scope based extension to Qt's traditional keyboard focus model.

Key Handling Overview

When the user presses or releases a key, the following occurs:

  1. Qt receives the key action and generates a key event.
  2. If a QQuickWindow is the active window, the key event is delivered to it.
  3. The key event is delivered by the scene to the Item with active focus. If no item has active focus, the key event is ignored.
  4. If the QQuickItem with active focus accepts the key event, propagation stops. Otherwise the event is sent to the Item's parent until the event is accepted, or the root item is reached.

If the Rectangle type in the following example has active focus and the A key is pressed, the event will not be propagated further. Upon pressing the B key, the event will propagate to the root item and thus be ignored.

Rectangle {
    width: 100
    height: 100
    focus: true
    Keys.onPressed: {
        if (event.key == Qt.Key_A) {
            console.log('Key A was pressed')
            event.accepted = true
        }
    }
}

Instructions and description for the exercise of the topic


Exhaustive reference material mentioned in this topic

Further reading topics/links:

Clone this wiki locally