My last article of Maemo 6 UI Framework was about the layouts and how they work and are designed. In that article, I criticized a bit the implementation because in my opinion they could have used QStateMachine and Qt Animation Framework combination to make the design simpler. One of my principles is that if I criticize something, I need to be able to provide an alternative solution, but it might not be a better than the orignal one:) I guess there’s no sense review anything if you are not able to provide any feedback and alternative ways to do things. So the result speaks most. Here’s a youtube video running animated layouts on N900.
The Alternative Design
Basically what did I do was that I spend three evenings (after my wife and son went to sleep) for coding and I ended up quite similar solution that was used Maemo 6 UI Framework, but in my opinion, my solution is simpler. Here’s the draft UML class diagram of what I came up. I’m sorry that I didn’t have time to document the source code, but I will add that later.
So what makes this different from the DuiLayout implementation then? Ok, these are matter of taste things, but IMHO is that this model is much simpler when it comes to animation. The basic idea is identical with DuiLayout. You have a DuiLayout that can have multiple DuiAbstractLayoutPolicy based policies. In my model there is almost identical classes EnumeLayout and EnumeLayoutPolicy. Each policy class must inherit EnumeLayoutPolicy like EnumeLinearLayoutPolicy in the UML diagram. The difference to DuiLayout is that the EnumeLayout has a QStateMachine instance and everytime when a new policy is added to the EnumeLayout, it requests QState object from the policy instance and that state object is added to the QStateMachine. The QState object of the first policy will be set as a root state for the QStateMachine.
Now that I remember to mention here, I was complaining that DuiLayout and DuiAbstractLayoutPolicy depends on each others and I thought that it was bad design. Well I don’t like that still, but I noticed that it might be quite difficult to get rid of that dependency. So I’ll take my words back. No bad feelings I hope:)
So each EnumeLayoutPolicy has a QState object. When a policy class is requested to “relayout” items in it, what it does is, that it assigns e.g. “pos” property of the each item to the QState object. At the moment of writing this the “pos” property is the only supported property, but I have plans to extend it so that you can basically assign any property you want. As I mentioned earlier, when a policy is added to the EnumeLayout it adds the QState object to the state machine. The EnumeLayout doesn’t do policy switching automatically, but it provides an interface for defining how to “chain” different policies. Internally EnumeLayout uses QSignalTransitions for triggering layout switching. When a EnumeLayout::addTransition(sourceStateId, targetStateId, sender, signal) method is called, it also checks if there are items that are not in the both policies. It is possible that all the items are in all policies, but there might be cases that one policy contains more or less items than the other policy. If this is the case, then all the items that are not in “target state” will be animated to invisible.
An Example of Usage
Let’s see the code. The code snippet below is the code for creating the layouts that you saw on the video in the beginning of this article. In clients point of view, the usage of the EnumeLayout stuff doesn’t differ much from the DuiLayout. Please feel free to disagree, but let me know.
QGraphicsWidget *widget = new QGraphicsWidget; widget->setPreferredSize(800,420); widget->setMaximumHeight(420); // Make items to overlap a bit EnumeLayout * layout = new EnumeLayout; widget->setLayout( layout ); widget->setPos(0,0); SimpleButton * b1 = new SimpleButton("Test1"); SimpleButton * b2 = new SimpleButton("Test2"); SimpleButton * b3 = new SimpleButton("Test3"); SimpleButton * b4 = new SimpleButton("Test4"); SimpleButton * b5 = new SimpleButton("Test5"); // Create a vertical policy EnumeLinearLayoutPolicy * vPolicy; vPolicy = new EnumeLinearLayoutPolicy(Qt::Vertical, layout); vPolicy->addItem(b1); vPolicy->addItem(b2); vPolicy->addItem(b3); vPolicy->addItem(b4); vPolicy->addItem(b5); int vId = layout->addPolicy( vPolicy ); // Create a horizontal policy EnumeLinearLayoutPolicy * hPolicy; hPolicy = new EnumeLinearLayoutPolicy(Qt::Horizontal, layout); hPolicy->addItem(b4); hPolicy->addItem(b5); hPolicy->addItem(b2); hPolicy->addItem(b1); int hId = layout->addPolicy( hPolicy ); // Create a QGraphicslayout, add it to widget and add // that widget to the policy QGraphicsGridLayout * grid = new QGraphicsGridLayout; SimpleButton * b6 = new SimpleButton("GridButton1"); SimpleButton * b7 = new SimpleButton("GridButton2"); SimpleButton * b8 = new SimpleButton("GridButton3"); SimpleButton * b9 = new SimpleButton("GridButton4"); grid->addItem(b6,0,0); grid->addItem(b7,0,1); grid->addItem(b8,1,1); grid->addItem(b9,1,0); QGraphicsWidget * w = new QGraphicsWidget; w->setLayout( grid ); // Create yet another policy to contain w with grid layout EnumeLinearLayoutPolicy * vGrid; vGrid = new EnumeLinearLayoutPolicy(Qt::Vertical, layout); vGrid->addItem(w); int vGridId = layout->addPolicy( vGrid ); // Make layout to switch policy by defining a transition from policy // to policy when a button is clicked. The signal could be any signal that // is wanted to trigger the transition. layout->addTransition(vId, hId, b1, SIGNAL(clicked())); layout->addTransition(hId, vId, b2, SIGNAL(clicked())); layout->addTransition(vId, vGridId, b5, SIGNAL(clicked())); layout->addTransition(vGridId, hId, b7, SIGNAL(clicked()));
The source code for EnumeLayout stuff is available at Gitorious.
git clone git://gitorious.org/enume/enume.git See the layout-demo directory.
Please remember that this piece of code is not near to anything final. The following stuff needs to be done:
- Only preferred size for items is used in layouting. There should be support for min and max sizes also.
- Strech factor is ignored
- EnumeLayoutPolicy should provide a list of properties that are assigned to QState. These can be used by EnumeLayout when creating transitions between states
- More policies like Grid, Flow
- Testing with hundreds or thousands widgets
That’s all for this time. I hope that if you found this article interesting, please leave a comment.