From 53c8f61bdd5349b44bbca32b71b814da5b3077d9 Mon Sep 17 00:00:00 2001 From: Ariya Hidayat Date: Tue, 28 Jul 2009 16:23:18 +0200 Subject: New example: Maps with a magnifying glass. --- mapzoom/mapzoom.cpp | 291 ++++++++++++++++++++++++++++++++++++++++++++++++++++ mapzoom/mapzoom.pro | 3 + 2 files changed, 294 insertions(+) create mode 100644 mapzoom/mapzoom.cpp create mode 100644 mapzoom/mapzoom.pro (limited to 'mapzoom') diff --git a/mapzoom/mapzoom.cpp b/mapzoom/mapzoom.cpp new file mode 100644 index 0000000..4bc19da --- /dev/null +++ b/mapzoom/mapzoom.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Graphics Dojo project on Qt Labs. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 or 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include +#include +#include + +#if QT_VERSION < 0x0040500 +#error You need Qt 4.5 or newer +#endif + +// how long (milliseconds) the user need to hold (after a tap on the screen) +// before triggering the magnifying glass feature +// 701, a prime number, is the sum of 229, 233, 239 +// (all three are also prime numbers, consecutive!) +#define HOLD_TIME 701 + +// maximum size of the magnifier +// Hint: see above to find why I picked this one :) +#define MAX_MAGNIFIER 229 + +#define MAP_HTML "" \ + "" \ + "" \ + "
" \ + "" + +class GMaps: public QWebView +{ + Q_OBJECT + +private: + bool pressed; + bool snapped; + QPoint pressPos; + QPoint dragPos; + QBasicTimer tapTimer; + bool zoomed; + QWebPage *zoomPage; + QPixmap zoomPixmap; + QPixmap maskPixmap; + +public: + GMaps(QWidget *parent = 0) + : QWebView(parent) + , pressed(false) + , snapped(false) + , zoomed(false) + { + zoomPage = new QWebPage(this); + connect(zoomPage, SIGNAL(repaintRequested(QRect)), SLOT(update())); + + QString content = MAP_HTML; + QWebFrame *frame = page()->mainFrame(); + frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); + frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); + frame->setHtml(content); + + frame = zoomPage->mainFrame(); + frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); + frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); + content.replace("zoom: 15", "zoom: 16"); + frame->setHtml(content); + + QTimer::singleShot(1000, this, SLOT(triggerLoading())); + } + + void setCenter(qreal latitude, qreal longitude) { + QString code = "map.set_center(new google.maps.LatLng(%1, %2));"; + QWebFrame *frame = page()->mainFrame(); + frame->evaluateJavaScript(code.arg(latitude).arg(longitude)); + frame = zoomPage->mainFrame(); + frame->evaluateJavaScript(code.arg(latitude).arg(longitude)); + } + +private slots: + + void triggerLoading() { + QString code = "init(59.9138204, 10.7387413)"; + QWebFrame *frame = page()->mainFrame(); + frame->evaluateJavaScript(code); + frame = zoomPage->mainFrame(); + frame->evaluateJavaScript(code); + } + +protected: + + void timerEvent(QTimerEvent *event) { + QWebView::timerEvent(event); + if (!zoomed) { + zoomed = true; + tapTimer.stop(); + zoomPage->setViewportSize(size() * 2); + QWebFrame *frame = page()->mainFrame(); + qreal lat = frame->evaluateJavaScript("map.get_center().lat()").toDouble(); + qreal lng = frame->evaluateJavaScript("map.get_center().lng()").toDouble(); + setCenter(lat, lng); + } + update(); + } + + void mousePressEvent(QMouseEvent *event) { + pressed = snapped = true; + pressPos = dragPos = event->pos(); + tapTimer.stop(); + tapTimer.start(HOLD_TIME, this); + QWebView::mousePressEvent(event); + } + + void mouseReleaseEvent(QMouseEvent *event) { + pressed = snapped = false; + tapTimer.stop(); + if (!zoomed) { + QWebView::mouseReleaseEvent(event); + } else { + zoomed = false; + event->accept(); + update(); + // fake at the press position + QMouseEvent *ev = new QMouseEvent(QEvent::MouseButtonRelease, + pressPos, Qt::LeftButton, + Qt::LeftButton, Qt::NoModifier); + QWebView::mouseReleaseEvent(ev); + delete ev; + } + } + + void mouseMoveEvent(QMouseEvent *event) { + if (!zoomed) { + if (!pressed || !snapped) { + QWebView::mouseMoveEvent(event); + return; + } else { + const int threshold = 40; + QPoint delta = event->pos() - pressPos; + if (snapped) { + snapped &= delta.x() < threshold; + snapped &= delta.y() < threshold; + snapped &= delta.x() > -threshold; + snapped &= delta.y() > -threshold; + } + if (!snapped) + tapTimer.stop(); + } + } else { + dragPos = event->pos(); + update(); + } + } + + void paintEvent(QPaintEvent *event) { + QWebView::paintEvent(event); + if (zoomed) { + + int dim = qMin(width(), height()); + int magnifierSize = qMin(MAX_MAGNIFIER, dim * 2 / 3); + int radius = magnifierSize / 2; + int ring = radius - 15; + QSize box = QSize(magnifierSize, magnifierSize); + + // reupdate our mask + if (maskPixmap.size() != box) { + maskPixmap = QPixmap(box); + maskPixmap.fill(Qt::transparent); + + QRadialGradient g; + g.setCenter(radius, radius); + g.setFocalPoint(radius, radius); + g.setRadius(radius); + g.setColorAt(1.0, QColor(255, 255, 255, 0)); + g.setColorAt(0.5, QColor(128, 128, 128, 255)); + + QPainter mask(&maskPixmap); + mask.setRenderHint(QPainter::Antialiasing); + mask.setCompositionMode(QPainter::CompositionMode_Source); + mask.setBrush(g); + mask.setPen(Qt::NoPen); + mask.drawRect(maskPixmap.rect()); + mask.setBrush(QColor(Qt::transparent)); + mask.drawEllipse(g.center(), ring, ring); + mask.end(); + } + + QPoint center = dragPos - QPoint(0, radius); + center = center + QPoint(0, radius / 2); + QPoint corner = center - QPoint(radius, radius); + QPoint xy = center * 2 - QPoint(radius, radius); + + if (zoomPixmap.size() != box) { + zoomPixmap = QPixmap(box); + zoomPixmap.fill(Qt::lightGray); + } + + if (true) { + QPainter p(&zoomPixmap); + p.translate(-xy); + QWebFrame *frame = zoomPage->mainFrame(); + frame->render(&p, QRect(xy, box)); + p.end(); + } + + QPainterPath clipPath; + clipPath.addEllipse(center, ring, ring); + + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + p.setClipPath(clipPath); + p.drawPixmap(corner, zoomPixmap); + p.setClipping(false); + p.drawPixmap(corner, maskPixmap); + p.setPen(Qt::gray); + p.drawPath(clipPath); + p.end(); + } + } +}; + +class MapZoom : public QMainWindow +{ + Q_OBJECT + +private: + GMaps *map; + +public: + MapZoom(): QMainWindow(0) { + map = new GMaps(this); + setCentralWidget(map); + QMenu *menu = menuBar()->addMenu("&Place"); + menu->addAction("&Oslo", this, SLOT(chooseOslo())); + menu->addAction("&Berlin", this, SLOT(chooseBerlin())); + menu->addAction("&Jakarta", this, SLOT(chooseJakarta())); + } + +private slots: + void chooseOslo() { + map->setCenter(59.9138204, 10.7387413); + } + + void chooseBerlin() { + map->setCenter(52.5056819, 13.3232027); + } + + void chooseJakarta() { + map->setCenter(-6.211544, 106.845172); + } +}; + +#include "mapzoom.moc" + +int main(int argc, char **argv) +{ +#if defined(Q_WS_X11) + QApplication::setGraphicsSystem("raster"); +#endif + + QApplication app(argc, argv); + + MapZoom w; + w.setWindowTitle("Maps (powered by Google)"); + w.resize(600, 450); + w.show(); + + return app.exec(); +} diff --git a/mapzoom/mapzoom.pro b/mapzoom/mapzoom.pro new file mode 100644 index 0000000..1362333 --- /dev/null +++ b/mapzoom/mapzoom.pro @@ -0,0 +1,3 @@ +TEMPLATE = app +SOURCES = mapzoom.cpp +QT += network webkit -- cgit v1.2.3