2
votes

I think I know how to use FocusScopes and how to handle keyboard focus.

But I can't find a clever way to figure out if one of my child items or theirs or anyone below me has keyboard focus.

The documentation for FocusScope says:

When a focus scope receives active focus, the contained element with focus set (if any) also gets the active focus. If this element is also a FocusScope, the proxying behavior continues. Both the focus scope and the sub-focused item will have activeFocus property set.

A FocusScope therefore will have activeFocus set to false when the focus was given to a contained FocusScope. Is there a way to figure out if that was the case? How can I know if at least a contained FocusScope received the focus?

1
I can of course write my own recursively checking hasFocus() function. But maybe there is a better way... - HWende

1 Answers

5
votes

Focus is a chain in QtQuick. That means all the ancestor FocusScope down to the current active child get active focus.

FocusScope is used to make some more simple focus abstraction : tell to a custom component that when the root object gets active focus, it has to forward it to a given child.

In the following example :

import QtQuick 2.0;

Rectangle {
    width: 400;
    height: 200;
    focus: true;

    FocusScope {
        id: scope1;
        anchors {
            top: parent.top;
            left: parent.left;
            right: parent.right;
            bottom: parent.verticalCenter;
        }

        Rectangle {
            id: rect1;
            color: (scope1.activeFocus ? "yellow" : "gray");
            border.width: 1;
            anchors.fill: parent;

            MouseArea {
                anchors.fill: parent;
                onClicked: { scope1.forceActiveFocus (); }
            }
            TextInput {
                id: input1;
                focus: true;
                anchors.centerIn: parent;
            }
        }
    }
    FocusScope {
        id: scope2;
        anchors {
            top: parent.verticalCenter;
            left: parent.left;
            right: parent.right;
            bottom: parent.bottom;
        }

        Rectangle {
            id: rect2;
            color: (scope2.activeFocus ? "yellow" : "gray");
            border.width: 1;
            anchors.fill: parent;

            MouseArea {
                anchors.fill: parent;
                onClicked: { scope2.forceActiveFocus (); }
            }
            TextInput {
                id: input2;
                focus: true;
                anchors.centerIn: parent;
            }
        }
    }
}

... we want two big areas that can have focus and we don't necessarly want to focus explicitely the inner TextInput (cause they ideally would be inside a custom component, so not accessible from outside).

So when an area gets clicked, we give the active focus to the parent scope, and the scope automatically proxies it to the child that has the focus:true flag (means that it wants the focus, not that it HAS it, that's why we have one flag in each TextInput).

The items that need to know if the inner input has active focus will instead simply request if the scope has it. They don't have to care was is inside.

If the scope contains another scope with focus:true, the focus is forwarded again until it reaches the latest item that wants focus.