Ok, this post (and the parts coming – I’ll just split this so that it won’t become a too long read) might interest a bit more wider audience than the previous ones.
So once you’ve started with QtQuick, played around with the examples, maybe done some own UI components and a few views, the thing you start wondering is “so how am I supposed to tie all this together to make it an application?” QtQuick itself doesn’t come with any kind of application framework. Just the basic building blocks you’ve seen in the examples. I (and Wolfgang here) think it’s a great thing that it doesn’t, because such a framework can go wrong in so many ways and restrict innovation. Naturally that would also be against the QtQuick philosophy of being just a framework for constructing objects easily and declaring interactions. Of course you usually still need to have one and probably(?) the boys and girls in the QtComponents project are making one. But the best part is that even if what they are proposing doesn’t suit you, with QtQuick you can just toss it all and do it yourself with relatively little effort. I’m not saying an ubiquitous application/view framework doesn’t have merits, but for standalone apps which don’t need tight integration to other being able to roll your own in a few moments is pretty damn great.
So our goal here is to make a small QtQuick application framework with the following features:
- Animated view switching
- Support for loading views on demand
- Context aware default screen furniture (Title bar, context sensitive buttons, navigation bar)
- A neat performance trick
To demonstrate the features we’ll build an app that utilizes the aforementioned concepts and sells crap to little girls. I haven’t come up with the perfect name for this, but for now we’ll call it KittyApp. You’re free to use this idea to make a million (see, I give away great startup ideas too!) with this if you want. In that case though you should probably rename it Kittr, Catsy, Kittify, Paw*d, Pussio.us, Cattlr or LitterBox. Anyway… our totally awesome KittyApp will look like this:
Anatomy of a feline themed social media solution
This might be painstakingly obvious, but let’s go through the parts of the UI.
Green – the title bar. This contains our context sensitive element (the title). Yellow – the changing view content. Blue – a decorative frame drawn on top of the view content. Purple – the navigation bar (also context sensitive).
A word about the frame. We are drawing it here in four separate parts. If you want to implement this kind of “hole-in-the-middle” frame then please resist the temptation of just slapping a BorderImage with a completely transparent middle section on top of the view. I don’t know the guts of raster, but I’d assume it isn’t optimized for noticing that the middle section is completely transparent and it’ll just waste precious CPU time iterating through the pixels that don’t need any drawing. I’m 99,9% sure that a GPU (if you’re using OpenGL or OpenVG backends) doesn’t check the texture for full transparency. After all what kind of an idiot asks the GPU to draw a completely transparent polygon? The right way to do it (albeit slightly more laborious) is to draw the top, left, right and bottom parts of the frame separately.
There’s two ways to handle switching views. Either you can do it with the states and transitions of QML or you can do it in a more imperative manner. We’re going for the latter. The first one is a completely viable option if your navigation is very simple, but if your app will have a complex navigation graph (possibly infinite), then I think the way I’m about to show you is better.
Our view transitions will be simple slides to left or right. If you need something else it should be fairly easy to implement. Without further ado, may I present you the ViewSwitcher:
Usage: call switchView(<the new view>, <scroll left or right?>). That’s about it. The parent element of your view should naturally be Item or derived from Item. Item, Rectangle, Image, BorderImage – anything goes. When defining your views you should keep in mind not to anchor the left or right edge of your view to anything, so just define width instead. This is because the views are animated by changing the x property.
This is how the views are instantiated in the KittyApp in our main.qml:
Every view is neatly tucked in a separate qml file. In addition to providing a good structure for your app, it will be needed when we implement on demand loading of views.
If you need your views to be notified when they’ve been activated or deactivated, implement activationComplete() and deactivationComplete() functions in your view. If you want do things before the view transition starts, you could easily improve the ViewSwitcher component by adding (for example activationStarting() and deactivationStarting()) function calls. The ‘if (foo.bar == undefined)’ clauses are there for convenience so that you don’t have to have empty functions in your view elements, in case the view doesn’t need to be notified.
Public service announcement: QML performance
This is already mentioned in the docs, but : always set elements which don’t need to be drawn fully transparent. QGraphicsView (which is the component responsible of orchestrating the drawing of our QML scene), is smart enough to skip completely transparent items. But the items are off the screen, aren’t they clipped anyway? Yes, but even the clipping takes precious CPU cycles and setting the elements transparent skips that part.
If you find that you’re not hitting the fps you want for your view transitions, you could try a snappy (like 200ms) animation that hides some elements from your view before the transition. For this you need to implement those activationStarting() and deactivationStarting() calls I mentioned earlier.
Ok, enough for now. In the next post we will add on demand loading of views which is very important for a reasonable startup time and memory consumption.