How to use QStatemachine to influence a ListView?

380 views Asked by At

I have got this Projekt which uses a QStatemachine to manage the UI, where I want to add a customized List. The UI is supposed to be only manipulated by key events. As far as I understand I need a ListView on the qml side.

The delegate of ListView only reacts on mouse input or direct key input. But I have use the QStatemachine in C++ to operate it, since it is handling all key events for the UI. What I want to happen when I press the right arrow key is vor the list to be shifted to the left.
(The currentItem is alway in the middle of the screen.)

this is the inital State of the lisfview

enter image description here

So my ListView is looking like this at the Moment.

Component {
            id:myDelegation
            Item {
               x: 50
               width: 80
               height: 60
               Rectangle {
                  width: 60
                  height: 60

                  Text {
                    text: name
                    anchors.centerIn: parent
                  }

                  color: parent.ListView.isCurrentItem ? "red" : "steelblue";
                  scale: parent.ListView.isCurrentItem ? 1.5 : 1;
              }

          }
        }


        ListView {
            id: listView1
            x: 0
            y: 50
            width: 1920
            height: 214
            orientation: ListView.Horizontal
            spacing: 4
            model: TileList{}
            delegate: myDelegation
            preferredHighlightBegin: width / 2 - 10
            preferredHighlightEnd: width / 2 + 10
            highlightRangeMode: ListView.StrictlyEnforceRange
        }

The c++ Statemachine is a QStatemachine which sends Signals to qml.

How do I bind the signals to the delegate of the Listview?

2

There are 2 answers

6
dtech On BEST ANSWER

Step one - expose the state machine as a context property so that it is visible to qml:

engine.rootContext()->setContextProperty("SM", stateMachinePtr);

Step two - use a Connections element to establish a connection:

Connections {
  target: SM
  onSomeSignal: doSomeStuff()
}
0
Kevin Krammer On

The easiest way is to just have the state machine set the "currentIndex"

A common pattern is to have an interface object that bridges between QML and the QStateMachine

class StateInterface : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int currentIndex MEMBER m_currentIndex NOTIFY currentIndexChanged)

public:
    explicit StateInterface(QObject *parent = 0);

signals:
    void currentIndexChanged() const;

private:
    int m_currentIndex;
};

An instance of that object is exposed to QML via the "context property" mechansism

StateInterface stateInterface;
qmlEngine->rootContext()->setContextProperty("_stateInterface", &stateInterface);

And used in QML as needed

ListView {
    currentIndex: _stateInterface.currentIndex
}

The QStateMachine uses the same stateInterface object as a target for state property assignments

QState *beginState = new QState(stateMachine);
beginState->assignProperty(&stateInterface, "currentIndex", 0);
// and so on.

The StateInterface object can also provide slots to be used by QML to affect state changes. E.g.

public slots:
    void triggerReset() { emit trigger reset(); }

signals:
    void reset();

And the QStateMachine can, for example, then react to those signals wth a signal transition into the beginState

To summarize this technique:

  1. the QStateMachine controls the application state
  2. all state data that is of interest to QML is exposed via one or more interface objects
  3. the QML side uses the state data in a nice, declarative way just like it would if it did the state handling itself