Now that Qt is hot and QGraphicsView is even hotter, I’m quite surprised that Trolls haven’t provided a good common component for kinetic panning. There are some existing demo codes like this FlickCharm in dojo examples, but the problem in FlickCharm is that it doesn’t work with QGraphicsWidgets. Please correct me, if I’m wrong. QML introduces a class called QmlGraphicsFlickable which is used for providing a kinetic scrolling for QML objects. Well this works for QML, but not directly for QGraphicsWidgets.
I wrote the first version of the PannableView for QTablet project about 1.5 year ago. My collegue Kypeli (Johan Paul) helped me by writing some of the algorithms for scrolling. There is a one video which I uploaded to youtube about a year ago. On that video I demonstrated the first version of the PannableView used by a home screen and by an application launcher of the QTablet. That demo ran quite well on N8X0. I thought that maybe I could improve it little bit and release it as a public component. Still, it’s not a perfect. It’s not even close to iPhone’s kinetic scrolling, but it’s better than nothing.
The video clip below demonstrates different ways how to use PannableView for example to make QGraphicsWebView pannable, how to create a simple slideshow and how it clips the area outside the PannableView’s borders. At first, all the demo apps are running on iMac and after that I’ll show how they run on N900. I accidentally ran all the demo apps on device with -graphicssystem opengl flags and it flipped all the images upside down. Nice bug in Qt.
The Usage of the Widget
I wanted to make the usage of the PannableView as easy as possible. So here is the example:
QApplication app(argc, argv); // Make QGraphicsWebView pannable QGraphicsWebView * webView = new QGraphicsWebView; // Set some hardcoded values to demonstrate the panning webView->setPreferredSize(1500, 2000); // Load some content webView->load(QUrl("http://maemo.org/")); // Create a PannableView object and set QGraphicsWebView for panning PannableView *pannableView; pannableView = new PannableView(Qt::Vertical | Qt::Horizontal ,800,480); pannableView->setWidget(webView); // Do the rest of the boring stuff. QGraphicsScene *scene = new QGraphicsScene; scene->addItem( pannableView ); QGraphicsView view(scene); view.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); view.setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); view.setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); view.setFrameStyle( QFrame::NoFrame ); view.resize(800,480); view.show();
For me this looks quite easy way to make any of the QGraphicsWidget based widgets to support kinetic scrolling.
The design of the widget is quite simple. The difficult part has always been the algorithm which is used for calculating how fast the swipe gesture was made and what kind of values should be put for X and Y coordinates and so on. It was also surprisingly difficult to make edge recognition to work properly. The basic idea how PannableView works is in bullet points below:
- PannableView defines borders for the pannable area and everything outside that area is clipped away. This means that if a geometry of the child object is larger than the geometry of the PannableView then the child object will be partially clipped.
- PannableView uses PannableWidget class internally. When user passes an object istance via PannableView::setWidget(QGraphicsWidget*), the object is passed to the PannableWidget. It puts it into a layout and defines a PannableWidget::shape() method in order to make clipping to work.
- When the widget “X” is set for panning, it’s made to stack behind the parent i.e. behind the PannableView. This way the widget “X” doesn’t get the mouse events, but PannableView gets those. When PannableView decides that now user has tapped the widget, it sends mouse events to the widgets under that point. If the user does a swipe gesture, then PannableView moves the PannableWidget’s position according the values calculated by the algorithm.
- The edge collisions are animated using Qt’s Animation Framework.
- PannableView emits posChanged(QRectF) signals which can be used for informing the widget that is set for panning that now this area is visible. This way it’s possible to load the stuff that is in visible area or do some buffering i.e. pre-load/unload stuff.
There are still things to do and I’m not so satisfied with the current algorithm. For me it looks too much spaghetti code and I believe there are more clever ways to implement that. You can check the TODO list below for future improvements:
- Implement a scroll indicator which displays the current position. NOTE this is only an indicator, not a scrollbar.
- Implement a better algorithm for calculating the values used during the scrolling.
- Make PannableView to use Qt’s property mechanism.
- Let user of the PannableView decide if animations should be used or not.
- Debian packaging
Where to Get It?
The source code is available, with LGPL license, from Gitorious (git clone git://gitorious.org/enume/enume.git). Just run qmake and make and that’s it. All the examples that I demonstrated on video are in enume/examples/pannableview directory. I didn’t do any debian packaging, but if you need that, contact me.
Please, let me know if you think that you can use this widget in your Qt application. It should be compatible with any QGraphicsWidget based widgets and the usage of it should be easy enough for everyone. Thanks for reading.