ariya
WebKit
Graphics View
Graphics Dojo
Posted by ariya
 in WebKit, Graphics View, Graphics Dojo
 on Saturday, November 15, 2008 @ 01:05

Kinetic scrolling is the popular term to denote the scrolling of a long list with a bit of physics so that user feels like moving a wheel. Such a list view is then often referred as a flick list, caused the scrolling involves some sort of flicking gestures. Made popular in iPhone, flick list quickly invades other mobile platforms with touch screen because it just feels so natural and more usable than using the conventional approach of scroll bars.

How about Qt-based GUI applications? Up to now we do not offer the flickable list in any standard widgets. A practical approach would be to subclass QAbstractScrollArea and then implement your own. This does not really solve the problem if you have already tons of widgets based on, say, QListView, as you might not want to change the class structure and inheritance. Is there another solution?

Enter Flick Charm, today’s dojo example. This charm is only a proof of concept and will not have all the goodies found in iPhone (e.g. no overshoot or bouncing). However, making your list flickable is a matter of writing the code:

FlickCharm charm;
charm.activateOn(widget);

where widget is any class derived from QAbstractScrollArea and this of course includes very useful classes like QScrollArea, QAbstractItemView (and thus also QListView, QTableView, QTreeView, and friends) and even QGraphicsView. As a bonus, QWebView is also supported so your QtWebKit-based browser can be easily made flickable, too. The beauty of Flick Charm is it works on any of those widgets and that you do not even need to change your code!

For the code and the example, check out:

svn checkout svn://labs.trolltech.com/svn/graphics/dojo/flickcharm
cd flickcharm && qmake && make && ./flickcharm

The secret of Flick Charm is the use of QObject::eventFilter to intercept the mouse events. From this, a state machine tracks the changes and apply the scrolling to the target widget. It was fairly straightforward to implement, evidenced from the class implementation that weighs around 300 lines of code only.

No kinetic scrolling demo is complete with a screencast. You can also watch it on YouTube or blip.tv.

Note: this Flick Charm automagically hides the scroll bars in the scroll area or web view. This is done because it does not make sense to have flickable list with still showing the scroll bars. If you are unhappy with this, enable the scroll bars again or just change the code.

Update: for PyQt users, check out the Python version of this charm, ported by Akos Polster.

15 Responses to “Flick list or kinetic scrolling”

» Posted by Adam Higerd
 on Saturday, November 15, 2008 @ 05:27

Well, I guess that handles the request I had in the last post!

» Posted by Philippe
 on Saturday, November 15, 2008 @ 07:22

Nice! Is it meant to be integrated to Qt 4.5?…

» Posted by Ku T.
 on Saturday, November 15, 2008 @ 13:15

Nice stuff. Unfortunately left clicks are not handled very good. You have to perform a double-click to get a single-click.
I think the proper (or atleast better) way would be to look for a QEvent::MouseButtonRelease and if there is no movement (within some range) since the last QEvent::MouseButtonPressed a mouse click should be propagated.

» Posted by Richard Moore
 on Saturday, November 15, 2008 @ 16:39

It would be neat if this responded to wheel events too.

» Reply from ariya
 on Saturday, November 15, 2008 @ 18:47
ariya

@Philippe: 4.5 is in feature freeze.

@Richard: this is only a dojo, not a complete solution, so wheel event support is left as an exercise for the reader :-)
Hint: the state machine needs to be modified for wheel support and this would make the charm more complicated than necessary, hence I left it just like that.

» Posted by qtuser
 on Saturday, November 15, 2008 @ 20:20

Hi,

Still a little confused. Will this work with 4.4.x or will this require 4.5?

thanks!

» Reply from ariya
 on Sunday, November 16, 2008 @ 14:09
ariya

@Ku T: Are you sure left-click does not work? I spent a great deal of time to ensure that both single-click and double-click still works. See e.g. that single-click on a link still opens that web site in the web view.

@qtuser: I didn’t mention anything 4.5-specific in the article. Thus you can safely assume that this works for both 4.4 and 4.5.

» Posted by Ku T.
 on Sunday, November 16, 2008 @ 18:35

@ariya:
Really strange. I just tried it under Windows (XP/Qt 4.4.3) and it works as intended.
While with Qt 4.4.3 on Ubuntu Intrepid, for both the WebKit and the GraphicsView I have to click twice to activate an item or a link. Maybe it is a Ubuntu (or qtcopy) patch?! From your video I see that you use Linux too, so it can’t be that alone. I have no Qt 4.5 build for Linux at the moment to test it with.

Another little thing:
You can’t really accelerate if you flick from the same spot. The view jumps back again and then scrolls over the same area. This happens on both Windows and Linux. For example, just flick constantly from the center to the top. You would expect that it accelerates more and more and scrolls down further. Instead it roughly stays around one position and jumps up and down.

» Posted by Andrea Grandi
 on Monday, November 17, 2008 @ 06:54

I really would like to see this implemented in Qt 4.6 at least :)

» Posted by Tim
 on Thursday, November 20, 2008 @ 09:31

Very nice Ariya. Saves me a ton of work :-)
Unfortunately it doesn’t works as nice for list views like it does for the canvas view.
You just can not control the scrolling in a list view it just going wild [Qt4.4.2 - VS2005].

» Posted by Zack Rusin
 on Monday, November 24, 2008 @ 19:38

Jacek wrote the code for this two years ago. It’s in dev/research/minibrowser, or at least was (also, his version supported bouncing on edges)

» Posted by Zandru
 on Wednesday, November 26, 2008 @ 16:27

thanks a lot!
Ever since i’ve laid hands on an iPhone, i wanted to make this work for our touchscreen-based project as well.
Hmm, looks like i can use my time on other goodies now :) like “slide to activate” widgets etc.

» Reply from ariya
 on Friday, November 28, 2008 @ 22:30
ariya

@Zack: I will be shocked if nobody has written something like this before :-) From what I saw, this one is different because it aims on making any scroll area flickable without touching its code.

» Posted by Treviño
 on Monday, December 01, 2008 @ 05:24

Thanks for this…

I’ve made also a little patch to show the scroll-bars only while scrolling:

Index: flickcharm.cpp
===================================================================
— flickcharm.cpp (revisione 858)
+++ flickcharm.cpp (copia locale)
@@ -164,6 +164,22 @@
frame->evaluateJavaScript(QString(”window.scrollTo(%1,%2);”).arg(p.x()).arg(p.y()));
}

+static void showScrollBars(QWidget *widget) {
+ QAbstractScrollArea *scrollArea = dynamic_cast(widget);
+ if (scrollArea) {
+ scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ }
+}
+
+static void hideScrollBars(QWidget *widget) {
+ QAbstractScrollArea *scrollArea = dynamic_cast(widget);
+ if (scrollArea) {
+ scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ }
+}
+
static QPoint deaccelerate(const QPoint &speed, int a = 1, int max = 64)
{
int x = qBound(-max, speed.x(), max);
@@ -283,6 +299,9 @@
while (item.hasNext()) {
item.next();
FlickData *data = item.value();
+
+ if (data->state == FlickData::ManualScroll || data->state == FlickData::AutoScroll)
+ showScrollBars(data->widget);

if (data->state == FlickData::ManualScroll) {
count++;
@@ -295,8 +314,10 @@
data->speed = deaccelerate(data->speed);
QPoint p = scrollOffset(data->widget);
setScrollOffset(data->widget, p - data->speed);
- if (data->speed == QPoint(0, 0))
+ if (data->speed == QPoint(0, 0)) {
data->state = FlickData::Steady;
+ hideScrollBars(data->widget);
+ }
}
}

Btw an implementation of kinetic scrolling should be also available on Qt Extended (and it has bouncing and more)…

» Posted by Treviño
 on Monday, December 01, 2008 @ 05:50

A little bit tuned: http://pastebin.ubuntu.com/78596/



© 2008 Nokia Corporation and/or its subsidiaries. Nokia, Qt and their respective logos are trademarks of Nokia Corporation in Finland and/or other countries worldwide.
All other trademarks are property of their respective owners.