Qt Reference Documentation

Maemo 5 QGraphicsWebView Example

Files:

The Maemo 5 QGraphicsWebView example shows how to use QAbstractKineticScroller within a QGraphicsView based application.

Screenshot of the Browser example

ViewportItem

Firstly, the ViewportItem container item, which contains the to-be-scrolled content item, needs to be implemented: It derives from QAbstractKineticScroller and QGraphicsWidget. Because it is only a container, some Flags need to be set to convert it to something resembling a scroll area first:

 class ViewportItem : public QGraphicsWidget, public QAbstractKineticScroller
 {
     Q_OBJECT
 public:
     ViewportItem()
         : QGraphicsWidget(), QAbstractKineticScroller(), m_widget(0)
     {
         setFlag(QGraphicsItem::ItemHasNoContents, true);
         setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
         setFlag(QGraphicsItem::ItemClipsToShape, true);
         setAttribute(Qt::WA_OpaquePaintEvent, true);
         setFiltersChildEvents(true);
     }

Additionally setFilterChildEvents is used to install an event filter on all children of the viewport, so that all mouse events can be intercepted and used for kinetic scrolling.

     void setWidget(QGraphicsWidget *widget)
     {
         if (m_widget) {
             m_widget->setParentItem(0);
             delete m_widget;
         }
         m_widget = widget;
         if (m_widget) {
             m_widget->setParentItem(this);
             m_widget->setAttribute(Qt::WA_OpaquePaintEvent, true);

             if (qgraphicsitem_cast<QGraphicsWebView *>(m_widget)) {
                 connect(m_widget, SIGNAL(loadFinished(bool)), this, SLOT(resizeWebViewToFrame()));
                 resizeWebViewToFrame();
             }
         }
     }

The scrollable graphics widget is bound to the kinetic scroller by calling setWidget(). This new graphics widget will be reparented to the ViewportItem in order to get correct viewport-like clipping. If the widget is a QGraphicsWebView, additionally a connection is made to a slot that resizes the widget to the full page size as soon as loading is finished.

     void resizeWebViewToFrame()
     {
         if (QGraphicsWebView *view = qgraphicsitem_cast<QGraphicsWebView *>(m_widget)) {
             if (view->page() && view->page()->mainFrame()) {
                 QSizeF s = view->page()->mainFrame()->contentsSize();
                 s = s.expandedTo(size());
                 view->setGeometry(QRectF(view->geometry().topLeft(), s));
             }
         }
     }

The size of the viewport depends on the size of the content. If the content is smaller than the viewport, it need to be expanded to the viewport's size (otherwise the scene's background would show through).

In order to get the kinetic scrolling to work, all pure virtual methods need to be implemented:

     QSize viewportSize() const
     {
         return size().toSize();
     }

     QPoint maximumScrollPosition() const
     {
         QSizeF s = m_widget ? m_widget->size() - size() : QSize(0, 0);
         return QPoint(qMax(0, int(s.width())), qMax(0, int(s.height())));
     }

     QPoint scrollPosition() const
     {
         return m_widget ? -m_widget->pos().toPoint() + m_overShoot : QPoint();
     }

     void setScrollPosition(const QPoint &p, const QPoint &overShoot)
     {
         m_overShoot = overShoot;
         if (m_widget)
             m_widget->setPos(-p + m_overShoot);
     }

viewportSize() returns the size of the viewport. Please note that QAbstractKineticScroller works with integer coordinates only.

maximumScrollPosition() returns the point, where normal scrolling ends (and overshoot would start). The minimumScrollPosition is always assumed to be (0,0).

scrollPosition() returns the current position (excluding overshoot).

setScrollPosition() actually moves the content within the viewport. This is also the place where overshooting is handled.

To actually enable the kinetic scroller, it needs to receive mouse events. These come from the sceneEventFilter() function:

     bool sceneEventFilter(QGraphicsItem *i, QEvent *e)
     {
         bool res = false;
         if (i && (i == m_widget) && m_widget->isEnabled()) {
             switch (e->type()) {
             case QEvent::GraphicsSceneMousePress:
             case QEvent::GraphicsSceneMouseMove:
             case QEvent::GraphicsSceneMouseRelease:
             case QEvent::GraphicsSceneMouseDoubleClick: {
                 res = handleMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
                 break;
             }
             default:
                 break;
             }
         }
         // prevent text selection and image dragging
         if (e->type() == QEvent::GraphicsSceneMouseMove)
             return true;
         return res ? true : QGraphicsWidget::sceneEventFilter(i, e);
     }

Important Hint

         // prevent text selection and image dragging
         if (e->type() == QEvent::GraphicsSceneMouseMove)
             return true;
         return res ? true : QGraphicsWidget::sceneEventFilter(i, e);

To prevent the QGraphicsWebView from making ugly text selections while scrolling, all events of type of GraphicsSceneMouseMove need to be filtered out (The downside is that this example does not allow text selection at all).

GraphicsView

To make the QGraphicsWebView look native, some things have to be configured in QGraphicsView:

     GraphicsView()
         : QGraphicsView(new QGraphicsScene()), viewport(0)
     {
         setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
         setOptimizationFlags(QGraphicsView::DontSavePainterState);

         setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
         setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

         setFrameShape(QFrame::NoFrame);
         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

         viewport = new ViewportItem();
         scene()->addItem(viewport);
     }

Next up is creating a ViewportItem and adding it to the scene.

The Maemo 5 window manager will automatically maximize windows when shown, so the ViewportItem needs to resize together with the QGraphicsView in its resizeEvent():

     void resizeEvent(QResizeEvent *e)
     {
         QGraphicsView::resizeEvent(e);
         setUpdatesEnabled(false);

         if (!viewport)
             return;

         QRectF rect(QPointF(0, 0), size());
         scene()->setSceneRect(rect);

         viewport->setGeometry(rect);
         setUpdatesEnabled(true);
         update();
     }

Using the GraphicsView

In order to call this example actaully a browser, the standard back and reload buttons, as well as an URL entry widget needs to be added:

     GraphicsView *gv = new GraphicsView();
     view = new QGraphicsWebView();
     gv->viewportItem()->setWidget(view);

MainWindow

 class MainWindow : public QMainWindow
 {
     Q_OBJECT

 public:
     MainWindow();

 protected slots:

     void adjustLocation();
     void changeLocation();
     void adjustTitle();
     void setProgress(int p);
     void finishLoading(bool);

 private:
     void addToolBarAction(QToolBar *toolBar, QWebPage::WebAction webaction, const char *iconname);

 private:
     QGraphicsWebView *view;
     QLineEdit *locationEdit;
 };
 MainWindow::MainWindow()
 {
     QNetworkProxyFactory::setUseSystemConfiguration(true);

     GraphicsView *gv = new GraphicsView();
     view = new QGraphicsWebView();
     gv->viewportItem()->setWidget(view);

     view->setPalette(QApplication::palette("QWebView"));
     view->page()->setPalette(QApplication::palette("QWebView"));
     view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);

     connect(view, SIGNAL(loadFinished(bool)), SLOT(adjustLocation()));
     connect(view, SIGNAL(titleChanged(QString)), SLOT(adjustTitle()));
     connect(view, SIGNAL(loadProgress(int)), SLOT(setProgress(int)));
     connect(view, SIGNAL(loadFinished(bool)), SLOT(finishLoading(bool)));

     locationEdit = new QLineEdit(this);
     locationEdit->setInputMethodHints(Qt::ImhUrlCharactersOnly | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase | Qt::ImhPreferLowercase);
     locationEdit->setSizePolicy(QSizePolicy::Expanding, locationEdit->sizePolicy().verticalPolicy());
     connect(locationEdit, SIGNAL(returnPressed()), SLOT(changeLocation()));

     QToolBar *toolBar = addToolBar(tr("Navigation"));
     addToolBarAction(toolBar, QWebPage::Back, "general_back");
     addToolBarAction(toolBar, QWebPage::Forward, "general_forward");
     addToolBarAction(toolBar, QWebPage::Reload, "general_refresh");
     addToolBarAction(toolBar, QWebPage::Stop, "general_stop");
     toolBar->addWidget(locationEdit);

     setCentralWidget(gv);

     QString url = QApplication::arguments().value(1);
     if (url.isEmpty())
         url = QLatin1String("www.google.com");
     locationEdit->setText(url);
     changeLocation();
 }

 void MainWindow::addToolBarAction(QToolBar *toolBar, QWebPage::WebAction webaction, const char *iconname)
 {
     if (QAction *a = view->pageAction(webaction)) {
         a->setIcon(QIcon::fromTheme(iconname));
         toolBar->addAction(a);
     }
 }

 void MainWindow::adjustLocation()
 {
     locationEdit->setText(view->url().toString());
 }

 void MainWindow::changeLocation()
 {
     view->load(QUrl::fromUserInput(locationEdit->text()));
     view->setFocus();
 }

 void MainWindow::adjustTitle()
 {
     setWindowTitle(view->title());
 }

 void MainWindow::setProgress(int p)
 {
     setAttribute(Qt::WA_Maemo5ShowProgressIndicator, (p > 0 && p < 100));
 }

 void MainWindow::finishLoading(bool)
 {
     setProgress(100);
 }
X

Thank you for giving your feedback.

Make sure it is related to this specific page. For more general bugs and requests, please use the Qt Bug Tracker.

[0]; s.parentNode.insertBefore(ga, s); })();