Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QML several items with active focus / keyboard shortcut control

Tags:

qt

qml

I'm trying to implement a keyboard shortcut control for my qml application. I know there's the possibility to do that with an Action element, but I don't want menus and toolbars which are then mandatory to use. That's why I'm approaching this topic with keyboard events. For this, I need to have the element performing the action to be in focus. But my goal is a global shortcut control, so theoratically I'd need to have all the elements in question in focus. I found the FocusScope type in the documentation, but I'm not sure if this is what I need. Does it mean that the focus of nested FocusScopes 'slides' through to the last element that's not a FocusScope and acquiring focus manually with focus: true thus only this last element holding focus? Or do all the elements on the way down the slide that acquire focus have the activeFocus property set?

Is this the right approach or would I need something else?

like image 907
DenverCoder21 Avatar asked Feb 10 '23 05:02

DenverCoder21


2 Answers

Focus in Qt Quick is a mess in my opinion. It always confuses me and I end up hacking around it with forceActiveFocus(). I'd recommend the new Shortcut type:

Shortcut {
    sequence: StandardKey.Quit
    context: Qt.ApplicationShortcut
    onActivated: Qt.quit()
}

With the context property, you can choose whether you want the shortcut to apply to the current window or the entire application.

The motivation for this type can be seen in the comments of patch set 5:

Shortcut aims to supersede Action. I want to kill the latter in the future because...

  • compare the actual user code: http://cutebin.fi/prwznhkbo
  • look at the amount of "action" related expressions all around BasicButton.qml
  • IMHO the whole concept doesn't quite fit mobile/embedded or QML

Action was a frequently requested feature. Now that they have it, the frequent questions are "how to use a different icon/text" or "how to know the source that triggered an action". Both are contradicting the sole purpose of Action, and neither "problem" would exist if they just wrote simpler QML code in the first place, as illustrated by the example snippet. :)

Evidently the most usable part of Action is the shortcut feature. Those who need shortcuts are not happy that they need to use Action, because "what's up with all this other stuff, I just want a shortcut".

like image 190
Mitch Avatar answered Feb 11 '23 18:02

Mitch


Maybe there are different ways of achieving this, but the way I know is the following one.

The idea is to have an Item which controls the key events you need to handle.

I'll explain myself with an example. As you will see, if we have input widgets (i.e. TextInput) we have to implement a mechanism to return the input to our Item in order to process again the keyboard events. In this example, the Qt.Key_Escape key will be used to set the focus back.

import QtQuick 2.4
import QtQuick.Controls 1.3

ApplicationWindow {
    id: mainwindow
    title: qsTr("Hello")
    width: 640
    height: 480
    visible: true

    Item {
        anchors.fill: parent
        focus: true

        Keys.onPressed: {
            if ( (event.key === Qt.Key_Q) && (event.modifiers & Qt.ShiftModifier) ) {
                rect.blue()
            } else if ( (event.key === Qt.Key_W) && (event.modifiers & Qt.AltModifier) ) {
                rect.red()
            } else if ( (event.key === Qt.Key_E) && (event.modifiers & Qt.AltModifier) ) {
                text.text = 'Key Alt+E was pressed'
            }
        }

        Rectangle{
            id: rect
            width: 100
            height: 100
            color: "black"

            function blue() {color = "blue"}
            function red() {color = "red"}
        }

        Text {
            id: text
            anchors.centerIn: parent
            font.pointSize: 20
        }

        TextInput {
            id: textinput
            anchors.top: text.bottom
            text: "sample text"

            Keys.onPressed: {
                if (event.key === Qt.Key_Escape) {
                    console.log('Key Escape was pressed');
                    parent.focus = true;
                }
            }
        }
    }
}

Edit #1: @Mitch suggested to use the Shortcut QML Type. If you can use it (it's available since Qt 5.5), the code will be slightly different. Anyway, you need also to set the focus to the main app in some cases depending on the shortcut sequences implemented. For example, if we're typing text, Shift+Q doesn't have effect in this example. We need to press Escape first.

import QtQuick 2.5
import QtQuick.Controls 1.3

ApplicationWindow {
    id: mainwindow
    title: qsTr("Hello")
    width: 640
    height: 480
    visible: true

    Shortcut {
            sequence: "Shift+Q"
            onActivated: rect.blue()
            context: Qt.ApplicationShortcut
    }

    Shortcut {
            sequence: "Alt+W"
            onActivated: rect.red()
            context: Qt.ApplicationShortcut
    }

    Shortcut {
            sequence: "Alt+E"
            onActivated: text.text = 'Key Alt+E was pressed'
            context: Qt.ApplicationShortcut
    }

    Item {
        anchors.fill: parent

        Rectangle{
            id: rect
            width: 100
            height: 100
            color: "black"

            function blue() {color = "blue"}
            function red() {color = "red"}
        }

        Text {
            id: text
            anchors.centerIn: parent
            font.pointSize: 20
        }

        TextInput {
            id: textinput
            anchors.top: text.bottom
            text: "sample text"

            Keys.onPressed: {
                if (event.key === Qt.Key_Escape) {
                    console.log('Key Escape was pressed');
                    parent.focus = true;
                }
            }
        }
    }
}
like image 23
Tarod Avatar answered Feb 11 '23 18:02

Tarod