forked from qt-creator/qt-creator
Make the html5applicationviewer class much less cluttered by splitting out all the touch navigation classes into separate files in a subdirectory. This should make it much more maintainable.
400 lines
14 KiB
C++
400 lines
14 KiB
C++
/*
|
|
This file was generated by the Html5 Application wizard of Qt Creator.
|
|
Html5ApplicationViewer is a convenience class containing mobile device specific
|
|
code such as screen orientation handling.
|
|
It is recommended not to modify this file, since newer versions of Qt Creator
|
|
may offer an updated version of it.
|
|
*/
|
|
|
|
#include <QCoreApplication>
|
|
#include <QPoint>
|
|
#include <QWidget>
|
|
#include <QWebPage>
|
|
#include <QWebFrame>
|
|
#include <QWebElement>
|
|
#include "webtouchnavigation.h"
|
|
|
|
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
|
|
}
|
|
}
|