forked from qt-creator/qt-creator
Added (optional) touch optimized navigation of HTML5 apps
Code provided by the Bostoner Browser team. The HTML5 wizard shows a checkbox "Enable touch optimized navigation" which enables flicking and enlarged click areas. By default, the option is turned off. Task-Number: QTCREATORBUG-3284
This commit is contained in:
@@ -18,6 +18,890 @@
|
||||
#include <QtWebKit/QGraphicsWebView>
|
||||
#include <QtWebKit/QWebFrame>
|
||||
|
||||
#ifdef TOUCH_OPTIMIZED_NAVIGATION
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QGraphicsSceneMouseEvent>
|
||||
#include <QtWebKit/QWebElement>
|
||||
|
||||
const int KCumlativeDistanceThreshold = 40;
|
||||
const int KDecelerationCount = 4;
|
||||
const int KDecelerationTimerSpeed = 10;
|
||||
const int KFlickScrollThreshold = 400;
|
||||
const int KJitterBufferThreshold = 200;
|
||||
const qreal KDecelerationSlowdownFactor = 0.975;
|
||||
|
||||
const int KTouchDownStartTime = 200;
|
||||
const int KHoverTimeoutThreshold = 100;
|
||||
const int KNodeSearchThreshold = 400;
|
||||
|
||||
class WebTouchPhysicsInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WebTouchPhysicsInterface(QObject *parent = 0);
|
||||
virtual ~WebTouchPhysicsInterface();
|
||||
static WebTouchPhysicsInterface* getSingleton();
|
||||
|
||||
virtual bool inMotion() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void start(const QPointF &pressPoint, const QWebFrame *frame) = 0;
|
||||
virtual bool move(const QPointF &pressPoint) = 0;
|
||||
virtual bool release(const QPointF &pressPoint) = 0;
|
||||
|
||||
signals:
|
||||
void positionChanged(const QPointF &point, const QPoint &startPressPoint);
|
||||
|
||||
public slots:
|
||||
virtual void setRange(const QRectF &range) = 0;
|
||||
|
||||
private:
|
||||
static WebTouchPhysicsInterface* s_instance;
|
||||
};
|
||||
|
||||
WebTouchPhysicsInterface* WebTouchPhysicsInterface::s_instance = 0;
|
||||
|
||||
WebTouchPhysicsInterface::WebTouchPhysicsInterface(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
WebTouchPhysicsInterface::~WebTouchPhysicsInterface()
|
||||
{
|
||||
if (s_instance == this)
|
||||
s_instance = 0;
|
||||
}
|
||||
|
||||
class WebTouchPhysics : public WebTouchPhysicsInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WebTouchPhysics(QObject *parent = 0);
|
||||
virtual ~WebTouchPhysics();
|
||||
|
||||
virtual bool inMotion();
|
||||
virtual void stop();
|
||||
virtual void start(const QPointF &pressPoint, const QWebFrame *frame);
|
||||
virtual bool move(const QPointF &pressPoint);
|
||||
virtual bool release(const QPointF &pressPoint);
|
||||
|
||||
signals:
|
||||
void setRange(const QRectF &range);
|
||||
|
||||
public slots:
|
||||
void decelerationTimerFired();
|
||||
void changePosition(const QPointF &point);
|
||||
bool isFlick(const QPointF &point, int distance) const;
|
||||
bool isPan(const QPointF &point, int distance) const;
|
||||
|
||||
private:
|
||||
QPointF m_previousPoint;
|
||||
QPoint m_startPressPoint;
|
||||
QPointF m_decelerationSpeed;
|
||||
QList<QPointF> m_decelerationPoints;
|
||||
QTimer m_decelerationTimer;
|
||||
QPointF m_cumlativeDistance;
|
||||
const QWebFrame* m_frame;
|
||||
bool m_inMotion;
|
||||
};
|
||||
|
||||
WebTouchPhysicsInterface* WebTouchPhysicsInterface::getSingleton()
|
||||
{
|
||||
if (!s_instance)
|
||||
s_instance = new WebTouchPhysics;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
int fastDistance(const QPoint &p, const QPoint &q)
|
||||
{
|
||||
return (p.x() - q.x()) * (p.x() - q.x()) + (p.y() - q.y()) * (p.y() - q.y());
|
||||
}
|
||||
|
||||
WebTouchPhysics::WebTouchPhysics(QObject *parent)
|
||||
: WebTouchPhysicsInterface(parent)
|
||||
, m_frame(0)
|
||||
, m_inMotion(false)
|
||||
{
|
||||
connect(&m_decelerationTimer, SIGNAL(timeout()), this, SLOT(decelerationTimerFired()));
|
||||
}
|
||||
|
||||
WebTouchPhysics::~WebTouchPhysics()
|
||||
{
|
||||
}
|
||||
|
||||
bool WebTouchPhysics::inMotion()
|
||||
{
|
||||
return m_inMotion;
|
||||
}
|
||||
|
||||
void WebTouchPhysics::stop()
|
||||
{
|
||||
m_decelerationTimer.stop();
|
||||
m_cumlativeDistance = QPoint();
|
||||
m_previousPoint = QPoint();
|
||||
m_startPressPoint = QPoint();
|
||||
m_decelerationPoints.clear();
|
||||
m_inMotion = false;
|
||||
}
|
||||
|
||||
void WebTouchPhysics::start(const QPointF &pressPoint, const QWebFrame *frame)
|
||||
{
|
||||
if (!frame)
|
||||
return;
|
||||
|
||||
m_frame = frame;
|
||||
m_decelerationPoints.push_front(pressPoint);
|
||||
m_decelerationTimer.setSingleShot(false);
|
||||
m_decelerationTimer.setInterval(KDecelerationTimerSpeed);
|
||||
m_previousPoint = pressPoint;
|
||||
m_startPressPoint = pressPoint.toPoint();
|
||||
}
|
||||
|
||||
bool WebTouchPhysics::move(const QPointF &pressPoint)
|
||||
{
|
||||
int distance = fastDistance(pressPoint.toPoint(), m_startPressPoint);
|
||||
if (isFlick(pressPoint, distance) || isPan(pressPoint, distance)) {
|
||||
changePosition(pressPoint);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebTouchPhysics::release(const QPointF &pressPoint)
|
||||
{
|
||||
// use the cumulative distance in this case because it seems like a positive user experience
|
||||
if (m_cumlativeDistance.manhattanLength() > KCumlativeDistanceThreshold) {
|
||||
m_decelerationSpeed = (m_decelerationPoints.back() - pressPoint) / (m_decelerationPoints.count() + 1);
|
||||
m_decelerationTimer.start();
|
||||
return true;
|
||||
} else {
|
||||
m_inMotion = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void WebTouchPhysics::changePosition(const QPointF &point)
|
||||
{
|
||||
m_inMotion = true;
|
||||
QPointF diff = m_previousPoint - point;
|
||||
emit positionChanged(diff, m_startPressPoint);
|
||||
|
||||
m_cumlativeDistance += (point - m_previousPoint);
|
||||
m_previousPoint = point;
|
||||
m_decelerationPoints.push_front(point);
|
||||
if (m_decelerationPoints.count() > KDecelerationCount)
|
||||
m_decelerationPoints.pop_back();
|
||||
}
|
||||
|
||||
void WebTouchPhysics::decelerationTimerFired()
|
||||
{
|
||||
if (!m_frame) {
|
||||
m_decelerationTimer.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (qAbs(m_decelerationSpeed.x()) < KDecelerationSlowdownFactor
|
||||
&& qAbs(m_decelerationSpeed.y()) < KDecelerationSlowdownFactor) {
|
||||
m_inMotion = false;
|
||||
m_decelerationTimer.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
m_decelerationSpeed *= KDecelerationSlowdownFactor;
|
||||
emit positionChanged(QPoint(m_decelerationSpeed.x(), m_decelerationSpeed.y()), m_startPressPoint);
|
||||
}
|
||||
|
||||
bool WebTouchPhysics::isFlick(const QPointF &pressPoint, int distance) const
|
||||
{
|
||||
Q_UNUSED(pressPoint)
|
||||
// this mouse move event is far away from the touch down position in
|
||||
// less than xx sec, the user must want to scroll quickly.
|
||||
return !m_inMotion && distance > KFlickScrollThreshold;
|
||||
}
|
||||
|
||||
bool WebTouchPhysics::isPan(const QPointF &pressPoint, int distance) const
|
||||
{
|
||||
Q_UNUSED(pressPoint)
|
||||
return distance > KJitterBufferThreshold;
|
||||
}
|
||||
|
||||
class WebTouchEvent
|
||||
{
|
||||
public:
|
||||
QPoint m_pos;
|
||||
QEvent::Type m_type;
|
||||
Qt::MouseButton m_button;
|
||||
Qt::MouseButtons m_buttons;
|
||||
bool m_graphicsSceneEvent;
|
||||
bool m_fired;
|
||||
bool m_editable;
|
||||
Qt::KeyboardModifiers m_modifier;
|
||||
|
||||
//Graphics scene event members
|
||||
QPointF m_scenePos;
|
||||
QPoint m_screenPos;
|
||||
QPointF m_buttonDownPos;
|
||||
QPointF m_buttonDownScenePos;
|
||||
QPoint m_buttonDownScreenPos;
|
||||
QPointF m_lastPos;
|
||||
QPointF m_lastScenePos;
|
||||
QPoint m_lastScreenPos;
|
||||
|
||||
WebTouchEvent()
|
||||
: m_type(QEvent::None)
|
||||
, m_button(Qt::NoButton)
|
||||
, m_buttons(Qt::NoButton)
|
||||
, m_graphicsSceneEvent(false)
|
||||
, m_fired(false)
|
||||
, m_editable(false)
|
||||
, m_modifier(Qt::NoModifier)
|
||||
{}
|
||||
|
||||
WebTouchEvent(const QMouseEvent *mouseEvent)
|
||||
{
|
||||
Q_ASSERT(mouseEvent != 0);
|
||||
m_type = mouseEvent->type();
|
||||
m_pos = mouseEvent->pos();
|
||||
m_button = mouseEvent->button();
|
||||
m_buttons = mouseEvent->buttons();
|
||||
m_modifier = mouseEvent->modifiers();
|
||||
m_fired = false;
|
||||
m_editable = false;
|
||||
m_graphicsSceneEvent = false;
|
||||
}
|
||||
|
||||
WebTouchEvent(const QGraphicsSceneMouseEvent *graphicsSceneMouseEvent)
|
||||
{
|
||||
Q_ASSERT(graphicsSceneMouseEvent != 0);
|
||||
m_type = graphicsSceneMouseEvent->type();
|
||||
m_pos = graphicsSceneMouseEvent->pos().toPoint();
|
||||
m_button = graphicsSceneMouseEvent->button();
|
||||
m_buttons = graphicsSceneMouseEvent->buttons();
|
||||
m_modifier = graphicsSceneMouseEvent->modifiers();
|
||||
m_scenePos = graphicsSceneMouseEvent->scenePos();
|
||||
m_screenPos = graphicsSceneMouseEvent->screenPos();
|
||||
m_buttonDownPos = graphicsSceneMouseEvent->buttonDownPos(graphicsSceneMouseEvent->button());
|
||||
m_buttonDownScenePos = graphicsSceneMouseEvent->buttonDownScenePos(graphicsSceneMouseEvent->button());
|
||||
m_buttonDownScreenPos = graphicsSceneMouseEvent->buttonDownScreenPos(graphicsSceneMouseEvent->button());
|
||||
m_lastPos = graphicsSceneMouseEvent->lastPos();
|
||||
m_lastScenePos = graphicsSceneMouseEvent->lastScenePos();
|
||||
m_lastScreenPos = graphicsSceneMouseEvent->lastScreenPos();
|
||||
m_fired = false;
|
||||
m_editable = false;
|
||||
m_graphicsSceneEvent = true;
|
||||
}
|
||||
};
|
||||
|
||||
class WebTouchScroller : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WebTouchScroller(QObject *parent = 0);
|
||||
virtual ~WebTouchScroller();
|
||||
|
||||
signals:
|
||||
void rangeChanged(const QRectF &range);
|
||||
|
||||
public slots:
|
||||
void setFrame(QWebFrame* frame);
|
||||
void scroll(const QPointF &delta, const QPoint &scrollStartPoint);
|
||||
|
||||
private:
|
||||
QWebFrame* m_webFrame;
|
||||
QSize m_range;
|
||||
};
|
||||
|
||||
void qtwebkit_webframe_scrollRecursively(QWebFrame* qFrame, int dx, int dy, const QPoint &pos)
|
||||
{
|
||||
Q_UNUSED(pos)
|
||||
|
||||
if (!qFrame)
|
||||
return;
|
||||
|
||||
bool scrollHorizontal = false;
|
||||
bool scrollVertical = false;
|
||||
|
||||
do {
|
||||
if (qFrame->scrollBarPolicy(Qt::Horizontal) == Qt::ScrollBarAlwaysOn) {
|
||||
if (dx > 0) // scroll right
|
||||
scrollHorizontal = qFrame->scrollBarValue(Qt::Horizontal) < qFrame->scrollBarMaximum(Qt::Horizontal);
|
||||
else if (dx < 0) // scroll left
|
||||
scrollHorizontal = qFrame->scrollBarValue(Qt::Horizontal) > qFrame->scrollBarMinimum(Qt::Horizontal);
|
||||
} else {
|
||||
scrollHorizontal = true;
|
||||
}
|
||||
|
||||
if (qFrame->scrollBarPolicy(Qt::Vertical) == Qt::ScrollBarAlwaysOn) {
|
||||
if (dy > 0) // scroll down
|
||||
scrollVertical = qFrame->scrollBarValue(Qt::Vertical) < qFrame->scrollBarMaximum(Qt::Vertical);
|
||||
else if (dy < 0) //scroll up
|
||||
scrollVertical = qFrame->scrollBarValue(Qt::Vertical) > qFrame->scrollBarMinimum(Qt::Vertical);
|
||||
} else {
|
||||
scrollVertical = true;
|
||||
}
|
||||
|
||||
if (scrollHorizontal || scrollVertical) {
|
||||
qFrame->scroll(dx, dy);
|
||||
return;
|
||||
}
|
||||
|
||||
qFrame = qFrame->parentFrame();
|
||||
} while (qFrame);
|
||||
}
|
||||
|
||||
WebTouchScroller::WebTouchScroller(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_webFrame(0)
|
||||
{
|
||||
}
|
||||
|
||||
WebTouchScroller::~WebTouchScroller()
|
||||
{
|
||||
}
|
||||
|
||||
void WebTouchScroller::setFrame(QWebFrame* frame)
|
||||
{
|
||||
m_webFrame = frame;
|
||||
}
|
||||
|
||||
void WebTouchScroller::scroll(const QPointF &delta, const QPoint &scrollStartPoint)
|
||||
{
|
||||
if (!m_webFrame)
|
||||
return;
|
||||
|
||||
qtwebkit_webframe_scrollRecursively(m_webFrame, delta.x(), delta.y(),
|
||||
scrollStartPoint - m_webFrame->scrollPosition());
|
||||
}
|
||||
|
||||
class WebTouchNavigation : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WebTouchNavigation(QObject *object, QWebPage *webPage);
|
||||
virtual ~WebTouchNavigation();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event);
|
||||
void handleDownEvent(const WebTouchEvent &event);
|
||||
void handleMoveEvent(const WebTouchEvent &event);
|
||||
void handleReleaseEvent(const WebTouchEvent &event);
|
||||
|
||||
public slots:
|
||||
void hoverTimerFired();
|
||||
void downTimerFired();
|
||||
void quickDownTimerFired();
|
||||
void quickUpTimerFired();
|
||||
|
||||
private:
|
||||
void invalidateLastTouchDown();
|
||||
void generateMouseEvent(const WebTouchEvent &touchEvent);
|
||||
|
||||
private:
|
||||
WebTouchPhysicsInterface* m_physics;
|
||||
WebTouchScroller* m_scroller;
|
||||
WebTouchEvent m_touchDown;
|
||||
QObject* m_viewObject;
|
||||
QWebPage* m_webPage;
|
||||
QWebFrame* m_webFrame;
|
||||
QTimer m_downTimer;
|
||||
QTimer m_hoverTimer;
|
||||
QTimer m_quickDownTimer;
|
||||
QTimer m_quickUpTimer;
|
||||
bool m_suppressMouseEvents;
|
||||
};
|
||||
|
||||
int xInRect(const QRect &r, int x)
|
||||
{
|
||||
int x1 = qMin(x, r.right()-2);
|
||||
return qMax(x1, r.left()+2);
|
||||
}
|
||||
|
||||
int yInRect(const QRect &r, int y)
|
||||
{
|
||||
int y1 = qMin(y, r.bottom()-2);
|
||||
return qMax(y1, r.top()+2);
|
||||
}
|
||||
|
||||
int wtnFastDistance(const QPoint &p, const QPoint &q)
|
||||
{
|
||||
return (p.x() - q.x()) * (p.x() - q.x()) + (p.y() - q.y()) * (p.y() - q.y());
|
||||
}
|
||||
|
||||
QPoint frameViewPosition(QWebFrame *frame)
|
||||
{
|
||||
QPoint fp;
|
||||
QWebFrame* f = frame;
|
||||
while (1) {
|
||||
fp += f->pos();
|
||||
f = f->parentFrame();
|
||||
if (f) {
|
||||
fp -= f->scrollPosition();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
QPoint closestElement(QObject *viewObject, QWebFrame *frame, WebTouchEvent &touchEvent, int searchThreshold)
|
||||
{
|
||||
Q_UNUSED(viewObject)
|
||||
QPoint adjustedPoint(touchEvent.m_pos);
|
||||
|
||||
QWebHitTestResult htr = frame->hitTestContent(adjustedPoint);
|
||||
|
||||
if (htr.isContentEditable()) {
|
||||
QString type = htr.element().attribute("type").toLower();
|
||||
// Don't summon the vkb for a hidden input element.
|
||||
// This should never happen anyway.
|
||||
if (type == "hidden") {
|
||||
touchEvent.m_editable = false;
|
||||
return adjustedPoint;
|
||||
}
|
||||
|
||||
// Don't summon the vkb for a disabled input; in fact, close the vkbd.
|
||||
QString disabled = htr.element().attribute("disabled").toLower();
|
||||
if (disabled == "disabled" || disabled == "true") {
|
||||
touchEvent.m_editable = false;
|
||||
return adjustedPoint;
|
||||
}
|
||||
|
||||
// QtWebKit EditorClientQt already enables these input hints. Thus, this should only be
|
||||
// enabled if QtWebKit happens to be old. Qt::ImhNoPredictiveText is only enalbed for Symbian / Maemo on WebKit side.
|
||||
|
||||
|
||||
touchEvent.m_editable = true;
|
||||
return adjustedPoint;
|
||||
}
|
||||
|
||||
if (!htr.element().tagName().toLower().compare("select") && htr.element().hasAttribute("multiple")) {
|
||||
touchEvent.m_modifier = Qt::ControlModifier;
|
||||
return adjustedPoint;
|
||||
}
|
||||
|
||||
// If clicked element need receive event, do NOT adjust
|
||||
// like : achor, input, map, button, textarea
|
||||
QString tagName = htr.element().tagName().toLower();
|
||||
if (!htr.linkElement().isNull() ||
|
||||
!tagName.compare("input") ||
|
||||
!tagName.compare("map") ||
|
||||
!tagName.compare("button") ||
|
||||
!tagName.compare("textarea") ||
|
||||
htr.element().hasAttribute("onClick"))
|
||||
return adjustedPoint;
|
||||
|
||||
// Attempt to find the closest radio button or checkbox. Their areas can be so
|
||||
// small that we need to adjust the our position to be exactly on them if in the vicinity.
|
||||
QString selector = "input[type=\"radio\"], input[type=\"checkbox\"]";
|
||||
QWebElementCollection elementList = frame->findAllElements(selector);
|
||||
QWebElementCollection::iterator it(elementList.begin());
|
||||
|
||||
// find the origin of current frame position in view based coordinate
|
||||
QPoint originPoint = frameViewPosition(frame);
|
||||
|
||||
// transfer the event position from view based coordinate to the current frame's content based coordinate
|
||||
QPoint frameScrollPoint = frame->scrollPosition();
|
||||
QPoint framePoint = adjustedPoint - originPoint + frameScrollPoint;
|
||||
|
||||
QPoint adjustedFramePoint = framePoint;
|
||||
|
||||
// find the closest element
|
||||
int maxDist = searchThreshold;
|
||||
while (it != elementList.end()) {
|
||||
QRect nRect((*it).geometry());
|
||||
if (nRect.isValid()) {
|
||||
QPoint pt(xInRect(nRect, framePoint.x()), yInRect(nRect, framePoint.y()));
|
||||
int dist = wtnFastDistance(pt, framePoint);
|
||||
if (dist < maxDist) {
|
||||
adjustedFramePoint = pt;
|
||||
maxDist = dist;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
// transfer the adjusted position from the current frame's content based coordinate to view based coordinate
|
||||
adjustedPoint = adjustedFramePoint - frameScrollPoint + originPoint;
|
||||
|
||||
return adjustedPoint;
|
||||
}
|
||||
|
||||
WebTouchNavigation::WebTouchNavigation(QObject *parent, QWebPage *webPage)
|
||||
: m_viewObject(parent)
|
||||
, m_webPage(webPage)
|
||||
, m_suppressMouseEvents(false)
|
||||
|
||||
{
|
||||
Q_ASSERT(m_webPage);
|
||||
connect(&m_downTimer, SIGNAL(timeout()), this, SLOT(downTimerFired()));
|
||||
connect(&m_hoverTimer, SIGNAL(timeout()), this, SLOT(hoverTimerFired()));
|
||||
connect(&m_quickDownTimer, SIGNAL(timeout()), this, SLOT(quickDownTimerFired()));
|
||||
connect(&m_quickUpTimer, SIGNAL(timeout()), this, SLOT(quickUpTimerFired()));
|
||||
|
||||
m_physics = WebTouchPhysicsInterface::getSingleton();
|
||||
m_physics->setParent(this);
|
||||
m_scroller = new WebTouchScroller(this);
|
||||
|
||||
connect(m_physics, SIGNAL(positionChanged(QPointF, QPoint)), m_scroller, SLOT(scroll(QPointF, QPoint)));
|
||||
connect(m_scroller, SIGNAL(rangeChanged(QRectF)), m_physics, SLOT(setRange(QRectF)));
|
||||
}
|
||||
|
||||
WebTouchNavigation::~WebTouchNavigation()
|
||||
{
|
||||
}
|
||||
|
||||
bool WebTouchNavigation::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
if (object != m_viewObject)
|
||||
return false;
|
||||
|
||||
bool eventHandled = false;
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseButtonPress:
|
||||
if (static_cast<QMouseEvent*>(event)->buttons() & Qt::LeftButton) {
|
||||
WebTouchEvent e(static_cast<QMouseEvent*>(event));
|
||||
handleDownEvent(e);
|
||||
}
|
||||
eventHandled = true;
|
||||
break;
|
||||
case QEvent::MouseMove:
|
||||
if (static_cast<QMouseEvent*>(event)->buttons() & Qt::LeftButton) {
|
||||
WebTouchEvent e(static_cast<QMouseEvent*>(event));
|
||||
handleMoveEvent(e);
|
||||
}
|
||||
eventHandled = true;
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
{
|
||||
WebTouchEvent e(static_cast<QMouseEvent*>(event));
|
||||
handleReleaseEvent(e);
|
||||
eventHandled = true;
|
||||
}
|
||||
break;
|
||||
case QEvent::GraphicsSceneMousePress:
|
||||
if (static_cast<QGraphicsSceneMouseEvent*>(event)->buttons() & Qt::LeftButton) {
|
||||
WebTouchEvent e(static_cast<QGraphicsSceneMouseEvent*>(event));
|
||||
handleDownEvent(e);
|
||||
}
|
||||
eventHandled = true;
|
||||
break;
|
||||
case QEvent::GraphicsSceneMouseMove:
|
||||
if (static_cast<QGraphicsSceneMouseEvent *>(event)->buttons() & Qt::LeftButton) {
|
||||
WebTouchEvent e(static_cast<QGraphicsSceneMouseEvent*>(event));
|
||||
handleMoveEvent(e);
|
||||
}
|
||||
eventHandled = true;
|
||||
break;
|
||||
case QEvent::GraphicsSceneMouseRelease:
|
||||
{
|
||||
WebTouchEvent e(static_cast<QGraphicsSceneMouseEvent*>(event));
|
||||
handleReleaseEvent(e);
|
||||
eventHandled = true;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonDblClick:
|
||||
case QEvent::ContextMenu:
|
||||
case QEvent::CursorChange:
|
||||
case QEvent::DragEnter:
|
||||
case QEvent::DragLeave:
|
||||
case QEvent::DragMove:
|
||||
case QEvent::Drop:
|
||||
case QEvent::GrabMouse:
|
||||
case QEvent::GraphicsSceneContextMenu:
|
||||
case QEvent::GraphicsSceneDragEnter:
|
||||
case QEvent::GraphicsSceneDragLeave:
|
||||
case QEvent::GraphicsSceneDragMove:
|
||||
case QEvent::GraphicsSceneDrop:
|
||||
case QEvent::GraphicsSceneHelp:
|
||||
case QEvent::GraphicsSceneHoverEnter:
|
||||
case QEvent::GraphicsSceneHoverLeave:
|
||||
case QEvent::GraphicsSceneHoverMove:
|
||||
case QEvent::HoverEnter:
|
||||
case QEvent::HoverLeave:
|
||||
case QEvent::HoverMove:
|
||||
case QEvent::Gesture:
|
||||
case QEvent::GestureOverride:
|
||||
eventHandled = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return eventHandled;
|
||||
}
|
||||
|
||||
void WebTouchNavigation::quickDownTimerFired()
|
||||
{
|
||||
m_touchDown.m_type = (m_touchDown.m_graphicsSceneEvent) ? QEvent::GraphicsSceneMousePress : QEvent::MouseButtonPress;
|
||||
m_touchDown.m_pos = closestElement(m_viewObject, m_webFrame, m_touchDown, KNodeSearchThreshold);
|
||||
m_touchDown.m_button = Qt::LeftButton;
|
||||
m_touchDown.m_buttons = Qt::NoButton;
|
||||
generateMouseEvent(m_touchDown);
|
||||
m_quickUpTimer.setSingleShot(true);
|
||||
m_quickUpTimer.start(0);
|
||||
}
|
||||
|
||||
void WebTouchNavigation::quickUpTimerFired()
|
||||
{
|
||||
m_touchDown.m_type = (m_touchDown.m_graphicsSceneEvent) ? QEvent::GraphicsSceneMouseRelease : QEvent::MouseButtonRelease;
|
||||
m_touchDown.m_button = Qt::LeftButton;
|
||||
m_touchDown.m_buttons = Qt::NoButton;
|
||||
generateMouseEvent(m_touchDown);
|
||||
}
|
||||
|
||||
void WebTouchNavigation::downTimerFired()
|
||||
{
|
||||
m_touchDown.m_type = (m_touchDown.m_graphicsSceneEvent) ? QEvent::GraphicsSceneMousePress : QEvent::MouseButtonPress;
|
||||
m_touchDown.m_pos = closestElement(m_viewObject, m_webFrame, m_touchDown, KNodeSearchThreshold);
|
||||
m_touchDown.m_button = Qt::LeftButton;
|
||||
m_touchDown.m_buttons = Qt::NoButton;
|
||||
generateMouseEvent(m_touchDown);
|
||||
m_touchDown.m_fired = true;
|
||||
}
|
||||
|
||||
void WebTouchNavigation::hoverTimerFired()
|
||||
{
|
||||
m_touchDown.m_type = (m_touchDown.m_graphicsSceneEvent) ? QEvent::GraphicsSceneMouseMove : QEvent::MouseMove;
|
||||
m_touchDown.m_button = Qt::NoButton;
|
||||
m_touchDown.m_buttons = Qt::NoButton;
|
||||
generateMouseEvent(m_touchDown);
|
||||
}
|
||||
|
||||
void WebTouchNavigation::invalidateLastTouchDown()
|
||||
{
|
||||
if (m_touchDown.m_fired) {
|
||||
// send mouse up event invalidate click
|
||||
m_touchDown.m_type = (m_touchDown.m_graphicsSceneEvent) ? QEvent::GraphicsSceneMouseRelease : QEvent::MouseButtonRelease;
|
||||
m_touchDown.m_pos = QPoint(-1, -1);
|
||||
m_touchDown.m_button = Qt::LeftButton;
|
||||
m_touchDown.m_buttons = Qt::NoButton;
|
||||
m_touchDown.m_editable = false;
|
||||
generateMouseEvent(m_touchDown);
|
||||
}
|
||||
}
|
||||
|
||||
void WebTouchNavigation::handleDownEvent(const WebTouchEvent &event)
|
||||
{
|
||||
// Stop previously running physics
|
||||
m_physics->stop();
|
||||
|
||||
m_downTimer.stop();
|
||||
m_hoverTimer.stop();
|
||||
m_quickDownTimer.stop();
|
||||
m_quickUpTimer.stop();
|
||||
|
||||
m_webFrame = m_webPage->frameAt(event.m_pos);
|
||||
if (!m_webFrame)
|
||||
m_webFrame = m_webPage->currentFrame();
|
||||
|
||||
m_scroller->setFrame(m_webFrame);
|
||||
|
||||
m_touchDown = event;
|
||||
|
||||
m_hoverTimer.setSingleShot(true);
|
||||
m_hoverTimer.start(KHoverTimeoutThreshold);
|
||||
|
||||
m_downTimer.setSingleShot(true);
|
||||
m_downTimer.start(KTouchDownStartTime);
|
||||
|
||||
// Start physics again
|
||||
m_physics->start(event.m_pos, m_webFrame);
|
||||
}
|
||||
|
||||
void WebTouchNavigation::handleMoveEvent(const WebTouchEvent &event)
|
||||
{
|
||||
if (m_physics->move(event.m_pos)) {
|
||||
// don't send mouse down event.
|
||||
m_downTimer.stop();
|
||||
m_hoverTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void WebTouchNavigation::handleReleaseEvent(const WebTouchEvent &event)
|
||||
{
|
||||
if (!m_physics->inMotion() && (m_hoverTimer.isActive() || m_downTimer.isActive())) { // Quick tap
|
||||
if (m_hoverTimer.isActive()) {
|
||||
m_touchDown.m_type = (m_touchDown.m_graphicsSceneEvent) ? QEvent::GraphicsSceneMouseMove : QEvent::MouseMove;
|
||||
m_touchDown.m_button = Qt::NoButton;
|
||||
m_touchDown.m_buttons = Qt::NoButton;
|
||||
generateMouseEvent(m_touchDown);
|
||||
}
|
||||
|
||||
m_hoverTimer.stop();
|
||||
m_downTimer.stop();
|
||||
|
||||
m_quickDownTimer.setSingleShot(true);
|
||||
m_quickDownTimer.start(0);
|
||||
return;
|
||||
}
|
||||
|
||||
m_hoverTimer.stop();
|
||||
m_downTimer.stop();
|
||||
|
||||
if (m_physics->release(event.m_pos)) {
|
||||
// Going to kinetic motion. Thus, invalidating last touch down.
|
||||
invalidateLastTouchDown();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_touchDown.m_fired) {
|
||||
// send mouse up event
|
||||
m_touchDown.m_type = (m_touchDown.m_graphicsSceneEvent) ? QEvent::GraphicsSceneMouseRelease : QEvent::MouseButtonRelease;
|
||||
m_touchDown.m_button = Qt::LeftButton;
|
||||
m_touchDown.m_buttons = Qt::NoButton;
|
||||
generateMouseEvent(m_touchDown);
|
||||
}
|
||||
}
|
||||
|
||||
void WebTouchNavigation::generateMouseEvent(const WebTouchEvent &touchEvent)
|
||||
{
|
||||
if (!touchEvent.m_editable && m_suppressMouseEvents) //do not suppress mouse events for input box, etc.
|
||||
return;
|
||||
|
||||
if (touchEvent.m_type == QEvent::GraphicsSceneMousePress
|
||||
|| touchEvent.m_type == QEvent::GraphicsSceneMouseMove
|
||||
|| touchEvent.m_type == QEvent::GraphicsSceneMouseRelease) {
|
||||
QGraphicsSceneMouseEvent ievmm(touchEvent.m_type);
|
||||
ievmm.setPos(touchEvent.m_pos);
|
||||
ievmm.setScenePos(touchEvent.m_scenePos);
|
||||
ievmm.setScreenPos(touchEvent.m_screenPos);
|
||||
ievmm.setButtonDownPos(touchEvent.m_button, touchEvent.m_buttonDownPos);
|
||||
ievmm.setButtonDownScenePos( touchEvent.m_button, touchEvent.m_buttonDownScenePos);
|
||||
ievmm.setButtonDownScreenPos( touchEvent.m_button, touchEvent.m_buttonDownScreenPos);
|
||||
ievmm.setLastPos(touchEvent.m_lastPos);
|
||||
ievmm.setLastScenePos(touchEvent.m_lastScenePos);
|
||||
ievmm.setLastScreenPos(touchEvent.m_lastScreenPos);
|
||||
ievmm.setButtons(touchEvent.m_buttons);
|
||||
ievmm.setButton( touchEvent.m_button);
|
||||
ievmm.setModifiers(touchEvent.m_modifier);
|
||||
m_webPage->event(&ievmm);
|
||||
} else {
|
||||
bool inputMethodEnabled = static_cast<QWidget*>(m_viewObject)->testAttribute(Qt::WA_InputMethodEnabled);
|
||||
if (touchEvent.m_type == QEvent::MouseButtonRelease && inputMethodEnabled) {
|
||||
if (touchEvent.m_editable) {
|
||||
QEvent rsipevent(QEvent::RequestSoftwareInputPanel);
|
||||
QCoreApplication::sendEvent(static_cast<QWidget*>(m_viewObject), &rsipevent);
|
||||
} else {
|
||||
static_cast<QWidget*>(m_viewObject)->setAttribute(Qt::WA_InputMethodEnabled, false); // disable input methods if user didn't tap on editable area.
|
||||
}
|
||||
}
|
||||
|
||||
QMouseEvent ievmm(touchEvent.m_type, touchEvent.m_pos, touchEvent.m_button, touchEvent.m_buttons, touchEvent.m_modifier);
|
||||
m_webPage->event(&ievmm);
|
||||
|
||||
if (touchEvent.m_type == QEvent::MouseButtonRelease && inputMethodEnabled)
|
||||
static_cast<QWidget*>(m_viewObject)->setAttribute(Qt::WA_InputMethodEnabled, inputMethodEnabled); // re-enable input methods if disabled
|
||||
}
|
||||
}
|
||||
|
||||
class WebNavigation : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WebNavigation(QObject *parent, QWebPage *webPage);
|
||||
virtual ~WebNavigation();
|
||||
|
||||
private:
|
||||
QObject *m_viewObject;
|
||||
QWebPage *m_webPage;
|
||||
WebTouchNavigation *m_webTouchNavigation;
|
||||
};
|
||||
|
||||
WebNavigation::WebNavigation(QObject *parent, QWebPage *webPage)
|
||||
: m_viewObject(parent)
|
||||
, m_webPage(webPage)
|
||||
, m_webTouchNavigation(0)
|
||||
{
|
||||
m_webTouchNavigation = new WebTouchNavigation(m_viewObject, m_webPage);
|
||||
m_viewObject->installEventFilter(m_webTouchNavigation);
|
||||
}
|
||||
|
||||
WebNavigation::~WebNavigation()
|
||||
{
|
||||
delete m_webTouchNavigation;
|
||||
}
|
||||
|
||||
class NavigationControllerPrivate
|
||||
{
|
||||
public:
|
||||
NavigationControllerPrivate(QWidget *parent, QGraphicsWebView *webView);
|
||||
~NavigationControllerPrivate();
|
||||
|
||||
QWebPage *m_webPage;
|
||||
QWidget *m_webWidget;
|
||||
QGraphicsWebView *m_graphicsWebView;
|
||||
WebNavigation *m_webNavigation;
|
||||
};
|
||||
|
||||
NavigationControllerPrivate::NavigationControllerPrivate(QWidget *parent, QGraphicsWebView *webView)
|
||||
: m_webPage(0)
|
||||
, m_webWidget(0)
|
||||
, m_graphicsWebView(webView)
|
||||
, m_webNavigation(0)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
m_graphicsWebView->setAcceptTouchEvents(true);
|
||||
m_webPage = new QWebPage;
|
||||
m_graphicsWebView->setPage(m_webPage);
|
||||
m_webNavigation = new WebNavigation(m_graphicsWebView, m_webPage);
|
||||
m_webNavigation->setParent(m_graphicsWebView);
|
||||
}
|
||||
|
||||
NavigationControllerPrivate::~NavigationControllerPrivate()
|
||||
{
|
||||
if (m_webNavigation)
|
||||
delete m_webNavigation;
|
||||
if (m_webPage)
|
||||
delete m_webPage;
|
||||
if (m_graphicsWebView)
|
||||
delete m_graphicsWebView;
|
||||
}
|
||||
|
||||
class NavigationController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NavigationController(QWidget *parent, QGraphicsWebView *webView);
|
||||
virtual ~NavigationController();
|
||||
|
||||
QWidget *webWidget() const;
|
||||
QWebPage* webPage() const;
|
||||
QGraphicsWebView* graphicsWebView() const;
|
||||
|
||||
signals:
|
||||
void pauseNavigation();
|
||||
void resumeNavigation();
|
||||
|
||||
private:
|
||||
class NavigationControllerPrivate *m_d;
|
||||
};
|
||||
|
||||
NavigationController::NavigationController(QWidget *parent, QGraphicsWebView *webView)
|
||||
: m_d(new NavigationControllerPrivate(parent, webView))
|
||||
{
|
||||
}
|
||||
|
||||
NavigationController::~NavigationController()
|
||||
{
|
||||
delete m_d;
|
||||
}
|
||||
|
||||
QWebPage* NavigationController::webPage() const
|
||||
{
|
||||
return m_d->m_webPage;
|
||||
}
|
||||
|
||||
QGraphicsWebView* NavigationController::graphicsWebView() const
|
||||
{
|
||||
return m_d->m_graphicsWebView;
|
||||
}
|
||||
#endif // TOUCH_OPTIMIZED_NAVIGATION
|
||||
|
||||
class Html5ApplicationViewerPrivate : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -38,6 +922,9 @@ signals:
|
||||
|
||||
public:
|
||||
QGraphicsWebView *m_webView;
|
||||
#ifdef TOUCH_OPTIMIZED_NAVIGATION
|
||||
NavigationController *m_controller;
|
||||
#endif // TOUCH_OPTIMIZED_NAVIGATION
|
||||
};
|
||||
|
||||
Html5ApplicationViewerPrivate::Html5ApplicationViewerPrivate(QWidget *parent)
|
||||
@@ -50,7 +937,14 @@ Html5ApplicationViewerPrivate::Html5ApplicationViewerPrivate(QWidget *parent)
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
m_webView = new QGraphicsWebView;
|
||||
m_webView->setAcceptTouchEvents(true);
|
||||
m_webView->setAcceptHoverEvents(false);
|
||||
setAttribute(Qt::WA_AcceptTouchEvents, true);
|
||||
scene->addItem(m_webView);
|
||||
scene->setActiveWindow(m_webView);
|
||||
#ifdef TOUCH_OPTIMIZED_NAVIGATION
|
||||
m_controller = new NavigationController(parent, m_webView);
|
||||
#endif // TOUCH_OPTIMIZED_NAVIGATION
|
||||
connect(m_webView->page()->mainFrame(),
|
||||
SIGNAL(javaScriptWindowObjectCleared()), SLOT(addToJavaScript()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user