[Interest] Declaratively handle key presses / navigation differently depending on state (QML)

Elvis Stansvik elvstone at gmail.com
Thu Mar 31 15:59:08 CEST 2016


2016-03-31 15:28 GMT+02:00 Jérôme Godbout <jerome at bodycad.com>:
> Just a thought, you may try the Qml Shortcut component:
> http://doc.qt.io/qt-5/qml-qtquick-shortcut.html
> Not sure about the Shortcut think, I haven't play around too much with it
> yet.
> Keep a model and the current index of the column and is edit mode.
>
> import QtQuick 2.4
> import QtQuick.Window 2.2
>
> Window {
>     id: window_
>     width: 800
>     height: 600
>     visible: true
>
>    property currentIndex: 0
>    property editMode: false
>    Shortcut
>     {
>       sequence: StandardKey.NextChild // Change for the actual key wanted
>       onActivated:
>       {
>           if(editMode)
>               ++myModel[currentIndex].value;
>           else
>               currentIndex = (currentIndex + 1) % myModel.length; // wrap
> around
>       }
>     }
>     // Add other shortcut here to enter/exit editMode: editMode = !editMode
>     // previous:  currentIndex = (currentIndex - 1 + myModel.length) %
> myModel.length;
>
>    property list<QtObject> myModel:
>    [
>        QtObject
>        {
>           property string name: 'meters'
>           property int value: 3
>        },
>        QtObject
>        {
>           property string name: 'centimeters'
>           property int value: 10
>        }
>    ]
>    Row
>    {
>      spacing: 30
>      Repeater
>      {
>          id: repeater_
>          model: window_.myModel
>          MyColumn // do the highlight
>          {
>              selected: window_.currentIndex == index
>              editMode: selected && window_.editMode
>              value: modelData.value
>              name: modelData.name
>          }
>      }
>    }
> }
>
> Would be more neat I think, and way easier to maintain or add/remove stuff.
> You could have a base object for each entry of the model that have default
> property too.

Thanks Gerome,

This looks like a nice approach, I'll try that out as well. The
wrapping behavior was just something I put in the example. In the real
app I want to transfer focus to some navigational items at the bottom
when going past the last input item, but good to see how it's done
with this approach anyway.

After my first post, I've also realized a couple of things:

* The onXXXPressed convenience handlers will accept the event automatically.
* I can use KeyNavigation.priority to control whether the item or
KeyNavigation gets priority.

Based on this, I could improve my initial approach a little
(simplified for brevity):

IntegerEditor.qml:

import QtQuick 2.4
import "."

Text {
    id: editor
    text: value

    // In the default state, KeyNavigation is given priority.
    KeyNavigation.priority: KeyNavigation.BeforeItem

    property int value: 0

    states: [
        State {
            name: "editing"
            PropertyChanges {
                target: editor
                color: "green"

                // In the editing state, the item is given priority.
                KeyNavigation.priority: KeyNavigation.AfterItem
            }
        }
    ]

    Keys.onReturnPressed: {
        state = state == "" ? "editing" : ""
    }

    Keys.onRightPressed: {
        if (state == "editing")
            value++
    }

    Keys.onLeftPressed: {
        if (state == "editing")
            value--
    }
}

This IntegerEditor can then be used like this (excerpt):

Row {
    IntegerEditor {
        id: metersEditor
        focus: true

        KeyNavigation.right: centimetersEditor
    }
    Text {
        text: ","
        font.family: metersEditor.font.family
        font.weight: metersEditor.font.weight
        font.pixelSize: metersEditor.font.weight
        anchors.baseline: metersEditor.baseline
    }
    IntegerEditor {
        id: centimetersEditor
        anchors.baseline: metersEditor.baseline

        KeyNavigation.right: metersEditor
    }
}


This still leaves some if (state == "blah") procedural code, but is a
lot simpler than what I first have.

But I'll experiment with the approaches you and Evan have suggested as
well. Thanks!

Elvis

>
> On Thu, Mar 31, 2016 at 8:37 AM, Elvis Stansvik <elvstone at gmail.com> wrote:
>>
>> Hi,
>>
>> I'm working on an item representing a number to be edited by the user.
>>
>> My input is quite limited (only Left, Right and Enter, where Left and
>> Right is actually the turning of a wheel), so the idea is to let the
>> user navigating to the item with Left/Right, which will give it focus.
>> If the user then presses Enter, the item enters and "editing" state.
>> In the "editing" state, Left and Right will increase/descreas the
>> number, and pressing Enter again will exit the "editing" state,
>> returning to the default state ("").
>>
>> How can I handle key presses differently depending on state without
>> resorting to procedural code like if/else on the state inside the
>> onXXXPressed handlers? I thought I'd be able to assign to
>> Keys.onXXXPressed inside State { PropertyChanges { ... }, but the
>> property is not recognized there (I'm guessing because it's an
>> attached property..?).
>>
>> Also, the KeyNavigation.left/right/etc. is a convenient way to do the
>> navigation between items, but it seems incompatible with the
>> Keys.onXXXPressed handlers? I thought I'd be able to skip accepting
>> the key even in a Keys.onXXXPressed handler, and have it pass through
>> to the KeyNavigation.xxx handler...? (I can do the navigation with
>> someItem.forceActiveFocus() instead, but it's more typing and more
>> error prone).
>>
>>
>> Below is a working but rather ugly example of two numbers that can be
>> edited this way. One can navigate between the numbers with Left/Right,
>> and toggle "editing" mode with Enter (in which Left and Right controls
>> the number value instead).
>>
>> How can I make this example more declarative / less ugly? (Besides the
>> obviously putting things in a reusable component).
>>
>> Example:
>>
>> import QtQuick 2.4
>> import QtQuick.Window 2.2
>>
>> Window {
>>     width: 800
>>     height: 600
>>     visible: true
>>
>>     Row {
>>         spacing: 30
>>         Column {
>>             Text {
>>                 text: "Meters"
>>             }
>>             Text {
>>                 id: meters
>>                 text: value
>>                 font.underline: activeFocus
>>                 font.pixelSize: 100
>>
>>                 property int value: 3
>>
>>                 focus: true
>>
>>                 states: [
>>                     State {
>>                         name: "editing"
>>                         PropertyChanges {
>>                             target: meters
>>                             color: "green"
>>                         }
>>                     }
>>                 ]
>>
>>                 Keys.onReturnPressed: {
>>                     if (state == "")
>>                         state = "editing"
>>                     else
>>                         state = ""
>>                     event.accepted = true
>>                 }
>>
>>                 Keys.onRightPressed: {
>>                     if (state == "editing")
>>                         value++
>>                     else
>>                         centimeters.forceActiveFocus()
>>                     event.accepted = true
>>                 }
>>
>>                 Keys.onLeftPressed: {
>>                     if (state == "editing")
>>                         value--
>>                     else
>>                         centimeters.forceActiveFocus()
>>                     event.accepted = true
>>                 }
>>             }
>>         }
>>         Column {
>>             Text {
>>                 text: "Centimeters"
>>             }
>>             Text {
>>                 id: centimeters
>>                 text: value
>>                 font.underline: activeFocus
>>                 font.pixelSize: 100
>>
>>                 property int value: 14
>>
>>                 states: [
>>                     State {
>>                         name: "editing"
>>                         PropertyChanges {
>>                             target: centimeters
>>                             color: "green"
>>                         }
>>                     }
>>                 ]
>>
>>                 Keys.onReturnPressed: {
>>                     if (state == "")
>>                         state = "editing"
>>                     else
>>                         state = ""
>>                     event.accepted = true
>>                 }
>>
>>                 Keys.onRightPressed: {
>>                     if (state == "editing")
>>                         value++
>>                     else
>>                         meters.forceActiveFocus()
>>                     event.accepted = true
>>                 }
>>
>>                 Keys.onLeftPressed: {
>>                     if (state == "editing")
>>                         value--
>>                     else
>>                         meters.forceActiveFocus()
>>                     event.accepted = true
>>                 }
>>             }
>>         }
>>     }
>> }
>>
>>
>> Thanks for any advice,
>> Elvis
>> _______________________________________________
>> Interest mailing list
>> Interest at qt-project.org
>> http://lists.qt-project.org/mailman/listinfo/interest
>
>



More information about the Interest mailing list