[Development] [Qt Quick] Automatically restoring focus to last focused item
Andrew den Exter
andrew.den.exter at qinetic.com.au
Mon May 9 15:17:40 CEST 2016
I was referring to ApplicationWindow's contentItem which is what you've
patched in qtquickcontrols2. For items within the contentItem to have
focus at startup you will also want to give that contentItem focus when it
is constructed. Add d->contentItem->setFocus(true) to your patch and
pressing alt will work again.
Then to restore focus when the menu is closed I guess in either
QQuickPopupPrivate::prepareExitTransition() or QQuickPopupPrivate::
finalizeExitTransition() you want again call setFocus(true) on
ApplicationWindow's contentItem again.
This works because making an item a focus scope isolates all of it's
children from focus changes around it. With your example when
forceActiveFocus() is called on the menu it removes focus from the
contentItem scope breaking the active focus chain to focusScope but leaves
the focus chain within contentItem intact so focusScope.focus is still true
but focusScope.activeFocus is false. If the menu is closed and
setFocus(true) is called on ApplicationWindow.contentItem then that
restores the missing link in the active focus chain to focusScope and
you're back to where you started.
On Mon, May 9, 2016 at 9:35 PM, Mitch Curtis <mitch.curtis at qt.io> wrote:
> Thanks for the reply, Andrew.
>
>
>
> It looks like the contentItem of QQuickWindow is already a focus scope [1]:
>
>
>
> contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
>
>
>
> Shouldn’t that already work, then?
>
>
>
> If I set QT_LOGGING_RULES= qt.quick.focus=true, and use the following
> example, I get the output below:
>
>
>
> http://pastebin.com/X9JMcpGh
>
>
>
> This is printed when the application has started:
>
>
>
> qt.quick.focus: QQuickWindowPrivate::setFocusInScope():
>
> qt.quick.focus: scope: QQuickRootItem(0x1e25bec0000)
>
> qt.quick.focus: scopeSubFocusItem: QObject(0x0)
>
> qt.quick.focus: item: QQuickFocusScope_QML_0(0x1e25bec66c0)
>
> qt.quick.focus: activeFocusItem: QObject(0x0)
>
> qt.quick.focus: QQuickWindowPrivate::setFocusInScope():
>
> qt.quick.focus: scope: QObject(0x0)
>
> qt.quick.focus: item: QQuickRootItem(0x1e25bec0000)
>
> qt.quick.focus: activeFocusItem: QObject(0x0)
>
> qml: activeFocusItem QQuickFocusScope_QML_0(0x1e25bec66c0)
>
>
>
> This is printed when I open the menu:
>
>
>
> qt.quick.focus: QQuickWindowPrivate::setFocusInScope():
>
> qt.quick.focus: scope: QQuickToolBar(0x1e25bec2dc0)
>
> qt.quick.focus: scopeSubFocusItem: QObject(0x0)
>
> qt.quick.focus: item: QQuickToolButton(0x1e25bec2a60)
>
> qt.quick.focus: activeFocusItem:
> QQuickFocusScope_QML_0(0x1e25bec66c0)
>
> qt.quick.focus: QQuickWindowPrivate::setFocusInScope():
>
> qt.quick.focus: scope: QQuickRootItem(0x1e25bec0000)
>
> qt.quick.focus: scopeSubFocusItem:
> QQuickFocusScope_QML_0(0x1e25bec66c0)
>
> qt.quick.focus: item: QQuickToolBar(0x1e25bec2dc0)
>
> qt.quick.focus: activeFocusItem:
> QQuickFocusScope_QML_0(0x1e25bec66c0)
>
> qml: activeFocusItem QQuickToolButton(0x1e25bec2a60)
>
> qml: @@@@@@@@@ clicked menu toolbutton
>
> qt.quick.focus: QQuickWindowPrivate::setFocusInScope():
>
> qt.quick.focus: scope: QQuickRootItem(0x1e25bec0000)
>
> qt.quick.focus: scopeSubFocusItem: QQuickToolBar(0x1e25bec2dc0)
>
> qt.quick.focus: item: QQuickPopupItem(0x1e25bec29a0)
>
> qt.quick.focus: activeFocusItem: QQuickToolButton(0x1e25bec2a60)
>
> qml: activeFocusItem QQuickPopupItem(0x1e25bec29a0)
>
>
>
> And this is printed when I close the menu:
>
>
>
> qt.quick.focus: QQuickWindowPrivate::clearFocusInScope():
>
> qt.quick.focus: scope: QQuickRootItem(0x1e25bec0000)
>
> qt.quick.focus: item: QQuickPopupItem(0x1e25bec29a0)
>
> qt.quick.focus: activeFocusItem: QQuickPopupItem(0x1e25bec29a0)
>
> qml: activeFocusItem QQuickRootItem(0x1e25bec0000)
>
> qml: @@@@@@@@@ closed menu
>
>
>
> If I apply the following patch to qtquickcontrols2, the root item becomes
> the active focus item, and so pressing alt does nothing:
>
>
>
> diff --git a/src/quicktemplates2/qquickapplicationwindow.cpp
> b/src/quicktemplates2/qquickapplicationwindow.cpp
>
> index 401313d..cb8a2fa 100644
>
> --- a/src/quicktemplates2/qquickapplicationwindow.cpp
>
> +++ b/src/quicktemplates2/qquickapplicationwindow.cpp
>
> @@ -424,6 +424,7 @@ QQuickItem
> *QQuickApplicationWindow::contentItem() const
>
> QQuickApplicationWindowPrivate *d =
> const_cast<QQuickApplicationWindowPrivate *>(d_func());
>
> if (!d->contentItem) {
>
> d->contentItem = new
> QQuickItem(QQuickWindow::contentItem());
>
> + d->contentItem->setFlag(QQuickItem::ItemIsFocusScope);
>
> d->relayout();
>
> }
>
> return d->contentItem;
>
>
>
> [1]
> http://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/quick/items/qquickwindow.cpp#n483
>
>
>
>
>
> *From:* Andrew den Exter [mailto:andrew.den.exter at qinetic.com.au]
> *Sent:* Friday, 6 May 2016 1:38 PM
> *To:* Mitch Curtis <mitch.curtis at qt.io>
> *Cc:* development at qt-project.org
> *Subject:* Re: [Development] [Qt Quick] Automatically restoring focus to
> last focused item
>
>
>
> That's a very universal solution to a specific problem and one that's
> going to have all kinds of unintended consequences. Without knowing why
> focus was taken from an item and given to another and why that other object
> relinquished focus it's impossible to know whether focus should be restored
> to that first item. That memory may have to go back more than one item as
> well, after all if you open one menu then another then the previous focus
> item is the first window and that's obviously not what you want to give
> focus to.
>
>
>
> What you want to do instead is introduce a FocusScope to
> ApplicationWindow. If the window's content item is a focus scope then
> giving focus to a menu which is outside of this scope will not affect focus
> within the scope, then whatever logic closes the menu can give focus back
> to the content item and that will implicitly return active focus to your
> item in the window body.
>
>
>
>
>
> Andrew
>
>
>
>
>
> On Fri, May 6, 2016 at 12:08 AM, Mitch Curtis <mitch.curtis at qt.io> wrote:
>
> Consider the example below (requires Qt 5.7):
>
> import QtQuick 2.6
> import QtQuick.Layouts 1.1
> import QtQuick.Window 2.2
> import QtQuick.Controls 2.0
>
> ApplicationWindow {
> width: 400
> height: 200
> visible: true
>
> onActiveFocusItemChanged: print("activeFocusItem", activeFocusItem)
>
> header: ToolBar {
> RowLayout {
> focus: false
> implicitWidth: children[0].implicitWidth
> implicitHeight: children[0].implicitHeight
>
> ToolButton {
> text: qsTr("File")
> onClicked: fileMenu.open()
>
> Menu {
> id: fileMenu
> y: parent.height
>
> MenuItem {
> text: qsTr("New")
> }
> MenuItem {
> text: qsTr("open")
> }
> MenuItem {
> text: qsTr("Close")
> }
> }
> }
> }
> }
>
> FocusScope {
> id: focusScope
> focus: true
> anchors.fill: parent
>
> property bool toggled: false
> onToggledChanged: print("toggled", toggled)
>
> Keys.onPressed: {
> if (event.modifiers === Qt.AltModifier) {
> focusScope.toggled = true;
> }
> }
> Keys.onReleased: {
> if ((event.modifiers === Qt.AltModifier || event.key ===
> Qt.Key_Alt) && !event.isAutoRepeat) {
> focusScope.toggled = false;
> }
> }
>
> RowLayout {
> anchors.centerIn: parent
>
> Button {
> id: penButton
> text: qsTr("Pen")
> highlighted: !focusScope.toggled
> }
> Button {
> id: eyedropperButton
> text: qsTr("Eyedropper")
> highlighted: focusScope.toggled
> }
> }
> }
> }
>
> The idea is that holding the Alt key down will toggle between two "modes".
> The mode is indicated by a highlighted button.
>
> Try switching between the buttons. Now open the menu by clicking the
> "File" button. The menu will receive focus and the focus scope will lose
> it. If you then close the menu and try to switch between the buttons again,
> it won't work because the focus scope never regained focus (the root item
> now has it). This is explained here [1]:
>
> "When a QML Item explicitly relinquishes focus (by setting its focus
> property to false while it has active focus), the system does not
> automatically select another type to receive focus. That is, it is possible
> for there to be no currently active focus."
>
> I'm not sure if "explicitly relinquishes focus" is exactly what's
> happening here, though. The FocusScope loses focus because something else
> was open temporarily. So now the developer has no other option than to
> listen to e.g. the menu visibility changes and explicitly set focus on the
> FocusScope accordingly. That's pretty gross if you ask me. I think we can
> do better. Specifically, I think that we should remember the last focused
> item, and give that focus, rather than give it to the root item. The
> question is, would it be an acceptable change for Qt 5? I'd assume that
> users are already working around this anyway, and so the fix wouldn't hurt.
>
> [1] http://doc.qt.io/qt-5/qtquick-input-focus.html
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/development
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/development/attachments/20160509/41424dc8/attachment.html>
More information about the Development
mailing list