qtwebkit-goes-mobile.qdoc   [plain text]


/*!
    \inmodule QtWebKit
    \page qtwebkit-goes-mobile.html
    \title QtWebKit Goes Mobile
    \contentspage QtWebKit
    \section1 Overview

    A lot of effort has been put into QtWebKit to make it attractive for
    use on mobile devices.

    The goal of this tutorial is to help you understand the mobile
    features and how to make the best of them, in other words, how to
    create a good mobile web view that can be used on touch devices.

    If you want to target mobile devices you should consider using \l{QGraphicsWebView}
    instead of \l{QWebView}. Since \l{QWebView} is based on the \l{QWidget}
    system, it cannot easily support rotation, overlays, hardware accelerated
    compositing and tiling. If you need a \l{QWidget} anyway, you can always
    construct a \l{QGraphicsView} (which is a \l{QWidget}) with a \l{QGraphicsWebView} inside.

    So let's start with a very simple \l{QGraphicsWebView} based "browser":

    \snippet webkitsnippets/qtwebkit_goes_mobile_snippets.cpp 0

    Here we set up a \l{QGraphicsView} application and add a
    \l{QGraphicsWebView} to the scene. Notice
    that we're disabling the scrollbars on the QGraphicsView because QtWebKit
    handles scrolling and scrollbars automatically. This is to allow scrolling
    optimizations and to enable web authors to interact with the scrollbars,
    for instance, to style them differently.

    On touch-based mobile devices a feature known as tiling is often used. It
    is used due to the interaction model (touch) as well as a scrolling
    optimization. With this optimization we will have to deal with scrolling
    ourselves, and we will have to disable features like scroll bar styling.
    This is not usually a problem, since mobile browsers do not usually show
    scroll bars, but use scroll indicators instead.

    Tiling basically means that the contents of the viewport is separated into
    a grid of tiles, so that when you update an area, instead of just updating
    the area, you actually update the whole tile or sub-regions of it.
    This offers a few advantages for scrolling as, when you scroll, you do not need
    to repaint the new visible area for each scroll step, but you simply update a row
    of tiles each time; these tiles are often only partly on the screen.
    This minimizes all the painting calls that we have to do and enables kinetic scrolling.

    Loading, rendering, and laying out are blocking operations. Though barely
    noticeable on desktop machines, these operations can block for a long time
    on a mobile device, letting the user believe the application has become
    unresponsive. Additionally, scrolling will also stall when the user uses
    his fingers to scroll, leading to a bad user experience.

    One way to overcome this issue, is to do all loading, laying out and
    painting (basically all non-UI related work) in another thread or process, and
    just blit the result from the web process/thread to the UI. There is research
    in progress to enable this for a future version of QtWebKit, using WebKit2, but for now,
    freezing the backing store can help when performing a zooming operation, for instance.
    This will be discussed later, in the \l{#Enabling the Tiling}{Enabling the Tiling} section.

    When using tiles, you can blit any tile available when scrolling. When no tile is available you
    can show a checkerboard tile instead, not letting the scrolling wait for the
    tiles to be updated. This results in a responsive interface, with the only
    disadvantage that you might see checkerboard tiles from time to time.

    The use of tiles also helps with zooming. Repainting at each zoom level change during
    a zoom animation is basically impossible on a mobile device (or desktop for
    that matter) and thus, with tiling, you can stop the tiles from being updated and
    just scale the existing tiles. Then, at the end of the animation, update
    tiles on top of the scaled ones.  For now we will ignore the blocking
    issue and concentrate on the tiling and the interaction model.
    More information about Tiling can be found here: \l{http://doc.qt.nokia.com/4.7/qwebsettings.html#WebAttribute-enum} (see the entry for TiledBackingStoreEnabled).


    \section1 Resize to Contents

    When using tiling, we want the \l{QGraphicsWebView} to act as our
    content, as it supports tiling. In order for this we need to make it
    resize itself to the size of its contents. For this we will use
    \l{QGraphicsWebView::resizesToContents}.

    This setting removes the scroll bars for us on the main frame and
    makes our \l{QGraphicsWebView} resize itself to the size of its content.

    Enabling it is as easy as setting the property:

    \snippet webkitsnippets/qtwebkit_goes_mobile_snippets.cpp 1

    The property should be used in conjunction with the
    QWebPage::preferredContentsSize property. If not explicitly set,
    it is automatically set to a reasonable value.

    If we expand our mobile web view to the size of the contents
    of its contained page, the view will be bigger than the device screen.


    \section1 Using a View as the Window onto the Contents

    The idea is to have a custom widget which has a \l{QGraphicsWebView} as a
    class member. Remember that the QGraphicsWebView will be as big as its
    content's size, so this custom widget will serve as a viewport onto the
    web view.

    The following code snippet illustrates this:

    \snippet webkitsnippets/qtwebkit_goes_mobile_snippets.cpp 2

    In order to properly handle mouse events, you must install an event filter
    on the web view or stack it behind its parent object
    (\l{QGraphicsItem::ItemStacksBehindParent}). By doing this the mouse events will
    reach a \c{MobileWebView} instance before they reach the member
    \l{QGraphicsWebView}. Keep in mind that you'll need to add some logic in order
    to distinguish between different mouse events and gestures, such as a
    single click, double click and click-and-pan. Besides, scrolling and
    zooming will have to be implemented manually.


    \section1 Adjusting How Contents are Laid Out

    When testing the above on a device, you will notice that many pages are not
    laid out very nicely. In particular, the width of the content may be larger
    than the width of the device. This is due to the way web contents are laid
    out. First, the viewport width is used for fitting the contents. If the
    contents do not fit due to a non-flexible element with a width larger than
    the viewport width, the minimum possible width will be used. As most pages
    are written with a desktop browser in mind, that makes only very few sites
    fit into the width of a mobile device.

    QtWebKit has a way to force a layout to a given width or height. What really
    matters here is the width. If you lay out a page to a given width, it will get
    that width and images might be cropped. The width or height is also used for
    laying out fixed elements, but when we resize the \l{QGraphicsWebView} to the
    size of the contents, fixed elements will not be relative to the view, which is
    the behavior found on most mobile browsers.

    We saw that the QWebPage::preferredContentsSize property is automatically set
    to a reasonable value when using \l{QGraphicsWebView::resizesToContents}.

    As you can imagine, laying out with a smaller viewport can cause pages to
    break, therefore a default value has been chosen so that it almost breaks no
    pages while still making pages fit. This value is 960x800. If the device
    has a greater resolution, this value can be changed like this:

    \snippet webkitsnippets/qtwebkit_goes_mobile_snippets.cpp 3

    You can play around with this and find a suitable size for your device,
    but we will keep the default size for now.


    \section1 The 'viewport' Meta-Tag

    As some sites do not work with 960 pixels width or want to have control of
    how the page is laid out, QtWebKit, Android, Firefox Mobile and
    the iPhone Safari browser support a meta-tag called \c viewport. This makes
    it possible for a web page to let the browser know how it wants to be shown.
    More info can be found in the
    \l{http://developer.apple.com/safari/library/documentation/appleapplications/reference/safariwebcontent/usingtheviewport/usingtheviewport.html}{Safari Developer Library}.

    You must connect the signal \c{QWebPage::viewportChangeRequested(const
    QWebPage::ViewportHints& hints)} to a slot of your mobile web view and use what
    is provided by \l{QWebPage::ViewportHints} to update your viewport size, scale
    range, and so on.


    \section1 Enabling the Tiling

    We haven't actually enabled tiling yet, so let's go ahead and do that. That
    is very simple as it is basically a setting:

    \snippet webkitsnippets/qtwebkit_goes_mobile_snippets.cpp 4

    Note that, if you are going to add animations to your zooming or scaling
    operations or want to implement fancy kinetic scrolling you might want to
    take a look at \l{QGraphicsWebView::setTiledBackingStoreFrozen()}. With this,
    you can avoid updates to your tiles during an animation, for instance.


    \section1 Avoiding Scrollable Subelements

    One big issue with the above is that, iframes and sites using frames can
    contain scrollable subelements. That doesn't work well with the touch
    interaction model, as you want a finger swipe to scroll the whole page and not
    end up just scrolling a subframe. Most mobile browsers work around this by
    enabling something called frame flattening. Going straight to the point:

    \snippet webkitsnippets/qtwebkit_goes_mobile_snippets.cpp 5

    This will make all frames from a web page expand themselves to the size of
    their contents, keeping us free of scrollable subareas.


*/